1 /*
2 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "js_ability_impl.h"
17 #include "ace_event_error_code.h"
18 #include "ace_log.h"
19 #include "component.h"
20 #include "component_utils.h"
21 #include "fatal_handler.h"
22 #include "js_app_context.h"
23 #include "js_app_environment.h"
24 #include "js_profiler.h"
25 #include "module_manager.h"
26 #include "presets/localization_module.h"
27 #include "presets/timer_module.h"
28 #include "securec.h"
29 #ifdef _MINI_MULTI_TASKS_
30 #include "string_util.h"
31 #endif
32
33 namespace OHOS {
34 namespace ACELite {
35 #ifdef _MINI_MULTI_TASKS_
36 namespace {
37 constexpr char EMPTY_OBJECT_JSON_STRING[] = "{}";
38 constexpr char ERROR_CODE_KEY[] = "errorCode";
39 } // namespace
40 #endif
InitEnvironment(const char * const abilityPath,const char * const bundleName,uint16_t token)41 void JSAbilityImpl::InitEnvironment(const char * const abilityPath, const char * const bundleName, uint16_t token)
42 {
43 if ((abilityPath == nullptr) || strlen(abilityPath) == 0 || (bundleName == nullptr) || strlen(bundleName) == 0) {
44 HILOG_ERROR(HILOG_MODULE_ACE, "invalid input parameters");
45 return;
46 }
47
48 if (isEnvInit_) {
49 HILOG_ERROR(HILOG_MODULE_ACE, "already initialized, return");
50 return;
51 }
52 // init engine && js fwk
53 JsAppEnvironment *appJsEnv = JsAppEnvironment::GetInstance();
54 appContext_ = JsAppContext::GetInstance();
55 // check if we should use snapshot mode, do this before everything,
56 // but after debugger config is set
57 appJsEnv->InitRuntimeMode();
58 appContext_->SetCurrentAbilityInfo(abilityPath, bundleName, token);
59 appContext_->SetTopJSAbilityImpl(this);
60 appJsEnv->InitJsFramework();
61 appContext_->LoadApiVersion();
62
63 // initialize js object after engine started up
64 abilityModel_ = UNDEFINED;
65 nativeElement_ = UNDEFINED;
66 isEnvInit_ = true;
67
68 // relocate app.js fullpath
69 const char * const appJSFileName = (appJsEnv->IsSnapshotMode()) ? "app.bc" : "app.js";
70 char *fileFullPath = RelocateJSSourceFilePath(abilityPath, appJSFileName);
71 if (fileFullPath == nullptr) {
72 HILOG_ERROR(HILOG_MODULE_ACE, "relocate js file failed");
73 ACE_ERROR_CODE_PRINT(EXCE_ACE_FWK_LAUNCH_FAILED, EXCE_ACE_APP_ENTRY_MISSING);
74 return;
75 }
76
77 appContext_->SetAbilityState(TopAbilityState::ABILITY_INITIALIZED);
78 START_TRACING(APP_CODE_EVAL);
79 MarkAppViewModelEvaling(true);
80 abilityModel_ = appContext_->Eval(fileFullPath, strlen(fileFullPath), true); // generate global.$app js object
81 MarkAppViewModelEvaling(false);
82 STOP_TRACING();
83
84 ace_free(fileFullPath);
85 fileFullPath = nullptr;
86 router_ = new Router();
87 if (router_ == nullptr) {
88 HILOG_ERROR(HILOG_MODULE_ACE, "malloc router heap memory failed.");
89 return;
90 }
91 }
92
CleanUp()93 void JSAbilityImpl::CleanUp()
94 {
95 if (appContext_ == nullptr) {
96 return;
97 }
98 appContext_->SetAbilityState(TopAbilityState::ABILITY_DESTROYING);
99 if (!isEnvInit_) {
100 return;
101 }
102 START_TRACING(APP_ON_DESTROY);
103 // clean up user's js
104 InvokeOnDestroy();
105 STOP_TRACING();
106
107 // delete router's resources about page
108 if (router_ != nullptr) {
109 delete router_;
110 router_ = nullptr;
111 }
112 #ifdef _MINI_MULTI_TASKS_
113 if (pageInfo_ != nullptr) {
114 ACE_FREE(pageInfo_);
115 pageInfo_ = nullptr;
116 }
117 #endif
118 TimersModule::Clear();
119 LocalModule::Clear();
120
121 appContext_->ClearContext();
122
123 ModuleManager::GetInstance()->OnTerminate();
124 JsAppEnvironment::GetInstance()->Cleanup();
125 isEnvInit_ = false;
126 appContext_->SetAbilityState(TopAbilityState::ABILITY_DESTROYED);
127 OUTPUT_TRACE();
128 }
129
DeliverCreate(const char * param)130 void JSAbilityImpl::DeliverCreate(const char *param)
131 {
132 if (appContext_ == nullptr) {
133 return;
134 }
135 appContext_->SetAbilityState(TopAbilityState::ABILITY_LAUNCHING);
136 START_TRACING(APP_ON_CREATE);
137 // call InvokeOnCreate
138 InvokeOnCreate();
139 STOP_TRACING();
140 // if we have done the render or not initialized yet, don't call render
141 if (rendered_) {
142 ACE_ERROR_CODE_PRINT(EXCE_ACE_FWK_LAUNCH_FAILED, EXCE_ACE_APP_RENDER_FAILED);
143 return;
144 }
145
146 #ifndef _MINI_MULTI_TASKS_
147 // call render to setup user interface
148 jerry_value_t object = UNDEFINED;
149 if (param == nullptr) {
150 object = jerry_create_object();
151 JerrySetStringProperty(object, ROUTER_PAGE_URI, PATH_DEFAULT);
152 } else {
153 object = jerry_json_parse(reinterpret_cast<const jerry_char_t *>(param), strlen(param));
154 }
155 if (router_) {
156 jerry_release_value(router_->Replace(object, false));
157 rendered_ = true;
158 }
159 jerry_release_value(object);
160 appContext_->SetAbilityState(TopAbilityState::ABILITY_LAUNCHDONE);
161 #endif
162 }
163
164 #ifdef _MINI_MULTI_TASKS_
OnRestoreData(AbilitySlite::AbilitySavedData * abilitySavedData)165 void JSAbilityImpl::OnRestoreData(AbilitySlite::AbilitySavedData *abilitySavedData)
166 {
167 if (pageInfo_ != nullptr) {
168 ACE_FREE(pageInfo_);
169 pageInfo_ = nullptr;
170 }
171
172 if (abilitySavedData == nullptr) {
173 InvokeOnRestoreData(EMPTY_OBJECT_JSON_STRING, AbilitySlite::SavedResultCode::SAVED_RESULT_NO_DATA);
174 return;
175 }
176
177 // deal user saved data
178 char *userData = StringUtil::Malloc(AbilitySlite::SAVED_DATA_LIMIT);
179 if (userData == nullptr) {
180 InvokeOnRestoreData(EMPTY_OBJECT_JSON_STRING, AbilitySlite::SavedResultCode::SAVED_RESULT_ALLOC_ERROR);
181 return;
182 }
183
184 uint16_t userDataLen = AbilitySlite::SAVED_DATA_LIMIT;
185 AbilitySlite::SavedResultCode userDataRet = abilitySavedData->GetUserSavedData(
186 reinterpret_cast<void *>(userData), AbilitySlite::SAVED_DATA_LIMIT, &userDataLen);
187 if (userDataRet != AbilitySlite::SavedResultCode::SAVED_RESULT_OK) {
188 InvokeOnRestoreData(EMPTY_OBJECT_JSON_STRING, userDataRet);
189 ACE_FREE(userData);
190 return;
191 }
192
193 // deal framework saved data pageInfo
194 pageInfo_ = StringUtil::Malloc(AbilitySlite::SAVED_DATA_LIMIT);
195 if (pageInfo_ == nullptr) {
196 InvokeOnRestoreData(EMPTY_OBJECT_JSON_STRING, AbilitySlite::SavedResultCode::SAVED_RESULT_NO_DATA);
197 return;
198 }
199 uint16_t pageInfoLen = AbilitySlite::SAVED_DATA_LIMIT;
200 AbilitySlite::SavedResultCode pageInfoRet = abilitySavedData->GetSavedData(
201 reinterpret_cast<void*>(pageInfo_), AbilitySlite::SAVED_DATA_LIMIT, &pageInfoLen);
202 if (pageInfoRet != AbilitySlite::SavedResultCode::SAVED_RESULT_OK) {
203 ACE_FREE(pageInfo_);
204 pageInfo_ = nullptr;
205 InvokeOnRestoreData(EMPTY_OBJECT_JSON_STRING, AbilitySlite::SavedResultCode::SAVED_RESULT_NO_DATA);
206 return;
207 }
208
209 // call js function
210 bool success = InvokeOnRestoreData(userData, AbilitySlite::SavedResultCode::SAVED_RESULT_OK);
211 ACE_FREE(userData);
212 if (!success) {
213 // if restore data failed, not restore page
214 ACE_FREE(pageInfo_);
215 pageInfo_ = nullptr;
216 return;
217 }
218 }
219 #endif
220
Show()221 void JSAbilityImpl::Show()
222 {
223 if (appContext_ == nullptr) {
224 return;
225 }
226 if (router_ == nullptr) {
227 HILOG_ERROR(HILOG_MODULE_ACE, "no router instance to perform the show request");
228 return;
229 }
230 #ifdef _MINI_MULTI_TASKS_
231 if (pageInfo_ == nullptr) {
232 GotoPage(PATH_DEFAULT);
233 } else {
234 GotoPage(pageInfo_);
235 ACE_FREE(pageInfo_);
236 pageInfo_ = nullptr;
237 }
238 #endif
239 appContext_->SetAbilityState(TopAbilityState::ABILITY_SHOWING);
240 router_->Show();
241 FatalHandler::GetInstance().NotifyVisibleStatusChange(true);
242 appContext_->SetAbilityState(TopAbilityState::ABILITY_SHOWN);
243 }
244
245 #ifdef _MINI_MULTI_TASKS_
OnSaveData(AbilitySlite::AbilitySavedData * abilitySavedData)246 void JSAbilityImpl::OnSaveData(AbilitySlite::AbilitySavedData *abilitySavedData)
247 {
248 if (abilitySavedData == nullptr) {
249 return;
250 }
251
252 if (IS_UNDEFINED(abilityModel_) || (router_ == nullptr)) {
253 HILOG_ERROR(HILOG_MODULE_ACE, "view model or router is invalid when call user's onSaveData");
254 abilitySavedData->SetSavedResultCode(AbilitySlite::SavedResultCode::SAVED_RESULT_NO_DATA);
255 return;
256 }
257 // get js function
258 jerry_value_t onSaveFunction = jerryx_get_property_str(abilityModel_, ABILITY_LIFECYCLE_CALLBACK_ON_SAVE_DATA);
259 if (IS_UNDEFINED(onSaveFunction)) {
260 // user does not set onRestore method
261 abilitySavedData->SetSavedResultCode(AbilitySlite::SavedResultCode::SAVED_RESULT_NO_DATA);
262 return;
263 }
264
265 JSValue jerryUserData = jerry_create_object();
266 jerry_value_t args[1] = { jerryUserData };
267 CallJSFunctionAutoRelease(onSaveFunction, abilityModel_, args, 1);
268
269 jerry_value_t dataJson = jerry_json_stringify(args[0]);
270
271 char *userData = MallocStringOf(dataJson);
272 ReleaseJerryValue(dataJson, jerryUserData, onSaveFunction, VA_ARG_END_FLAG);
273
274 if (userData == nullptr) {
275 abilitySavedData->SetSavedResultCode(AbilitySlite::SavedResultCode::SAVED_RESULT_ALLOC_ERROR);
276 return;
277 }
278
279 AbilitySlite::SavedResultCode retCode =
280 abilitySavedData->SetUserSavedData(reinterpret_cast<const void *>(userData), strlen(userData) + 1);
281 ACE_FREE(userData);
282 if (retCode != AbilitySlite::SavedResultCode::SAVED_RESULT_OK) {
283 abilitySavedData->SetSavedResultCode(retCode);
284 return;
285 }
286
287 const char* uri = appContext_->GetCurrentUri();
288 if (uri == nullptr) {
289 retCode =
290 abilitySavedData->SetSavedData(reinterpret_cast<const void *>(PATH_DEFAULT), strlen(PATH_DEFAULT) + 1);
291 } else {
292 retCode = abilitySavedData->SetSavedData(reinterpret_cast<const void *>(uri), strlen(uri) + 1);
293 }
294 if (retCode != AbilitySlite::SavedResultCode::SAVED_RESULT_OK) {
295 abilitySavedData->SetSavedResultCode(retCode);
296 return;
297 }
298 abilitySavedData->SetSavedResultCode(AbilitySlite::SavedResultCode::SAVED_RESULT_OK);
299 }
300 #endif
301
Hide() const302 void JSAbilityImpl::Hide() const
303 {
304 if (appContext_ == nullptr) {
305 return;
306 }
307 if (router_ == nullptr) {
308 HILOG_ERROR(HILOG_MODULE_ACE, "no router instance to perform the hide request");
309 return;
310 }
311 appContext_->SetAbilityState(TopAbilityState::ABILITY_HIDING);
312 router_->Hide();
313 FatalHandler::GetInstance().NotifyVisibleStatusChange(false);
314 appContext_->SetAbilityState(TopAbilityState::ABILITY_HIDDEN);
315 }
316
NotifyBackPressed() const317 void JSAbilityImpl::NotifyBackPressed() const
318 {
319 if (appContext_ == nullptr) {
320 return;
321 }
322
323 InvokeOnBackPressed();
324
325 appContext_->TerminateAbility();
326 }
327
InvokeOnCreate() const328 void JSAbilityImpl::InvokeOnCreate() const
329 {
330 if (IS_UNDEFINED(abilityModel_)) {
331 HILOG_ERROR(HILOG_MODULE_ACE, "view model is undefined when call user's init");
332 return;
333 }
334 jerry_value_t onCreateFunction = jerryx_get_property_str(abilityModel_, ABILITY_LIFECYCLE_CALLBACK_ON_CREATE);
335 if (IS_UNDEFINED(onCreateFunction)) {
336 // user does not set onInit method
337 return;
338 }
339 CallJSFunctionAutoRelease(onCreateFunction, abilityModel_, nullptr, 0);
340 jerry_release_value(onCreateFunction);
341 }
342
343 #ifdef _MINI_MULTI_TASKS_
344 // call js function
InvokeOnRestoreData(const char * userData,AbilitySlite::SavedResultCode retCode) const345 bool JSAbilityImpl::InvokeOnRestoreData(const char* userData, AbilitySlite::SavedResultCode retCode) const
346 {
347 // call render to setup user interface
348 if (userData == nullptr) {
349 return false;
350 }
351 if (IS_UNDEFINED(abilityModel_)) {
352 HILOG_ERROR(HILOG_MODULE_ACE, "view model is undefined when call user's onRestoreData");
353 return false;
354 }
355
356 jerry_value_t args[1];
357 args[0] = jerry_json_parse(reinterpret_cast<const jerry_char_t *>(userData), strlen(userData));
358
359 int16_t errorCode = 0;
360 switch (retCode) {
361 case AbilitySlite::SavedResultCode::SAVED_RESULT_OK:
362 errorCode = 0;
363 break;
364 case AbilitySlite::SavedResultCode::SAVED_RESULT_EXCEED_UPPER_LIMIT:
365 errorCode = 1;
366 break;
367 default:
368 errorCode = 2; // 2: default
369 break;
370 }
371 jerry_value_t propName = jerry_create_string(reinterpret_cast<const jerry_char_t *>(ERROR_CODE_KEY));
372 jerry_value_t propValue = jerry_create_number(errorCode);
373 jerry_value_t setResult = jerry_set_property(args[0], propName, propValue);
374
375 jerry_value_t onRestoreDataFunction =
376 jerryx_get_property_str(abilityModel_, ABILITY_LIFECYCLE_CALLBACK_ON_RESTORE_DATA);
377 if (IS_UNDEFINED(onRestoreDataFunction)) {
378 // user does not set onRestore method
379 ReleaseJerryValue(propName, propValue, setResult, args[0], VA_ARG_END_FLAG);
380 return false;
381 }
382 CallJSFunctionAutoRelease(onRestoreDataFunction, abilityModel_, args, 1); // todo lizhiqi args release?
383 ReleaseJerryValue(propName, propValue, setResult, args[0], onRestoreDataFunction, VA_ARG_END_FLAG);
384 return true;
385 }
386 #endif
387
InvokeOnDestroy() const388 void JSAbilityImpl::InvokeOnDestroy() const
389 {
390 InvokeMethodWithoutParameter(ABILITY_LIFECYCLE_CALLBACK_ON_DESTROY);
391 // release FeatureAbility object
392 jerry_release_value(abilityModel_);
393 }
394
InvokeOnBackPressed() const395 void JSAbilityImpl::InvokeOnBackPressed() const
396 {
397 InvokeMethodWithoutParameter(BACK_PRESSED_NAME);
398 }
399
InvokeMethodWithoutParameter(const char * const name) const400 void JSAbilityImpl::InvokeMethodWithoutParameter(const char * const name) const
401 {
402 if (FatalHandler::GetInstance().IsJSRuntimeFatal()) {
403 // can not continue to involve any JS object creating on engine in case runtime fatal
404 return;
405 }
406 if ((name == nullptr) || strlen(name) == 0) {
407 return;
408 }
409
410 jerry_value_t function = jerryx_get_property_str(abilityModel_, name);
411 if (IS_UNDEFINED(function)) {
412 // user does not set onBackpress method
413 return;
414 }
415 CallJSFunctionAutoRelease(function, abilityModel_, nullptr, 0);
416 jerry_release_value(function);
417 }
418
MarkAppViewModelEvaling(bool evaling) const419 void JSAbilityImpl::MarkAppViewModelEvaling(bool evaling) const
420 {
421 jerry_value_t propNameValue = jerry_create_string(reinterpret_cast<const jerry_char_t *>("__appVing__"));
422 jerry_value_t globalObject = jerry_get_global_object();
423 jerry_value_t evalingValue = jerry_create_boolean(evaling);
424 jerry_release_value(jerry_set_property(globalObject, propNameValue, evalingValue));
425 jerry_release_value(evalingValue);
426 jerry_release_value(propNameValue);
427 jerry_release_value(globalObject);
428 }
429
430 #ifdef _MINI_MULTI_TASKS_
GotoPage(const char * uri)431 void JSAbilityImpl::GotoPage(const char* uri)
432 {
433 if ((uri == nullptr) || (router_ == nullptr)) {
434 return;
435 }
436
437 jerry_value_t object = jerry_create_object();
438 JerrySetStringProperty(object, ROUTER_PAGE_URI, uri);
439 jerry_release_value(router_->Replace(object, false));
440 jerry_release_value(object);
441
442 rendered_ = true;
443 }
444 #endif
445 } // namespace ACELite
446 } // namespace OHOS
447