1 /*
2 * Copyright (c) 2021-2022 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 "frameworks/bridge/js_frontend/engine/quickjs/qjs_engine.h"
17
18 #include <algorithm>
19 #include <regex>
20 #include <string>
21 #include <unistd.h>
22 #include <unordered_map>
23
24 #include "third_party/quickjs/message_server.h"
25
26 #include "base/i18n/localization.h"
27 #include "base/json/json_util.h"
28 #include "base/log/ace_trace.h"
29 #include "base/log/event_report.h"
30 #include "base/log/log.h"
31 #include "base/utils/linear_map.h"
32 #include "base/utils/system_properties.h"
33 #include "base/utils/time_util.h"
34 #include "base/utils/utils.h"
35 #include "core/common/ace_application_info.h"
36 #include "core/common/container.h"
37 #include "core/common/container_scope.h"
38 #include "core/event/ace_event_helper.h"
39 #include "core/event/back_end_event_manager.h"
40 #include "frameworks/bridge/common/dom/dom_type.h"
41 #include "frameworks/bridge/common/media_query/media_query_info.h"
42 #include "frameworks/bridge/common/plugin_adapter/plugin_bridge.h"
43 #include "frameworks/bridge/common/utils/utils.h"
44 #include "frameworks/bridge/js_frontend/engine/common/js_api_perf.h"
45 #include "frameworks/bridge/js_frontend/engine/common/js_constants.h"
46 #include "frameworks/bridge/js_frontend/engine/common/js_engine_loader.h"
47 #include "frameworks/bridge/js_frontend/engine/common/runtime_constants.h"
48 #include "frameworks/bridge/js_frontend/engine/quickjs/badge_bridge.h"
49 #include "frameworks/bridge/js_frontend/engine/quickjs/canvas_bridge.h"
50 #include "frameworks/bridge/js_frontend/engine/quickjs/chart_bridge.h"
51 #include "frameworks/bridge/js_frontend/engine/quickjs/clock_bridge.h"
52 #include "frameworks/bridge/js_frontend/engine/quickjs/component_api_bridge.h"
53 #include "frameworks/bridge/js_frontend/engine/quickjs/image_animator_bridge.h"
54 #include "frameworks/bridge/js_frontend/engine/quickjs/list_bridge.h"
55 #include "frameworks/bridge/js_frontend/engine/quickjs/offscreen_canvas_bridge.h"
56 #include "frameworks/bridge/js_frontend/engine/quickjs/qjs_group_js_bridge.h"
57 #include "frameworks/bridge/js_frontend/engine/quickjs/qjs_utils.h"
58 #include "frameworks/bridge/js_frontend/engine/quickjs/qjs_xcomponent_bridge.h"
59 #include "frameworks/bridge/js_frontend/engine/quickjs/stepper_bridge.h"
60 #include "frameworks/bridge/js_frontend/js_ace_page.h"
61
62 extern const uint32_t js_framework_size;
63 extern const uint8_t js_framework[];
64
65 namespace OHOS::Ace::Framework {
66 namespace {
67
68 #ifndef ENABLE_JS_DEBUG
69 const char JS_MAIN_ENTRY[] = "___mainEntry___";
70 const char JS_EXT[] = ".js";
71 const char BIN_EXT[] = ".bin";
72 #endif
73 const char MAP_EXT[] = ".map";
74 constexpr int32_t CUSTOM_FULL_WINDOW_LENGTH = 3;
75 constexpr int32_t ARGS_FULL_WINDOW_LENGTH = 2;
76 constexpr int32_t ARGS_READ_RESOURCE_LENGTH = 2;
77 constexpr int32_t MAX_READ_TEXT_LENGTH = 4096;
78 const std::regex URI_PATTERN("^\\/([a-z0-9A-Z_]+\\/)*[a-z0-9A-Z_]+\\.?[a-z0-9A-Z_]*$");
79
80 static int32_t globalNodeId = 100000;
81 std::map<const std::string, std::string> dataMap_;
82
CallEvalBuf(JSContext * ctx,const char * buf,size_t bufLen,const char * filename,int32_t evalFlags,int32_t instanceId)83 int32_t CallEvalBuf(
84 JSContext* ctx, const char* buf, size_t bufLen, const char* filename, int32_t evalFlags, int32_t instanceId)
85 {
86 JSValue val = JS_Eval(ctx, buf, bufLen, filename, evalFlags);
87 int32_t ret = JS_CALL_SUCCESS;
88 if (JS_IsException(val)) {
89 LOGE("[Qjs Native] EvalBuf failed!");
90 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::EVAL_BUFFER_ERROR, instanceId);
91 ret = JS_CALL_FAIL;
92 }
93 JS_FreeValue(ctx, val);
94 js_std_loop(ctx);
95 return ret;
96 }
97
CallReadObject(JSContext * ctx,const uint8_t * buf,size_t bufLen,bool persist=false,int32_t instanceId=0,const char * pageUrl=nullptr)98 JSValue CallReadObject(JSContext* ctx, const uint8_t* buf, size_t bufLen, bool persist = false, int32_t instanceId = 0,
99 const char* pageUrl = nullptr)
100 {
101 int32_t flags = persist ? (JS_READ_OBJ_ROM_DATA | JS_READ_OBJ_BYTECODE) : JS_READ_OBJ_BYTECODE;
102 JSValue obj = JS_ReadObject(ctx, buf, bufLen, flags);
103 if (JS_IsException(obj)) {
104 LOGE("[Qjs Native] ReadObject failed!");
105 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::READ_OBJECT_ERROR, instanceId, pageUrl);
106 return obj;
107 }
108 return JS_EvalFunction(ctx, obj);
109 }
110
GetRunningPage(JSContext * ctx)111 RefPtr<JsAcePage> GetRunningPage(JSContext* ctx)
112 {
113 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
114 ACE_DCHECK(instance);
115 return instance->GetRunningPage();
116 }
117
GetStagingPage(JSContext * ctx)118 RefPtr<JsAcePage> GetStagingPage(JSContext* ctx)
119 {
120 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
121 ACE_DCHECK(instance);
122 return instance->GetStagingPage();
123 }
124
GetArrayValue(JSContext * ctx,JSValueConst arrayVal,std::string & result)125 void GetArrayValue(JSContext* ctx, JSValueConst arrayVal, std::string& result)
126 {
127 int32_t length = QJSUtils::JsGetArrayLength(ctx, arrayVal);
128 for (int32_t i = 0; i < length; ++i) {
129 if (i != 0) {
130 result.append(1, DOM_PICKER_SPLIT_ARRAY); // only need one char to split.
131 }
132 JSValue itemVal = JS_GetPropertyUint32(ctx, arrayVal, i);
133 if (JS_IsString(itemVal) || JS_IsNumber(itemVal) || JS_IsBool(itemVal)) {
134 ScopedString val(ctx, itemVal);
135 const char* itemStr = val.get();
136 result.append(itemStr);
137 JS_FreeValue(ctx, itemVal);
138 continue;
139 }
140 if (JS_IsArray(ctx, itemVal)) {
141 int32_t subLength = QJSUtils::JsGetArrayLength(ctx, itemVal);
142 for (int32_t j = 0; j < subLength; ++j) {
143 if (j != 0) {
144 result.append(1, DOM_PICKER_SPLIT_ITEM); // only need one char to split
145 }
146 JSValue subItemVal = JS_GetPropertyUint32(ctx, itemVal, j);
147 ScopedString subVal(ctx, subItemVal);
148 const char* subItemStr = subVal.get();
149 result.append(subItemStr);
150 JS_FreeValue(ctx, subItemVal);
151 }
152 JS_FreeValue(ctx, itemVal);
153 continue;
154 }
155 JS_FreeValue(ctx, itemVal);
156 }
157 }
158
GetAttrImage(JSContext * ctx,JSValueConst valObject,ImageProperties & imageProperties)159 void GetAttrImage(JSContext* ctx, JSValueConst valObject, ImageProperties& imageProperties)
160 {
161 JSPropertyEnum* pTab = nullptr;
162 uint32_t len = 0;
163 if (!CheckAndGetJsProperty(ctx, valObject, &pTab, &len)) {
164 return;
165 }
166 for (uint32_t i = 0; i < len; ++i) {
167 const char* key = JS_AtomToCString(ctx, pTab[i].atom);
168 if (key == nullptr) {
169 JS_FreeAtom(ctx, pTab[i].atom);
170 LOGW("key is null. Ignoring!");
171 continue;
172 }
173 JSValue valItem = JS_GetProperty(ctx, valObject, pTab[i].atom);
174 if (JS_IsString(valItem) || JS_IsNumber(valItem) || JS_IsBool(valItem)) {
175 ScopedString styleVal(ctx, valItem);
176 const char* valStr = styleVal.get();
177 if (strcmp(key, "src") == 0) {
178 imageProperties.src = valStr;
179 } else if (strcmp(key, "width") == 0) {
180 imageProperties.width = StringToDimension(valStr);
181 } else if (strcmp(key, "height") == 0) {
182 imageProperties.height = StringToDimension(valStr);
183 } else if (strcmp(key, "top") == 0) {
184 imageProperties.top = StringToDimension(valStr);
185 } else if (strcmp(key, "left") == 0) {
186 imageProperties.left = StringToDimension(valStr);
187 } else if (strcmp(key, "duration") == 0) {
188 imageProperties.duration = StringToInt(valStr);
189 } else {
190 LOGD("key : %{public}s unsupported. Ignoring!", key);
191 }
192 } else {
193 LOGD("value of unsupported type. Ignoring!");
194 }
195 JS_FreeAtom(ctx, pTab[i].atom);
196 JS_FreeCString(ctx, key);
197 JS_FreeValue(ctx, valItem);
198 }
199 js_free(ctx, pTab);
200 }
201
GetAttrImages(JSContext * ctx,JSValueConst arrayVal,std::vector<ImageProperties> & images)202 void GetAttrImages(JSContext* ctx, JSValueConst arrayVal, std::vector<ImageProperties>& images)
203 {
204 int32_t length = QJSUtils::JsGetArrayLength(ctx, arrayVal);
205 for (int32_t i = 0; i < length; ++i) {
206 JSValue valArray = JS_GetPropertyUint32(ctx, arrayVal, i);
207 ImageProperties imageProperties;
208 if (JS_IsObject(valArray)) {
209 GetAttrImage(ctx, valArray, imageProperties);
210 images.push_back(imageProperties);
211 }
212 JS_FreeValue(ctx, valArray);
213 }
214 }
215
SetDomAttributes(JSContext * ctx,JSValueConst fromMap,JsCommandDomElementOperator & command)216 bool SetDomAttributes(JSContext* ctx, JSValueConst fromMap, JsCommandDomElementOperator& command)
217 {
218 ACE_SCOPED_TRACE("SetDomAttributes");
219
220 bool hasShowAttr = false;
221 JSPropertyEnum* pTab = nullptr;
222 uint32_t len = 0;
223 if (!CheckAndGetJsProperty(ctx, fromMap, &pTab, &len)) {
224 return hasShowAttr;
225 }
226
227 std::vector<std::pair<std::string, std::string>> attrs;
228 for (uint32_t i = 0; i < len; i++) {
229 const char* key = JS_AtomToCString(ctx, pTab[i].atom);
230 if (key == nullptr) {
231 JS_FreeAtom(ctx, pTab[i].atom);
232 LOGW("key is null. Ignoring!");
233 continue;
234 }
235 std::string keyString = key;
236 JSValue val = JS_GetProperty(ctx, fromMap, pTab[i].atom);
237 if (JS_IsString(val) || JS_IsNumber(val) || JS_IsBool(val)) {
238 ScopedString styleVal(ctx, val);
239 const char* valStr = styleVal.get();
240 LOGD("SetDomAttributes: key: %{private}s, attr: %{private}s", key, valStr);
241 if (keyString.compare(DOM_ID) == 0) {
242 command.SetId(valStr);
243 } else if (keyString.compare(DOM_TARGET) == 0) {
244 command.SetTarget(valStr);
245 } else if (keyString.compare(DOM_SHARE_ID) == 0) {
246 command.SetShareId(valStr);
247 }
248 attrs.emplace_back(key, valStr);
249 if (keyString.compare(DOM_SHOW) == 0) {
250 hasShowAttr = true;
251 }
252 } else if (JS_IsArray(ctx, val)) {
253 if (keyString.compare("datasets") == 0) {
254 auto chartBridge = AceType::MakeRefPtr<ChartBridge>();
255 chartBridge->GetAttrDatasets(ctx, val);
256 command.SetDatasets(chartBridge->GetDatasets());
257 } else if (keyString.compare("images") == 0) {
258 std::vector<ImageProperties> images;
259 GetAttrImages(ctx, val, images);
260 command.SetImagesAttr(std::move(images));
261 } else if (keyString.compare("segments") == 0) {
262 auto chartBridge = AceType::MakeRefPtr<ChartBridge>();
263 chartBridge->ParseAttrSegmentArray(ctx, val);
264 command.SetSegments(chartBridge->GetSegments());
265 } else {
266 std::string valStr;
267 GetArrayValue(ctx, val, valStr);
268 LOGD("SetDomAttributes: key: %{private}s, attr: %{private}s", key, valStr.c_str());
269 attrs.emplace_back(key, valStr);
270 }
271 } else if (JS_IsObject(val)) {
272 if (keyString.compare("options") == 0) {
273 auto chartBridge = AceType::MakeRefPtr<ChartBridge>();
274 chartBridge->GetAttrOptionsObject(ctx, val);
275 command.SetOptions(chartBridge->GetChartOptions());
276 } else if (keyString.compare("segments") == 0) {
277 auto chartBridge = AceType::MakeRefPtr<ChartBridge>();
278 chartBridge->ParseAttrSingleSegment(ctx, val);
279 command.SetSegments(chartBridge->GetSegments());
280 } else if (keyString.compare(DOM_CLOCK_CONFIG) == 0) {
281 auto clockBridge = AceType::MakeRefPtr<ClockBridge>();
282 clockBridge->ParseClockConfig(ctx, val);
283 command.SetClockConfig(clockBridge->GetClockConfig());
284 } else if (keyString.compare(DOM_NODE_TAG_LABEL) == 0) {
285 auto stepperBridge = AceType::MakeRefPtr<StepperBridge>();
286 StepperLabels label;
287 stepperBridge->GetAttrLabel(ctx, val, label);
288 command.SetStepperLabel(label);
289 } else if (keyString.compare(DOM_BADGE_CONFIG) == 0) {
290 auto badgeBridge = AceType::MakeRefPtr<BadgeBridge>();
291 badgeBridge->ParseBadgeConfig(ctx, val);
292 command.SetBadgeConfig(badgeBridge->GetBadgeConfig());
293 } else {
294 LOGD("key %{public}s unsupported. Ignoring!", key);
295 }
296 } else if (JS_IsUndefined(val)) {
297 LOGE("value of key[%{private}s] is undefined. Ignoring!", key);
298 } else {
299 LOGE("value of key[%{private}s] of unsupported type. Ignoring!", key);
300 }
301 JS_FreeAtom(ctx, pTab[i].atom);
302 JS_FreeCString(ctx, key);
303 JS_FreeValue(ctx, val);
304 }
305
306 command.SetAttributes(std::move(attrs));
307 js_free(ctx, pTab);
308 return hasShowAttr;
309 }
310
GetAndRegisterFamily(JSContext * ctx,JSValueConst valArray,std::string & familyStyle)311 void GetAndRegisterFamily(JSContext* ctx, JSValueConst valArray, std::string& familyStyle)
312 {
313 JSPropertyEnum* tab = nullptr;
314 uint32_t aLen = 0;
315 JS_GetOwnPropertyNames(ctx, &tab, &aLen, valArray, JS_GPN_STRING_MASK);
316
317 std::string familyVal;
318 std::string srcVal;
319 for (uint32_t j = 0; j < aLen; j++) {
320 // ValObject is one row of family object
321 JSValue valObject = JS_GetProperty(ctx, valArray, tab[j].atom);
322 const char* keyObject = JS_AtomToCString(ctx, tab[j].atom);
323 if (JS_IsString(valObject)) {
324 ScopedString styleVal(ctx, valObject);
325 const char* valStr = styleVal.get();
326 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
327 ACE_DCHECK(instance);
328 if (keyObject != nullptr && strcmp(keyObject, "fontFamily") == 0) {
329 familyVal = valStr;
330 if (!srcVal.empty()) {
331 instance->GetDelegate()->RegisterFont(familyVal, srcVal);
332 familyVal.erase();
333 srcVal.erase();
334 }
335 if (familyStyle.length() > 0) {
336 familyStyle.append(",");
337 }
338 familyStyle.append(valStr);
339 } else if (keyObject != nullptr && strcmp(keyObject, "src") == 0) {
340 srcVal = valStr;
341 // The format of font src is: url("src"), here get the src.
342 srcVal = srcVal.substr(5, srcVal.length() - 7);
343 if (!familyVal.empty()) {
344 instance->GetDelegate()->RegisterFont(familyVal, srcVal);
345 familyVal.erase();
346 srcVal.erase();
347 }
348 }
349 }
350 JS_FreeAtom(ctx, tab[j].atom);
351 JS_FreeCString(ctx, keyObject);
352 JS_FreeValue(ctx, valObject);
353 }
354 js_free(ctx, tab);
355 }
356
GetStyleFamilyValue(JSContext * ctx,JSValueConst arrayVal,std::string & familyStyle)357 void GetStyleFamilyValue(JSContext* ctx, JSValueConst arrayVal, std::string& familyStyle)
358 {
359 int32_t length = QJSUtils::JsGetArrayLength(ctx, arrayVal);
360 for (int32_t i = 0; i < length; ++i) {
361 // ValArray is one row of family array
362 JSValue valArray = JS_GetPropertyUint32(ctx, arrayVal, i);
363 if (JS_IsObject(valArray)) {
364 GetAndRegisterFamily(ctx, valArray, familyStyle);
365 }
366 JS_FreeValue(ctx, valArray);
367 }
368 }
369
GetStyleAnimationName(JSContext * ctx,JSValueConst arrayVal,std::vector<std::unordered_map<std::string,std::string>> & styleVec)370 void GetStyleAnimationName(
371 JSContext* ctx, JSValueConst arrayVal, std::vector<std::unordered_map<std::string, std::string>>& styleVec)
372 {
373 int32_t length = QJSUtils::JsGetArrayLength(ctx, arrayVal);
374 for (int32_t i = 0; i < length; ++i) {
375 std::unordered_map<std::string, std::string> animationNameKeyFrame;
376 JSValue valArray = JS_GetPropertyUint32(ctx, arrayVal, i);
377 if (JS_IsObject(valArray)) {
378 JSPropertyEnum* tab = nullptr;
379 uint32_t anotherLen = 0;
380 JS_GetOwnPropertyNames(ctx, &tab, &anotherLen, valArray, JS_GPN_STRING_MASK);
381 for (uint32_t j = 0; j < anotherLen; ++j) {
382 const char* key = JS_AtomToCString(ctx, tab[j].atom);
383 if (key == nullptr) {
384 JS_FreeAtom(ctx, tab[j].atom);
385 JS_FreeValue(ctx, valArray);
386 LOGW("key is null. Ignoring!");
387 continue;
388 }
389 JSValue valObject = JS_GetProperty(ctx, valArray, tab[j].atom);
390 if (JS_IsString(valObject) || JS_IsNumber(valObject)) {
391 ScopedString styleVal(ctx, valObject);
392 const char* valStr = styleVal.get();
393 animationNameKeyFrame.emplace(key, valStr);
394 } else {
395 LOGD("GetStyleAnimationName: unsupported type :%{public}d", JS_VALUE_GET_TAG(valObject));
396 }
397 JS_FreeAtom(ctx, tab[j].atom);
398 JS_FreeCString(ctx, key);
399 JS_FreeValue(ctx, valObject);
400 }
401 js_free(ctx, tab);
402 }
403 if (animationNameKeyFrame.size() > 0) {
404 styleVec.emplace_back(animationNameKeyFrame);
405 }
406 JS_FreeValue(ctx, valArray);
407 }
408 }
409
SetDomStyle(JSContext * ctx,JSValueConst fromMap,JsCommandDomElementOperator & command)410 void SetDomStyle(JSContext* ctx, JSValueConst fromMap, JsCommandDomElementOperator& command)
411 {
412 ACE_SCOPED_TRACE("SetDomStyle");
413
414 JSPropertyEnum* pTab = nullptr;
415 uint32_t len = 0;
416 if (!CheckAndGetJsProperty(ctx, fromMap, &pTab, &len)) {
417 return;
418 }
419
420 std::vector<std::pair<std::string, std::string>> styles;
421 for (uint32_t i = 0; i < len; i++) {
422 const char* key = JS_AtomToCString(ctx, pTab[i].atom);
423 if (key == nullptr) {
424 JS_FreeAtom(ctx, pTab[i].atom);
425 LOGW("key is null. Ignoring!");
426 continue;
427 }
428 std::string keyString = key;
429 JSValue val = JS_GetProperty(ctx, fromMap, pTab[i].atom);
430 if (JS_IsString(val) || JS_IsNumber(val) || JS_IsBool(val)) {
431 ScopedString styleVal(ctx, val);
432 const char* valStr = styleVal.get();
433 LOGD("SetDomStyle: key: %{private}s, style: %{private}s", key, valStr);
434 styles.emplace_back(key, valStr);
435 } else if (JS_IsArray(ctx, val)) {
436 if (keyString.compare(DOM_TEXT_FONT_FAMILY) == 0) {
437 // Deal with special case such as fontFamily, suppose all the keys in the array are the same.
438 std::string familyStyle;
439 GetStyleFamilyValue(ctx, val, familyStyle);
440 styles.emplace_back(key, familyStyle);
441 } else if (keyString.compare(DOM_ANIMATION_NAME) == 0) {
442 // Deal with special case animationName, it different with fontfamily,
443 // the keys in the array are different.
444 std::vector<std::unordered_map<std::string, std::string>> animationStyles;
445 GetStyleAnimationName(ctx, val, animationStyles);
446 command.SetAnimationStyles(std::move(animationStyles));
447 } else if (keyString.compare(DOM_TRANSITION_ENTER) == 0) {
448 std::vector<std::unordered_map<std::string, std::string>> transitionEnter;
449 GetStyleAnimationName(ctx, val, transitionEnter);
450 command.SetTransitionEnter(std::move(transitionEnter));
451 } else if (keyString.compare(DOM_TRANSITION_EXIT) == 0) {
452 std::vector<std::unordered_map<std::string, std::string>> transitionExit;
453 GetStyleAnimationName(ctx, val, transitionExit);
454 command.SetTransitionExit(std::move(transitionExit));
455 } else if (keyString.compare(DOM_SHARED_TRANSITION_NAME) == 0) {
456 std::vector<std::unordered_map<std::string, std::string>> sharedTransitionName;
457 GetStyleAnimationName(ctx, val, sharedTransitionName);
458 command.SetSharedTransitionName(std::move(sharedTransitionName));
459 } else {
460 LOGD("value is array, key unsupported. Ignoring!");
461 }
462 } else if (JS_IsUndefined(val)) {
463 LOGD("value is undefined. Ignoring!");
464 } else {
465 LOGD("value of unsupported type. Ignoring!");
466 }
467 JS_FreeAtom(ctx, pTab[i].atom);
468 JS_FreeCString(ctx, key);
469 JS_FreeValue(ctx, val);
470 }
471 bool isIine = false;
472 for (int32_t i = 0; i < static_cast<int32_t>(styles.size()); i++) {
473 std::string key = styles[i].first;
474 std::string value = styles[i].second;
475 if (key == "display" && value == "inline") {
476 isIine = true;
477 break;
478 }
479 }
480
481 if (isIine) {
482 std::vector<std::pair<std::string, std::string>> stylesFinally;
483 for (int32_t i = 0; i < static_cast<int32_t>(styles.size()); i++) {
484 std::string key = styles[i].first;
485 std::string value = styles[i].second;
486 if (key == "width" || key == "height" || key.find("margin") != std::string::npos ||
487 key.find("padding") != std::string::npos) {
488 continue;
489 } else {
490 stylesFinally.emplace_back(key, value);
491 }
492 }
493 command.SetStyles(std::move(stylesFinally));
494 } else {
495 command.SetStyles(std::move(styles));
496 }
497
498 QjsEngineInstance* instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
499 auto pipelineContext = AceType::DynamicCast<PipelineContext>(instance->GetDelegate()->GetPipelineContext());
500 command.SetPipelineContext(pipelineContext);
501 js_free(ctx, pTab);
502 }
503
AddDomEvent(JSContext * ctx,JSValueConst fromArray,JsCommandDomElementOperator & command)504 void AddDomEvent(JSContext* ctx, JSValueConst fromArray, JsCommandDomElementOperator& command)
505 {
506 ACE_SCOPED_TRACE("AddDomEvent");
507
508 std::vector<std::string> eventsMap;
509 int32_t length = QJSUtils::JsGetArrayLength(ctx, fromArray);
510 for (int32_t i = 0; i < length; i++) {
511 JSValue val = JS_GetPropertyUint32(ctx, fromArray, i);
512 if (JS_IsString(val)) {
513 ScopedString styleVal(ctx, val);
514 const char* valStr = styleVal.get();
515 eventsMap.emplace_back(valStr);
516 } else {
517 LOGW("value of unsupported type. Ignoring!");
518 }
519 JS_FreeValue(ctx, val);
520 }
521 command.AddEvents(std::move(eventsMap));
522 }
523
JsRemoveElement(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)524 JSValue JsRemoveElement(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
525 {
526 if ((argv == nullptr) || (argc != REMOVE_ELEMENT_ARGS_LEN)) {
527 LOGE("JsRemoveElement: the arg is error when call removeElement");
528 return JS_EXCEPTION;
529 }
530
531 auto page = GetRunningPage(ctx);
532 if (page == nullptr) {
533 return JS_EXCEPTION;
534 }
535
536 int32_t instanceId = 0;
537 JS_ToInt32(ctx, &instanceId, argv[REMOVE_ELEMENT_INSTANCE_ID]);
538 if (page->GetPageId() != instanceId) {
539 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
540 ACE_DCHECK(instance);
541 page = instance->GetDelegate()->GetPage(instanceId);
542 if (page == nullptr) {
543 LOGE("JsRemoveElement fail to get page, pageId: %{public}d", instanceId);
544 return JS_EXCEPTION;
545 }
546 }
547
548 int32_t nodeId = 0;
549 JS_ToInt32(ctx, &nodeId, argv[0]);
550 nodeId = (nodeId == 0) ? DOM_ROOT_NODE_ID_BASE + page->GetPageId() : nodeId;
551 page->PushCommand(Referenced::MakeRefPtr<JsCommandRemoveDomElement>(nodeId));
552 return JS_NULL;
553 }
554
JsUpdateElementAttrs(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)555 JSValue JsUpdateElementAttrs(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
556 {
557 if ((argv == nullptr) || (argc != UPLOAD_ELEMENT_ARGS_LEN)) {
558 LOGE("the arg is error");
559 return JS_EXCEPTION;
560 }
561
562 auto page = GetStagingPage(ctx);
563 if (page == nullptr) {
564 return JS_EXCEPTION;
565 }
566
567 int32_t instanceId = 0;
568 JS_ToInt32(ctx, &instanceId, argv[UPLOAD_ELEMENT_INSTANCE_ID]);
569 if (page->GetPageId() != instanceId) {
570 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
571 ACE_DCHECK(instance);
572 page = instance->GetDelegate()->GetPage(instanceId);
573 if (page == nullptr) {
574 LOGE("JsUpdateElementAttrs fail to get page, pageId :%{public}d", instanceId);
575 return JS_EXCEPTION;
576 }
577 }
578
579 int32_t nodeId = 0;
580 JS_ToInt32(ctx, &nodeId, argv[0]);
581 nodeId = (nodeId == 0) ? DOM_ROOT_NODE_ID_BASE + page->GetPageId() : nodeId;
582 auto command = Referenced::MakeRefPtr<JsCommandUpdateDomElementAttrs>(nodeId);
583 if (SetDomAttributes(ctx, argv[1], *command)) {
584 page->ReserveShowCommand(command);
585 }
586 page->PushCommand(command);
587 return JS_NULL;
588 }
589
JsUpdateElementStyles(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)590 JSValue JsUpdateElementStyles(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
591 {
592 if ((argv == nullptr) || (argc != UPLOAD_ELEMENT_ARGS_LEN)) {
593 LOGE("the arg is error");
594 return JS_EXCEPTION;
595 }
596
597 auto page = GetStagingPage(ctx);
598 if (page == nullptr) {
599 return JS_EXCEPTION;
600 }
601
602 int32_t instanceId = 0;
603 JS_ToInt32(ctx, &instanceId, argv[UPLOAD_ELEMENT_INSTANCE_ID]);
604 if (page->GetPageId() != instanceId) {
605 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
606 ACE_DCHECK(instance);
607 page = instance->GetDelegate()->GetPage(instanceId);
608 if (page == nullptr) {
609 LOGE("JsUpdateElementStyles fail to get page, pageId: %{public}d", instanceId);
610 return JS_EXCEPTION;
611 }
612 }
613
614 int32_t nodeId = 0;
615 JS_ToInt32(ctx, &nodeId, argv[0]);
616 nodeId = (nodeId == 0) ? DOM_ROOT_NODE_ID_BASE + page->GetPageId() : nodeId;
617 auto command = Referenced::MakeRefPtr<JsCommandUpdateDomElementStyles>(nodeId);
618 SetDomStyle(ctx, argv[1], *command);
619 page->PushCommand(command);
620 return JS_NULL;
621 }
622
JsDomCreateBody(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)623 JSValue JsDomCreateBody(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
624 {
625 if ((argv == nullptr) || (argc != 5)) {
626 LOGE("the arg is error");
627 return JS_EXCEPTION;
628 }
629
630 auto page = GetStagingPage(ctx);
631 if (page == nullptr) {
632 return JS_EXCEPTION;
633 }
634
635 int32_t nodeId = DOM_ROOT_NODE_ID_BASE + page->GetPageId();
636 LOGD("JsDomCreateBody: pageId: %{private}d, nodeId: %{private}d:", page->GetPageId(), nodeId);
637
638 ScopedString tag(ctx, argv[1]);
639 auto command = Referenced::MakeRefPtr<JsCommandCreateDomBody>(tag.get(), nodeId);
640
641 SetDomAttributes(ctx, argv[2], *command);
642 SetDomStyle(ctx, argv[3], *command);
643 AddDomEvent(ctx, argv[4], *command);
644 page->PushCommand(command);
645 return JS_NULL;
646 }
647
CreateDomElement(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)648 int32_t CreateDomElement(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
649 {
650 ACE_SCOPED_TRACE("CreateDomElement");
651 if (argv == nullptr) {
652 LOGE("the arg is error");
653 return -1;
654 }
655
656 auto page = GetStagingPage(ctx);
657 if (page == nullptr) {
658 LOGE("the page is nullptr");
659 return -1;
660 }
661 int32_t nodeId = ++globalNodeId;
662 ScopedString tag(ctx, argv[0]);
663 auto command = Referenced::MakeRefPtr<JsCommandCreateDomElement>(tag.get(), nodeId);
664 page->PushCommand(command);
665 // Flush command as fragment immediately when pushed too many commands.
666 if (!page->CheckPageCreated() && page->GetCommandSize() > FRAGMENT_SIZE) {
667 page->FlushCommands();
668 }
669 return globalNodeId;
670 }
671
JsDomAddElement(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)672 JSValue JsDomAddElement(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
673 {
674 ACE_SCOPED_TRACE("JsDomAddElement");
675
676 if ((argv == nullptr) || (argc != ADD_ELEMENT_ARGS_LEN)) {
677 LOGE("the arg is error");
678 return JS_EXCEPTION;
679 }
680
681 auto page = GetStagingPage(ctx);
682 if (page == nullptr) {
683 return JS_EXCEPTION;
684 }
685
686 int32_t instanceId = 0;
687 JS_ToInt32(ctx, &instanceId, argv[ADD_ELEMENT_INSTANCE_ID]);
688 if (page->GetPageId() != instanceId) {
689 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
690 ACE_DCHECK(instance);
691 page = instance->GetDelegate()->GetPage(instanceId);
692 if (page == nullptr) {
693 LOGE("JsDomAddElement fail to get page, pageId: %{public}d", instanceId);
694 return JS_EXCEPTION;
695 }
696 }
697
698 int32_t parentNodeId;
699 JS_ToInt32(ctx, &parentNodeId, argv[0]);
700 parentNodeId = parentNodeId ? parentNodeId : (DOM_ROOT_NODE_ID_BASE + page->GetPageId());
701
702 int32_t nodeId;
703 JS_ToInt32(ctx, &nodeId, argv[1]);
704 ScopedString tag(ctx, argv[2]);
705 LOGD("JsDomAddElement: pageId: %{private}d, parentNodeId: %{private}d, nodeId: %{private}d, tag: %{private}s",
706 page->GetPageId(), parentNodeId, nodeId, tag.get());
707 auto command = Referenced::MakeRefPtr<JsCommandAddDomElement>(tag.get(), nodeId, parentNodeId);
708 SetDomAttributes(ctx, argv[3], *command);
709 SetDomStyle(ctx, argv[4], *command);
710 AddDomEvent(ctx, argv[5], *command);
711
712 ScopedString customFlag(ctx, argv[6]);
713 if (customFlag.get()) {
714 std::unique_ptr<JsonValue> customPtr = JsonUtil::ParseJsonString(customFlag.get());
715 if (customPtr && customPtr->IsBool()) {
716 command->SetIsCustomComponent(customPtr->GetBool());
717 }
718 }
719 int32_t itemIndex;
720 JS_ToInt32(ctx, &itemIndex, argv[7]);
721 command->SetForIndex(itemIndex);
722 page->PushCommand(command);
723 // Flush command as fragment immediately when pushed too many commands.
724 if (!page->CheckPageCreated() && page->GetCommandSize() > FRAGMENT_SIZE) {
725 page->FlushCommands();
726 }
727 return JS_NULL;
728 }
729
730 // JS Framework calls this function after initial loading of the page has been done.
JsOnCreateFinish(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)731 JSValue JsOnCreateFinish(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
732 {
733 LOGD("JsOnCreateFinish");
734 auto page = GetStagingPage(ctx);
735 if (page == nullptr) {
736 return JS_EXCEPTION;
737 }
738 page->SetPageCreated();
739 return JS_NULL;
740 }
741
742 // JS Framework calls this function after JS framework has finished processing an event.
JsOnUpdateFinish(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)743 JSValue JsOnUpdateFinish(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
744 {
745 LOGD("JsOnUpdateFinish");
746 auto page = GetRunningPage(ctx);
747 if (page == nullptr) {
748 return JS_EXCEPTION;
749 }
750
751 if (page->CheckPageCreated()) {
752 QjsEngineInstance* instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
753 ACE_DCHECK(instance);
754 instance->GetDelegate()->TriggerPageUpdate(page->GetPageId());
755 }
756 return JS_NULL;
757 }
758
JsParseRouteUrl(JSContext * ctx,JSValueConst argv,const std::string & key)759 std::string JsParseRouteUrl(JSContext* ctx, JSValueConst argv, const std::string& key)
760 {
761 std::string pageRoute;
762 ScopedString args(ctx, argv);
763 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
764 if (argsPtr != nullptr && argsPtr->GetValue(key) != nullptr && argsPtr->GetValue(key)->IsString()) {
765 pageRoute = argsPtr->GetValue(key)->GetString();
766 }
767 LOGI("JsParseRouteUrl pageRoute = %{private}s", pageRoute.c_str());
768 return pageRoute;
769 }
770
JsParseRouteUrlSpecial(JSContext * ctx,JSValueConst argv)771 std::string JsParseRouteUrlSpecial(JSContext* ctx, JSValueConst argv)
772 {
773 std::string pageRoute;
774 ScopedString args(ctx, argv);
775 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
776
777 if (argsPtr->Contains(ROUTE_KEY_URI)) {
778 pageRoute = argsPtr->GetValue(ROUTE_KEY_URI)->GetString();
779 } else if (argsPtr->Contains(ROUTE_KEY_PATH)) {
780 pageRoute = argsPtr->GetValue(ROUTE_KEY_PATH)->GetString();
781 }
782 LOGI("JsParseRouteUrl pageRoute = %{private}s", pageRoute.c_str());
783 return pageRoute;
784 }
785
JsParseRouteParams(JSContext * ctx,JSValueConst argv,const std::string & key)786 std::string JsParseRouteParams(JSContext* ctx, JSValueConst argv, const std::string& key)
787 {
788 std::string params;
789 ScopedString args(ctx, argv);
790
791 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
792 if (argsPtr != nullptr && argsPtr->Contains(key) && argsPtr->GetValue(key)->IsObject()) {
793 params = argsPtr->GetValue(key)->ToString();
794 }
795 return params;
796 }
797
JsParseIntParams(JSContext * ctx,JSValueConst argv,const std::string & key)798 int32_t JsParseIntParams(JSContext* ctx, JSValueConst argv, const std::string& key)
799 {
800 ScopedString args(ctx, argv);
801
802 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
803 if (argsPtr != nullptr && argsPtr->Contains(key) && argsPtr->GetValue(key)->IsNumber()) {
804 return argsPtr->GetValue(key)->GetInt();
805 }
806
807 return 0;
808 }
809
JsParseRouteOverwrite(JSContext * ctx,JSValueConst argv,const std::string & key)810 bool JsParseRouteOverwrite(JSContext* ctx, JSValueConst argv, const std::string& key)
811 {
812 ScopedString args(ctx, argv);
813
814 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
815 if (argsPtr != nullptr && argsPtr->Contains(key)) {
816 return true;
817 }
818 return false;
819 }
820
JsParseDialogButtons(JSContext * ctx,JSValueConst argv,const std::string & key)821 std::vector<ButtonInfo> JsParseDialogButtons(JSContext* ctx, JSValueConst argv, const std::string& key)
822 {
823 std::vector<ButtonInfo> dialogButtons;
824 ScopedString args(ctx, argv);
825 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
826 if (argsPtr != nullptr && argsPtr->GetValue(key) != nullptr && argsPtr->GetValue(key)->IsArray()) {
827 for (int32_t i = 0; i < argsPtr->GetValue(key)->GetArraySize(); ++i) {
828 auto button = argsPtr->GetValue(key)->GetArrayItem(i);
829 if (!button || !button->GetValue("text")->IsString()) {
830 continue;
831 }
832 ButtonInfo buttonInfo;
833 if (button->GetValue("text")) {
834 buttonInfo.text = button->GetValue("text")->GetString();
835 }
836 if (button->GetValue("color")) {
837 buttonInfo.textColor = button->GetValue("color")->GetString();
838 }
839 dialogButtons.emplace_back(buttonInfo);
840 }
841 }
842 return dialogButtons;
843 }
844
EnableAlertBeforeBackPage(JSContext * ctx,JSValueConst argv)845 JSValue EnableAlertBeforeBackPage(JSContext* ctx, JSValueConst argv)
846 {
847 ScopedString args(ctx, argv);
848 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
849
850 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
851 if (argsPtr != nullptr && argsPtr->IsObject()) {
852 if (argsPtr->GetValue(PROMPT_KEY_MESSAGE) == nullptr || !argsPtr->GetValue(PROMPT_KEY_MESSAGE)->IsString()) {
853 LOGE("enableAlertBeforeBackPage message is null");
854 const std::string fail = JsParseRouteUrl(ctx, argv, COMMON_FAIL);
855 const std::string complete = JsParseRouteUrl(ctx, argv, COMMON_COMPLETE);
856 instance->CallJs(fail, R"({"errMsg":"enableAlertBeforeBackPage:massage is null"})", false);
857 instance->CallJs(complete, R"({"errMsg":"enableAlertBeforeBackPage:massage is null"})", false);
858 return JS_NULL;
859 }
860 } else {
861 LOGE("enableAlertBeforeBackPage message is null");
862 std::string callBackStr(args.get());
863 // Get callbackId and clear redundant symbols
864 if (callBackStr.size() > 2 && callBackStr.front() == '\"' && callBackStr.back() == '\"') {
865 callBackStr = callBackStr.substr(1, callBackStr.size() - 2);
866 instance->CallJs(callBackStr,
867 R"({"arguments":[{"errMsg":"enableAlertBeforeBackPage:massage is null"}],"method":"fail"})", false);
868 }
869 return JS_NULL;
870 }
871
872 const std::string message = JsParseRouteUrl(ctx, argv, PROMPT_KEY_MESSAGE);
873 const std::string success = JsParseRouteUrl(ctx, argv, COMMON_SUCCESS);
874 const std::string fail = JsParseRouteUrl(ctx, argv, COMMON_FAIL);
875 const std::string complete = JsParseRouteUrl(ctx, argv, COMMON_COMPLETE);
876 auto callback = [instance, success, fail, complete](int32_t callbackType) {
877 switch (callbackType) {
878 case 0:
879 instance->CallJs(success, R"({"errMsg":"enableAlertBeforeBackPage:ok"})", false);
880 instance->CallJs(complete, R"({"errMsg":"enableAlertBeforeBackPage:ok"})", false);
881 break;
882 case 1:
883 instance->CallJs(fail, R"({"errMsg":"enableAlertBeforeBackPage:fail cancel"})", false);
884 instance->CallJs(complete, R"({"errMsg":"enableAlertBeforeBackPage:fail cancel"})", false);
885 break;
886 default:
887 break;
888 }
889 };
890 instance->GetDelegate()->EnableAlertBeforeBackPage(message, std::move(callback));
891 return JS_NULL;
892 }
893
DisableAlertBeforeBackPage(JSContext * ctx,JSValueConst argv)894 JSValue DisableAlertBeforeBackPage(JSContext* ctx, JSValueConst argv)
895 {
896 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
897 instance->GetDelegate()->DisableAlertBeforeBackPage();
898
899 ScopedString args(ctx, argv);
900 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
901 if (argsPtr->IsObject()) {
902 const std::string success = JsParseRouteUrl(ctx, argv, COMMON_SUCCESS);
903 const std::string complete = JsParseRouteUrl(ctx, argv, COMMON_COMPLETE);
904 instance->CallJs(success, R"({"errMsg":"disableAlertBeforeBackPage:ok"})", false);
905 instance->CallJs(complete, R"({"errMsg":"disableAlertBeforeBackPage:ok"})", false);
906 return JS_NULL;
907 }
908
909 std::string callBackStr(args.get());
910 // Get callbackId and clear redundant symbols
911 if (callBackStr.size() > 2 && callBackStr.front() == '\"' && callBackStr.back() == '\"') {
912 callBackStr = callBackStr.substr(1, callBackStr.size() - 2);
913 instance->CallJs(
914 callBackStr, R"({"arguments":[{"errMsg":"disableAlertBeforeBackPage:ok"}],"method":"success"})", false);
915 }
916 return JS_NULL;
917 }
918
PostponePageTransition(JSContext * ctx,JSValueConst argv)919 JSValue PostponePageTransition(JSContext* ctx, JSValueConst argv)
920 {
921 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
922 instance->GetDelegate()->PostponePageTransition();
923 return JS_NULL;
924 }
925
LaunchPageTransition(JSContext * ctx,JSValueConst argv)926 JSValue LaunchPageTransition(JSContext* ctx, JSValueConst argv)
927 {
928 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
929 instance->GetDelegate()->LaunchPageTransition();
930 return JS_NULL;
931 }
932
JsHandlePageRoute(JSContext * ctx,JSValueConst argv,const std::string & methodName)933 JSValue JsHandlePageRoute(JSContext* ctx, JSValueConst argv, const std::string& methodName)
934 {
935 std::string uri = "";
936 if (methodName == ROUTE_PAGE_BACK) {
937 uri = JsParseRouteUrlSpecial(ctx, argv);
938 } else {
939 uri = JsParseRouteUrl(ctx, argv, ROUTE_KEY_URI);
940 }
941 std::string params = JsParseRouteParams(ctx, argv, ROUTE_KEY_PARAMS);
942 bool dontOverwrite = JsParseRouteOverwrite(ctx, argv, ROUTE_KEY_DONT_OVERWRITE);
943
944 std::unique_ptr<JsonValue> routerParamsData = JsonUtil::Create(true);
945 routerParamsData->Put("paramsData", JsonUtil::ParseJsonString(params));
946 routerParamsData->Put("dontOverwrite", dontOverwrite);
947 params = routerParamsData->ToString();
948
949 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
950 // Operator map for page route.
951 static const LinearMapNode<JSValue (*)(const std::string&, const std::string&, QjsEngineInstance&)>
952 pageRouteOperators[] = {
953 { ROUTE_PAGE_BACK,
954 [](const std::string& uri, const std::string& params, QjsEngineInstance& instance) {
955 LOGD("JsBackRoute uri = %{private}s", uri.c_str());
956 instance.GetDelegate()->Back(uri, params);
957 return JS_NULL;
958 } },
959 { ROUTE_PAGE_CLEAR,
960 [](const std::string& uri, const std::string& params, QjsEngineInstance& instance) {
961 LOGD("Clear Page Route");
962 instance.GetDelegate()->Clear();
963 return JS_NULL;
964 } },
965 { ROUTE_PAGE_GET_LENGTH,
966 [](const std::string& uri, const std::string& params, QjsEngineInstance& instance) {
967 int32_t routeLength = instance.GetDelegate()->GetStackSize();
968 std::string indexLength = std::to_string(routeLength);
969 LOGD("JsGetLengthRoute routeLength=%{private}s", indexLength.c_str());
970 auto ctx = instance.GetQjsContext();
971 return JS_NewString(ctx, indexLength.c_str());
972 } },
973 { ROUTE_PAGE_GET_STATE,
974 [](const std::string& uri, const std::string& params, QjsEngineInstance& instance) {
975 int32_t routeIndex = 0;
976 std::string routeName;
977 std::string routePath;
978 instance.GetDelegate()->GetState(routeIndex, routeName, routePath);
979 LOGD("JsGetStateRoute index=%{private}d name=%{private}s path=%{private}s", routeIndex,
980 routeName.c_str(), routePath.c_str());
981
982 auto ctx = instance.GetQjsContext();
983 JSValue routeData = JS_NewObject(ctx);
984 JS_SetPropertyStr(ctx, routeData, "index", JS_NewInt32(ctx, routeIndex));
985 JS_SetPropertyStr(ctx, routeData, "name", JS_NewString(ctx, routeName.c_str()));
986 JS_SetPropertyStr(ctx, routeData, "path", JS_NewString(ctx, routePath.c_str()));
987 return routeData;
988 } },
989 { ROUTE_PAGE_PUSH,
990 [](const std::string& uri, const std::string& params, QjsEngineInstance& instance) {
991 LOGD("JsPushRoute uri = %{private}s", uri.c_str());
992 instance.GetDelegate()->Push(uri, params);
993 return JS_NULL;
994 } },
995 { ROUTE_PAGE_REPLACE,
996 [](const std::string& uri, const std::string& params, QjsEngineInstance& instance) {
997 LOGD("JsReplaceRoute uri = %{private}s", uri.c_str());
998 instance.GetDelegate()->Replace(uri, params);
999 return JS_NULL;
1000 } },
1001 };
1002 auto operatorIter = BinarySearchFindIndex(pageRouteOperators, ArraySize(pageRouteOperators), methodName.c_str());
1003 if (operatorIter != -1) {
1004 return pageRouteOperators[operatorIter].value(uri, params, *instance);
1005 } else if (methodName == ROUTE_ENABLE_ALERT_BEFORE_BACK_PAGE) {
1006 return EnableAlertBeforeBackPage(ctx, argv);
1007 } else if (methodName == ROUTE_DISABLE_ALERT_BEFORE_BACK_PAGE) {
1008 return DisableAlertBeforeBackPage(ctx, argv);
1009 } else if (methodName == ROUTE_POSTPONE) {
1010 return PostponePageTransition(ctx, argv);
1011 } else if (methodName == ROUTE_LAUNCH) {
1012 return LaunchPageTransition(ctx, argv);
1013 } else {
1014 LOGW("system.router not support method = %{private}s", methodName.c_str());
1015 }
1016 return JS_NULL;
1017 }
1018
JsShowToast(JSContext * ctx,JSValueConst argv)1019 JSValue JsShowToast(JSContext* ctx, JSValueConst argv)
1020 {
1021 ScopedString args(ctx, argv);
1022 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
1023 std::string message;
1024 int32_t duration = 0;
1025 std::string bottom;
1026 if (argsPtr != nullptr) {
1027 if (argsPtr->GetValue(PROMPT_KEY_MESSAGE) != nullptr) {
1028 if (argsPtr->GetValue(PROMPT_KEY_MESSAGE)->IsString()) {
1029 message = argsPtr->GetValue(PROMPT_KEY_MESSAGE)->GetString();
1030 } else {
1031 message = argsPtr->GetValue(PROMPT_KEY_MESSAGE)->ToString();
1032 }
1033 }
1034 if (argsPtr->GetValue(PROMPT_KEY_DURATION) != nullptr && argsPtr->GetValue(PROMPT_KEY_DURATION)->IsNumber()) {
1035 duration = argsPtr->GetValue(PROMPT_KEY_DURATION)->GetInt();
1036 }
1037 if (argsPtr->GetValue(PROMPT_KEY_BOTTOM) != nullptr) {
1038 if (argsPtr->GetValue(PROMPT_KEY_BOTTOM)->IsString()) {
1039 bottom = argsPtr->GetValue(PROMPT_KEY_BOTTOM)->GetString();
1040 } else if (argsPtr->GetValue(PROMPT_KEY_BOTTOM)->IsNumber()) {
1041 bottom = std::to_string(argsPtr->GetValue(PROMPT_KEY_BOTTOM)->GetInt());
1042 }
1043 }
1044 }
1045 LOGD("JsShowToast message = %{private}s duration = %{private}d bottom = %{private}s", message.c_str(), duration,
1046 bottom.c_str());
1047 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1048 ACE_DCHECK(instance);
1049 instance->GetDelegate()->ShowToast(message, duration, bottom);
1050 return JS_NULL;
1051 }
1052
JsShowDialog(JSContext * ctx,JSValueConst argv)1053 JSValue JsShowDialog(JSContext* ctx, JSValueConst argv)
1054 {
1055 const std::string title = JsParseRouteUrl(ctx, argv, PROMPT_KEY_TITLE);
1056 const std::string message = JsParseRouteUrl(ctx, argv, PROMPT_KEY_MESSAGE);
1057 std::vector<ButtonInfo> buttons = JsParseDialogButtons(ctx, argv, PROMPT_KEY_BUTTONS);
1058 const std::string success = JsParseRouteUrl(ctx, argv, COMMON_SUCCESS);
1059 const std::string cancel = JsParseRouteUrl(ctx, argv, COMMON_CANCEL);
1060 const std::string complete = JsParseRouteUrl(ctx, argv, COMMON_COMPLETE);
1061 bool autoCancel = true;
1062
1063 ScopedString args(ctx, argv);
1064 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
1065 if (argsPtr != nullptr && argsPtr->GetValue(PROMPT_DIALOG_AUTO_CANCEL) != nullptr &&
1066 argsPtr->GetValue(PROMPT_DIALOG_AUTO_CANCEL)->IsBool()) {
1067 autoCancel = argsPtr->GetValue(PROMPT_DIALOG_AUTO_CANCEL)->GetBool();
1068 }
1069
1070 std::set<std::string> callbacks;
1071 if (!success.empty()) {
1072 callbacks.emplace(COMMON_SUCCESS);
1073 }
1074 if (!cancel.empty()) {
1075 callbacks.emplace(COMMON_CANCEL);
1076 }
1077 if (!complete.empty()) {
1078 callbacks.emplace(COMMON_COMPLETE);
1079 }
1080
1081 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1082 auto callback = [instance, success, cancel, complete](int32_t callbackType, int32_t successType) {
1083 switch (callbackType) {
1084 case 0:
1085 instance->CallJs(success.c_str(),
1086 std::string("{\"index\":").append(std::to_string(successType)).append("}").c_str(), false);
1087 break;
1088 case 1:
1089 instance->CallJs(cancel.c_str(), std::string("\"cancel\",null").c_str(), false);
1090 break;
1091 case 2:
1092 instance->CallJs(complete.c_str(), std::string("\"complete\",null").c_str(), false);
1093 break;
1094 default:
1095 break;
1096 }
1097 };
1098 instance->GetDelegate()->ShowDialog(title, message, buttons, autoCancel, std::move(callback), callbacks);
1099 return JS_NULL;
1100 }
1101
JsShowActionMenu(JSContext * ctx,JSValueConst argv)1102 JSValue JsShowActionMenu(JSContext* ctx, JSValueConst argv)
1103 {
1104 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1105
1106 ScopedString args(ctx, argv);
1107 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
1108 if (argsPtr == nullptr) {
1109 LOGE("argsPtr is nullptr");
1110 return JS_NULL;
1111 }
1112
1113 std::vector<ButtonInfo> buttons = JsParseDialogButtons(ctx, argv, PROMPT_KEY_BUTTONS);
1114 // The number of buttons cannot be zero or more than six
1115 if (buttons.empty() || buttons.size() > 6) {
1116 LOGE("buttons is invalid");
1117 if (argsPtr->IsObject()) {
1118 const std::string fail = JsParseRouteUrl(ctx, argv, COMMON_FAIL);
1119 const std::string complete = JsParseRouteUrl(ctx, argv, COMMON_COMPLETE);
1120 instance->CallJs(fail, R"({"errMsg":"enableAlertBeforeBackPage:buttons is invalid"})", false);
1121 instance->CallJs(complete, R"({"errMsg":"enableAlertBeforeBackPage:buttons is invalid"})", false);
1122 } else {
1123 std::string callBackStr(args.get());
1124 // Get callbackId and clear redundant symbols
1125 if (callBackStr.size() > 2 && callBackStr.front() == '\"' && callBackStr.back() == '\"') {
1126 callBackStr = callBackStr.substr(1, callBackStr.size() - 2);
1127 instance->CallJs(callBackStr,
1128 R"({"arguments":[{"errMsg":"enableAlertBeforeBackPage:buttons is invalid"}],"method":"fail"})",
1129 false);
1130 }
1131 }
1132 return JS_NULL;
1133 }
1134
1135 const std::string title = JsParseRouteUrl(ctx, argv, PROMPT_KEY_TITLE);
1136 const std::string success = JsParseRouteUrl(ctx, argv, COMMON_SUCCESS);
1137 const std::string fail = JsParseRouteUrl(ctx, argv, COMMON_FAIL);
1138 const std::string complete = JsParseRouteUrl(ctx, argv, COMMON_COMPLETE);
1139 auto callback = [instance, success, fail, complete](int32_t callbackType, int32_t successType) {
1140 switch (callbackType) {
1141 case 0:
1142 instance->CallJs(success.c_str(), std::string(R"({"errMsg":"showActionMenu:ok","tapIndex":)")
1143 .append(std::to_string(successType)).append("}").c_str(), false);
1144 instance->CallJs(complete.c_str(), std::string(R"({"errMsg":"showActionMenu:ok","tapIndex":)")
1145 .append(std::to_string(successType)).append("}").c_str(), false);
1146 break;
1147 case 1:
1148 instance->CallJs(fail, R"({"errMsg":"showActionMenu:fail cancel"})", false);
1149 instance->CallJs(complete, R"({"errMsg":"showActionMenu:fail cancel"})", false);
1150 break;
1151 default:
1152 LOGE("callbackType is invalid");
1153 break;
1154 }
1155 };
1156 instance->GetDelegate()->ShowActionMenu(title, buttons, std::move(callback));
1157 return JS_NULL;
1158 }
1159
JsHandlePrompt(JSContext * ctx,JSValueConst argv,const std::string & methodName)1160 JSValue JsHandlePrompt(JSContext* ctx, JSValueConst argv, const std::string& methodName)
1161 {
1162 if (methodName == PROMPT_SHOW_TOAST) {
1163 return JsShowToast(ctx, argv);
1164 } else if (methodName == PROMPT_SHOW_DIALOG) {
1165 return JsShowDialog(ctx, argv);
1166 } else if (methodName == PROMPT_SHOW_ACTION_MENU) {
1167 return JsShowActionMenu(ctx, argv);
1168 } else {
1169 LOGW("system.prompt not support method = %{private}s", methodName.c_str());
1170 }
1171 return JS_NULL;
1172 }
1173
JsHandleAnimationFrame(JSContext * ctx,JSValueConst argv,const std::string & methodName)1174 JSValue JsHandleAnimationFrame(JSContext* ctx, JSValueConst argv, const std::string& methodName)
1175 {
1176 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1177 if (instance == nullptr) {
1178 return JS_NULL;
1179 }
1180 const char* callbackIdJsStr = JS_ToCString(ctx, argv);
1181 if (callbackIdJsStr == nullptr) {
1182 LOGW("system animation callbackId is null");
1183 return JS_NULL;
1184 }
1185 if (methodName == ANIMATION_REQUEST_ANIMATION_FRAME) {
1186 instance->GetDelegate()->RequestAnimationFrame(std::string(callbackIdJsStr));
1187 } else if (methodName == ANIMATION_CANCEL_ANIMATION_FRAME) {
1188 instance->GetDelegate()->CancelAnimationFrame(std::string(callbackIdJsStr));
1189 } else {
1190 LOGW("animationFrame not support method = %{private}s", methodName.c_str());
1191 }
1192 JS_FreeCString(ctx, callbackIdJsStr);
1193 return JS_NULL;
1194 }
1195
JsHandleAnimator(JSContext * ctx,JSValueConst argv,const std::string & methodName)1196 JSValue JsHandleAnimator(JSContext* ctx, JSValueConst argv, const std::string& methodName)
1197 {
1198 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1199 if (instance == nullptr) {
1200 return JS_NULL;
1201 }
1202 const char* args = JS_ToCString(ctx, argv);
1203 auto page = GetStagingPage(ctx);
1204 if (!page) {
1205 LOGE("JsHandleAnimator failed, page is null.");
1206 return JS_NULL;
1207 }
1208 auto resultAnimator = JSValue();
1209 if (methodName == ANIMATOR_CREATE_ANIMATOR || methodName == ANIMATOR_CREATE) {
1210 int32_t bridgeId = AnimatorBridgeUtils::JsCreateBridgeId();
1211 resultAnimator = AnimatorBridgeUtils::CreateAnimatorContext(ctx, page->GetPageId(), bridgeId);
1212 auto animatorBridge = AceType::MakeRefPtr<AnimatorBridge>(ctx, resultAnimator);
1213 auto task = AceType::MakeRefPtr<AnimatorTaskCreate>(animatorBridge, args);
1214 page->PushCommand(Referenced::MakeRefPtr<JsCommandAnimator>(bridgeId, task));
1215 }
1216 return resultAnimator;
1217 }
1218
JsAddListener(JSContext * ctx,JSValueConst argv)1219 JSValue JsAddListener(JSContext* ctx, JSValueConst argv)
1220 {
1221 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1222 if (instance == nullptr) {
1223 return JS_NULL;
1224 }
1225 JSPropertyEnum* pTab = nullptr;
1226 uint32_t len = 0;
1227 if (JS_IsObject(argv) && JS_IsArray(ctx, argv)) {
1228 JS_GetOwnPropertyNames(ctx, &pTab, &len, argv, JS_GPN_STRING_MASK);
1229 if (len == 0) {
1230 js_free(ctx, pTab);
1231 return JS_NULL;
1232 }
1233 JSValue jsListenerId = JS_GetProperty(ctx, argv, pTab[0].atom);
1234 const char* listenerId = JS_ToCString(ctx, jsListenerId);
1235 auto mediaQuery = instance->GetDelegate()->GetMediaQueryInfoInstance();
1236 if (mediaQuery) {
1237 mediaQuery->SetListenerId(std::string(listenerId));
1238 }
1239 JS_FreeCString(ctx, listenerId);
1240 JS_FreeValue(ctx, jsListenerId);
1241 js_free(ctx, pTab);
1242 }
1243 return JS_NULL;
1244 }
1245
JsParseCallbackResult(JSContext * ctx,JSValueConst jsResult,const std::string & key,std::string & result)1246 void JsParseCallbackResult(JSContext* ctx, JSValueConst jsResult, const std::string& key, std::string& result)
1247 {
1248 ScopedString args(ctx, jsResult);
1249 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(args.get());
1250 if (argsPtr != nullptr && argsPtr->GetValue(key) != nullptr) {
1251 if (argsPtr->GetValue(key)->IsString()) {
1252 result = argsPtr->GetValue(key)->GetString();
1253 } else if (argsPtr->GetValue(key)->IsNumber()) {
1254 result = argsPtr->GetValue(key)->ToString();
1255 }
1256 }
1257 }
1258
JsHandleCallback(JSContext * ctx,JSValueConst argv,const std::string & methodName)1259 JSValue JsHandleCallback(JSContext* ctx, JSValueConst argv, const std::string& methodName)
1260 {
1261 if (methodName == CALLBACK_NATIVE) {
1262 if (JS_IsObject(argv) && JS_IsArray(ctx, argv)) {
1263 JSValue jsCallbackId = JS_GetPropertyUint32(ctx, argv, 0);
1264 JSValue jsResult = JS_GetPropertyUint32(ctx, argv, 1);
1265 const char* callbackId = JS_ToCString(ctx, jsCallbackId);
1266 std::string result = JS_ToCString(ctx, jsResult);
1267 JsParseCallbackResult(ctx, jsResult, KEY_STEPPER_PENDING_INDEX, result);
1268 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1269 ACE_DCHECK(instance);
1270 instance->GetDelegate()->SetCallBackResult(std::string(callbackId), result);
1271
1272 JS_FreeCString(ctx, callbackId);
1273 JS_FreeValue(ctx, jsCallbackId);
1274 JS_FreeValue(ctx, jsResult);
1275 }
1276 } else if (methodName == APP_DESTROY_FINISH) {
1277 LOGD("JsHandleCallback: appDestroyFinish should release resource here");
1278 } else {
1279 LOGW("internal.jsResult not support method = %{private}s", methodName.c_str());
1280 }
1281 js_std_loop(ctx);
1282 return JS_NULL;
1283 }
1284
JsHandleImage(JSContext * ctx,JSValueConst argv)1285 JSValue JsHandleImage(JSContext* ctx, JSValueConst argv)
1286 {
1287 auto src = JsParseRouteUrl(ctx, argv, "src");
1288 auto success = JsParseRouteUrl(ctx, argv, "success");
1289 auto fail = JsParseRouteUrl(ctx, argv, "fail");
1290
1291 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1292 if (instance == nullptr) {
1293 return JS_NULL;
1294 }
1295
1296 auto&& callback = [instance, success, fail](bool callbackType, int32_t width, int32_t height) {
1297 if (callbackType) {
1298 instance->CallJs(success,
1299 std::string("{\"width\":")
1300 .append(std::to_string(width))
1301 .append(", \"height\":")
1302 .append(std::to_string(height))
1303 .append("}"),
1304 false);
1305 } else {
1306 instance->CallJs(fail, std::string("\"fail\",null"), false);
1307 }
1308 };
1309 instance->GetDelegate()->HandleImage(src, callback);
1310 return JS_NULL;
1311 }
1312
ParseResourceStringParam(std::string & str,const char * paramName,JSContext * ctx,const JSValue & jsObject)1313 bool ParseResourceStringParam(std::string& str, const char* paramName, JSContext* ctx, const JSValue& jsObject)
1314 {
1315 if (JS_IsObject(jsObject)) {
1316 JSValue strValue = JS_GetPropertyStr(ctx, jsObject, paramName);
1317 if (JS_IsString(strValue)) {
1318 ScopedString uriValueStr(ctx, strValue);
1319 str = uriValueStr.get();
1320 return true;
1321 }
1322 JS_FreeValue(ctx, strValue);
1323 }
1324 return false;
1325 }
1326
ParseResourceNumberParam(int32_t & num,const char * paramName,JSContext * ctx,const JSValue & jsObject)1327 bool ParseResourceNumberParam(int32_t& num, const char* paramName, JSContext* ctx, const JSValue& jsObject)
1328 {
1329 if (JS_IsObject(jsObject)) {
1330 JSValue numValue = JS_GetPropertyStr(ctx, jsObject, paramName);
1331 if (JS_IsUndefined(numValue)) {
1332 JS_FreeValue(ctx, numValue);
1333 return false;
1334 }
1335 ScopedString numValueStr(ctx, numValue);
1336 num = ParseResourceInputNumberParam(numValueStr.get());
1337 return true;
1338 }
1339 return false;
1340 }
1341
ParseResourceParam(std::string & uri,int32_t & position,int32_t & length,JSContext * ctx,const JSValue & jsObject)1342 void ParseResourceParam(std::string& uri, int32_t& position, int32_t& length, JSContext* ctx, const JSValue& jsObject)
1343 {
1344 ParseResourceStringParam(uri, READ_KEY_URI, ctx, jsObject);
1345 ParseResourceNumberParam(position, READ_KEY_POSITION, ctx, jsObject);
1346 ParseResourceNumberParam(length, READ_KEY_LENGTH, ctx, jsObject);
1347 }
1348
JsReadArrayBuffer(JSContext * ctx,JSValueConst argv)1349 JSValue JsReadArrayBuffer(JSContext* ctx, JSValueConst argv)
1350 {
1351 if (!JS_IsObject(argv) || !JS_IsArray(ctx, argv)) {
1352 LOGE("JsReadArrayBuffer: argv is not illegal");
1353 return JS_NULL;
1354 }
1355
1356 JSPropertyEnum* pTab = nullptr;
1357 uint32_t len = 0;
1358 JS_GetOwnPropertyNames(ctx, &pTab, &len, argv, JS_GPN_STRING_MASK);
1359 if (len < ARGS_READ_RESOURCE_LENGTH) {
1360 LOGE("JsReadArrayBuffer: invalid callback value");
1361 js_free(ctx, pTab);
1362 return JS_EXCEPTION;
1363 }
1364
1365 JSValue jsObject = JS_GetProperty(ctx, argv, pTab[0].atom);
1366 std::string uri;
1367 int32_t position = 1;
1368 int32_t length = MAX_READ_TEXT_LENGTH;
1369 ParseResourceParam(uri, position, length, ctx, jsObject);
1370 JSValue jsCallbackId = JS_GetProperty(ctx, argv, pTab[1].atom);
1371 JS_FreeValue(ctx, jsObject);
1372 js_free(ctx, pTab);
1373 ScopedString callbackId(ctx, jsCallbackId);
1374 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1375 std::smatch result;
1376 if (!std::regex_match(uri, result, URI_PATTERN)) {
1377 LOGE("JsReadArrayBuffer file uri pattern not correct");
1378 instance->CallJs(callbackId.get(),
1379 R"({"arguments":["file uri pattern not correct",202],"method":"fail"})");
1380 JS_FreeValue(ctx, jsCallbackId);
1381 return JS_NULL;
1382 }
1383
1384 std::vector<uint8_t> binaryContent;
1385 if (!(instance->GetDelegate()->GetResourceData(uri, binaryContent))) {
1386 instance->CallJs(callbackId.get(),
1387 R"({"arguments":["read file failed",301],"method":"fail"})");
1388 JS_FreeValue(ctx, jsCallbackId);
1389 return JS_NULL;
1390 }
1391
1392 auto fileLength = static_cast<int32_t>(binaryContent.size());
1393 if ((position > fileLength) || (position <= 0) || (length <= 0)) {
1394 instance->CallJs(callbackId.get(),
1395 R"({"arguments":["wrong start position or wrong read length", 202],"method":"fail"})");
1396 JS_FreeValue(ctx, jsCallbackId);
1397 return JS_NULL;
1398 }
1399
1400 length = position + length - 1 > fileLength ? fileLength - position + 1 : length;
1401 JSValue binaryData = JS_NewObject(ctx);
1402 JSValue binaryArray = JS_NewArrayBufferCopy(ctx, &binaryContent[position - 1], length);
1403 JS_SetPropertyStr(ctx, binaryData, "buffer", binaryArray);
1404 instance->CallJs(callbackId.get(), R"({"arguments":["read array buffer success"],"method":"success"})");
1405 js_std_loop(ctx);
1406 return binaryData;
1407 }
1408
JsReadText(JSContext * ctx,JSValueConst argv)1409 JSValue JsReadText(JSContext* ctx, JSValueConst argv)
1410 {
1411 if (!JS_IsObject(argv) || !JS_IsArray(ctx, argv)) {
1412 LOGE("JsReadText: argv is not illegal");
1413 return JS_NULL;
1414 }
1415
1416 JSPropertyEnum* pTab = nullptr;
1417 uint32_t len = 0;
1418 JS_GetOwnPropertyNames(ctx, &pTab, &len, argv, JS_GPN_STRING_MASK);
1419 if (len < ARGS_READ_RESOURCE_LENGTH) {
1420 LOGE("JsReadText: invalid callback value");
1421 js_free(ctx, pTab);
1422 return JS_EXCEPTION;
1423 }
1424
1425 JSValue jsObject = JS_GetProperty(ctx, argv, pTab[0].atom);
1426 std::string uri;
1427 ParseResourceStringParam(uri, READ_KEY_URI, ctx, jsObject);
1428 JSValue jsCallbackId = JS_GetProperty(ctx, argv, pTab[1].atom);
1429 ScopedString callbackId(ctx, jsCallbackId);
1430 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1431 std::smatch result;
1432 if (!std::regex_match(uri, result, URI_PATTERN)) {
1433 instance->CallJs(callbackId.get(),
1434 R"({"arguments":["file uri pattern not correct",202],"method":"fail"})");
1435 JS_FreeValue(ctx, jsCallbackId);
1436 JS_FreeValue(ctx, jsObject);
1437 js_free(ctx, pTab);
1438 return JS_NULL;
1439 }
1440
1441 std::string fileText;
1442 if (!(instance->GetDelegate()->GetResourceData(uri, fileText))) {
1443 instance->CallJs(callbackId.get(), R"({"arguments":["read file failed",301],"method":"fail"})");
1444 JS_FreeValue(ctx, jsCallbackId);
1445 JS_FreeValue(ctx, jsObject);
1446 js_free(ctx, pTab);
1447 return JS_NULL;
1448 }
1449
1450 int32_t position = 0;
1451 int32_t length = 0;
1452 auto fileLength = ParseUtf8TextLength(fileText);
1453 if (!ParseResourceNumberParam(position, READ_KEY_POSITION, ctx, jsObject)) {
1454 position = 1;
1455 }
1456
1457 if (!ParseResourceNumberParam(length, READ_KEY_LENGTH, ctx, jsObject) || (length > fileLength - position + 1)) {
1458 length = (fileLength - position + 1 <= 0) ? 0 : fileLength - position + 1;
1459 }
1460
1461 if (fileLength == 0) {
1462 if ((position <= 0) || (length < 0)) {
1463 instance->CallJs(callbackId.get(),
1464 R"({"arguments":["wrong start position or wrong read length",202],"method":"fail"})");
1465 JS_FreeValue(ctx, jsCallbackId);
1466 JS_FreeValue(ctx, jsObject);
1467 js_free(ctx, pTab);
1468 return JS_NULL;
1469 }
1470 } else {
1471 if ((position > fileLength) || (position <= 0) || (length < 0)) {
1472 instance->CallJs(callbackId.get(),
1473 R"({"arguments":["wrong start position or wrong read length",202],"method":"fail"})");
1474 JS_FreeValue(ctx, jsCallbackId);
1475 JS_FreeValue(ctx, jsObject);
1476 js_free(ctx, pTab);
1477 return JS_NULL;
1478 }
1479
1480 auto substrPos = ParseUtf8TextSubstrStartPos(fileText, position);
1481 auto substrEndPos = ParseUtf8TextSubstrEndPos(fileText, position + length - 1);
1482 if (substrPos - 1 < 0 || substrEndPos - substrPos < 0) {
1483 LOGE("JsReadText: Invalid value of substrPos: %{public}d and substrEndPos: %{public}d", substrPos,
1484 substrEndPos);
1485 return JS_NULL;
1486 }
1487 fileText =
1488 fileText.substr(static_cast<uint32_t>(substrPos - 1), static_cast<uint32_t>(substrEndPos - substrPos + 1));
1489 HandleEscapeCharaterInUtf8TextForJson(fileText);
1490 }
1491
1492 instance->CallJs(callbackId.get(),
1493 std::string("{\"arguments\":[").append("{\"text\":\"").append(fileText).append("\"}],\"method\":\"success\"}"),
1494 false);
1495 JS_FreeValue(ctx, jsCallbackId);
1496 JS_FreeValue(ctx, jsObject);
1497 js_std_loop(ctx);
1498 js_free(ctx, pTab);
1499 return JS_NULL;
1500 }
1501
JsHandleReadResource(JSContext * ctx,JSValueConst argv,const std::string & methodName)1502 JSValue JsHandleReadResource(JSContext* ctx, JSValueConst argv, const std::string& methodName)
1503 {
1504 LOGD("JsHandleReadResource");
1505 if (methodName == READ_TEXT) {
1506 return JsReadText(ctx, argv);
1507 } else if (methodName == READ_ARRAY_BUFFER) {
1508 return JsReadArrayBuffer(ctx, argv);
1509 } else {
1510 LOGW("system.resource not support method = %{private}s", methodName.c_str());
1511 }
1512 return JS_NULL;
1513 }
1514
JsSetTimer(JSContext * ctx,JSValueConst argv,bool isInterval)1515 void JsSetTimer(JSContext* ctx, JSValueConst argv, bool isInterval)
1516 {
1517 JSPropertyEnum* pTab = nullptr;
1518 uint32_t len = 0;
1519 if (JS_IsObject(argv) && JS_IsArray(ctx, argv)) {
1520 JS_GetOwnPropertyNames(ctx, &pTab, &len, argv, JS_GPN_STRING_MASK);
1521 if (len < 2) {
1522 LOGW("JsSetTimer: invalid callback value");
1523 return;
1524 }
1525
1526 JSValue jsCallBackId = JS_GetProperty(ctx, argv, pTab[0].atom);
1527 JSValue jsDelay = JS_GetProperty(ctx, argv, pTab[1].atom);
1528 const char* callBackId = JS_ToCString(ctx, jsCallBackId);
1529 const char* delay = JS_ToCString(ctx, jsDelay);
1530 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1531 ACE_DCHECK(instance);
1532 LOGD("JsSetTimer callbackId: %{private}s, delay: %{private}s", callBackId, delay);
1533 instance->GetDelegate()->WaitTimer(std::string(callBackId), std::string(delay), isInterval, true);
1534
1535 JS_FreeCString(ctx, callBackId);
1536 JS_FreeCString(ctx, delay);
1537 JS_FreeValue(ctx, jsCallBackId);
1538 JS_FreeValue(ctx, jsDelay);
1539 js_free(ctx, pTab);
1540 }
1541 }
1542
JsClearTimeout(JSContext * ctx,JSValueConst argv)1543 void JsClearTimeout(JSContext* ctx, JSValueConst argv)
1544 {
1545 JSPropertyEnum* pTab = nullptr;
1546 uint32_t len = 0;
1547 if (JS_IsObject(argv) && JS_IsArray(ctx, argv)) {
1548 JS_GetOwnPropertyNames(ctx, &pTab, &len, argv, JS_GPN_STRING_MASK);
1549 if (len < 1) {
1550 LOGW("JsClearTimeout: invalid callback value");
1551 return;
1552 }
1553
1554 JSValue jsCallBackId = JS_GetProperty(ctx, argv, pTab[0].atom);
1555 const char* callBackId = JS_ToCString(ctx, jsCallBackId);
1556 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1557 ACE_DCHECK(instance);
1558 LOGD("ClearTimeOut callBackId: %{private}s", callBackId);
1559 instance->GetDelegate()->ClearTimer(std::string(callBackId));
1560
1561 JS_FreeCString(ctx, callBackId);
1562 JS_FreeValue(ctx, jsCallBackId);
1563 js_free(ctx, pTab);
1564 }
1565 js_std_loop(ctx);
1566 }
1567
JsSetLocale(JSContext * ctx,JSValueConst argv)1568 JSValue JsSetLocale(JSContext* ctx, JSValueConst argv)
1569 {
1570 ScopedString args(ctx, argv);
1571 std::unique_ptr<JsonValue> localeJson = JsonUtil::ParseJsonString(args.get());
1572 if (localeJson) {
1573 std::string language;
1574 if (localeJson->GetValue(LOCALE_LANGUAGE) != nullptr && localeJson->GetValue(LOCALE_LANGUAGE)->IsString()) {
1575 language = localeJson->GetValue(LOCALE_LANGUAGE)->GetString();
1576 }
1577 std::string countryOrRegion;
1578 if (localeJson->GetValue(LOCALE_COUNTRY_OR_REGION) != nullptr &&
1579 localeJson->GetValue(LOCALE_COUNTRY_OR_REGION)->IsString()) {
1580 countryOrRegion = localeJson->GetValue(LOCALE_COUNTRY_OR_REGION)->GetString();
1581 }
1582 if (!countryOrRegion.empty() && !language.empty()) {
1583 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1584 ACE_DCHECK(instance);
1585 instance->ChangeLocale(language, countryOrRegion);
1586 }
1587 }
1588 return JS_NULL;
1589 }
1590
JsHandleSetTimeout(JSContext * ctx,JSValueConst argv,const std::string & methodName)1591 JSValue JsHandleSetTimeout(JSContext* ctx, JSValueConst argv, const std::string& methodName)
1592 {
1593 if (methodName == SET_TIMEOUT) {
1594 JsSetTimer(ctx, argv, false);
1595 } else if (methodName == CLEAR_TIMEOUT || methodName == CLEAR_INTERVAL) {
1596 JsClearTimeout(ctx, argv);
1597 } else if (methodName == SET_INTERVAL) {
1598 JsSetTimer(ctx, argv, true);
1599 } else {
1600 LOGW("Unsupported method for timer module!");
1601 return JS_NULL;
1602 }
1603 return JS_NULL;
1604 }
1605
JsHandleMediaQuery(JSContext * ctx,JSValueConst argv,const std::string & methodName)1606 JSValue JsHandleMediaQuery(JSContext* ctx, JSValueConst argv, const std::string& methodName)
1607 {
1608 if (methodName == ADD_LISTENER) {
1609 JsAddListener(ctx, argv);
1610 } else if (methodName == GET_DEVICE_TYPE) {
1611 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1612 if (instance != nullptr) {
1613 auto mediaQuery = instance->GetDelegate()->GetMediaQueryInfoInstance();
1614 if (mediaQuery) {
1615 return JS_NewString(ctx, mediaQuery->GetDeviceType().c_str());
1616 }
1617 }
1618 } else {
1619 LOGW("system.mediaquery not support method = %{private}s", methodName.c_str());
1620 }
1621 return JS_NULL;
1622 }
1623
GetAppInfo(JSContext * ctx)1624 JSValue GetAppInfo(JSContext* ctx)
1625 {
1626 QjsEngineInstance* instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1627 JSValue appID = JS_NewString(ctx, instance->GetDelegate()->GetAppID().c_str());
1628 JSValue appName = JS_NewString(ctx, instance->GetDelegate()->GetAppName().c_str());
1629 JSValue versionName = JS_NewString(ctx, instance->GetDelegate()->GetVersionName().c_str());
1630 JSValue versionCode = JS_NewInt32(ctx, instance->GetDelegate()->GetVersionCode());
1631
1632 JSValue resData = JS_NewObject(ctx);
1633 JS_SetPropertyStr(ctx, resData, "appID", appID);
1634 JS_SetPropertyStr(ctx, resData, "appName", appName);
1635 JS_SetPropertyStr(ctx, resData, "versionName", versionName);
1636 JS_SetPropertyStr(ctx, resData, "versionCode", versionCode);
1637
1638 return resData;
1639 }
1640
Terminate(JSContext * ctx)1641 JSValue Terminate(JSContext* ctx)
1642 {
1643 QjsEngineInstance* instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1644 WeakPtr<PipelineBase> pipelineContextWeak = instance->GetDelegate()->GetPipelineContext();
1645 auto uiTaskExecutor = instance->GetDelegate()->GetUiTask();
1646 uiTaskExecutor.PostTask([pipelineContextWeak]() mutable {
1647 auto pipelineContext = pipelineContextWeak.Upgrade();
1648 if (pipelineContext) {
1649 pipelineContext->Finish();
1650 }
1651 });
1652 return JS_NULL;
1653 }
1654
ParseFullWindowParams(JSContext * ctx,JSValue params,std::string & duration)1655 void ParseFullWindowParams(JSContext* ctx, JSValue params, std::string& duration)
1656 {
1657 JSPropertyEnum* tab = nullptr;
1658 uint32_t paramLen = 0;
1659 if (JS_IsObject(params)) {
1660 JS_GetOwnPropertyNames(ctx, &tab, ¶mLen, params, JS_GPN_STRING_MASK);
1661 const char* jsDurationKey = JS_AtomToCString(ctx, tab[0].atom);
1662 if (jsDurationKey == nullptr) {
1663 JS_FreeAtom(ctx, tab[0].atom);
1664 js_free(ctx, tab);
1665 LOGE("jsDurationKey is null.");
1666 return;
1667 }
1668 if (std::strcmp(jsDurationKey, "duration") == 0) {
1669 JSValue valObject = JS_GetProperty(ctx, params, tab[0].atom);
1670 if (JS_IsString(valObject) || JS_IsNumber(valObject)) {
1671 ScopedString styleVal(ctx, valObject);
1672 const char* valDuration = styleVal.get();
1673 duration = valDuration;
1674 }
1675 JS_FreeValue(ctx, valObject);
1676 }
1677 JS_FreeAtom(ctx, tab[0].atom);
1678 JS_FreeCString(ctx, jsDurationKey);
1679 }
1680 js_free(ctx, tab);
1681 }
1682
RequestFullWindow(JSContext * ctx,JSValueConst argv)1683 JSValue RequestFullWindow(JSContext* ctx, JSValueConst argv)
1684 {
1685 JSPropertyEnum* pTab = nullptr;
1686 uint32_t len = 0;
1687 int32_t duration = -1; // default scene
1688 if (JS_IsObject(argv) && JS_IsArray(ctx, argv)) {
1689 JS_GetOwnPropertyNames(ctx, &pTab, &len, argv, JS_GPN_STRING_MASK);
1690 if (len < ARGS_FULL_WINDOW_LENGTH) {
1691 LOGW("RequestFullWindow: invalid callback value");
1692 js_free(ctx, pTab);
1693 return JS_NULL;
1694 }
1695
1696 if (len == CUSTOM_FULL_WINDOW_LENGTH) {
1697 JSValue jsDuration = JS_GetProperty(ctx, argv, pTab[0].atom);
1698 std::string valDuration;
1699 ParseFullWindowParams(ctx, jsDuration, valDuration);
1700 if (!valDuration.empty()) {
1701 duration = StringToInt(valDuration);
1702 }
1703 if (duration < 0) {
1704 duration = -1; // default scene
1705 }
1706 JS_FreeValue(ctx, jsDuration);
1707 }
1708 js_free(ctx, pTab);
1709 }
1710 QjsEngineInstance* instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1711 WeakPtr<PipelineBase> pipelineContextWeak = instance->GetDelegate()->GetPipelineContext();
1712 auto uiTaskExecutor = instance->GetDelegate()->GetUiTask();
1713 uiTaskExecutor.PostTask([pipelineContextWeak, duration]() mutable {
1714 auto pipelineContext = pipelineContextWeak.Upgrade();
1715 if (pipelineContext) {
1716 pipelineContext->RequestFullWindow(duration);
1717 }
1718 });
1719 return JS_NULL;
1720 }
1721
GetPackageInfoCallback(JSContext * ctx,JSValueConst jsMessage,const char * callbackId)1722 void GetPackageInfoCallback(JSContext* ctx, JSValueConst jsMessage, const char* callbackId)
1723 {
1724 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1725 if (!JS_IsObject(jsMessage)) {
1726 LOGE("GetPackageInfoCallback: jsMessage is not Object");
1727 instance->CallJs(
1728 callbackId, std::string("{\"arguments\":[\"jsMessage is not Object\",200],\"method\":\"fail\"}"), false);
1729 return;
1730 }
1731 JSValue jsPackageName = JS_GetPropertyStr(ctx, jsMessage, APP_PACKAGE_NAME);
1732 if (!JS_IsString(jsPackageName)) {
1733 LOGE("GetPackageInfoCallback: package name is not String");
1734 instance->CallJs(callbackId,
1735 std::string("{\"arguments\":[\"packageName is not available string\",202],\"method\":\"fail\"}"), false);
1736 } else {
1737 ScopedString packageName(ctx, jsPackageName);
1738 AceBundleInfo bundleInfo;
1739 if (AceApplicationInfo::GetInstance().GetBundleInfo(packageName.get(), bundleInfo)) {
1740 auto infoList = JsonUtil::Create(true);
1741 infoList->Put("versionName", bundleInfo.versionName.c_str());
1742 infoList->Put("versionCode", std::to_string(bundleInfo.versionCode).c_str());
1743 instance->CallJs(callbackId,
1744 std::string("{\"arguments\":[").append(infoList->ToString()).append("],\"method\":\"success\"}"),
1745 false);
1746 } else {
1747 LOGE("can not get info by GetBundleInfo");
1748 instance->CallJs(
1749 callbackId, std::string("{\"arguments\":[\"can not get info\",200],\"method\":\"fail\"}"), false);
1750 }
1751 }
1752 JS_FreeValue(ctx, jsPackageName);
1753 }
1754
GetPackageInfo(JSContext * ctx,JSValueConst argv)1755 JSValue GetPackageInfo(JSContext* ctx, JSValueConst argv)
1756 {
1757 if (!JS_IsObject(argv)) {
1758 LOGE("GetPackageInfo: argv is not Object");
1759 return JS_NULL;
1760 }
1761
1762 if (!JS_IsArray(ctx, argv)) {
1763 LOGE("GetPackageInfo: argv is not Array");
1764 return JS_NULL;
1765 }
1766
1767 JSPropertyEnum* pTab = nullptr;
1768 uint32_t len = 0;
1769
1770 JS_GetOwnPropertyNames(ctx, &pTab, &len, argv, JS_GPN_STRING_MASK);
1771 if (len < 2) {
1772 LOGE("GetPackageInfo: invalid callback value");
1773 js_free(ctx, pTab);
1774 return JS_EXCEPTION;
1775 }
1776
1777 JSValue jsMessage = JS_GetProperty(ctx, argv, pTab[0].atom);
1778 JSValue jsCallbackId = JS_GetProperty(ctx, argv, pTab[1].atom);
1779 const char* callbackId = JS_ToCString(ctx, jsCallbackId);
1780 LOGD("system app getPackageInfo callBackID is %{public}s", callbackId);
1781 GetPackageInfoCallback(ctx, jsMessage, callbackId);
1782
1783 JS_FreeCString(ctx, callbackId);
1784 JS_FreeValue(ctx, jsMessage);
1785 JS_FreeValue(ctx, jsCallbackId);
1786 js_free(ctx, pTab);
1787
1788 return JS_NULL;
1789 }
1790
SetScreenOnVisible(JSContext * ctx,JSValueConst argv)1791 JSValue SetScreenOnVisible(JSContext* ctx, JSValueConst argv)
1792 {
1793 if (!JS_IsObject(argv) || !JS_IsArray(ctx, argv)) {
1794 LOGE("SetScreenOnVisible: argv is not illegal");
1795 return JS_NULL;
1796 }
1797 JSPropertyEnum* pTab = nullptr;
1798 uint32_t len = 0;
1799 JS_GetOwnPropertyNames(ctx, &pTab, &len, argv, JS_GPN_STRING_MASK);
1800 if (len < 2) {
1801 LOGE("SetScreenOnVisible: invalid callback value");
1802 js_free(ctx, pTab);
1803 return JS_EXCEPTION;
1804 }
1805 JSValue jsObject = JS_GetProperty(ctx, argv, pTab[0].atom);
1806 std::string flag = "false";
1807 if (JS_IsObject(jsObject)) {
1808 JSValue flagValue = JS_GetPropertyStr(ctx, jsObject, APP_SCREEN_ON_VISIBLE_FLAG);
1809 if (JS_IsBool(flagValue)) {
1810 ScopedString flagValueStr(ctx, flagValue);
1811 flag = flagValueStr.get();
1812 }
1813 }
1814 JSValue jsCallbackId = JS_GetProperty(ctx, argv, pTab[1].atom);
1815 ScopedString callbackId(ctx, jsCallbackId);
1816 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1817 if (StringToBool(flag)) {
1818 instance->CallJs(callbackId.get(), R"({"arguments":[], "method":"success"})");
1819 } else {
1820 instance->CallJs(
1821 callbackId.get(), R"({"arguments":["fail to set false flag in rich platform", 200],"method":"fail"})");
1822 }
1823 JS_FreeValue(ctx, jsObject);
1824 JS_FreeValue(ctx, jsCallbackId);
1825 js_free(ctx, pTab);
1826 return JS_NULL;
1827 }
1828
SetSwipeToDismiss(JSContext * ctx,JSValueConst argv)1829 JSValue SetSwipeToDismiss(JSContext* ctx, JSValueConst argv)
1830 {
1831 if (!JS_IsObject(argv) || !JS_IsArray(ctx, argv)) {
1832 LOGE("SetSwipeToDismiss: argv is not illegal");
1833 return JS_NULL;
1834 }
1835
1836 JSPropertyEnum* pTab = nullptr;
1837 uint32_t len = 0;
1838 JS_GetOwnPropertyNames(ctx, &pTab, &len, argv, JS_GPN_STRING_MASK);
1839 if (len < 2) {
1840 LOGE("SetSwipeToDismiss: invalid callback value");
1841 js_free(ctx, pTab);
1842 return JS_EXCEPTION;
1843 }
1844
1845 bool forbidQuit = false;
1846 JSValue jsObject = JS_GetProperty(ctx, argv, pTab[0].atom);
1847 if (JS_IsBool(jsObject)) {
1848 forbidQuit = JS_ToBool(ctx, jsObject);
1849 }
1850
1851 QjsEngineInstance* instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1852 WeakPtr<PipelineContext> pipelineContextWeak =
1853 AceType::DynamicCast<PipelineContext>(instance->GetDelegate()->GetPipelineContext());
1854 auto uiTaskExecutor = instance->GetDelegate()->GetUiTask();
1855 uiTaskExecutor.PostTask([pipelineContextWeak, forbidQuit]() mutable {
1856 auto pipelineContext = pipelineContextWeak.Upgrade();
1857 if (pipelineContext) {
1858 pipelineContext->SetForbidPlatformQuit(forbidQuit);
1859 }
1860 });
1861
1862 JS_FreeValue(ctx, jsObject);
1863 js_free(ctx, pTab);
1864 return JS_NULL;
1865 }
1866
JsHandleAppApi(JSContext * ctx,JSValueConst argv,const std::string & methodName)1867 JSValue JsHandleAppApi(JSContext* ctx, JSValueConst argv, const std::string& methodName)
1868 {
1869 if (methodName == APP_GET_INFO) {
1870 return GetAppInfo(ctx);
1871 } else if (methodName == APP_TERMINATE) {
1872 return Terminate(ctx);
1873 } else if (methodName == APP_GET_PACKAGE_INFO) {
1874 return GetPackageInfo(ctx, argv);
1875 } else if (methodName == APP_REQUEST_FULL_WINDOW) {
1876 return RequestFullWindow(ctx, argv);
1877 } else if (methodName == APP_SCREEN_ON_VISIBLE) {
1878 return SetScreenOnVisible(ctx, argv);
1879 } else if (methodName == APP_SET_SWIPE_TO_DISMISS) {
1880 return SetSwipeToDismiss(ctx, argv);
1881 } else {
1882 return JS_NULL;
1883 }
1884 }
1885
JsGetDeviceInfo(JSContext * ctx,JSValueConst argv)1886 JSValue JsGetDeviceInfo(JSContext* ctx, JSValueConst argv)
1887 {
1888 const char* callbackId = JS_ToCString(ctx, argv);
1889 if (callbackId == nullptr) {
1890 LOGE("system device getInfo callBackID is null");
1891 } else {
1892 LOGD("system device getInfo callBackID = %{public}s", callbackId);
1893 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1894 std::pair<std::string, bool> ret = PluginBridge::GetDeviceInfo();
1895 if (ret.second) {
1896 instance->CallJs(callbackId,
1897 std::string("{\"arguments\":[").append(ret.first).append("],\"method\":\"success\"}"), false);
1898 } else {
1899 instance->CallJs(callbackId,
1900 std::string("{\"arguments\":[").append(ret.first).append(",200],\"method\":\"fail\"}"), false);
1901 }
1902 JS_FreeCString(ctx, callbackId);
1903 }
1904
1905 return JS_NULL;
1906 }
1907
GetLocale(JSContext * ctx)1908 JSValue GetLocale(JSContext* ctx)
1909 {
1910 JSValue language = JS_NewString(ctx, AceApplicationInfo::GetInstance().GetLanguage().c_str());
1911 JSValue countryOrRegion = JS_NewString(ctx, AceApplicationInfo::GetInstance().GetCountryOrRegion().c_str());
1912 JSValue dir = JS_NewString(
1913 ctx, AceApplicationInfo::GetInstance().IsRightToLeft() ? LOCALE_TEXT_DIR_RTL : LOCALE_TEXT_DIR_LTR);
1914 JSValue unicodeSetting = JS_NewString(ctx, AceApplicationInfo::GetInstance().GetUnicodeSetting().c_str());
1915
1916 JSValue resData = JS_NewObject(ctx);
1917 JS_SetPropertyStr(ctx, resData, LOCALE_LANGUAGE, language);
1918 JS_SetPropertyStr(ctx, resData, LOCALE_COUNTRY_OR_REGION, countryOrRegion);
1919 JS_SetPropertyStr(ctx, resData, LOCALE_TEXT_DIR, dir);
1920 JS_SetPropertyStr(ctx, resData, LOCALE_UNICODE_SETTING, unicodeSetting);
1921
1922 return resData;
1923 }
1924
JsCallComponent(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1925 JSValue JsCallComponent(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1926 {
1927 if ((argv == nullptr) || (argc != 3)) {
1928 LOGE("argc error, argc = %{private}d", argc);
1929 return JS_NULL;
1930 }
1931 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1932 if (instance == nullptr) {
1933 LOGE("JsCallComponent failed, instance is null.");
1934 return JS_NULL;
1935 }
1936 auto page = GetRunningPage(ctx);
1937 if (!page) {
1938 LOGE("JsCallComponent failed, page is null.");
1939 return JS_NULL;
1940 }
1941 int32_t nodeId;
1942 JS_ToInt32(ctx, &nodeId, argv[0]);
1943 nodeId = (nodeId == 0) ? DOM_ROOT_NODE_ID_BASE + page->GetPageId() : nodeId;
1944 ScopedString methodName(ctx, argv[1]);
1945 ScopedString args(ctx, argv[2]);
1946 LOGD("nodeId = %{private}d, methodName = %{private}s, args = %{private}s", nodeId, methodName.get(), args.get());
1947 auto engine = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1948 if (engine == nullptr) {
1949 return JS_NULL;
1950 }
1951 // handle canvas getContext
1952 if (std::strcmp(methodName.get(), "getContext") == 0) {
1953 auto bridge = AceType::DynamicCast<CanvasBridge>(page->GetBridgeById(nodeId));
1954 if (bridge) {
1955 bridge->HandleJsContext(ctx, nodeId, args.get(), engine);
1956 return bridge->GetRenderContext();
1957 }
1958 return JS_NULL;
1959 } else if (std::strcmp(methodName.get(), "toDataURL") == 0) {
1960 auto bridge = AceType::DynamicCast<CanvasBridge>(page->GetBridgeById(nodeId));
1961 if (bridge) {
1962 bridge->HandleToDataURL(ctx, nodeId, args.get());
1963 return bridge->GetDataURL();
1964 }
1965 return JS_NULL;
1966 } else if (std::strcmp(methodName.get(), "getScrollOffset") == 0) {
1967 // handle getScrollOffset method of scroll view, like div, stack and list.
1968 return ComponentApiBridge::JsGetScrollOffset(ctx, nodeId);
1969 } else if (std::strcmp(methodName.get(), "getBoundingClientRect") == 0) {
1970 return ComponentApiBridge::JsGetBoundingRect(ctx, nodeId);
1971 } else if (std::strcmp(methodName.get(), "getInspector") == 0) {
1972 return ComponentApiBridge::JsGetInspector(ctx, nodeId);
1973 } else if (std::strcmp(methodName.get(), "scrollTo") == 0) {
1974 ComponentApiBridge::JsScrollTo(ctx, args.get(), nodeId);
1975 } else if (std::strcmp(methodName.get(), "getXComponentContext") == 0) {
1976 auto bridge = AceType::DynamicCast<QjsXComponentBridge>(page->GetXComponentBridgeById(nodeId));
1977 if (bridge) {
1978 bridge->HandleContext(ctx, nodeId, args.get());
1979 return bridge->GetRenderContext();
1980 }
1981 } else if (std::strcmp(methodName.get(), "getXComponentSurfaceId") == 0) {
1982 return QjsXComponentBridge::JsGetXComponentSurfaceId(ctx, nodeId);
1983 } else if (std::strcmp(methodName.get(), "setXComponentSurfaceSize") == 0) {
1984 QjsXComponentBridge::JsSetXComponentSurfaceSize(ctx, args.get(), nodeId);
1985 return JS_NULL;
1986 }
1987
1988 auto resultValue = JSValue();
1989 // handle animation animate method
1990 if (std::strcmp(methodName.get(), "animate") == 0) {
1991 resultValue = AnimationBridgeUtils::CreateAnimationContext(ctx, page->GetPageId(), nodeId);
1992 auto animationBridge = AceType::MakeRefPtr<AnimationBridge>(ctx, resultValue, nodeId);
1993 auto task = AceType::MakeRefPtr<AnimationBridgeTaskCreate>(animationBridge, args.get());
1994 page->PushCommand(Referenced::MakeRefPtr<JsCommandAnimation>(nodeId, task));
1995 } else if (std::strcmp(methodName.get(), "currentOffset") == 0) {
1996 // handle list currentOffset method
1997 return ListBridge::JsGetCurrentOffset(ctx, nodeId);
1998 } else if (std::strcmp(methodName.get(), "getState") == 0) {
1999 // handle image animator getState method.
2000 return ImageAnimatorBridge::JsGetState(ctx, nodeId);
2001 } else if (std::strcmp(methodName.get(), "createIntersectionObserver") == 0) {
2002 return ComponentApiBridge::JsCreateObserver(ctx, args.get(), nodeId);
2003 } else if (std::strcmp(methodName.get(), "addChild") == 0) {
2004 auto sPage = GetStagingPage(ctx);
2005 if (sPage == nullptr) {
2006 return JS_EXCEPTION;
2007 }
2008 int32_t childNodeId = 0;
2009 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args.get());
2010 if (argsValue && argsValue->IsArray()) {
2011 std::unique_ptr<JsonValue> cNodeId = argsValue->GetArrayItem(0)->GetValue("__nodeId");
2012 if (cNodeId && cNodeId->IsNumber()) {
2013 childNodeId = cNodeId->GetInt();
2014 }
2015 }
2016 auto domDocument = sPage->GetDomDocument();
2017 if (domDocument) {
2018 RefPtr<DOMNode> node = domDocument->GetDOMNodeById(childNodeId);
2019 if (node == nullptr) {
2020 LOGE("node is nullptr");
2021 }
2022 auto command = Referenced::MakeRefPtr<JsCommandAppendElement>(node ? node->GetTag() : "", childNodeId,
2023 nodeId);
2024 sPage->PushCommand(command);
2025 if (!sPage->CheckPageCreated() && sPage->GetCommandSize() > FRAGMENT_SIZE) {
2026 sPage->FlushCommands();
2027 }
2028 }
2029 } else {
2030 page->PushCommand(Referenced::MakeRefPtr<JsCommandCallDomElementMethod>(nodeId, methodName.get(), args.get()));
2031 }
2032 // focus method should delayed util show attribute update.
2033 if (page->CheckPageCreated() && strlen(DOM_FOCUS) >= strlen(methodName.get()) &&
2034 strcmp(DOM_FOCUS, methodName.get()) != 0) {
2035 instance->GetDelegate()->TriggerPageUpdate(page->GetPageId(), true);
2036 }
2037 return resultValue;
2038 }
2039
JsCallConfiguration(JSContext * ctx,const std::string & method,JSValueConst argv)2040 JSValue JsCallConfiguration(JSContext* ctx, const std::string& method, JSValueConst argv)
2041 {
2042 if (CONFIGURATION_GET_LOCALE == method) {
2043 return GetLocale(ctx);
2044 } else if (CONFIGURATION_SET_LOCALE == method) {
2045 return JsSetLocale(ctx, argv);
2046 }
2047 return JS_NULL;
2048 }
2049
2050
JsHandleOffscreenCanvas(JSContext * ctx,JSValueConst value,JSValueConst argv,const std::string & methodName)2051 JSValue JsHandleOffscreenCanvas(JSContext* ctx, JSValueConst value, JSValueConst argv, const std::string& methodName)
2052 {
2053 LOGD("JsHandleOffscreenCanvas: methodName = %{public}s", methodName.c_str());
2054
2055 auto page = GetRunningPage(ctx);
2056 if (!page) {
2057 LOGE("JsHandleOffscreenCanvas failed, page is null.");
2058 return JS_NULL;
2059 }
2060
2061 if (methodName == OFFSCREEN_CANVAS_CREATE) {
2062 int32_t width = JsParseIntParams(ctx, argv, "width");
2063 int32_t height = JsParseIntParams(ctx, argv, "height");
2064
2065 auto bridge = AceType::MakeRefPtr<OffscreenCanvasBridge>(ctx, width, height);
2066 page->PushOffscreenCanvasBridge(bridge->bridgeId_, bridge);
2067
2068 return bridge->GetBridge(ctx);
2069
2070 } else {
2071 LOGE("JsHandleOffscreenCanvas: methodName not find");
2072 }
2073
2074 return JS_NULL;
2075 }
JsHandleModule(std::string moduleName,std::string methodName,JSContext * ctx,JSValueConst value,JSValueConst * argv)2076 JSValue JsHandleModule(std::string moduleName, std::string methodName, JSContext* ctx, JSValueConst value,
2077 JSValueConst* argv)
2078 {
2079 static const LinearMapNode<JSValue (*)(JSContext*, JSValueConst, JSValueConst*,
2080 const std::string&)> jsHandleMap[] = {
2081 { "animation",
2082 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2083 return JsHandleAnimationFrame(ctx, argv[1], methodName);
2084 } },
2085 { "internal.jsResult",
2086 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2087 return JsHandleCallback(ctx, argv[1], methodName);
2088 } },
2089 { "ohos.animator",
2090 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2091 return JsHandleAnimator(ctx, argv[1], methodName);
2092 } },
2093 { "system.app",
2094 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2095 return JsHandleAppApi(ctx, argv[1], methodName);
2096 } },
2097 { "system.configuration",
2098 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2099 return JsCallConfiguration(ctx, methodName, argv[1]);
2100 } },
2101 { "system.device",
2102 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2103 if (methodName == "getInfo") {
2104 return JsGetDeviceInfo(ctx, argv[1]);
2105 } else {
2106 return JS_NULL;
2107 }
2108 } },
2109 { "system.image",
2110 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2111 return JsHandleImage(ctx, argv[1]);
2112 } },
2113 { "system.mediaquery",
2114 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2115 return JsHandleMediaQuery(ctx, argv[1], methodName);
2116 } },
2117 { "system.offscreenCanvas",
2118 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2119 return JsHandleOffscreenCanvas(ctx, value, argv[1], methodName);
2120 } },
2121 { "system.prompt",
2122 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2123 return JsHandlePrompt(ctx, argv[1], methodName);
2124 } },
2125 { "system.resource",
2126 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2127 return JsHandleReadResource(ctx, argv[1], methodName);
2128 } },
2129 { "system.router",
2130 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2131 return JsHandlePageRoute(ctx, argv[1], methodName);
2132 } },
2133 { "timer",
2134 [](JSContext* ctx, JSValueConst value, JSValueConst* argv, const std::string& methodName) {
2135 return JsHandleSetTimeout(ctx, argv[1], methodName);
2136 } },
2137 };
2138
2139 auto jsHandleIter = BinarySearchFindIndex(jsHandleMap, ArraySize(jsHandleMap), moduleName.c_str());
2140 if (jsHandleIter != -1) {
2141 return jsHandleMap[jsHandleIter].value(ctx, value, argv, methodName);
2142 }
2143
2144 return JS_NULL;
2145 }
2146
JsCallNative(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2147 JSValue JsCallNative(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2148 {
2149 ACE_SCOPED_TRACE("JsCallNative");
2150
2151 if ((argv == nullptr) || (argc != 2)) {
2152 LOGE("the arg is error");
2153 return JS_EXCEPTION;
2154 }
2155
2156 ScopedString moduleAndMethod(ctx, argv[0]);
2157 ScopedString args(ctx, argv[1]);
2158 LOGD("JsCallNative moduleAndMethod = %{private}s, args = %{private}s", moduleAndMethod.get(), args.get());
2159
2160 std::unique_ptr<JsonValue> moduleAndMethodPtr = JsonUtil::ParseJsonString(moduleAndMethod.get());
2161 if (!moduleAndMethodPtr) {
2162 LOGE("Get moduleAndMethod from argv failed");
2163 return JS_NULL;
2164 }
2165
2166 std::unique_ptr<JsonValue> modulePtr = moduleAndMethodPtr->GetValue("module");
2167 if (!modulePtr) {
2168 LOGE("Get module from moduleAndMethodPtr failed");
2169 return JS_NULL;
2170 }
2171
2172 std::unique_ptr<JsonValue> methodPtr = moduleAndMethodPtr->GetValue("method");
2173 if (!methodPtr) {
2174 LOGE("Get method from moduleAndMethodPtr failed");
2175 return JS_NULL;
2176 }
2177
2178 const std::string moduleName = modulePtr->GetString();
2179 const std::string methodName = methodPtr->GetString();
2180
2181 LOGD("JsCallNative moduleName = %{private}s, methodName = %{private}s", moduleName.c_str(), methodName.c_str());
2182 js_std_loop(ctx);
2183 return JsHandleModule(moduleName, methodName, ctx, value, argv);
2184 }
2185
2186 #if defined(ENABLE_JS_DEBUG) || defined(ENABLE_JS_DEBUG_PREVIEW)
JsCompileAndRunBundle(JSContext * ctx,JSValueConst thisVal,int argc,JSValueConst * argv)2187 JSValue JsCompileAndRunBundle(JSContext* ctx, JSValueConst thisVal, int argc, JSValueConst* argv)
2188 {
2189 ScopedString str(ctx, argv[0]);
2190 ScopedString url(ctx, argv[1]);
2191 JSValue CppToJSRet = JS_Eval(ctx, str.get(), strlen(str.get()), url.get(), JS_EVAL_TYPE_GLOBAL);
2192
2193 if (JS_IsException(CppToJSRet)) {
2194 LOGE("Qjs JsCompileAndRunBundle FAILED !!");
2195 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::COMPILE_AND_RUN_BUNDLE_ERROR);
2196 return JS_NULL;
2197 }
2198
2199 js_std_loop(ctx);
2200 return CppToJSRet;
2201 }
2202 #endif
2203
ParseLogContent(JSContext * ctx,int32_t argc,JSValueConst * argv)2204 std::string ParseLogContent(JSContext* ctx, int32_t argc, JSValueConst* argv)
2205 {
2206 std::string formatStr = ScopedString(ctx, argv[0]).get();
2207 if (argc == 1 || formatStr.empty()) {
2208 return formatStr;
2209 }
2210 std::string ret;
2211 size_t len = formatStr.size();
2212 size_t pos = 0;
2213 int32_t count = 1;
2214 for (; pos < len; ++pos) {
2215 if (count >= argc) {
2216 break;
2217 }
2218 if (formatStr[pos] == '%') {
2219 if (pos + 1 >= len) {
2220 break;
2221 }
2222 switch (formatStr[pos + 1]) {
2223 case 's':
2224 case 'j':
2225 case 'd':
2226 case 'O':
2227 case 'o':
2228 case 'i':
2229 case 'f':
2230 case 'c':
2231 ret += ScopedString(ctx, argv[count++]).get();
2232 ++pos;
2233 break;
2234 case '%':
2235 ret += formatStr[pos];
2236 ++pos;
2237 break;
2238 default:
2239 ret += formatStr[pos];
2240 break;
2241 }
2242 } else {
2243 ret += formatStr[pos];
2244 }
2245 }
2246 if (pos < len) {
2247 ret += formatStr.substr(pos, len - pos);
2248 }
2249 return ret;
2250 }
2251
AppLogPrint(JSContext * ctx,JsLogLevel level,JSValueConst value,int32_t argc,JSValueConst * argv)2252 JSValue AppLogPrint(JSContext* ctx, JsLogLevel level, JSValueConst value, int32_t argc, JSValueConst* argv)
2253 {
2254 ACE_SCOPED_TRACE("AppLogPrint(level=%d)", static_cast<int32_t>(level));
2255
2256 // Should have 1 parameters.
2257 if ((argv == nullptr) || (argc == 0)) {
2258 LOGE("the arg is error");
2259 return JS_EXCEPTION;
2260 }
2261 std::string printLog = ParseLogContent(ctx, argc, argv);
2262 switch (level) {
2263 case JsLogLevel::DEBUG:
2264 APP_LOGD("app Log: %{public}s", printLog.c_str());
2265 break;
2266 case JsLogLevel::INFO:
2267 APP_LOGI("app Log: %{public}s", printLog.c_str());
2268 break;
2269 case JsLogLevel::WARNING:
2270 APP_LOGW("app Log: %{public}s", printLog.c_str());
2271 break;
2272 case JsLogLevel::ERROR:
2273 APP_LOGE("app Log: %{public}s", printLog.c_str());
2274 break;
2275 default:
2276 break;
2277 }
2278
2279 return JS_NULL;
2280 }
2281
AppLogPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2282 JSValue AppLogPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2283 {
2284 return AppLogPrint(ctx, JsLogLevel::INFO, value, argc, argv);
2285 }
2286
AppDebugLogPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2287 JSValue AppDebugLogPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2288 {
2289 return AppLogPrint(ctx, JsLogLevel::DEBUG, value, argc, argv);
2290 }
2291
AppInfoLogPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2292 JSValue AppInfoLogPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2293 {
2294 return AppLogPrint(ctx, JsLogLevel::INFO, value, argc, argv);
2295 }
2296
AppWarnLogPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2297 JSValue AppWarnLogPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2298 {
2299 return AppLogPrint(ctx, JsLogLevel::WARNING, value, argc, argv);
2300 }
2301
AppErrorLogPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2302 JSValue AppErrorLogPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2303 {
2304 return AppLogPrint(ctx, JsLogLevel::ERROR, value, argc, argv);
2305 }
2306
GetNodeId(JSContext * ctx,JSValueConst value)2307 int32_t GetNodeId(JSContext* ctx, JSValueConst value)
2308 {
2309 int32_t id = 0;
2310 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__nodeId");
2311 if (JS_IsInteger(nodeId) && (JS_ToInt32(ctx, &id, nodeId)) < 0) {
2312 id = 0;
2313 }
2314 JS_FreeValue(ctx, nodeId);
2315 return id;
2316 }
2317
JsSetAttribute(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2318 JSValue JsSetAttribute(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2319 {
2320 auto page = GetRunningPage(ctx);
2321 if (page == nullptr) {
2322 return JS_EXCEPTION;
2323 }
2324 if (!JS_IsString(argv[0]) || !JS_IsString(argv[1])) {
2325 LOGE("args is not string ");
2326 return JS_NULL;
2327 }
2328 ScopedString key(ctx, argv[0]);
2329 JSValue attr = JS_NewObject(ctx);
2330 JS_SetPropertyStr(ctx, attr, key.get(), argv[1]);
2331
2332 int32_t nodeId = GetNodeId(ctx, value);
2333 auto command = Referenced::MakeRefPtr<JsCommandUpdateDomElementAttrs>(nodeId);
2334 if (SetDomAttributes(ctx, attr, *command)) {
2335 page->ReserveShowCommand(command);
2336 }
2337 page->PushCommand(command);
2338 return JS_NULL;
2339 }
2340
JsSetStyle(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2341 JSValue JsSetStyle(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2342 {
2343 auto page = GetRunningPage(ctx);
2344 if (page == nullptr) {
2345 return JS_EXCEPTION;
2346 }
2347 if (!JS_IsString(argv[0]) || !JS_IsString(argv[1])) {
2348 LOGE("args is not string ");
2349 return JS_NULL;
2350 }
2351 ScopedString key(ctx, argv[0]);
2352 JSValue style = JS_NewObject(ctx);
2353 JS_SetPropertyStr(ctx, style, key.get(), argv[1]);
2354 int32_t nodeId = GetNodeId(ctx, value);
2355 auto command = Referenced::MakeRefPtr<JsCommandUpdateDomElementStyles>(nodeId);
2356 SetDomStyle(ctx, style, *command);
2357 page->PushCommand(command);
2358 return JS_NULL;
2359 }
2360
JsAppendChild(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2361 JSValue JsAppendChild(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2362 {
2363 auto page = GetStagingPage(ctx);
2364 if (page == nullptr) {
2365 LOGE("page is nullptr");
2366 return JS_EXCEPTION;
2367 }
2368
2369 int32_t id = GetNodeId(ctx, argv[0]);
2370 auto domDocument = page->GetDomDocument();
2371 if (domDocument) {
2372 RefPtr<DOMNode> node = domDocument->GetDOMNodeById(id);
2373 if (node == nullptr) {
2374 LOGE("node is nullptr");
2375 }
2376 int32_t parentNodeId = 0;
2377 JSValue bridgeId = JS_GetPropertyStr(ctx, value, "__nodeId");
2378 if (JS_IsInteger(bridgeId)) {
2379 if ((JS_ToInt32(ctx, &parentNodeId, bridgeId)) < 0) {
2380 parentNodeId = 0;
2381 }
2382 }
2383 RefPtr<DOMNode> parentNode = domDocument->GetDOMNodeById(parentNodeId);
2384 if (parentNode == nullptr) {
2385 LOGE("node is nullptr");
2386 }
2387 if (node != nullptr && parentNode != nullptr) {
2388 parentNode->RemoveNode(node);
2389 }
2390 auto command = Referenced::MakeRefPtr<JsCommandAppendElement>(node ? node->GetTag() : "", id, parentNodeId);
2391 page->PushCommand(command);
2392 if (!page->CheckPageCreated() && page->GetCommandSize() > FRAGMENT_SIZE) {
2393 page->FlushCommands();
2394 }
2395 }
2396 return JS_NULL;
2397 }
2398
JsFocus(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2399 JSValue JsFocus(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2400 {
2401 auto page = GetStagingPage(ctx);
2402 if (page == nullptr) {
2403 LOGE("page is nullptr");
2404 return JS_NULL;
2405 }
2406 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
2407 if (instance == nullptr) {
2408 LOGE("JsFocus failed, instance is null.");
2409 return JS_NULL;
2410 }
2411 instance->GetDelegate()->TriggerPageUpdate(page->GetPageId(), true);
2412 return JS_NULL;
2413 }
2414
JsAnimate(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2415 JSValue JsAnimate(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2416 {
2417 auto page = GetStagingPage(ctx);
2418 if (page == nullptr) {
2419 LOGE("page is nullptr");
2420 return JS_NULL;
2421 }
2422 int32_t nodeId = GetNodeId(ctx, value);
2423 ScopedString args(ctx, argv[0]);
2424 auto resultValue = AnimationBridgeUtils::CreateAnimationContext(ctx, page->GetPageId(), nodeId);
2425 auto animationBridge = AceType::MakeRefPtr<AnimationBridge>(ctx, resultValue, nodeId);
2426 auto task = AceType::MakeRefPtr<AnimationBridgeTaskCreate>(animationBridge, args.get());
2427 page->PushCommand(Referenced::MakeRefPtr<JsCommandAnimation>(nodeId, task));
2428 return JS_NULL;
2429 }
2430
JsGetBoundingClientRect(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2431 JSValue JsGetBoundingClientRect(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2432 {
2433 auto page = GetStagingPage(ctx);
2434 if (page == nullptr) {
2435 LOGE("page is nullptr");
2436 return JS_NULL;
2437 }
2438 int32_t nodeId = GetNodeId(ctx, value);
2439 ComponentApiBridge::JsGetBoundingRect(ctx, nodeId);
2440 return JS_NULL;
2441 }
2442
JsGetInspector(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2443 JSValue JsGetInspector(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2444 {
2445 auto page = GetStagingPage(ctx);
2446 if (page == nullptr) {
2447 LOGE("page is nullptr");
2448 return JS_NULL;
2449 }
2450 int32_t nodeId = GetNodeId(ctx, value);
2451 ComponentApiBridge::JsGetInspector(ctx, nodeId);
2452 return JS_NULL;
2453 }
2454
JsCreateElement(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2455 JSValue JsCreateElement(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2456 {
2457 int32_t newNodeId = CreateDomElement(ctx, value, argc, argv);
2458 JSValue node = JS_NewObject(ctx);
2459 JS_SetPropertyStr(ctx, node, "__nodeId", JS_NewInt32(ctx, newNodeId));
2460 JS_SetPropertyStr(ctx, node, "setAttribute", JS_NewCFunction(ctx, JsSetAttribute, "setAttribute", 2));
2461 JS_SetPropertyStr(ctx, node, "setStyle", JS_NewCFunction(ctx, JsSetStyle, "setStyle", 2));
2462 JS_SetPropertyStr(ctx, node, "addChild", JS_NewCFunction(ctx, JsAppendChild, "addChild", 1));
2463 JS_SetPropertyStr(ctx, node, "focus", JS_NewCFunction(ctx, JsFocus, "focus", 1));
2464 JS_SetPropertyStr(ctx, node, "animate", JS_NewCFunction(ctx, JsAnimate, "animate", 1));
2465 JS_SetPropertyStr(ctx, node, "getBoundingClientRect",
2466 JS_NewCFunction(ctx, JsGetBoundingClientRect, "getBoundingClientRect", 1));
2467 JS_SetPropertyStr(ctx, node, "getInspector", JS_NewCFunction(ctx, JsGetInspector, "getInspector", 1));
2468 return node;
2469 }
2470
JsLogPrint(JSContext * ctx,JsLogLevel level,JSValueConst value,int32_t argc,JSValueConst * argv)2471 JSValue JsLogPrint(JSContext* ctx, JsLogLevel level, JSValueConst value, int32_t argc, JSValueConst* argv)
2472 {
2473 ACE_SCOPED_TRACE("JsLogPrint(level=%d)", static_cast<int32_t>(level));
2474
2475 // Should have 1 parameters.
2476 if ((argv == nullptr) || (argc == 0)) {
2477 LOGE("the arg is error");
2478 return JS_EXCEPTION;
2479 }
2480 std::string printLog = ParseLogContent(ctx, argc, argv);
2481 switch (level) {
2482 case JsLogLevel::DEBUG:
2483 LOGD("ace Log: %{public}s", printLog.c_str());
2484 break;
2485 case JsLogLevel::INFO:
2486 LOGI("ace Log: %{public}s", printLog.c_str());
2487 break;
2488 case JsLogLevel::WARNING:
2489 LOGW("ace Log: %{public}s", printLog.c_str());
2490 break;
2491 case JsLogLevel::ERROR:
2492 LOGE("ace Log: %{public}s", printLog.c_str());
2493 break;
2494 default:
2495 break;
2496 }
2497
2498 return JS_NULL;
2499 }
2500
JsLogPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2501 JSValue JsLogPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2502 {
2503 return JsLogPrint(ctx, JsLogLevel::INFO, value, argc, argv);
2504 }
2505
JsDebugLogPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2506 JSValue JsDebugLogPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2507 {
2508 return JsLogPrint(ctx, JsLogLevel::DEBUG, value, argc, argv);
2509 }
2510
JsInfoLogPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2511 JSValue JsInfoLogPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2512 {
2513 return JsLogPrint(ctx, JsLogLevel::INFO, value, argc, argv);
2514 }
2515
JsWarnLogPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2516 JSValue JsWarnLogPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2517 {
2518 return JsLogPrint(ctx, JsLogLevel::WARNING, value, argc, argv);
2519 }
2520
JsErrorLogPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2521 JSValue JsErrorLogPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2522 {
2523 return JsLogPrint(ctx, JsLogLevel::ERROR, value, argc, argv);
2524 }
2525
JsPerfPrint(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2526 JSValue JsPerfPrint(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2527 {
2528 std::string ret = JsApiPerf::GetInstance().PrintToLogs();
2529
2530 JSValue retString = JS_NewString(ctx, ret.c_str());
2531
2532 return retString;
2533 }
2534
AppClearData(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2535 JSValue AppClearData(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2536 {
2537 if ((argv == nullptr) || (argc == 0)) {
2538 if (dataMap_.size() > 0) {
2539 dataMap_.clear();
2540 }
2541 return JS_TRUE;
2542 }
2543 if (dataMap_.count(ScopedString::Stringify(ctx, argv[0])) == 1) {
2544 dataMap_.erase(ScopedString::Stringify(ctx, argv[0]));
2545 return JS_TRUE;
2546 }
2547 return JS_FALSE;
2548 }
2549
AppSetData(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2550 JSValue AppSetData(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2551 {
2552 if ((argv == nullptr) || (argc != 2)) {
2553 return JS_EXCEPTION;
2554 }
2555 std::string key = ScopedString::Stringify(ctx, argv[0]);
2556 std::string mapValue = ScopedString::Stringify(ctx, argv[1]);
2557 dataMap_[key] = mapValue;
2558 return JS_TRUE;
2559 }
2560
AppGetData(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2561 JSValue AppGetData(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2562 {
2563 if ((argv == nullptr) || (argc == 0)) {
2564 return JS_NULL;
2565 }
2566 std::string key = ScopedString::Stringify(ctx, argv[0]);
2567 if (dataMap_.count(key) == 1) {
2568 std::string mapValue = dataMap_[key];
2569 return QJSUtils::ParseJSON(ctx, mapValue.c_str(), mapValue.length());
2570 }
2571 return JS_NULL;
2572 }
2573
AppSetDataImage(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2574 JSValue AppSetDataImage(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2575 {
2576 auto page = GetStagingPage(ctx);
2577 if (page == nullptr) {
2578 LOGE("page is nullptr");
2579 return JS_EXCEPTION;
2580 }
2581 if ((argv == nullptr) || (argc != 3)) {
2582 return JS_EXCEPTION;
2583 }
2584 return JS_NULL;
2585 }
2586
JsPerfSleep(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2587 JSValue JsPerfSleep(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2588 {
2589 int32_t valInt = 0;
2590 JS_ToInt32(ctx, &valInt, argv[0]);
2591
2592 usleep(valInt);
2593
2594 return JS_NULL;
2595 }
2596
JsPerfBegin(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2597 JSValue JsPerfBegin(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2598 {
2599 int64_t currentTime = GetMicroTickCount();
2600 ScopedString functionName(ctx, argv[0]);
2601
2602 JsApiPerf::GetInstance().InsertJsBeginLog(functionName.get(), currentTime);
2603
2604 return JS_NULL;
2605 }
2606
JsPerfEnd(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2607 JSValue JsPerfEnd(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2608 {
2609 int64_t currentTime = GetMicroTickCount();
2610 ScopedString functionName(ctx, argv[0]);
2611
2612 JsApiPerf::GetInstance().InsertJsEndLog(functionName.get(), currentTime);
2613
2614 return JS_NULL;
2615 }
2616
JSHiViewReport(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2617 JSValue JSHiViewReport(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2618 {
2619 // Should have 2 parameters.
2620 if ((argv == nullptr) || (argc != 2)) {
2621 LOGE("the arg is error %{public}d", argc);
2622 return JS_EXCEPTION;
2623 }
2624 if (JS_IsInteger(argv[0]) && JS_IsString(argv[1])) {
2625 ScopedString eventId(ctx, argv[0]);
2626 ScopedString eventJsonStr(ctx, argv[1]);
2627 std::string jsonStr = eventJsonStr.get();
2628 EventReport::JsEventReport(StringToInt(eventId.get()), jsonStr);
2629 LOGD("JsEventReport success");
2630 return JS_NULL;
2631 } else {
2632 LOGE("parameter type error ");
2633 return JS_EXCEPTION;
2634 }
2635 }
2636
JsPluralRulesFormat(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2637 JSValue JsPluralRulesFormat(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2638 {
2639 // Should have one parameters.
2640 if ((argv == nullptr) || (argc != 1)) {
2641 LOGE("the arg is error %{public}d", argc);
2642 return JS_NULL;
2643 }
2644 if (JS_IsInteger(argv[0])) {
2645 ScopedString choice(ctx, argv[0]);
2646 JSValue result =
2647 JS_NewString(ctx, Localization::GetInstance()->PluralRulesFormat(StringToDouble(choice.get())).c_str());
2648 return result;
2649 } else {
2650 LOGE("parameter type error ");
2651 return JS_NULL;
2652 }
2653 }
2654
2655 // Follow definition in quickjs.
2656 #define JS_CFUNC_DEF_CPP(name, length, func) \
2657 { \
2658 name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, \
2659 { \
2660 { \
2661 length, JS_CFUNC_generic, \
2662 { \
2663 func \
2664 } \
2665 } \
2666 } \
2667 }
2668
2669 const JSCFunctionListEntry JS_ACE_FUNCS[] = {
2670 JS_CFUNC_DEF_CPP("domCreateBody", 5, JsDomCreateBody),
2671 JS_CFUNC_DEF_CPP("domAddElement", 9, JsDomAddElement),
2672 JS_CFUNC_DEF_CPP("updateElementAttrs", 3, JsUpdateElementAttrs),
2673 JS_CFUNC_DEF_CPP("updateElementStyles", 3, JsUpdateElementStyles),
2674 JS_CFUNC_DEF_CPP("onCreateFinish", 0, JsOnCreateFinish),
2675 JS_CFUNC_DEF_CPP("onUpdateFinish", 0, JsOnUpdateFinish),
2676 JS_CFUNC_DEF_CPP("removeElement", 2, JsRemoveElement),
2677 JS_CFUNC_DEF_CPP("callNative", 1, JsCallNative),
2678 JS_CFUNC_DEF_CPP("callComponent", 3, JsCallComponent),
2679 #if defined(ENABLE_JS_DEBUG) || defined(ENABLE_JS_DEBUG_PREVIEW)
2680 JS_CFUNC_DEF_CPP("compileAndRunBundle", 4, JsCompileAndRunBundle),
2681 #endif
2682 };
2683
JsAceInit(JSContext * ctx,JSModuleDef * moduleDef)2684 int32_t JsAceInit(JSContext* ctx, JSModuleDef* moduleDef)
2685 {
2686 return JS_SetModuleExportList(ctx, moduleDef, JS_ACE_FUNCS, countof(JS_ACE_FUNCS));
2687 }
2688
InitAceModules(JSContext * ctx)2689 JSModuleDef* InitAceModules(JSContext* ctx)
2690 {
2691 LOGD("QjsEngine: InitAceModules");
2692 JSModuleDef* jsModule = JS_NewCModule(ctx, "ace", JsAceInit);
2693 if (jsModule == nullptr) {
2694 return nullptr;
2695 }
2696 JS_AddModuleExportList(ctx, jsModule, JS_ACE_FUNCS, countof(JS_ACE_FUNCS));
2697 return jsModule;
2698 }
2699
JsCallNativeHandler(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)2700 JSValue JsCallNativeHandler(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
2701 {
2702 if (argc != 2 || argv == nullptr || !JS_IsString(argv[0]) || !JS_IsString(argv[1])) {
2703 LOGE("JsCallNativeHandler: invalid parameters");
2704 return JS_NULL;
2705 }
2706
2707 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
2708 if (instance == nullptr) {
2709 return JS_NULL;
2710 }
2711
2712 ScopedString event(ctx, argv[0]);
2713 ScopedString params(ctx, argv[1]);
2714 instance->GetDelegate()->CallNativeHandler(event.str(), params.str());
2715 return JS_NULL;
2716 }
2717
InitJsConsoleObject(JSContext * ctx,const JSValue & globalObj)2718 void InitJsConsoleObject(JSContext* ctx, const JSValue& globalObj)
2719 {
2720 JSValue console, aceConsole;
2721 // app log method
2722 console = JS_NewObject(ctx);
2723 JS_SetPropertyStr(ctx, console, "log", JS_NewCFunction(ctx, AppLogPrint, "log", 1));
2724 JS_SetPropertyStr(ctx, console, "debug", JS_NewCFunction(ctx, AppDebugLogPrint, "debug", 1));
2725 JS_SetPropertyStr(ctx, console, "info", JS_NewCFunction(ctx, AppInfoLogPrint, "info", 1));
2726 JS_SetPropertyStr(ctx, console, "warn", JS_NewCFunction(ctx, AppWarnLogPrint, "warn", 1));
2727 JS_SetPropertyStr(ctx, console, "error", JS_NewCFunction(ctx, AppErrorLogPrint, "error", 1));
2728 JS_SetPropertyStr(ctx, globalObj, "console", console);
2729
2730 // js framework log method
2731 aceConsole = JS_NewObject(ctx);
2732 JS_SetPropertyStr(ctx, aceConsole, "log", JS_NewCFunction(ctx, JsLogPrint, "log", 1));
2733 JS_SetPropertyStr(ctx, aceConsole, "debug", JS_NewCFunction(ctx, JsDebugLogPrint, "debug", 1));
2734 JS_SetPropertyStr(ctx, aceConsole, "info", JS_NewCFunction(ctx, JsInfoLogPrint, "info", 1));
2735 JS_SetPropertyStr(ctx, aceConsole, "warn", JS_NewCFunction(ctx, JsWarnLogPrint, "warn", 1));
2736 JS_SetPropertyStr(ctx, aceConsole, "error", JS_NewCFunction(ctx, JsErrorLogPrint, "error", 1));
2737 JS_SetPropertyStr(ctx, globalObj, "aceConsole", aceConsole);
2738 JS_SetPropertyStr(ctx, globalObj, "callNativeHandler",
2739 JS_NewCFunction(ctx, JsCallNativeHandler, "callNativeHandler", 1));
2740 }
2741
InitJsDocumentObject(JSContext * ctx,const JSValue & globalObj)2742 void InitJsDocumentObject(JSContext* ctx, const JSValue& globalObj)
2743 {
2744 JSValue dom = JS_NewObject(ctx);
2745 JS_SetPropertyStr(ctx, dom, "createElement", JS_NewCFunction(ctx, JsCreateElement, "createElement", 1));
2746 JS_SetPropertyStr(ctx, globalObj, "dom", dom);
2747 }
2748
InitJsContext(JSContext * ctx,size_t maxStackSize,int32_t instanceId,const QjsEngineInstance * qjsEngineInstance,const std::unordered_map<std::string,void * > & extraNativeObject)2749 bool InitJsContext(JSContext* ctx, size_t maxStackSize, int32_t instanceId, const QjsEngineInstance* qjsEngineInstance,
2750 const std::unordered_map<std::string, void*>& extraNativeObject)
2751 {
2752 LOGI("QjsEngine: InitJsContext");
2753 if (ctx == nullptr) {
2754 LOGE("Qjs cannot allocate JS context");
2755 EventReport::SendJsException(JsExcepType::JS_ENGINE_INIT_ERR);
2756 return false;
2757 }
2758
2759 // Note: default 256KB is not enough
2760 JS_SetMaxStackSize(ctx, maxStackSize);
2761
2762 // Inject ace native functions module
2763 InitAceModules(ctx);
2764
2765 JSValue globalObj, perfUtil;
2766 globalObj = JS_GetGlobalObject(ctx);
2767 perfUtil = JS_NewObject(ctx);
2768
2769 InitJsConsoleObject(ctx, globalObj);
2770 InitJsDocumentObject(ctx, globalObj);
2771
2772 JS_SetPropertyStr(ctx, perfUtil, "printlog", JS_NewCFunction(ctx, JsPerfPrint, "printlog", 0));
2773 JS_SetPropertyStr(ctx, perfUtil, "sleep", JS_NewCFunction(ctx, JsPerfSleep, "sleep", 1));
2774 JS_SetPropertyStr(ctx, perfUtil, "begin", JS_NewCFunction(ctx, JsPerfBegin, "begin", 1));
2775 JS_SetPropertyStr(ctx, perfUtil, "end", JS_NewCFunction(ctx, JsPerfEnd, "end", 1));
2776 JS_SetPropertyStr(ctx, globalObj, "perfutil", perfUtil);
2777
2778 #if !defined(PREVIEW)
2779 for (const auto& [key, value] : extraNativeObject) {
2780 auto nativeObjectInfo = std::make_unique<NativeObjectInfo>();
2781 nativeObjectInfo->nativeObject = value;
2782 JSValue abilityValue = JS_NewExternal(ctx, nativeObjectInfo.release(),
2783 [](JSContext* ctx, void *data, void *hint) {
2784 std::unique_ptr<NativeObjectInfo> info(static_cast<NativeObjectInfo*>(data));
2785 }, nullptr);
2786 JS_SetPropertyStr(ctx, globalObj, key.c_str(), abilityValue);
2787 }
2788 #endif
2789
2790 JSValue hiView;
2791 hiView = JS_NewObject(ctx);
2792 JS_SetPropertyStr(ctx, hiView, "report", JS_NewCFunction(ctx, JSHiViewReport, "report", 2));
2793 JS_SetPropertyStr(ctx, globalObj, "hiView", hiView);
2794
2795 JSValue i18nPluralRules;
2796 i18nPluralRules = JS_NewObject(ctx);
2797 JS_SetPropertyStr(ctx, i18nPluralRules, "select", JS_NewCFunction(ctx, JsPluralRulesFormat, "select", 1));
2798 JS_SetPropertyStr(ctx, globalObj, "i18nPluralRules", i18nPluralRules);
2799
2800 const char* str = "import * as ace from 'ace';\n"
2801 "var global = globalThis;\n"
2802 "global.ace = ace;\n"
2803 #if defined(ENABLE_JS_DEBUG) || defined(ENABLE_JS_DEBUG_PREVIEW)
2804 "global.compileAndRunBundle = ace.compileAndRunBundle;\n"
2805 #endif
2806 "global.callNative = ace.callNative;\n";
2807
2808 if (JS_CALL_FAIL == CallEvalBuf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE, instanceId)) {
2809 LOGW("Qjs created JS context but failed to init Ace modules!");
2810 }
2811
2812 JS_FreeValue(ctx, globalObj);
2813 return true;
2814 }
2815
LoadJsFramework(JSContext * ctx,const uint8_t buf[],const uint32_t bufSize,int32_t instanceId)2816 JSValue LoadJsFramework(JSContext* ctx, const uint8_t buf[], const uint32_t bufSize, int32_t instanceId)
2817 {
2818 ACE_SCOPED_TRACE("LoadJsFramework");
2819
2820 LOGI("Qjs loading JS framework");
2821 JSValue ret = CallReadObject(ctx, buf, bufSize, true, instanceId);
2822 if (JS_IsException(ret)) {
2823 LOGD("Qjs loading JSFramework failed!");
2824 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::LOAD_JS_FRAMEWORK_ERROR, instanceId);
2825 }
2826 js_std_loop(ctx);
2827 return ret;
2828 }
2829
2830 } // namespace
2831
2832 // -----------------------
2833 // Start QjsEngineInstance
2834 // -----------------------
FlushCommandBuffer(void * context,const std::string & command)2835 void QjsEngineInstance::FlushCommandBuffer(void* context, const std::string& command)
2836 {
2837 LOGI("flush command buffer");
2838 auto ctx = static_cast<JSContext*>(context);
2839 int32_t result = CallEvalBuf(ctx, command.c_str(), command.size(), "<input>", JS_EVAL_TYPE_GLOBAL, instanceId_);
2840 if (JS_CALL_FAIL == result) {
2841 LOGE("failed to flush command buffer");
2842 }
2843 }
InitJsEnv(JSRuntime * runtime,JSContext * context,const std::unordered_map<std::string,void * > & extraNativeObject)2844 bool QjsEngineInstance::InitJsEnv(
2845 JSRuntime* runtime, JSContext* context, const std::unordered_map<std::string, void*>& extraNativeObject)
2846 {
2847 LOGI("InitJsEnv");
2848 if (runtime == nullptr) {
2849 runtime = JS_NewRuntime();
2850 }
2851 if (runtime_ != nullptr) {
2852 JS_FreeRuntime(runtime_);
2853 }
2854 runtime_ = runtime;
2855 if (runtime_ == nullptr) {
2856 LOGE("Qjs cannot allocate JS runtime");
2857 EventReport::SendJsException(JsExcepType::JS_ENGINE_INIT_ERR);
2858 return false;
2859 }
2860
2861 if (context == nullptr) {
2862 context = JS_NewContext(runtime_);
2863 }
2864 if (context_ != nullptr) {
2865 JS_FreeContext(context_);
2866 }
2867 context_ = context;
2868 if (!InitJsContext(context_, MAX_STACK_SIZE, instanceId_, this, extraNativeObject)) {
2869 LOGE("Qjs cannot allocate JS context");
2870 EventReport::SendJsException(JsExcepType::JS_ENGINE_INIT_ERR);
2871 JS_FreeRuntime(runtime_);
2872 return false;
2873 }
2874
2875 auto groupJsBridge = AceType::DynamicCast<QuickJsGroupJsBridge>(frontendDelegate_->GetGroupJsBridge());
2876 if (!groupJsBridge || JS_CALL_FAIL == groupJsBridge->InitializeGroupJsBridge(context_)) {
2877 LOGE("Qjs Initialize GroupJsBridge failed!");
2878 EventReport::SendJsException(JsExcepType::JS_ENGINE_INIT_ERR);
2879 return false;
2880 }
2881
2882 JSValue retVal = LoadJsFramework(GetQjsContext(), js_framework, js_framework_size, instanceId_);
2883 bool result = JS_IsException(retVal) ? false : true;
2884 if (context) {
2885 JS_FreeValue(context, retVal);
2886 }
2887
2888 return result;
2889 }
2890
FreeGroupJsBridge()2891 void QjsEngineInstance::FreeGroupJsBridge()
2892 {
2893 // free JSValue reference of channel bridge and animation bridge
2894 if (!frontendDelegate_) {
2895 LOGE("frontend delegate is null.");
2896 return;
2897 }
2898
2899 auto groupJsBridge = AceType::DynamicCast<QuickJsGroupJsBridge>(frontendDelegate_->GetGroupJsBridge());
2900 if (!groupJsBridge) {
2901 LOGE("group js bridge is null.");
2902 return;
2903 }
2904 groupJsBridge->Uninitialize();
2905 }
2906
~QjsEngineInstance()2907 QjsEngineInstance::~QjsEngineInstance()
2908 {
2909 if (runningPage_) {
2910 runningPage_->OnJsEngineDestroy();
2911 }
2912
2913 if (stagingPage_) {
2914 stagingPage_->OnJsEngineDestroy();
2915 }
2916 FreeGroupJsBridge();
2917
2918 if (context_) {
2919 JS_FreeContext(context_);
2920 }
2921 if (runtime_) {
2922 JS_FreeRuntime(runtime_);
2923 }
2924 }
2925
FireJsEvent(const std::string & param)2926 JSValue QjsEngineInstance::FireJsEvent(const std::string& param)
2927 {
2928 LOGI("FireJsEvent");
2929 JSContext* ctx = GetQjsContext();
2930 ACE_DCHECK(ctx);
2931 QJSHandleScope handleScope(ctx);
2932 JSValue globalObj = JS_GetGlobalObject(ctx);
2933 JSValue callJsFunc = QJSUtils::GetPropertyStr(ctx, globalObj, "callJS");
2934 if (!JS_IsFunction(ctx, callJsFunc)) {
2935 LOGE("cannot find 'callJS' function from global object, this should not happen!");
2936 JS_FreeValue(ctx, globalObj);
2937 return JS_UNDEFINED;
2938 }
2939
2940 if (!runningPage_) {
2941 LOGE("runningPage_ is null!");
2942 JS_FreeValue(ctx, globalObj);
2943 return JS_UNDEFINED;
2944 }
2945 JSValue jsValue = QJSUtils::ParseJSON(ctx, param.c_str(), param.size(), nullptr);
2946 if (JS_IsArray(ctx, jsValue)) {
2947 JSValue itemVal = JS_GetPropertyUint32(ctx, jsValue, 0);
2948 if (JS_IsObject(itemVal)) {
2949 JSValue args = JS_GetPropertyStr(ctx, itemVal, "args");
2950 if (JS_IsArray(ctx, args)) {
2951 JSValue stdDrag = JS_GetPropertyUint32(ctx, args, 1);
2952 if (IsDragEvent(ScopedString::Stringify(ctx, stdDrag))) {
2953 JSValue arg2 = JS_GetPropertyUint32(ctx, args, 2);
2954 if (JS_IsObject(arg2)) {
2955 JSValue arg1 = JS_NewObject(ctx);
2956 JS_SetPropertyStr(ctx, arg1, "clearData", JS_NewCFunction(ctx, AppClearData, "clearData", 1));
2957 JS_SetPropertyStr(ctx, arg1, "getData", JS_NewCFunction(ctx, AppGetData, "getData", 1));
2958 JS_SetPropertyStr(ctx, arg1, "setData", JS_NewCFunction(ctx, AppSetData, "setData", 2));
2959 JS_SetPropertyStr(
2960 ctx, arg1, "setDragImage", JS_NewCFunction(ctx, AppSetDataImage, "setDragImage", 3));
2961 JS_SetPropertyStr(ctx, arg2, "dataTransfer", arg1);
2962 JS_FreeValue(ctx, arg1);
2963 }
2964 JS_FreeValue(ctx, arg2);
2965 }
2966 JS_FreeValue(ctx, stdDrag);
2967 }
2968 JS_FreeValue(ctx, args);
2969 }
2970 JS_FreeValue(ctx, itemVal);
2971 }
2972 JSValueConst argv[] = {
2973 QJSUtils::NewString(ctx, std::to_string(runningPage_->GetPageId()).c_str()),
2974 jsValue,
2975 };
2976
2977 JSValue retVal = JS_Call(ctx, callJsFunc, globalObj, countof(argv), argv);
2978
2979 js_std_loop(ctx);
2980 JS_FreeValue(ctx, globalObj);
2981
2982 // It is up to the caller to check this value. No exception checks here.
2983 return retVal;
2984 }
2985
IsDragEvent(const std::string & param)2986 bool QjsEngineInstance::IsDragEvent(const std::string& param)
2987 {
2988 std::string::size_type idx = param.find("drag");
2989 return !(idx == std::string::npos);
2990 }
2991
CallJs(const std::string & callbackId,const std::string & args,bool keepAlive,bool isGlobal)2992 void QjsEngineInstance::CallJs(const std::string& callbackId, const std::string& args, bool keepAlive, bool isGlobal)
2993 {
2994 ACE_SCOPED_TRACE("QjsEngineInstance::CallJS");
2995
2996 std::string keepAliveStr = keepAlive ? "true" : "false";
2997 std::string callBuff = std::string("[{\"args\": [\"")
2998 .append(callbackId)
2999 .append("\",")
3000 .append(args)
3001 .append(",")
3002 .append(keepAliveStr)
3003 .append("], \"method\":\"callback\"}]");
3004 LOGD("CallJs string: %{private}s", callBuff.c_str());
3005
3006 JSContext* ctx = GetQjsContext();
3007 QJSHandleScope handleScope(ctx);
3008 JSValue globalObj = JS_GetGlobalObject(ctx);
3009 JSValue callJsFunc = QJSUtils::GetPropertyStr(ctx, globalObj, "callJS");
3010 if (!JS_IsFunction(ctx, callJsFunc)) {
3011 LOGE("cannot find 'callJS' function from global object, this should not happen!");
3012 JS_FreeValue(ctx, globalObj);
3013 return;
3014 }
3015
3016 int32_t instanceId = isGlobal ? DEFAULT_APP_ID : stagingPage_->GetPageId();
3017 JSValueConst argv[] = {
3018 QJSUtils::NewString(ctx, std::to_string(instanceId).c_str()),
3019 QJSUtils::ParseJSON(ctx, callBuff.c_str(), callBuff.size(), nullptr),
3020 };
3021 JSValue retVal = QJSUtils::Call(ctx, callJsFunc, globalObj, countof(argv), argv);
3022
3023 if (JS_IsException(retVal)) {
3024 LOGE("JS framework execute callback failed");
3025 JS_FreeValue(ctx, globalObj);
3026 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::JS_CALLBACK_ERROR, instanceId_, stagingPage_->GetUrl().c_str(),
3027 stagingPage_);
3028 return;
3029 }
3030 js_std_loop(ctx);
3031 JS_FreeValue(ctx, globalObj);
3032 }
3033
CallAnimationStartJs(JSValue animationContext)3034 void QjsEngineInstance::CallAnimationStartJs(JSValue animationContext)
3035 {
3036 JSContext* ctx = GetQjsContext();
3037 QJSHandleScope handleScope(ctx);
3038 auto proto = QJSUtils::GetPropertyStr(ctx, animationContext, "onstart");
3039 if (!JS_IsFunction(ctx, proto)) {
3040 LOGD("cannot find 'CallAnimationStartJs' function from global object, this should not happen!");
3041 return;
3042 }
3043 LOGD("animation onstart event call");
3044 JSValue retVal = QJSUtils::Call(ctx, proto, animationContext, 0, {});
3045 JSValue globalObj = JS_GetGlobalObject(ctx);
3046
3047 if (JS_IsException(retVal)) {
3048 LOGE("JS framework execute callAnimationStart failed");
3049 JS_FreeValue(ctx, globalObj);
3050 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::JS_CALLBACK_ERROR, instanceId_, stagingPage_->GetUrl().c_str(),
3051 stagingPage_);
3052 return;
3053 }
3054 js_std_loop(ctx);
3055 }
3056
CallAnimationFinishJs(JSValue animationContext)3057 void QjsEngineInstance::CallAnimationFinishJs(JSValue animationContext)
3058 {
3059 JSContext* ctx = GetQjsContext();
3060 QJSHandleScope handleScope(ctx);
3061 auto proto = QJSUtils::GetPropertyStr(ctx, animationContext, "onfinish");
3062 if (!JS_IsFunction(ctx, proto)) {
3063 LOGD("cannot find 'CallAnimationFinishJs' function from global object, this should not happen!");
3064 return;
3065 }
3066 LOGD("animation onfinish event call");
3067 JSValue retVal = QJSUtils::Call(ctx, proto, animationContext, 0, {});
3068 JSValue globalObj = JS_GetGlobalObject(ctx);
3069
3070 if (JS_IsException(retVal)) {
3071 LOGE("JS framework execute callAnimationFinish failed");
3072 JS_FreeValue(ctx, globalObj);
3073 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::JS_CALLBACK_ERROR, instanceId_, stagingPage_->GetUrl().c_str(),
3074 stagingPage_);
3075 return;
3076 }
3077 js_std_loop(ctx);
3078 }
3079
CallAnimationCancelJs(JSValue animationContext)3080 void QjsEngineInstance::CallAnimationCancelJs(JSValue animationContext)
3081 {
3082 JSContext* ctx = GetQjsContext();
3083 QJSHandleScope handleScope(ctx);
3084 auto proto = QJSUtils::GetPropertyStr(ctx, animationContext, "oncancel");
3085 if (!JS_IsFunction(ctx, proto)) {
3086 return;
3087 }
3088
3089 LOGD("animation oncancel event call");
3090 JSValue retVal = QJSUtils::Call(ctx, proto, animationContext, 0, {});
3091 JSValue globalObj = JS_GetGlobalObject(ctx);
3092
3093 if (JS_IsException(retVal)) {
3094 LOGE("JS framework execute callAnimationCancel failed");
3095 JS_FreeValue(ctx, globalObj);
3096 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::JS_CALLBACK_ERROR, instanceId_, stagingPage_->GetUrl().c_str(),
3097 stagingPage_);
3098 return;
3099 }
3100 js_std_loop(ctx);
3101 }
3102
CallAnimationRepeatJs(JSValue animationContext)3103 void QjsEngineInstance::CallAnimationRepeatJs(JSValue animationContext)
3104 {
3105 JSContext* ctx = GetQjsContext();
3106 QJSHandleScope handleScope(ctx);
3107 auto proto = QJSUtils::GetPropertyStr(ctx, animationContext, "onrepeat");
3108 if (!JS_IsFunction(ctx, proto)) {
3109 return;
3110 }
3111
3112 LOGD("animation onrepeat event call");
3113 JSValue retVal = QJSUtils::Call(ctx, proto, animationContext, 0, {});
3114 JSValue globalObj = JS_GetGlobalObject(ctx);
3115
3116 if (JS_IsException(retVal)) {
3117 LOGE("JS framework execute callAnimationRepeatJs failed");
3118 JS_FreeValue(ctx, globalObj);
3119 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::JS_CALLBACK_ERROR, instanceId_, stagingPage_->GetUrl().c_str(),
3120 stagingPage_);
3121 return;
3122 }
3123 js_std_loop(ctx);
3124 }
3125
CallAnimationFrameJs(JSValue animationContext,const char * str)3126 void QjsEngineInstance::CallAnimationFrameJs(JSValue animationContext, const char* str)
3127 {
3128 JSContext* ctx = GetQjsContext();
3129 QJSHandleScope handleScope(ctx);
3130 auto proto = QJSUtils::GetPropertyStr(ctx, animationContext, "onframe");
3131 if (!JS_IsFunction(ctx, proto)) {
3132 return;
3133 }
3134
3135 LOGD("animation onframe event call");
3136 JSValue value = QJSUtils::NewString(ctx, str);
3137 JSValueConst argv[] = { value };
3138 QJSUtils::Call(ctx, proto, animationContext, 1, argv);
3139 js_std_loop(ctx);
3140 }
3141
3142 // -----------------------
3143 // Start QjsEngine
3144 // -----------------------
Initialize(const RefPtr<FrontendDelegate> & delegate)3145 bool QjsEngine::Initialize(const RefPtr<FrontendDelegate>& delegate)
3146 {
3147 ACE_SCOPED_TRACE("QjsEngine::Initialize");
3148 LOGI("Initialize");
3149
3150 JSRuntime* runtime = nullptr;
3151 JSContext* context = nullptr;
3152
3153 // put JS_NewContext as early as possible to make stack_top in context
3154 // closer to the top stack frame pointer of JS thread.
3155 runtime = JS_NewRuntime();
3156 if (runtime != nullptr) {
3157 context = JS_NewContext(runtime);
3158 }
3159
3160 LOGD("QjsEngine initialize");
3161
3162 #ifdef ENABLE_JS_DEBUG
3163 LOGI("QjsEngine debug mode");
3164 std::string instanceName = GetInstanceName();
3165 if (instanceName.empty()) {
3166 LOGE("GetInstanceName fail, %s", instanceName.c_str());
3167 return false;
3168 }
3169 constexpr int32_t COMPONENT_NAME_MAX_LEN = 100;
3170 char componentName[COMPONENT_NAME_MAX_LEN];
3171 if (!DBG_CopyComponentNameFromAce(instanceName.c_str(), componentName, COMPONENT_NAME_MAX_LEN)) {
3172 LOGE("GetInstanceName strcpy_s fail");
3173 return false;
3174 }
3175 DBG_SetComponentNameAndInstanceId(componentName, strlen(componentName), instanceId_);
3176 DBG_SetNeedDebugBreakPoint(NeedDebugBreakPoint());
3177 #endif
3178 ACE_DCHECK(delegate);
3179
3180 engineInstance_ = AceType::MakeRefPtr<QjsEngineInstance>(delegate, instanceId_);
3181 nativeEngine_ = new QuickJSNativeEngine(runtime, context, static_cast<void*>(this));
3182 engineInstance_->SetQuickJSNativeEngine(static_cast<QuickJSNativeEngine*>(nativeEngine_));
3183 bool ret = engineInstance_->InitJsEnv(runtime, context, GetExtraNativeObject());
3184
3185 SetPostTask(nativeEngine_);
3186 #if !defined(PREVIEW)
3187 nativeEngine_->CheckUVLoop();
3188 #endif
3189 RegisterWorker();
3190 if (delegate && delegate->GetAssetManager()) {
3191 std::vector<std::string> packagePath = delegate->GetAssetManager()->GetLibPath();
3192 if (!packagePath.empty()) {
3193 auto qjsEngine = static_cast<QuickJSNativeEngine*>(nativeEngine_);
3194 qjsEngine->SetPackagePath(packagePath);
3195 }
3196 }
3197
3198 return ret;
3199 }
3200
SetPostTask(NativeEngine * nativeEngine)3201 void QjsEngine::SetPostTask(NativeEngine* nativeEngine)
3202 {
3203 LOGI("SetPostTask");
3204 auto weakDelegate = AceType::WeakClaim(AceType::RawPtr(engineInstance_->GetDelegate()));
3205 auto&& postTask = [weakDelegate, weakEngine = AceType::WeakClaim(this), id = instanceId_](bool needSync) {
3206 auto delegate = weakDelegate.Upgrade();
3207 if (delegate == nullptr) {
3208 LOGE("delegate is nullptr");
3209 return;
3210 }
3211
3212 delegate->PostJsTask([weakEngine, needSync, id]() {
3213 ContainerScope scope(id);
3214 auto jsEngine = weakEngine.Upgrade();
3215 if (jsEngine == nullptr) {
3216 LOGW("jsEngine is nullptr");
3217 return;
3218 }
3219 auto nativeEngine = jsEngine->GetNativeEngine();
3220 if (nativeEngine == nullptr) {
3221 return;
3222 }
3223 nativeEngine->Loop(LOOP_NOWAIT, needSync);
3224 });
3225 };
3226 nativeEngine_->SetPostTask(postTask);
3227 }
3228
RegisterInitWorkerFunc()3229 void QjsEngine::RegisterInitWorkerFunc()
3230 {
3231 auto&& initWorkerFunc = [](NativeEngine* nativeEngine) {
3232 LOGI("WorkerCore RegisterInitWorkerFunc called");
3233 if (nativeEngine == nullptr) {
3234 LOGE("nativeEngine is nullptr");
3235 return;
3236 }
3237 auto qjsNativeEngine = static_cast<QuickJSNativeEngine*>(nativeEngine);
3238 if (qjsNativeEngine == nullptr) {
3239 LOGE("qjsNativeEngine is nullptr");
3240 return;
3241 }
3242
3243 JSContext* ctx = qjsNativeEngine->GetContext();
3244 if (ctx == nullptr) {
3245 LOGE("ctx is nullptr");
3246 return;
3247 }
3248
3249 // Create a stack-allocated handle scope.
3250 QJSHandleScope handleScope(ctx);
3251
3252 // Note: default 256KB is not enough
3253 JS_SetMaxStackSize(ctx, MAX_STACK_SIZE);
3254
3255 JSValue globalObj = JS_GetGlobalObject(ctx);
3256 InitJsConsoleObject(ctx, globalObj);
3257 JS_FreeValue(ctx, globalObj);
3258 };
3259 nativeEngine_->SetInitWorkerFunc(initWorkerFunc);
3260 }
3261
RegisterAssetFunc()3262 void QjsEngine::RegisterAssetFunc()
3263 {
3264 auto weakDelegate = AceType::WeakClaim(AceType::RawPtr(engineInstance_->GetDelegate()));
3265 auto&& assetFunc = [weakDelegate](const std::string& uri, std::vector<uint8_t>& content, std::string& ami) {
3266 LOGI("WorkerCore RegisterAssetFunc called");
3267 auto delegate = weakDelegate.Upgrade();
3268 if (delegate == nullptr) {
3269 LOGE("delegate is nullptr");
3270 return;
3271 }
3272 delegate->GetResourceData(uri, content, ami);
3273 };
3274 nativeEngine_->SetGetAssetFunc(assetFunc);
3275 }
3276
RegisterWorker()3277 void QjsEngine::RegisterWorker()
3278 {
3279 RegisterInitWorkerFunc();
3280 RegisterAssetFunc();
3281 }
3282
~QjsEngine()3283 QjsEngine::~QjsEngine()
3284 {
3285 LOG_DESTROY();
3286 if (nativeEngine_ != nullptr) {
3287 #if !defined(PREVIEW)
3288 nativeEngine_->CancelCheckUVLoop();
3289 #endif
3290 nativeEngine_->DeleteEngine();
3291 delete nativeEngine_;
3292 }
3293 if (engineInstance_ && engineInstance_->GetQjsRuntime()) {
3294 JS_RunGC(engineInstance_->GetQjsRuntime());
3295 engineInstance_->SetNativeEngine(nullptr);
3296 }
3297 }
3298
GetLoadOptions(std::string & optionStr,bool isMainPage,const RefPtr<JsAcePage> & page)3299 void QjsEngine::GetLoadOptions(std::string& optionStr, bool isMainPage, const RefPtr<JsAcePage>& page)
3300 {
3301 auto mediaQuery = engineInstance_->GetDelegate()->GetMediaQueryInfoInstance();
3302 auto renderOption = MediaQueryInfo::GetMediaQueryJsonInfo();
3303 if (mediaQuery) {
3304 renderOption->Put("isInit", mediaQuery->GetIsInit());
3305 renderOption->Put("bundleUrl", page->GetUrl().c_str());
3306 }
3307 renderOption->Put("appInstanceId", "10002");
3308 renderOption->Put("pcPreview", PC_PREVIEW);
3309 renderOption->Put("packageName", engineInstance_->GetDelegate()->GetAppID().c_str());
3310
3311 // get resourceConfig
3312 engineInstance_->GetDelegate()->GetResourceConfiguration(renderOption);
3313
3314 // get i18n message
3315 std::string language = AceApplicationInfo::GetInstance().GetLanguage();
3316 std::string region = AceApplicationInfo::GetInstance().GetCountryOrRegion();
3317 engineInstance_->GetDelegate()->GetI18nData(renderOption);
3318 std::string local = language + "_" + region;
3319 renderOption->Put("language", local.c_str());
3320
3321 if (isMainPage) {
3322 JSContext* ctx = engineInstance_->GetQjsContext();
3323 std::string commonsJsContent;
3324 if (engineInstance_->GetDelegate()->GetAssetContent("commons.js", commonsJsContent)) {
3325 auto commonsJsResult = CallEvalBuf(ctx, commonsJsContent.c_str(), commonsJsContent.size(),
3326 "commons.js", JS_EVAL_TYPE_MODULE, instanceId_);
3327 if (commonsJsResult == JS_CALL_FAIL) {
3328 LOGE("fail to execute load commonsjs script");
3329 }
3330 }
3331 std::string vendorsJsContent;
3332 if (engineInstance_->GetDelegate()->GetAssetContent("vendors.js", vendorsJsContent)) {
3333 bool vendorsJsResult = CallEvalBuf(ctx, vendorsJsContent.c_str(), vendorsJsContent.size(),
3334 "vendors.js", JS_EVAL_TYPE_MODULE, instanceId_);
3335 if (vendorsJsResult == JS_CALL_FAIL) {
3336 LOGE("fail to execute load vendorsjs script");
3337 }
3338 }
3339 std::string code;
3340 std::string appMap;
3341 if (engineInstance_->GetDelegate()->GetAssetContent("app.js.map", appMap)) {
3342 page->SetAppMap(appMap);
3343 } else {
3344 LOGW("sourceMap of app.js is missing!");
3345 }
3346
3347 if (engineInstance_->GetDelegate()->GetAssetContent("app.js", code)) {
3348 renderOption->Put("appCreate", "true");
3349 renderOption->Put("appCode", code.c_str());
3350 } else {
3351 LOGE("app.js is missing!");
3352 }
3353 } else {
3354 renderOption->Put("appCreate", "false");
3355 }
3356 optionStr = renderOption->ToString();
3357 }
3358
LoadJs(const std::string & url,const RefPtr<JsAcePage> & page,bool isMainPage)3359 void QjsEngine::LoadJs(const std::string& url, const RefPtr<JsAcePage>& page, bool isMainPage)
3360 {
3361 LOGI("QjsEngine LoadJs");
3362 ACE_SCOPED_TRACE("QjsEngine::LoadJs");
3363 ACE_DCHECK(engineInstance_);
3364
3365 engineInstance_->SetStagingPage(page);
3366 if (isMainPage) {
3367 ACE_DCHECK(!engineInstance_->GetRunningPage());
3368 engineInstance_->SetRunningPage(page);
3369 }
3370 JSContext* ctx = engineInstance_->GetQjsContext();
3371
3372 // Create a stack-allocated handle scope.
3373 QJSHandleScope handleScope(ctx);
3374
3375 // Memorize the context that this JSContext is working with.
3376 JS_SetContextOpaque(ctx, reinterpret_cast<void*>(AceType::RawPtr(engineInstance_)));
3377
3378 JSValue globalObj = JS_GetGlobalObject(ctx);
3379 JSValue createInstanceFunc = QJSUtils::GetPropertyStr(ctx, globalObj, "createInstance");
3380 if (!JS_IsFunction(ctx, createInstanceFunc)) {
3381 LOGD("createInstance is not found, cannot load js!");
3382 JS_FreeValue(ctx, globalObj);
3383 return;
3384 }
3385 #ifdef ENABLE_JS_DEBUG
3386 std::string jsContent;
3387 if (!engineInstance_->GetDelegate()->GetAssetContent(url, jsContent)) {
3388 LOGE("js file load failed!");
3389 JS_FreeValue(ctx, globalObj);
3390 return;
3391 }
3392 #else
3393 JSValue jsCode = JS_UNDEFINED;
3394 auto pos = url.rfind(JS_EXT);
3395 if (pos != std::string::npos && pos == url.length() - (sizeof(JS_EXT) - 1)) {
3396 std::vector<uint8_t> binContent;
3397 if (engineInstance_->GetDelegate()->GetAssetContent(url.substr(0, pos) + BIN_EXT, binContent)) {
3398 jsCode =
3399 CallReadObject(ctx, binContent.data(), binContent.size(), false, instanceId_, page->GetUrl().c_str());
3400 if (!JS_IsException(jsCode)) {
3401 JS_FreeValue(ctx, jsCode);
3402 jsCode = QJSUtils::GetPropertyStr(ctx, globalObj, JS_MAIN_ENTRY);
3403 JS_SetPropertyStr(ctx, globalObj, JS_MAIN_ENTRY, JS_UNDEFINED);
3404 } else {
3405 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::READ_OBJECT_ERROR, instanceId_, page->GetUrl().c_str(),
3406 page);
3407 }
3408 }
3409 }
3410
3411 if (JS_IsFunction(ctx, jsCode)) {
3412 LOGI("Using binary source code");
3413 } else {
3414 std::string jsContent;
3415 if (!engineInstance_->GetDelegate()->GetAssetContent(url, jsContent)) {
3416 LOGE("js file load failed!");
3417 JS_FreeValue(ctx, globalObj);
3418 return;
3419 }
3420 jsCode = QJSUtils::NewStringLen(ctx, jsContent.c_str(), jsContent.size());
3421 }
3422 #endif
3423 std::string pageMap;
3424 if (engineInstance_->GetDelegate()->GetAssetContent(url + MAP_EXT, pageMap)) {
3425 page->SetPageMap(pageMap);
3426 } else {
3427 LOGW("source map of page load failed!");
3428 }
3429 std::string jsonData = page->GetPageParams();
3430 if (jsonData.empty()) {
3431 jsonData = "{}";
3432 }
3433 std::string optionStr;
3434 GetLoadOptions(optionStr, isMainPage, page);
3435 JSValue instanceId = QJSUtils::NewString(ctx, std::to_string(page->GetPageId()).c_str());
3436 JSValue renderOptions = QJSUtils::ParseJSON(ctx, optionStr.c_str(), optionStr.size(), nullptr);
3437 JSValue data = QJSUtils::ParseJSON(ctx, jsonData.c_str(), jsonData.size(), nullptr);
3438 JSValue info = QJSUtils::NewObject(ctx);
3439 #ifdef ENABLE_JS_DEBUG
3440 JSValue jsSrc = QJSUtils::NewStringLen(ctx, jsContent.c_str(), jsContent.length());
3441 JSValueConst argv[] = { instanceId, jsSrc, renderOptions, data, info };
3442 #else
3443 JSValueConst argv[] = { instanceId, jsCode, renderOptions, data, info };
3444 #endif
3445 JSValue retVal = QJSUtils::Call(ctx, createInstanceFunc, JS_UNDEFINED, countof(argv), argv);
3446
3447 if (JS_IsException(retVal)) {
3448 LOGE("JS framework load js bundle failed!");
3449 JS_FreeValue(ctx, globalObj);
3450 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::LOAD_JS_BUNDLE_ERROR, instanceId_, page->GetUrl().c_str(), page);
3451 return;
3452 }
3453
3454 JS_FreeValue(ctx, retVal);
3455 JS_FreeValue(ctx, globalObj);
3456 js_std_loop(ctx);
3457 }
3458
UpdateRunningPage(const RefPtr<JsAcePage> & page)3459 void QjsEngine::UpdateRunningPage(const RefPtr<JsAcePage>& page)
3460 {
3461 ACE_DCHECK(engineInstance_);
3462 engineInstance_->SetRunningPage(page);
3463 js_std_loop(engineInstance_->GetQjsContext());
3464 }
3465
UpdateStagingPage(const RefPtr<JsAcePage> & page)3466 void QjsEngine::UpdateStagingPage(const RefPtr<JsAcePage>& page)
3467 {
3468 ACE_DCHECK(engineInstance_);
3469 engineInstance_->SetStagingPage(page);
3470 js_std_loop(engineInstance_->GetQjsContext());
3471 }
3472
ResetStagingPage()3473 void QjsEngine::ResetStagingPage()
3474 {
3475 ACE_DCHECK(engineInstance_);
3476 auto runningPage = engineInstance_->GetRunningPage();
3477 engineInstance_->ResetStagingPage(runningPage);
3478 js_std_loop(engineInstance_->GetQjsContext());
3479 }
3480
DestroyPageInstance(int32_t pageId)3481 void QjsEngine::DestroyPageInstance(int32_t pageId)
3482 {
3483 LOGI("QjsEngine DestroyPageInstance");
3484 JSContext* ctx = engineInstance_->GetQjsContext();
3485 ACE_DCHECK(ctx);
3486
3487 QJSHandleScope handleScope(ctx);
3488 JSValue globalObj = JS_GetGlobalObject(ctx);
3489 JSValue destroyInstanceFunc = QJSUtils::GetPropertyStr(ctx, globalObj, "destroyInstance");
3490 if (!JS_IsFunction(ctx, destroyInstanceFunc)) {
3491 LOGE("destroyInstance is not found, cannot destroy page instance!");
3492 JS_FreeValue(ctx, globalObj);
3493 return;
3494 }
3495
3496 JSValue instanceId = QJSUtils::NewString(ctx, std::to_string(pageId).c_str());
3497 JSValueConst argv[] = { instanceId };
3498 JSValue retVal = QJSUtils::Call(ctx, destroyInstanceFunc, JS_UNDEFINED, countof(argv), argv);
3499
3500 if (JS_IsException(retVal)) {
3501 LOGE("Qjs DestroyPageInstance FAILED!");
3502 JS_FreeValue(ctx, globalObj);
3503
3504 auto page = engineInstance_->GetDelegate()->GetPage(pageId);
3505 if (page) {
3506 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::DESTROY_PAGE_ERROR, instanceId_, page->GetUrl().c_str(),
3507 page);
3508 } else {
3509 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::DESTROY_PAGE_ERROR, instanceId_,
3510 engineInstance_->GetRunningPage()->GetUrl().c_str(), engineInstance_->GetRunningPage());
3511 }
3512 return;
3513 }
3514
3515 RunGarbageCollection();
3516 JS_FreeValue(ctx, globalObj);
3517 js_std_loop(engineInstance_->GetQjsContext());
3518 }
3519
UpdateApplicationState(const std::string & packageName,Frontend::State state)3520 void QjsEngine::UpdateApplicationState(const std::string& packageName, Frontend::State state)
3521 {
3522 LOGI("UpdateApplicationState: destroy app instance from jsfwk, packageName %{public}s", packageName.c_str());
3523 JSContext* ctx = engineInstance_->GetQjsContext();
3524 ACE_DCHECK(ctx);
3525
3526 std::string stateType;
3527 switch (state) {
3528 case Frontend::State::ON_CREATE:
3529 break;
3530 case Frontend::State::ON_DESTROY:
3531 stateType = "appDestroy";
3532 break;
3533 case Frontend::State::ON_SHOW:
3534 stateType = "appShow";
3535 break;
3536 case Frontend::State::ON_HIDE:
3537 stateType = "appHide";
3538 break;
3539 default:
3540 LOGE("error State: %d", state);
3541 break;
3542 }
3543
3544 QJSHandleScope handleScope(ctx);
3545 JSValue globalObj = JS_GetGlobalObject(ctx);
3546 JSValue appDestroyFunc = QJSUtils::GetPropertyStr(ctx, globalObj, stateType.c_str());
3547 if (!JS_IsFunction(ctx, appDestroyFunc)) {
3548 LOGE("appDestroyFunc is not found, cannot destroy page instance!");
3549 JS_FreeValue(ctx, globalObj);
3550 return;
3551 }
3552
3553 JSValue name = QJSUtils::NewString(ctx, packageName.c_str());
3554 JSValueConst argv[] = { name };
3555 JSValue retVal = QJSUtils::Call(ctx, appDestroyFunc, JS_UNDEFINED, countof(argv), argv);
3556
3557 if (JS_IsException(retVal)) {
3558 LOGE("Qjs appDestroyFunc FAILED!");
3559 QJSUtils::JsStdDumpErrorAce(ctx, JsErrorType::DESTROY_APP_ERROR, instanceId_, nullptr,
3560 engineInstance_->GetRunningPage());
3561 }
3562
3563 JS_FreeValue(ctx, globalObj);
3564 js_std_loop(engineInstance_->GetQjsContext());
3565 }
3566
OnStartContinuation()3567 bool QjsEngine::OnStartContinuation()
3568 {
3569 JSContext* ctx = engineInstance_->GetQjsContext();
3570 QJSHandleScope handleScope(ctx);
3571 JSValue globalObj = JS_GetGlobalObject(ctx);
3572 JSValue appObj = QJSUtils::GetPropertyStr(ctx, globalObj, "aceapp");
3573 if (!JS_IsObject(appObj)) {
3574 LOGE("aceapp is not found.");
3575 return false;
3576 }
3577 JSValue defObj = QJSUtils::GetPropertyStr(ctx, appObj, "$def");
3578 if (!JS_IsObject(defObj)) {
3579 LOGE("$def is not found.");
3580 return false;
3581 }
3582 JSValue func = QJSUtils::GetPropertyStr(ctx, defObj, "onStartContinuation");
3583 if (!JS_IsFunction(ctx, func)) {
3584 LOGE("onStartContinuation is not found.");
3585 JS_FreeValue(ctx, globalObj);
3586 return false;
3587 }
3588 JSValueConst argv[] = {};
3589 JSValue ret = QJSUtils::Call(ctx, func, defObj, countof(argv), argv);
3590 std::string result = JS_ToCString(ctx, ret);
3591 js_std_loop(ctx);
3592 JS_FreeValue(ctx, globalObj);
3593 return (result == "true");
3594 }
3595
OnCompleteContinuation(int32_t code)3596 void QjsEngine::OnCompleteContinuation(int32_t code)
3597 {
3598 JSContext* ctx = engineInstance_->GetQjsContext();
3599 QJSHandleScope handleScope(ctx);
3600 JSValue globalObj = JS_GetGlobalObject(ctx);
3601 JSValue appObj = QJSUtils::GetPropertyStr(ctx, globalObj, "aceapp");
3602 if (!JS_IsObject(appObj)) {
3603 LOGE("aceapp is not found.");
3604 return;
3605 }
3606 JSValue defObj = QJSUtils::GetPropertyStr(ctx, appObj, "$def");
3607 if (!JS_IsObject(defObj)) {
3608 LOGE("$def is not found.");
3609 return;
3610 }
3611 JSValue func = QJSUtils::GetPropertyStr(ctx, defObj, "onCompleteContinuation");
3612 if (!JS_IsFunction(ctx, func)) {
3613 LOGE("onCompleteContinuation is not found.");
3614 JS_FreeValue(ctx, globalObj);
3615 return;
3616 }
3617 JSValueConst argv[] = { JS_NewInt32(ctx, code) };
3618 QJSUtils::Call(ctx, func, defObj, countof(argv), argv);
3619 js_std_loop(ctx);
3620 JS_FreeValue(ctx, globalObj);
3621 }
3622
OnRemoteTerminated()3623 void QjsEngine::OnRemoteTerminated()
3624 {
3625 JSContext* ctx = engineInstance_->GetQjsContext();
3626 QJSHandleScope handleScope(ctx);
3627 JSValue globalObj = JS_GetGlobalObject(ctx);
3628 JSValue appObj = QJSUtils::GetPropertyStr(ctx, globalObj, "aceapp");
3629 if (!JS_IsObject(appObj)) {
3630 LOGE("aceapp is not found.");
3631 return;
3632 }
3633 JSValue defObj = QJSUtils::GetPropertyStr(ctx, appObj, "$def");
3634 if (!JS_IsObject(defObj)) {
3635 LOGE("$def is not found.");
3636 return;
3637 }
3638 JSValue func = QJSUtils::GetPropertyStr(ctx, defObj, "onRemoteTerminated");
3639 if (!JS_IsFunction(ctx, func)) {
3640 LOGE("onRemoteTerminated is not found.");
3641 JS_FreeValue(ctx, globalObj);
3642 return;
3643 }
3644 JSValueConst argv[] = {};
3645 QJSUtils::Call(ctx, func, defObj, countof(argv), argv);
3646 js_std_loop(ctx);
3647 JS_FreeValue(ctx, globalObj);
3648 }
3649
OnSaveData(std::string & data)3650 void QjsEngine::OnSaveData(std::string& data)
3651 {
3652 JSContext* ctx = engineInstance_->GetQjsContext();
3653 QJSHandleScope handleScope(ctx);
3654 JSValue globalObj = JS_GetGlobalObject(ctx);
3655 JSValue appObj = QJSUtils::GetPropertyStr(ctx, globalObj, "aceapp");
3656 if (!JS_IsObject(appObj)) {
3657 LOGE("aceapp is not found.");
3658 return;
3659 }
3660 JSValue defObj = QJSUtils::GetPropertyStr(ctx, appObj, "$def");
3661 if (!JS_IsObject(defObj)) {
3662 LOGE("$def is not found.");
3663 return;
3664 }
3665 JSValue func = QJSUtils::GetPropertyStr(ctx, defObj, "onSaveData");
3666 if (!JS_IsFunction(ctx, func)) {
3667 LOGE("onSaveData is not found.");
3668 JS_FreeValue(ctx, globalObj);
3669 return;
3670 }
3671 JSValue object = JS_NewObject(ctx);
3672 JSValueConst argv[] = { object };
3673 JSValue ret = QJSUtils::Call(ctx, func, defObj, countof(argv), argv);
3674 if (JS_ToCString(ctx, ret) == std::string("true")) {
3675 data = ScopedString::Stringify(ctx, object);
3676 }
3677 js_std_loop(ctx);
3678 JS_FreeValue(ctx, globalObj);
3679 }
3680
OnRestoreData(const std::string & data)3681 bool QjsEngine::OnRestoreData(const std::string& data)
3682 {
3683 JSContext* ctx = engineInstance_->GetQjsContext();
3684 QJSHandleScope handleScope(ctx);
3685 JSValue globalObj = JS_GetGlobalObject(ctx);
3686 JSValue appObj = QJSUtils::GetPropertyStr(ctx, globalObj, "aceapp");
3687 if (!JS_IsObject(appObj)) {
3688 LOGE("aceapp is not found.");
3689 return false;
3690 }
3691 JSValue defObj = QJSUtils::GetPropertyStr(ctx, appObj, "$def");
3692 if (!JS_IsObject(defObj)) {
3693 LOGE("$def is not found.");
3694 return false;
3695 }
3696 JSValue func = QJSUtils::GetPropertyStr(ctx, defObj, "onRestoreData");
3697 if (!JS_IsFunction(ctx, func)) {
3698 LOGE("onRestoreData is not found.");
3699 JS_FreeValue(ctx, globalObj);
3700 return false;
3701 }
3702 JSValue jsonObj = JS_ParseJSON(ctx, data.c_str(), data.length(), "");
3703 if (JS_IsUndefined(jsonObj) || JS_IsException(jsonObj)) {
3704 LOGE("Parse json for restore data failed.");
3705 return false;
3706 }
3707 JSValueConst argv[] = { jsonObj };
3708 JSValue ret = QJSUtils::Call(ctx, func, defObj, countof(argv), argv);
3709 std::string result = JS_ToCString(ctx, ret);
3710 js_std_loop(ctx);
3711 JS_FreeValue(ctx, globalObj);
3712 return (result == "true");
3713 }
3714
TimerCallback(const std::string & callbackId,const std::string & delay,bool isInterval)3715 void QjsEngine::TimerCallback(const std::string& callbackId, const std::string& delay, bool isInterval)
3716 {
3717 if (isInterval) {
3718 engineInstance_->CallJs(callbackId, std::string("{}"), true, true);
3719 engineInstance_->GetDelegate()->WaitTimer(callbackId, delay, isInterval, false);
3720 } else {
3721 engineInstance_->CallJs(callbackId, std::string("{}"), false, true);
3722 engineInstance_->GetDelegate()->ClearTimer(callbackId);
3723 }
3724 }
3725
MediaQueryCallback(const std::string & callbackId,const std::string & args)3726 void QjsEngine::MediaQueryCallback(const std::string& callbackId, const std::string& args)
3727 {
3728 JsEngine::MediaQueryCallback(callbackId, args);
3729 if (!callbackId.empty() && engineInstance_) {
3730 engineInstance_->CallJs(callbackId.c_str(), args.c_str(), true, false);
3731 }
3732 }
3733
RequestAnimationCallback(const std::string & callbackId,uint64_t timeStamp)3734 void QjsEngine::RequestAnimationCallback(const std::string& callbackId, uint64_t timeStamp)
3735 {
3736 if (engineInstance_) {
3737 engineInstance_->CallJs(callbackId.c_str(), std::to_string(timeStamp).c_str(), false, true);
3738 engineInstance_->GetDelegate()->CancelAnimationFrame(callbackId);
3739 }
3740 }
3741
JsCallback(const std::string & callbackId,const std::string & args)3742 void QjsEngine::JsCallback(const std::string& callbackId, const std::string& args)
3743 {
3744 if (engineInstance_) {
3745 engineInstance_->CallJs(callbackId.c_str(), args.c_str(), true, false);
3746 }
3747 }
3748
FireAsyncEvent(const std::string & eventId,const std::string & param)3749 void QjsEngine::FireAsyncEvent(const std::string& eventId, const std::string& param)
3750 {
3751 std::string callBuf = std::string("[{\"args\": [\"")
3752 .append(eventId)
3753 .append("\",")
3754 .append(param)
3755 .append("], \"method\":\"fireEvent\"}]");
3756 LOGD("FireAsyncEvent string: %{private}s", callBuf.c_str());
3757
3758 ACE_DCHECK(engineInstance_);
3759 JSValue cppToJsRet = engineInstance_->FireJsEvent(callBuf);
3760 if (JS_IsException(cppToJsRet)) {
3761 LOGE("Qjs FireAsyncEvent FAILED !! jsCall: %{private}s", callBuf.c_str());
3762 QJSUtils::JsStdDumpErrorAce(engineInstance_->GetQjsContext(), JsErrorType::FIRE_EVENT_ERROR, instanceId_,
3763 nullptr, engineInstance_->GetRunningPage());
3764 }
3765 js_std_loop(engineInstance_->GetQjsContext());
3766 JS_FreeValue(engineInstance_->GetQjsContext(), cppToJsRet);
3767 }
3768
FireSyncEvent(const std::string & eventId,const std::string & param)3769 void QjsEngine::FireSyncEvent(const std::string& eventId, const std::string& param)
3770 {
3771 std::string callBuf = std::string("[{\"args\": [\"")
3772 .append(eventId)
3773 .append("\",")
3774 .append(param)
3775 .append("], \"method\":\"fireEventSync\"}]");
3776 LOGD("FireSyncEvent string: %{private}s", callBuf.c_str());
3777
3778 ACE_DCHECK(engineInstance_);
3779 JSValue cppToJsRet = engineInstance_->FireJsEvent(callBuf.c_str());
3780 if (JS_IsException(cppToJsRet)) {
3781 LOGE("Qjs FireSyncEvent FAILED !! jsCall: %{private}s", callBuf.c_str());
3782 QJSUtils::JsStdDumpErrorAce(engineInstance_->GetQjsContext(), JsErrorType::FIRE_EVENT_ERROR, instanceId_,
3783 nullptr, engineInstance_->GetRunningPage());
3784 }
3785 js_std_loop(engineInstance_->GetQjsContext());
3786 JS_FreeValue(engineInstance_->GetQjsContext(), cppToJsRet);
3787 }
3788
FireExternalEvent(const std::string & componentId,const uint32_t nodeId,const bool isDestroy)3789 void QjsEngine::FireExternalEvent(const std::string& componentId, const uint32_t nodeId, const bool isDestroy)
3790 {
3791 ACE_DCHECK(engineInstance_);
3792 if (isDestroy) {
3793 return;
3794 }
3795 auto context = engineInstance_->GetQjsContext();
3796
3797 auto page = engineInstance_->GetRunningPage();
3798 if (page == nullptr) {
3799 LOGE("FireExternalEvent GetRunningPage is nullptr");
3800 return;
3801 }
3802 std::string arguments;
3803 auto bridge = AceType::DynamicCast<QjsXComponentBridge>(page->GetXComponentBridgeById(nodeId));
3804 if (bridge) {
3805 bridge->HandleContext(context, nodeId, arguments);
3806 return;
3807 }
3808 }
3809
SetJsMessageDispatcher(const RefPtr<JsMessageDispatcher> & dispatcher)3810 void QjsEngine::SetJsMessageDispatcher(const RefPtr<JsMessageDispatcher>& dispatcher)
3811 {
3812 ACE_DCHECK(engineInstance_);
3813 engineInstance_->SetJsMessageDispatcher(dispatcher);
3814 }
3815
RunGarbageCollection()3816 void QjsEngine::RunGarbageCollection()
3817 {
3818 if (engineInstance_ && engineInstance_->GetQjsRuntime()) {
3819 JS_RunGC(engineInstance_->GetQjsRuntime());
3820 }
3821 }
3822
GetGroupJsBridge()3823 RefPtr<GroupJsBridge> QjsEngine::GetGroupJsBridge()
3824 {
3825 return AceType::MakeRefPtr<QuickJsGroupJsBridge>();
3826 }
3827
3828 } // namespace OHOS::Ace::Framework
3829