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