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