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_app_context.h"
17 #include "ace_event_error_code.h"
18 #include "ace_log.h"
19 #if (defined(__LINUX__) || defined(__LITEOS_A__))
20 #include "ace_ability.h"
21 #endif
22 #if (FEATURE_API_VERSION == 1)
23 #include "bundle_manager.h"
24 #endif // FEATURE_API_VERSION
25 #include "component_factory.h"
26 #include "component_utils.h"
27 #include "fatal_handler.h"
28 #include "js_app_environment.h"
29 #include "js_profiler.h"
30 #include "platform_adapter.h"
31 #include "product_adapter.h"
32 #include "securec.h"
33 #include "string_util.h"
34 #include "task_manager.h"
35 #include "ui_view_group.h"
36 #if (OHOS_ACELITE_PRODUCT_WATCH != 1)
37 #include "ability_env.h"
38 #endif
39 
40 namespace OHOS {
41 namespace ACELite {
42 constexpr char URI_PREFIX_DATA[] = "internal://app";
43 constexpr uint8_t URI_PREFIX_DATA_LENGTH = 14;
ClearContext()44 void JsAppContext::ClearContext()
45 {
46     // reset current ability path and uuid
47     ReleaseAbilityInfo();
48 }
49 
50 // check byte code file snapshot version is OK with the current
CheckSnapshotVersion(const char * bcFileContent,uint32_t contentLength) const51 void JsAppContext::CheckSnapshotVersion(const char *bcFileContent, uint32_t contentLength) const
52 {
53     // this is part of engine struct definations
54     typedef struct {
55         uint32_t magic; // four byte magic number
56         uint32_t version; // version number
57     } JerrySnapshotHeaderT;
58     if ((bcFileContent == nullptr) || (contentLength == 0) || (contentLength <= sizeof(JerrySnapshotHeaderT))) {
59         return;
60     }
61     const uint8_t *snapshotData = reinterpret_cast<const uint8_t *>(bcFileContent);
62     const JerrySnapshotHeaderT *headerP = reinterpret_cast<const JerrySnapshotHeaderT *>(snapshotData);
63     // JERRY_SNAPSHOT_VERSION is defined in jerryscript-snapshot.h
64     if (headerP->version != JERRY_SNAPSHOT_VERSION) {
65         HILOG_ERROR(HILOG_MODULE_ACE, "invalid snapshot version[%{public}d]", headerP->version);
66     }
67 }
68 
69 /**
70  * return value should be released by caller when it's not used
71  */
Eval(char * fullPath,size_t fullPathLength,bool isAppEval) const72 jerry_value_t JsAppContext::Eval(char *fullPath, size_t fullPathLength, bool isAppEval) const
73 {
74     if ((fullPath == nullptr) || (fullPathLength == 0)) {
75         HILOG_ERROR(HILOG_MODULE_ACE, "Failed to eval js code cause by empty JavaScript script.");
76         ACE_ERROR_CODE_PRINT(EXCE_ACE_ROUTER_REPLACE_FAILED, EXCE_ACE_PAGE_INDEX_MISSING);
77         return UNDEFINED;
78     }
79 
80     uint32_t contentLength = 0;
81     START_TRACING(PAGE_CODE_LOAD);
82     bool isSnapshotMode = JsAppEnvironment::GetInstance()->IsSnapshotMode();
83     char *jsCode = EvaluateFile(isSnapshotMode, contentLength, fullPath, fullPathLength);
84     STOP_TRACING();
85     if ((jsCode == nullptr) || (contentLength > FILE_CONTENT_LENGTH_MAX)) {
86         HILOG_ERROR(HILOG_MODULE_ACE, "empty js file or length is incorrect, eval user code failed");
87         ACE_ERROR_CODE_PRINT(EXCE_ACE_ROUTER_REPLACE_FAILED, EXCE_ACE_PAGE_FILE_READ_FAILED);
88         ACE_FREE(jsCode);
89         return UNDEFINED;
90     }
91 
92     START_TRACING(PAGE_CODE_EVAL);
93     jerry_value_t viewModel = UNDEFINED;
94     if (isSnapshotMode) {
95         CheckSnapshotVersion(jsCode, contentLength);
96         const uint32_t *snapshotContent = reinterpret_cast<const uint32_t *>(jsCode);
97         viewModel = jerry_exec_snapshot(snapshotContent, contentLength, 0, 1);
98     } else {
99         const jerry_char_t *jsScript = reinterpret_cast<const jerry_char_t *>(jsCode);
100         jerry_value_t retValue = jerry_parse(reinterpret_cast<const jerry_char_t *>(fullPath), fullPathLength,
101                                              jsScript, contentLength, JERRY_PARSE_NO_OPTS);
102         if (jerry_value_is_error(retValue)) {
103             ACE_ERROR_CODE_PRINT(EXCE_ACE_ROUTER_REPLACE_FAILED, EXCE_ACE_PAGE_JS_EVAL_FAILED);
104             PrintErrorMessage(retValue);
105             // free js code buffer
106             ace_free(jsCode);
107             jsCode = nullptr;
108             jerry_release_value(retValue);
109             STOP_TRACING();
110             return UNDEFINED;
111         }
112         viewModel = jerry_run(retValue);
113         jerry_release_value(retValue);
114     }
115 
116     STOP_TRACING();
117     // free js code buffer
118     ace_free(jsCode);
119     jsCode = nullptr;
120 
121     if (jerry_value_is_error(viewModel)) {
122         ACE_ERROR_CODE_PRINT(EXCE_ACE_ROUTER_REPLACE_FAILED, EXCE_ACE_PAGE_JS_EVAL_FAILED);
123         PrintErrorMessage(viewModel);
124         jerry_release_value(viewModel);
125         return UNDEFINED;
126     }
127 
128     SetGlobalNamedProperty(isAppEval, viewModel);
129     return viewModel;
130 }
131 
EvaluateFile(bool & isSnapshotMode,uint32_t & outLength,char * fullPath,size_t fullPathLength) const132 char *JsAppContext::EvaluateFile(bool &isSnapshotMode,
133                                  uint32_t &outLength,
134                                  char *fullPath,
135                                  size_t fullPathLength) const
136 {
137     if (fullPath == nullptr || fullPathLength == 0) {
138         return nullptr;
139     }
140     const uint8_t fileSuffixLength = 3; // file suffix is fixed, .js or .bc
141     size_t filePathLen = strlen(fullPath);
142     if ((filePathLen == 0) || (filePathLen != fullPathLength) || (fullPathLength < fileSuffixLength)) {
143         return nullptr;
144     }
145     outLength = 0;
146     char *jsCode = ReadFile(fullPath, outLength, isSnapshotMode);
147     if ((jsCode != nullptr) && (outLength <= FILE_CONTENT_LENGTH_MAX)) {
148         // read successfully
149         return jsCode;
150     }
151     // make sure the memory is freed
152     ACE_FREE(jsCode);
153 
154     const char * const anotherSuffx = isSnapshotMode ? ".js" : ".bc";
155     // change file suffix to another mode file
156     if (strcpy_s((fullPath + (fullPathLength - fileSuffixLength)), (fileSuffixLength + 1), anotherSuffx) != EOK) {
157         return nullptr;
158     }
159     // snapshot mode changed to another
160     isSnapshotMode = !isSnapshotMode;
161     HILOG_ERROR(HILOG_MODULE_ACE, "JS mode changed unexpected [%{public}d]", isSnapshotMode);
162     jsCode = ReadFile(fullPath, outLength, isSnapshotMode);
163     return jsCode;
164 }
165 
SetGlobalNamedProperty(bool isAppEval,jerry_value_t viewModel) const166 void JsAppContext::SetGlobalNamedProperty(bool isAppEval, jerry_value_t viewModel) const
167 {
168     jerry_value_t globalObject = jerry_get_global_object();
169     if (isAppEval) {
170         JerrySetNamedProperty(globalObject, ATTR_APP, viewModel);
171     } else {
172         JerrySetNamedProperty(globalObject, ATTR_ROOT, viewModel);
173     }
174     jerry_release_value(globalObject);
175 }
176 
Render(jerry_value_t viewModel) const177 jerry_value_t JsAppContext::Render(jerry_value_t viewModel) const
178 {
179     if (jerry_value_is_error(viewModel)) {
180         HILOG_ERROR(HILOG_MODULE_ACE, "Failed to render app cause by render error.");
181         return UNDEFINED;
182     }
183 
184     if (jerry_value_is_undefined(viewModel)) {
185         HILOG_ERROR(HILOG_MODULE_ACE, "Nothing to render as it is undefined.");
186         return UNDEFINED;
187     }
188 
189     jerry_value_t renderFunction = jerryx_get_property_str(viewModel, ATTR_RENDER);
190     if (jerry_value_is_undefined(renderFunction)) {
191         ACE_ERROR_CODE_PRINT(EXCE_ACE_ROUTER_REPLACE_FAILED, EXCE_ACE_PAGE_NO_RENDER_FUNC);
192         return UNDEFINED;
193     }
194     jerry_value_t nativeElement = CallJSFunction(renderFunction, viewModel, nullptr, 0);
195     if (jerry_value_is_undefined(nativeElement)) {
196         ACE_ERROR_CODE_PRINT(EXCE_ACE_ROUTER_REPLACE_FAILED, EXCE_ACE_PAGE_RENDER_FAILED);
197     }
198     jerry_release_value(renderFunction);
199     return nativeElement;
200 }
201 
TerminateAbility() const202 void JsAppContext::TerminateAbility() const
203 {
204     if (currentToken_ == 0) {
205         // if no running js ability, drop
206         return;
207     }
208     FatalHandler::GetInstance().SetExitingFlag(true);
209     Terminate(currentToken_);
210 }
211 
SetCurrentAbilityInfo(const char * const abilityPath,const char * const bundleName,uint16_t token)212 void JsAppContext::SetCurrentAbilityInfo(const char * const abilityPath, const char * const bundleName, uint16_t token)
213 {
214     // release old first
215     ReleaseAbilityInfo();
216 
217     if (abilityPath != nullptr) {
218         size_t abilityPathLen = strlen(abilityPath);
219         if ((abilityPathLen > 0) && (abilityPathLen < PATH_LENGTH_MAX)) {
220             currentAbilityPath_ = static_cast<char *>(ace_malloc(abilityPathLen + 1));
221             if (currentAbilityPath_ == nullptr) {
222                 HILOG_ERROR(HILOG_MODULE_ACE, "malloc buffer for current ability path failed");
223                 return;
224             }
225             if (memcpy_s(currentAbilityPath_, abilityPathLen, abilityPath, abilityPathLen) != 0) {
226                 ace_free(currentAbilityPath_);
227                 currentAbilityPath_ = nullptr;
228                 return;
229             }
230             currentAbilityPath_[abilityPathLen] = 0;
231         }
232     }
233 
234     if (bundleName != nullptr) {
235         size_t bundleNameLen = strlen(bundleName);
236         if ((bundleNameLen > 0) && (bundleNameLen < NAME_LENGTH_MAX)) {
237             currentBundleName_ = static_cast<char *>(ace_malloc(bundleNameLen + 1));
238             if (currentBundleName_ == nullptr) {
239                 ace_free(currentAbilityPath_);
240                 currentAbilityPath_ = nullptr;
241                 HILOG_ERROR(HILOG_MODULE_ACE, "malloc buffer for current uuid failed");
242                 return;
243             }
244             if (memcpy_s(currentBundleName_, bundleNameLen, bundleName, bundleNameLen) != 0) {
245                 ace_free(currentAbilityPath_);
246                 currentAbilityPath_ = nullptr;
247                 ace_free(currentBundleName_);
248                 currentBundleName_ = nullptr;
249                 return;
250             }
251             currentBundleName_[bundleNameLen] = 0;
252         }
253     }
254 
255     currentToken_ = token;
256 }
257 
SetCurrentJsPath(const char * const jsPath)258 void JsAppContext::SetCurrentJsPath(const char * const jsPath)
259 {
260     // release old first
261     if (currentJsPath_) {
262         ace_free(currentJsPath_);
263         currentJsPath_ = nullptr;
264     }
265 
266     if (jsPath != nullptr) {
267         size_t jsPathLen = strlen(jsPath);
268         if ((jsPathLen > 0) && (jsPathLen < PATH_LENGTH_MAX)) {
269             currentJsPath_ = static_cast<char *>(ace_malloc(jsPathLen + 1));
270             if (currentJsPath_ == nullptr) {
271                 HILOG_ERROR(HILOG_MODULE_ACE, "malloc buffer for current js path failed");
272                 return;
273             }
274             if (memcpy_s(currentJsPath_, jsPathLen, jsPath, jsPathLen) != 0) {
275                 ace_free(currentJsPath_);
276                 currentJsPath_ = nullptr;
277                 return;
278             }
279             currentJsPath_[jsPathLen] = '\0';
280         }
281     }
282 }
283 
284 #ifdef _MINI_MULTI_TASKS_
SetCurrentUri(const char * const uri)285 void JsAppContext::SetCurrentUri(const char* const uri)
286 {
287     // release old first
288     if (currentUri_) {
289         ace_free(currentUri_);
290         currentUri_ = nullptr;
291     }
292 
293     if (uri == nullptr) {
294         return;
295     }
296 
297     size_t uriLen = strlen(uri);
298     if ((uriLen > 0) && (uriLen < PATH_LENGTH_MAX)) {
299         currentUri_ = static_cast<char*>(ace_malloc(uriLen + 1));
300         if (currentUri_ == nullptr) {
301             HILOG_ERROR(HILOG_MODULE_ACE, "malloc buffer for current uri failed");
302             return;
303         }
304         if (memcpy_s(currentUri_, uriLen, uri, uriLen) != 0) {
305             ace_free(currentUri_);
306             currentUri_ = nullptr;
307             return;
308         }
309         currentUri_[uriLen] = '\0';
310     }
311 }
312 #endif
313 
ReleaseAbilityInfo()314 void JsAppContext::ReleaseAbilityInfo()
315 {
316     if (currentBundleName_) {
317         ace_free(currentBundleName_);
318         currentBundleName_ = nullptr;
319     }
320 
321     if (currentAbilityPath_) {
322         ace_free(currentAbilityPath_);
323         currentAbilityPath_ = nullptr;
324     }
325 
326     if (currentJsPath_) {
327         ace_free(currentJsPath_);
328         currentJsPath_ = nullptr;
329     }
330 #ifdef _MINI_MULTI_TASKS_
331     if (currentUri_) {
332         ace_free(currentUri_);
333         currentUri_ = nullptr;
334     }
335 #endif
336 }
337 
GetResourcePath(const char * uri) const338 char *JsAppContext::GetResourcePath(const char *uri) const
339 {
340     if (uri == nullptr) {
341         HILOG_ERROR(HILOG_MODULE_ACE, "uri is null.");
342         return nullptr;
343     }
344     size_t size = strlen(uri);
345     if (size == 0 || size > NAME_LENGTH_MAX) {
346         HILOG_ERROR(HILOG_MODULE_ACE, "uri is empty or too long.");
347         return nullptr;
348     }
349     if (StringUtil::StartsWith(uri, URI_PREFIX_DATA)) {
350         char *path = StringUtil::Slice(uri, URI_PREFIX_DATA_LENGTH);
351         if (path == nullptr) {
352             HILOG_ERROR(HILOG_MODULE_ACE, "fail to get resource path.");
353             return nullptr;
354         }
355 #if (OHOS_ACELITE_PRODUCT_WATCH == 1)
356         // no GetDataPath API provided on watch, contact the path by the product configuration insteadly
357         char *relocatedPath = ProcessResourcePathByConfiguration(size, path);
358 #else
359         const char *dataPath = GetDataPath();
360         if (dataPath == nullptr || strlen(dataPath) == 0) {
361             dataPath = currentBundleName_;
362         }
363         char *relocatedPath = RelocateResourceFilePath(dataPath, path);
364 #endif
365         ACE_FREE(path);
366         return relocatedPath;
367     }
368     return RelocateResourceFilePath(currentAbilityPath_, uri);
369 }
370 
ProcessResourcePathByConfiguration(size_t origUriLength,const char * slicedFilePath) const371 char *JsAppContext::ProcessResourcePathByConfiguration(size_t origUriLength, const char *slicedFilePath) const
372 {
373     const char *appDataRoot = ProductAdapter::GetPrivateDataRootPath();
374     if (appDataRoot == nullptr || origUriLength == 0 || slicedFilePath == nullptr) {
375         return nullptr;
376     }
377     size_t rootPathLen = strlen(appDataRoot);
378     if (rootPathLen == 0) {
379         return nullptr;
380     }
381     size_t dataPathSize = rootPathLen + strlen(currentBundleName_) + origUriLength - URI_PREFIX_DATA_LENGTH + 1;
382     char *dataPath = StringUtil::Malloc(dataPathSize);
383     if (dataPath == nullptr) {
384         HILOG_ERROR(HILOG_MODULE_ACE, "fail to malloc data path buffer.");
385         return nullptr;
386     }
387     const char *fmtStr = "%s%s";
388     if (appDataRoot[rootPathLen - 1] != RESOURCE_SEPARATOR) {
389         fmtStr = "%s/%s";
390     }
391     if (sprintf_s(dataPath, dataPathSize + 1, fmtStr, appDataRoot, currentBundleName_) < 0) {
392         HILOG_ERROR(HILOG_MODULE_ACE, "fail to get resource path.");
393         ACE_FREE(dataPath);
394         return nullptr;
395     }
396     char *relocatedPath = RelocateResourceFilePath(dataPath, slicedFilePath);
397     ACE_FREE(dataPath);
398     return relocatedPath;
399 }
400 
LoadApiVersion()401 void JsAppContext::LoadApiVersion()
402 {
403 #if (FEATURE_API_VERSION == 1)
404     BundleInfo bundle = {0};
405     uint8_t retCode = GetBundleInfo(currentBundleName_, false, &bundle);
406     if (retCode != 0) {
407         HILOG_ERROR(HILOG_MODULE_ACE, "fail to get api version.");
408         return;
409     }
410     compatibleApi_ = bundle.compatibleApi;
411     targetApi_ = bundle.targetApi;
412 #else
413     const int32_t currentApiVersion = 6;
414     compatibleApi_ = currentApiVersion;
415     targetApi_ = currentApiVersion;
416 #endif
417 }
418 
GetCompatibleApi() const419 int32_t JsAppContext::GetCompatibleApi() const
420 {
421     return compatibleApi_;
422 }
423 
SetCompatibleApi(int32_t compatibleApi)424 void JsAppContext::SetCompatibleApi(int32_t compatibleApi)
425 {
426     compatibleApi_ = compatibleApi;
427 }
428 
GetTargetApi() const429 int32_t JsAppContext::GetTargetApi() const
430 {
431     return targetApi_;
432 }
433 
SetTargetApi(int32_t targetApi)434 void JsAppContext::SetTargetApi(int32_t targetApi)
435 {
436     targetApi_ = targetApi;
437 }
438 
GetAbilityState() const439 TopAbilityState JsAppContext::GetAbilityState() const
440 {
441     return abilityState_;
442 }
443 
SetAbilityState(TopAbilityState state)444 void JsAppContext::SetAbilityState(TopAbilityState state)
445 {
446     abilityState_ = state;
447 }
448 } // namespace ACELite
449 } // namespace OHOS
450