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
ReleaseAbilityInfo()284 void JsAppContext::ReleaseAbilityInfo()
285 {
286 if (currentBundleName_) {
287 ace_free(currentBundleName_);
288 currentBundleName_ = nullptr;
289 }
290
291 if (currentAbilityPath_) {
292 ace_free(currentAbilityPath_);
293 currentAbilityPath_ = nullptr;
294 }
295
296 if (currentJsPath_) {
297 ace_free(currentJsPath_);
298 currentJsPath_ = nullptr;
299 }
300 }
301
GetResourcePath(const char * uri) const302 char *JsAppContext::GetResourcePath(const char *uri) const
303 {
304 if (uri == nullptr) {
305 HILOG_ERROR(HILOG_MODULE_ACE, "uri is null.");
306 return nullptr;
307 }
308 size_t size = strlen(uri);
309 if (size == 0 || size > NAME_LENGTH_MAX) {
310 HILOG_ERROR(HILOG_MODULE_ACE, "uri is empty or too long.");
311 return nullptr;
312 }
313 if (StringUtil::StartsWith(uri, URI_PREFIX_DATA)) {
314 char *path = StringUtil::Slice(uri, URI_PREFIX_DATA_LENGTH);
315 if (path == nullptr) {
316 HILOG_ERROR(HILOG_MODULE_ACE, "fail to get resource path.");
317 return nullptr;
318 }
319 #if (OHOS_ACELITE_PRODUCT_WATCH == 1)
320 // no GetDataPath API provided on watch, contact the path by the product configuration insteadly
321 char *relocatedPath = ProcessResourcePathByConfiguration(size, path);
322 #else
323 const char *dataPath = GetDataPath();
324 if (dataPath == nullptr || strlen(dataPath) == 0) {
325 dataPath = currentBundleName_;
326 }
327 char *relocatedPath = RelocateResourceFilePath(dataPath, path);
328 #endif
329 ACE_FREE(path);
330 return relocatedPath;
331 }
332 return RelocateResourceFilePath(currentAbilityPath_, uri);
333 }
334
ProcessResourcePathByConfiguration(size_t origUriLength,const char * slicedFilePath) const335 char *JsAppContext::ProcessResourcePathByConfiguration(size_t origUriLength, const char *slicedFilePath) const
336 {
337 const char *appDataRoot = ProductAdapter::GetPrivateDataRootPath();
338 if (appDataRoot == nullptr || origUriLength == 0 || slicedFilePath == nullptr) {
339 return nullptr;
340 }
341 size_t rootPathLen = strlen(appDataRoot);
342 if (rootPathLen == 0) {
343 return nullptr;
344 }
345 size_t dataPathSize = rootPathLen + strlen(currentBundleName_) + origUriLength - URI_PREFIX_DATA_LENGTH + 1;
346 char *dataPath = StringUtil::Malloc(dataPathSize);
347 if (dataPath == nullptr) {
348 HILOG_ERROR(HILOG_MODULE_ACE, "fail to malloc data path buffer.");
349 return nullptr;
350 }
351 const char *fmtStr = "%s%s";
352 if (appDataRoot[rootPathLen - 1] != RESOURCE_SEPARATOR) {
353 fmtStr = "%s/%s";
354 }
355 if (sprintf_s(dataPath, dataPathSize + 1, fmtStr, appDataRoot, currentBundleName_) < 0) {
356 HILOG_ERROR(HILOG_MODULE_ACE, "fail to get resource path.");
357 ACE_FREE(dataPath);
358 return nullptr;
359 }
360 char *relocatedPath = RelocateResourceFilePath(dataPath, slicedFilePath);
361 ACE_FREE(dataPath);
362 return relocatedPath;
363 }
364
LoadApiVersion()365 void JsAppContext::LoadApiVersion()
366 {
367 #if (FEATURE_API_VERSION == 1)
368 BundleInfo bundle = {0};
369 uint8_t retCode = GetBundleInfo(currentBundleName_, false, &bundle);
370 if (retCode != 0) {
371 HILOG_ERROR(HILOG_MODULE_ACE, "fail to get api version.");
372 return;
373 }
374 compatibleApi_ = bundle.compatibleApi;
375 targetApi_ = bundle.targetApi;
376 #else
377 const int32_t currentApiVersion = 6;
378 compatibleApi_ = currentApiVersion;
379 targetApi_ = currentApiVersion;
380 #endif
381 }
382
GetCompatibleApi() const383 int32_t JsAppContext::GetCompatibleApi() const
384 {
385 return compatibleApi_;
386 }
387
SetCompatibleApi(int32_t compatibleApi)388 void JsAppContext::SetCompatibleApi(int32_t compatibleApi)
389 {
390 compatibleApi_ = compatibleApi;
391 }
392
GetTargetApi() const393 int32_t JsAppContext::GetTargetApi() const
394 {
395 return targetApi_;
396 }
397
SetTargetApi(int32_t targetApi)398 void JsAppContext::SetTargetApi(int32_t targetApi)
399 {
400 targetApi_ = targetApi;
401 }
402
GetAbilityState() const403 TopAbilityState JsAppContext::GetAbilityState() const
404 {
405 return abilityState_;
406 }
407
SetAbilityState(TopAbilityState state)408 void JsAppContext::SetAbilityState(TopAbilityState state)
409 {
410 abilityState_ = state;
411 }
412 } // namespace ACELite
413 } // namespace OHOS
414