1 /*
2 * Copyright (c) 2023 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 "bridge/declarative_frontend/jsview/js_navigation_stack.h"
17
18 #include "bridge/common/utils/engine_helper.h"
19 #include "bridge/declarative_frontend/engine/js_converter.h"
20 #include "bridge/declarative_frontend/engine/functions/js_function.h"
21 #include "bridge/declarative_frontend/engine/js_execution_scope_defines.h"
22 #include "bridge/declarative_frontend/jsview/js_nav_path_stack.h"
23 #include "bridge/declarative_frontend/jsview/js_navdestination_context.h"
24 #include "core/components_ng/base/ui_node.h"
25 #include "core/components_ng/base/view_stack_processor.h"
26 #include "core/components_ng/pattern/custom/custom_node.h"
27 #include "core/components_ng/pattern/navrouter/navdestination_model.h"
28 #include "core/components_v2/inspector/inspector_constants.h"
29 #include "frameworks/base/json/json_util.h"
30
31 namespace OHOS::Ace::Framework {
32 namespace {
33 constexpr int32_t ARGC_COUNT_TWO = 2;
34 constexpr int32_t MAX_PARSE_DEPTH = 3;
35 constexpr uint32_t MAX_PARSE_LENGTH = 1024;
36 constexpr uint32_t MAX_PARSE_PROPERTY_SIZE = 15;
37 constexpr int32_t INVALID_DESTINATION_MODE = -1;
38 constexpr char JS_STRINGIFIED_UNDEFINED[] = "undefined";
39 constexpr char JS_NAV_PATH_STACK_GETNATIVESTACK_FUNC[] = "getNativeStack";
40 constexpr char JS_NAV_PATH_STACK_SETPARENT_FUNC[] = "setParent";
41
GetNapiEnv()42 napi_env GetNapiEnv()
43 {
44 auto engine = EngineHelper::GetCurrentEngine();
45 CHECK_NULL_RETURN(engine, nullptr);
46 NativeEngine* nativeEngine = engine->GetNativeEngine();
47 CHECK_NULL_RETURN(nativeEngine, nullptr);
48 return reinterpret_cast<napi_env>(nativeEngine);
49 }
50 }
51
GetName()52 std::string JSRouteInfo::GetName()
53 {
54 return name_;
55 }
56
SetName(const std::string & name)57 void JSRouteInfo::SetName(const std::string& name)
58 {
59 name_ = name;
60 }
61
SetParam(const JSRef<JSVal> & param)62 void JSRouteInfo::SetParam(const JSRef<JSVal>& param)
63 {
64 param_ = param;
65 }
66
GetParam() const67 JSRef<JSVal> JSRouteInfo::GetParam() const
68 {
69 return param_;
70 }
71
SetDataSourceObj(const JSRef<JSObject> & dataSourceObj)72 void JSNavigationStack::SetDataSourceObj(const JSRef<JSObject>& dataSourceObj)
73 {
74 // clean callback from old JSNavPathStack
75 UpdateOnStateChangedCallback(dataSourceObj_, nullptr);
76 UpdateCheckNavDestinationExistsFunc(dataSourceObj_, nullptr);
77 dataSourceObj_ = dataSourceObj;
78 // add callback to new JSNavPathStack
79 RemoveStack();
80 UpdateOnStateChangedCallback(dataSourceObj_, onStateChangedCallback_);
81 auto checkNavDestinationExistsFunc = [weakStack = WeakClaim(this)](const JSRef<JSObject>& info) -> int32_t {
82 auto stack = weakStack.Upgrade();
83 if (stack == nullptr) {
84 return ERROR_CODE_INTERNAL_ERROR;
85 }
86 auto errorCode = stack->CheckNavDestinationExists(info);
87 if (errorCode != ERROR_CODE_NO_ERROR) {
88 stack->RemoveInvalidPage(info);
89 }
90 return errorCode;
91 };
92 UpdateCheckNavDestinationExistsFunc(dataSourceObj_, checkNavDestinationExistsFunc);
93 }
94
UpdateCheckNavDestinationExistsFunc(JSRef<JSObject> obj,std::function<int32_t (JSRef<JSObject>)> checkFunc)95 void JSNavigationStack::UpdateCheckNavDestinationExistsFunc(JSRef<JSObject> obj,
96 std::function<int32_t(JSRef<JSObject>)> checkFunc)
97 {
98 if (obj->IsEmpty()) {
99 return;
100 }
101
102 auto property = obj->GetProperty(JS_NAV_PATH_STACK_GETNATIVESTACK_FUNC);
103 if (!property->IsFunction()) {
104 return;
105 }
106
107 auto getNativeStackFunc = JSRef<JSFunc>::Cast(property);
108 auto nativeStack = getNativeStackFunc->Call(obj);
109 if (nativeStack->IsEmpty() || !nativeStack->IsObject()) {
110 return;
111 }
112
113 auto nativeStackObj = JSRef<JSObject>::Cast(nativeStack);
114 JSNavPathStack* stack = nativeStackObj->Unwrap<JSNavPathStack>();
115 CHECK_NULL_VOID(stack);
116
117 stack->SetCheckNavDestinationExistsFunc(checkFunc);
118 }
119
GetDataSourceObj()120 const JSRef<JSObject>& JSNavigationStack::GetDataSourceObj()
121 {
122 return dataSourceObj_;
123 }
124
SetNavDestBuilderFunc(const JSRef<JSFunc> & navDestBuilderFunc)125 void JSNavigationStack::SetNavDestBuilderFunc(const JSRef<JSFunc>& navDestBuilderFunc)
126 {
127 navDestBuilderFunc_ = navDestBuilderFunc;
128 }
129
IsEmpty()130 bool JSNavigationStack::IsEmpty()
131 {
132 return dataSourceObj_->IsEmpty();
133 }
134
Pop()135 void JSNavigationStack::Pop()
136 {
137 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
138 if (dataSourceObj_->IsEmpty()) {
139 return;
140 }
141 auto popFunc = dataSourceObj_->GetProperty("pop");
142 if (!popFunc->IsFunction()) {
143 return;
144 }
145 auto func = JSRef<JSFunc>::Cast(popFunc);
146 JSRef<JSVal>::Cast(func->Call(dataSourceObj_));
147 }
148
Push(const std::string & name,const RefPtr<NG::RouteInfo> & routeInfo)149 void JSNavigationStack::Push(const std::string& name, const RefPtr<NG::RouteInfo>& routeInfo)
150 {
151 // obtain param from NavPathStack
152 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
153 JSRef<JSVal> param;
154 if (routeInfo) {
155 auto jsRouteInfo = AceType::DynamicCast<JSRouteInfo>(routeInfo);
156 param = jsRouteInfo->GetParam();
157 } else {
158 auto getParamByNameFunc = dataSourceObj_->GetProperty("getParamByName");
159 if (getParamByNameFunc->IsFunction()) {
160 auto getFunc = JSRef<JSFunc>::Cast(getParamByNameFunc);
161 auto funcArray = getFunc->Call(dataSourceObj_);
162 if (funcArray->IsArray()) {
163 auto result = JSRef<JSArray>::Cast(funcArray);
164 param = result->GetValueAt(0);
165 }
166 }
167 }
168 auto pushNameFunc = dataSourceObj_->GetProperty("pushName");
169 if (pushNameFunc->IsFunction()) {
170 auto pushFunc = JSRef<JSFunc>::Cast(pushNameFunc);
171 JSRef<JSVal> params[2];
172 params[0] = JSRef<JSVal>::Make(ToJSValue(name));
173 params[1] = param;
174 pushFunc->Call(dataSourceObj_, 2, params);
175 }
176 }
177
PushName(const std::string & name,const JSRef<JSVal> & param)178 void JSNavigationStack::PushName(const std::string& name, const JSRef<JSVal>& param)
179 {
180 // obtain param from routeInfo
181 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
182 auto pushNameFunc = dataSourceObj_->GetProperty("pushName");
183 if (pushNameFunc->IsFunction()) {
184 auto pushFunc = JSRef<JSFunc>::Cast(pushNameFunc);
185 JSRef<JSVal> params[2];
186 params[0] = JSRef<JSVal>::Make(ToJSValue(name));
187 params[1] = param;
188 pushFunc->Call(dataSourceObj_, 2, params);
189 }
190 }
191
Push(const std::string & name,int32_t index)192 void JSNavigationStack::Push(const std::string& name, int32_t index)
193 {
194 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
195 auto getParamByIndexFunc = dataSourceObj_->GetProperty("getParamByIndex");
196 if (!getParamByIndexFunc->IsFunction()) {
197 return ;
198 }
199 auto pushNameFunc = dataSourceObj_->GetProperty("pushName");
200 if (!pushNameFunc->IsFunction()) {
201 return ;
202 }
203 auto getFunc = JSRef<JSFunc>::Cast(getParamByIndexFunc);
204 auto param = JSRef<JSVal>::Cast(getFunc->Call(dataSourceObj_));
205 auto pushFunc = JSRef<JSFunc>::Cast(pushNameFunc);
206 JSRef<JSVal> params[ARGC_COUNT_TWO];
207 params[0] = JSRef<JSVal>::Make(ToJSValue(name));
208 params[1] = param;
209 pushFunc->Call(dataSourceObj_, ARGC_COUNT_TWO, params);
210 }
211
RemoveName(const std::string & name)212 void JSNavigationStack::RemoveName(const std::string& name)
213 {
214 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
215 if (dataSourceObj_->IsEmpty()) {
216 return;
217 }
218 auto removeByNameFunc = dataSourceObj_->GetProperty("removeByName");
219 if (!removeByNameFunc->IsFunction()) {
220 return;
221 }
222 auto func = JSRef<JSFunc>::Cast(removeByNameFunc);
223 JSRef<JSVal> params[1];
224 params[0] = JSRef<JSVal>::Make(ToJSValue(name));
225 func->Call(dataSourceObj_, 1, params);
226 }
227
RemoveIndex(int32_t index)228 void JSNavigationStack::RemoveIndex(int32_t index)
229 {
230 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
231 if (dataSourceObj_->IsEmpty()) {
232 return;
233 }
234 auto removeIndexFunc = dataSourceObj_->GetProperty("removeIndex");
235 if (removeIndexFunc->IsFunction()) {
236 auto func = JSRef<JSFunc>::Cast(removeIndexFunc);
237 JSRef<JSVal> params[1];
238 params[0] = JSRef<JSVal>::Make(ToJSValue(index));
239 func->Call(dataSourceObj_, 1, params);
240 }
241 }
242
Clear()243 void JSNavigationStack::Clear()
244 {
245 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
246 if (dataSourceObj_->IsEmpty()) {
247 return;
248 }
249 auto clearFunc = dataSourceObj_->GetProperty("clear");
250 if (!clearFunc->IsFunction()) {
251 return;
252 }
253 auto func = JSRef<JSFunc>::Cast(clearFunc);
254 func->Call(dataSourceObj_);
255 }
256
GetAllPathName()257 std::vector<std::string> JSNavigationStack::GetAllPathName()
258 {
259 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, {});
260 if (dataSourceObj_->IsEmpty()) {
261 return {};
262 }
263 auto getAllPathNameFunc = dataSourceObj_->GetProperty("getAllPathName");
264 if (!getAllPathNameFunc->IsFunction()) {
265 return {};
266 }
267 auto func = JSRef<JSFunc>::Cast(getAllPathNameFunc);
268 auto funcArray = func->Call(dataSourceObj_);
269 if (!funcArray->IsArray()) {
270 return {};
271 }
272 auto array = JSRef<JSArray>::Cast(funcArray);
273 if (array->IsEmpty()) {
274 return {};
275 }
276 std::vector<std::string> pathNames;
277 for (size_t i = 0; i < array->Length(); i++) {
278 auto value = array->GetValueAt(i);
279 if (value->IsString()) {
280 pathNames.emplace_back(value->ToString());
281 }
282 }
283
284 return pathNames;
285 }
286
GetAllPathIndex()287 std::vector<int32_t> JSNavigationStack::GetAllPathIndex()
288 {
289 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, {});
290 if (dataSourceObj_->IsEmpty()) {
291 return {};
292 }
293 auto getAllPathIndexFunc = dataSourceObj_->GetProperty("getAllPathIndex");
294 if (!getAllPathIndexFunc->IsFunction()) {
295 return {};
296 }
297 auto func = JSRef<JSFunc>::Cast(getAllPathIndexFunc);
298 auto funcArray = func->Call(dataSourceObj_);
299 if (!funcArray->IsArray()) {
300 return {};
301 }
302 auto array = JSRef<JSArray>::Cast(funcArray);
303 if (array->IsEmpty()) {
304 return {};
305 }
306 std::vector<int32_t> pathIndex;
307 for (size_t i = 0; i < array->Length(); i++) {
308 auto value = array->GetValueAt(i);
309 if (value->IsNumber()) {
310 pathIndex.emplace_back(value->ToNumber<int32_t>());
311 }
312 }
313
314 return pathIndex;
315 }
316
InitNavPathIndex(const std::vector<std::string> & pathNames)317 void JSNavigationStack::InitNavPathIndex(const std::vector<std::string>& pathNames)
318 {
319 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
320 if (dataSourceObj_->IsEmpty()) {
321 return;
322 }
323
324 JSRef<JSArray> nameArray = JSRef<JSArray>::New();
325 JSRef<JSVal> params[1];
326 for (size_t i = 0; i < pathNames.size(); i++) {
327 JSRef<JSVal> info = JSRef<JSVal>::Make(ToJSValue(pathNames[i]));
328 nameArray->SetValueAt(i, info);
329 }
330 params[0] = nameArray;
331 auto initNavPathIndexFunc = dataSourceObj_->GetProperty("initNavPathIndex");
332 if (!initNavPathIndexFunc->IsFunction()) {
333 return;
334 }
335 auto func = JSRef<JSFunc>::Cast(initNavPathIndexFunc);
336 func->Call(dataSourceObj_, 1, params);
337 }
338
SetDestinationIdToJsStack(int32_t index,const std::string & navDestinationId)339 void JSNavigationStack::SetDestinationIdToJsStack(int32_t index, const std::string& navDestinationId)
340 {
341 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
342 auto pathInfo = GetJsPathInfo(index);
343 if (pathInfo->IsEmpty()) {
344 return;
345 }
346 pathInfo->SetProperty<std::string>("navDestinationId", navDestinationId);
347 }
348
CallByPushDestination(int32_t index)349 bool JSNavigationStack::CallByPushDestination(int32_t index)
350 {
351 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
352 auto pathInfo = GetJsPathInfo(index);
353 if (pathInfo->IsEmpty()) {
354 return false;
355 }
356 auto isPushDestination = pathInfo->GetPropertyValue<bool>("pushDestination", false);
357 return isPushDestination;
358 }
359
CreateNodeByIndex(int32_t index,const WeakPtr<NG::UINode> & customNode)360 RefPtr<NG::UINode> JSNavigationStack::CreateNodeByIndex(int32_t index, const WeakPtr<NG::UINode>& customNode)
361 {
362 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, nullptr);
363 auto name = GetNameByIndex(index);
364 auto param = GetParamByIndex(index);
365 RefPtr<NG::UINode> node;
366 if (GetNodeFromPreBuildList(index, name, param, node)) {
367 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "get node from prebuild list");
368 return node;
369 }
370 RefPtr<NG::NavDestinationGroupNode> desNode;
371 NG::ScopedViewStackProcessor scopedViewStackProcessor;
372 int32_t errorCode = LoadDestination(name, param, customNode, node, desNode);
373 if (errorCode != ERROR_CODE_NO_ERROR) {
374 if (CallByPushDestination(index)) {
375 return nullptr;
376 }
377 TAG_LOGE(AceLogTag::ACE_NAVIGATION, "can't find target destination by index, create empty node");
378 return AceType::DynamicCast<NG::UINode>(NavDestinationModel::GetInstance()->CreateEmpty());
379 }
380 auto pattern = AceType::DynamicCast<NG::NavDestinationPattern>(desNode->GetPattern());
381 if (pattern) {
382 pattern->SetName(name);
383 pattern->SetIndex(index);
384 auto onPop = GetOnPopByIndex(index);
385 auto isEntry = GetIsEntryByIndex(index);
386 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "create destination node, isEntry %{public}d", isEntry);
387 auto pathInfo = AceType::MakeRefPtr<JSNavPathInfo>(name, param, onPop, isEntry);
388 pattern->SetNavPathInfo(pathInfo);
389 pattern->SetNavigationStack(WeakClaim(this));
390 }
391 return node;
392 }
393
CreateNodeByRouteInfo(const RefPtr<NG::RouteInfo> & routeInfo,const WeakPtr<NG::UINode> & customNode)394 RefPtr<NG::UINode> JSNavigationStack::CreateNodeByRouteInfo(const RefPtr<NG::RouteInfo>& routeInfo,
395 const WeakPtr<NG::UINode>& customNode)
396 {
397 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, nullptr);
398 auto jsRouteInfo = AceType::DynamicCast<JSRouteInfo>(routeInfo);
399 if (!jsRouteInfo) {
400 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "route info is invalid");
401 return DynamicCast<NG::UINode>(NavDestinationModel::GetInstance()->CreateEmpty());
402 }
403 auto name = jsRouteInfo->GetName();
404 auto param = jsRouteInfo->GetParam();
405 RefPtr<NG::UINode> node;
406 RefPtr<NG::NavDestinationGroupNode> desNode;
407 int32_t errorCode = LoadDestination(name, param, customNode, node, desNode);
408 if (errorCode == ERROR_CODE_NO_ERROR) {
409 auto pattern = AceType::DynamicCast<NG::NavDestinationPattern>(desNode->GetPattern());
410 if (pattern) {
411 auto pathInfo = AceType::MakeRefPtr<JSNavPathInfo>(name, param);
412 pattern->SetNavPathInfo(pathInfo);
413 pattern->SetName(name);
414 pattern->SetNavigationStack(WeakClaim(this));
415 }
416 return node;
417 }
418 return DynamicCast<NG::UINode>(NavDestinationModel::GetInstance()->CreateEmpty());
419 }
420
SetJSExecutionContext(const JSExecutionContext & context)421 void JSNavigationStack::SetJSExecutionContext(const JSExecutionContext& context)
422 {
423 executionContext_ = context;
424 }
425
GetNameByIndex(int32_t index)426 std::string JSNavigationStack::GetNameByIndex(int32_t index)
427 {
428 auto pathNames = GetAllPathName();
429 if (index < 0 || index >= static_cast<int32_t>(pathNames.size())) {
430 return "";
431 }
432
433 return pathNames[index];
434 }
435
GetParamByIndex(int32_t index) const436 JSRef<JSVal> JSNavigationStack::GetParamByIndex(int32_t index) const
437 {
438 if (dataSourceObj_->IsEmpty()) {
439 return JSRef<JSVal>::Make();
440 }
441 auto getParamByIndexFunc = dataSourceObj_->GetProperty("getParamByIndex");
442 if (!getParamByIndexFunc->IsFunction()) {
443 return JSRef<JSVal>::Make();
444 }
445 auto func = JSRef<JSFunc>::Cast(getParamByIndexFunc);
446 JSRef<JSVal> params[1];
447 params[0] = JSRef<JSVal>::Make(ToJSValue(index));
448 return func->Call(dataSourceObj_, 1, params);
449 }
450
GetOnPopByIndex(int32_t index) const451 JSRef<JSVal> JSNavigationStack::GetOnPopByIndex(int32_t index) const
452 {
453 if (dataSourceObj_->IsEmpty()) {
454 return JSRef<JSVal>::Make();
455 }
456 auto getOnPopByIndexFunc = dataSourceObj_->GetProperty("getOnPopByIndex");
457 if (!getOnPopByIndexFunc->IsFunction()) {
458 return JSRef<JSVal>::Make();
459 }
460 auto func = JSRef<JSFunc>::Cast(getOnPopByIndexFunc);
461 JSRef<JSVal> params[1];
462 params[0] = JSRef<JSVal>::Make(ToJSValue(index));
463 return func->Call(dataSourceObj_, 1, params);
464 }
465
GetIsEntryByIndex(int32_t index)466 bool JSNavigationStack::GetIsEntryByIndex(int32_t index)
467 {
468 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
469 if (dataSourceObj_->IsEmpty()) {
470 return false;
471 }
472 auto getIsEntryFunc = dataSourceObj_->GetProperty("getIsEntryByIndex");
473 if (!getIsEntryFunc->IsFunction()) {
474 return false;
475 }
476 auto func = JSRef<JSFunc>::Cast(getIsEntryFunc);
477 JSRef<JSVal> params[1];
478 params[0] = JSRef<JSVal>::Make(ToJSValue(index));
479 auto result = func->Call(dataSourceObj_, 1, params);
480 if (!result->IsBoolean()) {
481 return false;
482 }
483 return result->ToBoolean();
484 }
485
SetIsEntryByIndex(int32_t index,bool isEntry)486 void JSNavigationStack::SetIsEntryByIndex(int32_t index, bool isEntry)
487 {
488 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
489 if (dataSourceObj_->IsEmpty()) {
490 return;
491 }
492 auto setIsEntryFunc = dataSourceObj_->GetProperty("setIsEntryByIndex");
493 if (!setIsEntryFunc->IsFunction()) {
494 return;
495 }
496 auto func = JSRef<JSFunc>::Cast(setIsEntryFunc);
497 JSRef<JSVal> params[ARGC_COUNT_TWO];
498 params[0] = JSRef<JSVal>::Make(ToJSValue(index));
499 params[1] = JSRef<JSVal>::Make(ToJSValue(isEntry));
500 func->Call(dataSourceObj_, ARGC_COUNT_TWO, params);
501 }
502
GetNavDestinationNodeInUINode(RefPtr<NG::UINode> node,RefPtr<NG::NavDestinationGroupNode> & desNode)503 bool JSNavigationStack::GetNavDestinationNodeInUINode(
504 RefPtr<NG::UINode> node, RefPtr<NG::NavDestinationGroupNode>& desNode)
505 {
506 RefPtr<NG::CustomNode> customNode;
507 while (node) {
508 if (node->GetTag() == V2::JS_VIEW_ETS_TAG) {
509 customNode = AceType::DynamicCast<NG::CustomNode>(node);
510 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "render current custom node: %{public}s",
511 customNode->GetCustomTag().c_str());
512 // record parent navigationNode before customNode is rendered in case of navDestinationNode
513 auto navigationNode = GetNavigationNode();
514 customNode->SetNavigationNode(navigationNode);
515 // render, and find deep further
516 customNode->Render();
517 } else if (node->GetTag() == V2::NAVDESTINATION_VIEW_ETS_TAG) {
518 desNode = AceType::DynamicCast<NG::NavDestinationGroupNode>(node);
519 if (desNode) {
520 desNode->SetNavDestinationCustomNode(AceType::WeakClaim(AceType::RawPtr(customNode)));
521 }
522 return true;
523 }
524 auto children = node->GetChildren();
525 if (children.size() != 1) {
526 TAG_LOGI(AceLogTag::ACE_NAVIGATION,
527 "router map is invalid, child size is not one: %{public}zu", children.size());
528 }
529 node = children.front();
530 }
531 return false;
532 }
533
GetReplaceValue() const534 int32_t JSNavigationStack::GetReplaceValue() const
535 {
536 if (dataSourceObj_->IsEmpty()) {
537 return false;
538 }
539 auto replace = dataSourceObj_->GetProperty("isReplace");
540 return replace->ToNumber<int32_t>();
541 }
542
UpdateReplaceValue(int32_t replaceValue) const543 void JSNavigationStack::UpdateReplaceValue(int32_t replaceValue) const
544 {
545 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
546 if (dataSourceObj_->IsEmpty()) {
547 return;
548 }
549 auto setIsReplaceFunc = dataSourceObj_->GetProperty("setIsReplace");
550 if (!setIsReplaceFunc->IsFunction()) {
551 return;
552 }
553 auto replaceFunc = JSRef<JSFunc>::Cast(setIsReplaceFunc);
554 JSRef<JSVal> params[1];
555 params[0] = JSRef<JSVal>::Make(ToJSValue(replaceValue));
556 replaceFunc->Call(dataSourceObj_, 1, params);
557 }
558
GetRouteParam() const559 std::string JSNavigationStack::GetRouteParam() const
560 {
561 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, "");
562 auto size = GetSize();
563 if (size > 0) {
564 auto param = GetParamByIndex(size - 1);
565 return ConvertParamToString(param, true);
566 }
567 return "";
568 }
569
GetSize() const570 int32_t JSNavigationStack::GetSize() const
571 {
572 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, 0);
573 if (dataSourceObj_->IsEmpty()) {
574 return 0;
575 }
576 auto sizeFunc = dataSourceObj_->GetProperty("size");
577 if (!sizeFunc->IsFunction()) {
578 return 0;
579 }
580 auto func = JSRef<JSFunc>::Cast(sizeFunc);
581 auto jsValue = JSRef<JSVal>::Cast(func->Call(dataSourceObj_));
582 if (jsValue->IsNumber()) {
583 return jsValue->ToNumber<int32_t>();
584 }
585 return 0;
586 }
587
ConvertParamToString(const JSRef<JSVal> & param,bool needLimit) const588 std::string JSNavigationStack::ConvertParamToString(const JSRef<JSVal>& param, bool needLimit) const
589 {
590 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, "");
591 if (param->IsBoolean()) {
592 bool ret = param->ToBoolean();
593 return ret ? "true" : "false";
594 } else if (param->IsNumber()) {
595 double ret = param->ToNumber<double>();
596 std::ostringstream oss;
597 oss<< ret;
598 return oss.str();
599 } else if (param->IsString()) {
600 std::string ret = param->ToString();
601 if (needLimit && ret.size() > MAX_PARSE_LENGTH) {
602 return ret.substr(0, MAX_PARSE_LENGTH);
603 }
604 return ret;
605 } else if (param->IsObject()) {
606 JSRef<JSObject> obj = JSRef<JSObject>::Cast(param);
607 auto jsonObj = JsonUtil::Create(true);
608 ParseJsObject(jsonObj, obj, MAX_PARSE_DEPTH, needLimit);
609 return jsonObj->ToString();
610 }
611 return "";
612 }
613
ParseJsObject(std::unique_ptr<JsonValue> & json,const JSRef<JSObject> & obj,int32_t depthLimit,bool needLimit) const614 void JSNavigationStack::ParseJsObject(
615 std::unique_ptr<JsonValue>& json, const JSRef<JSObject>& obj, int32_t depthLimit, bool needLimit) const
616 {
617 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
618 if (depthLimit == 0) {
619 return;
620 }
621 depthLimit--;
622 auto propertyNames = obj->GetPropertyNames();
623 if (!propertyNames->IsArray()) {
624 return;
625 }
626 size_t size = propertyNames->Length();
627 if (needLimit && size > MAX_PARSE_PROPERTY_SIZE) {
628 size = MAX_PARSE_PROPERTY_SIZE;
629 }
630 for (size_t i = 0; i < size; i++) {
631 JSRef<JSVal> name = propertyNames->GetValueAt(i);
632 if (!name->IsString()) {
633 continue;
634 }
635 auto propertyName = name->ToString();
636 auto key = propertyName.c_str();
637 JSRef<JSVal> value = obj->GetProperty(key);
638 if (value->IsBoolean()) {
639 bool ret = value->ToBoolean();
640 json->Put(key, ret ? "true" : "false");
641 } else if (value->IsNumber()) {
642 double ret = value->ToNumber<double>();
643 std::ostringstream oss;
644 oss << ret;
645 json->Put(key, oss.str().c_str());
646 } else if (value->IsString()) {
647 std::string ret = value->ToString();
648 if (needLimit && ret.size() > MAX_PARSE_LENGTH) {
649 json->Put(key, ret.substr(0, MAX_PARSE_LENGTH).c_str());
650 } else {
651 json->Put(key, ret.c_str());
652 }
653 } else if (value->IsObject()) {
654 JSRef<JSObject> childObj = JSRef<JSObject>::Cast(value);
655 auto childJson = JsonUtil::Create(true);
656 ParseJsObject(childJson, childObj, depthLimit, needLimit);
657 json->Put(key, childJson);
658 }
659 }
660 }
661
GetAnimatedValue() const662 bool JSNavigationStack::GetAnimatedValue() const
663 {
664 if (dataSourceObj_->IsEmpty()) {
665 return true;
666 }
667 auto animated = dataSourceObj_->GetProperty("animated");
668 return animated->ToBoolean();
669 }
670
UpdateAnimatedValue(bool animated)671 void JSNavigationStack::UpdateAnimatedValue(bool animated)
672 {
673 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
674 if (dataSourceObj_->IsEmpty()) {
675 return;
676 }
677 auto setAnimatedFunc = dataSourceObj_->GetProperty("setAnimated");
678 if (!setAnimatedFunc->IsFunction()) {
679 return;
680 }
681 auto animatedFunc = JSRef<JSFunc>::Cast(setAnimatedFunc);
682 JSRef<JSVal> params[1];
683 params[0] = JSRef<JSVal>::Make(ToJSValue(animated));
684 animatedFunc->Call(dataSourceObj_, 1, params);
685 }
686
687
GetDisableAnimation() const688 bool JSNavigationStack::GetDisableAnimation() const
689 {
690 if (dataSourceObj_->IsEmpty()) {
691 return false;
692 }
693 auto disableAllAnimation = dataSourceObj_->GetProperty("disableAllAnimation");
694 return disableAllAnimation->ToBoolean();
695 }
696
UpdateOnStateChangedCallback(JSRef<JSObject> obj,std::function<void ()> callback)697 void JSNavigationStack::UpdateOnStateChangedCallback(JSRef<JSObject> obj, std::function<void()> callback)
698 {
699 if (obj->IsEmpty()) {
700 return;
701 }
702
703 auto property = obj->GetProperty(JS_NAV_PATH_STACK_GETNATIVESTACK_FUNC);
704 if (!property->IsFunction()) {
705 return;
706 }
707
708 auto getNativeStackFunc = JSRef<JSFunc>::Cast(property);
709 auto nativeStack = getNativeStackFunc->Call(obj);
710 if (nativeStack->IsEmpty() || !nativeStack->IsObject()) {
711 return;
712 }
713
714 auto nativeStackObj = JSRef<JSObject>::Cast(nativeStack);
715 JSNavPathStack* stack = nativeStackObj->Unwrap<JSNavPathStack>();
716 CHECK_NULL_VOID(stack);
717 stack->SetOnStateChangedCallback(callback);
718 // When switching the navigation stack, it is necessary to immediately trigger a refresh
719 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "navigation necessary to immediately trigger a refresh");
720 stack->OnStateChanged();
721 stack->SetOnPopCallback([weakStack = AceType::WeakClaim(this)](const JSRef<JSVal>& param) {
722 auto navigationStack = weakStack.Upgrade();
723 CHECK_NULL_VOID(navigationStack);
724 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(navigationStack->executionContext_);
725 auto size = navigationStack->GetSize();
726 if (size == 0) {
727 return;
728 }
729 auto pathInfo = navigationStack->GetJsPathInfo(size - 1);
730 if (pathInfo->IsEmpty()) {
731 return;
732 }
733 auto navDestinationId = pathInfo->GetProperty("navDestinationId");
734 if (!navDestinationId->IsString()) {
735 return;
736 }
737 auto id = navDestinationId->ToString();
738 auto navPathList = navigationStack->GetAllNavDestinationNodes();
739 for (auto iter : navPathList) {
740 if (navigationStack->ExecutePopCallback(iter.second, std::atoi(id.c_str()), param)) {
741 return;
742 }
743 }
744 });
745 }
746
OnAttachToParent(RefPtr<NG::NavigationStack> parent)747 void JSNavigationStack::OnAttachToParent(RefPtr<NG::NavigationStack> parent)
748 {
749 auto parentStack = AceType::DynamicCast<JSNavigationStack>(parent);
750 if (!parentStack) {
751 return;
752 }
753
754 SetJSParentStack(JSRef<JSVal>::Cast(parentStack->GetDataSourceObj()));
755 }
756
OnDetachFromParent()757 void JSNavigationStack::OnDetachFromParent()
758 {
759 JSRef<JSVal> undefined(JSVal::Undefined());
760 SetJSParentStack(undefined);
761 }
762
SetJSParentStack(JSRef<JSVal> parent)763 void JSNavigationStack::SetJSParentStack(JSRef<JSVal> parent)
764 {
765 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
766 if (dataSourceObj_->IsEmpty()) {
767 return;
768 }
769
770 auto property = dataSourceObj_->GetProperty(JS_NAV_PATH_STACK_SETPARENT_FUNC);
771 if (!property->IsFunction()) {
772 return;
773 }
774
775 auto func = JSRef<JSFunc>::Cast(property);
776 JSRef<JSVal> params[1];
777 params[0] = parent;
778 func->Call(dataSourceObj_, 1, params);
779 }
780
RemoveInvalidPage(const JSRef<JSObject> & info)781 void JSNavigationStack::RemoveInvalidPage(const JSRef<JSObject>& info)
782 {
783 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
784 if (dataSourceObj_->IsEmpty()) {
785 return;
786 }
787 auto removeInvalidPage = dataSourceObj_->GetProperty("removeInvalidPage");
788 if (removeInvalidPage->IsFunction()) {
789 auto func = JSRef<JSFunc>::Cast(removeInvalidPage);
790 auto pathName = info->GetProperty("name");
791 auto param = info->GetProperty("param");
792 JSRef<JSVal> params[ARGC_COUNT_TWO] = { pathName, param };
793 func->Call(dataSourceObj_, ARGC_COUNT_TWO, params);
794 }
795 }
796
SaveNodeToPreBuildList(const std::string & name,const JSRef<JSVal> & param,RefPtr<NG::UINode> & node)797 void JSNavigationStack::SaveNodeToPreBuildList(const std::string& name, const JSRef<JSVal>& param,
798 RefPtr<NG::UINode>& node)
799 {
800 preBuildNodeList_.emplace_back(name, param, node, GetSize() - 1);
801 }
802
GetNodeFromPreBuildList(int32_t index,const std::string & name,const JSRef<JSVal> & param,RefPtr<NG::UINode> & node)803 bool JSNavigationStack::GetNodeFromPreBuildList(int32_t index, const std::string& name,
804 const JSRef<JSVal>& param, RefPtr<NG::UINode>& node)
805 {
806 auto isJsObjEqual = [](const JSRef<JSVal>& objLeft, const JSRef<JSVal>& objRight) {
807 return (objLeft->IsEmpty() && objRight->IsEmpty()) ||
808 (objLeft->GetLocalHandle()->IsStrictEquals(objLeft->GetEcmaVM(), objRight->GetLocalHandle()));
809 };
810 for (auto it = preBuildNodeList_.begin(); it != preBuildNodeList_.end(); ++it) {
811 if (it->name == name && isJsObjEqual(it->param, param) && it->index == index) {
812 node = it->uiNode;
813 preBuildNodeList_.erase(it);
814 return true;
815 }
816 }
817 return false;
818 }
819
ClearPreBuildNodeList()820 void JSNavigationStack::ClearPreBuildNodeList()
821 {
822 preBuildNodeList_.clear();
823 }
824
CheckNavDestinationExists(const JSRef<JSObject> & navPathInfo)825 int32_t JSNavigationStack::CheckNavDestinationExists(const JSRef<JSObject>& navPathInfo)
826 {
827 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, ERROR_CODE_DESTINATION_NOT_FOUND);
828 auto pathName = navPathInfo->GetProperty("name");
829 auto param = navPathInfo->GetProperty("param");
830 JSRef<JSVal> params[ARGC_COUNT_TWO] = { pathName, param };
831 auto name = pathName->ToString();
832 RefPtr<NG::UINode> node;
833 auto navigationNode = AceType::DynamicCast<NG::NavigationGroupNode>(navigationNode_.Upgrade());
834 CHECK_NULL_RETURN(navigationNode, ERROR_CODE_INTERNAL_ERROR);
835 auto navigationPattern = AceType::DynamicCast<NG::NavigationPattern>(navigationNode->GetPattern());
836 CHECK_NULL_RETURN(navigationPattern, ERROR_CODE_INTERNAL_ERROR);
837 RefPtr<NG::NavDestinationGroupNode> desNode;
838 int32_t errorCode = LoadDestination(name, param, navigationPattern->GetParentCustomNode(),
839 node, desNode);
840 if (errorCode != ERROR_CODE_NO_ERROR) {
841 return errorCode;
842 }
843 auto pattern = AceType::DynamicCast<NG::NavDestinationPattern>(desNode->GetPattern());
844 if (pattern) {
845 auto onPop = navPathInfo->GetProperty("onPop");
846 auto isEntryVal = navPathInfo->GetProperty("isEntry");
847 bool isEntry = isEntryVal->IsBoolean() ? isEntryVal->ToBoolean() : false;
848 auto pathInfo = AceType::MakeRefPtr<JSNavPathInfo>(name, param, onPop, isEntry);
849 pattern->SetName(name);
850 pattern->SetIndex(GetSize() - 1);
851 pattern->SetNavPathInfo(pathInfo);
852 pattern->SetNavigationStack(WeakClaim(this));
853 }
854 SaveNodeToPreBuildList(name, param, node);
855 return ERROR_CODE_NO_ERROR;
856 }
857
DumpStackInfo() const858 std::vector<std::string> JSNavigationStack::DumpStackInfo() const
859 {
860 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, {});
861 std::vector<std::string> dumpInfos;
862 for (size_t i = 0; i < navPathList_.size(); ++i) {
863 const auto& name = navPathList_[i].first;
864 std::string info = "[" + std::to_string(i) + "]{ name: \"" + name + "\"";
865 std::string param = ConvertParamToString(GetParamByIndex(i));
866 if (param.length() > 0) {
867 info += ", param: " + param;
868 }
869 info += " }";
870 dumpInfos.push_back(std::move(info));
871 }
872 return dumpInfos;
873 }
874
FireNavigationInterception(bool isBefore,const RefPtr<NG::NavDestinationContext> & from,const RefPtr<NG::NavDestinationContext> & to,NG::NavigationOperation operation,bool isAnimated)875 void JSNavigationStack::FireNavigationInterception(bool isBefore, const RefPtr<NG::NavDestinationContext>& from,
876 const RefPtr<NG::NavDestinationContext>& to, NG::NavigationOperation operation, bool isAnimated)
877 {
878 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
879 std::string targetName = isBefore ? "willShow" : "didShow";
880 JSRef<JSFunc> targetFunc;
881 if (!CheckAndGetInterceptionFunc(targetName, targetFunc)) {
882 return;
883 }
884 const uint8_t argsNum = 4;
885 JSRef<JSVal> params[argsNum];
886 auto preDestination = AceType::DynamicCast<NG::NavDestinationContext>(from);
887 if (!preDestination) {
888 params[0] = JSRef<JSVal>::Make(ToJSValue("navBar"));
889 } else if (preDestination->GetIsEmpty()) {
890 params[0] = JSRef<JSObject>::New();
891 } else {
892 JSRef<JSObject> preObj = JSClass<JSNavDestinationContext>::NewInstance();
893 auto preProxy = Referenced::Claim(preObj->Unwrap<JSNavDestinationContext>());
894 preProxy->SetNavDestinationContext(from);
895 params[0] = preObj;
896 }
897
898 auto topDestination = AceType::DynamicCast<NG::NavDestinationContext>(to);
899 if (!topDestination) {
900 params[1] = JSRef<JSVal>::Make(ToJSValue("navBar"));
901 } else if (topDestination->GetIsEmpty()) {
902 params[1] = JSRef<JSObject>::New();
903 } else {
904 JSRef<JSObject> topObj = JSClass<JSNavDestinationContext>::NewInstance();
905 auto topProxy = Referenced::Claim(topObj->Unwrap<JSNavDestinationContext>());
906 topProxy->SetNavDestinationContext(to);
907 params[1] = topObj;
908 }
909 const uint8_t operationIndex = 2;
910 params[operationIndex] = JSRef<JSVal>::Make(ToJSValue(static_cast<int32_t>(operation)));
911 const uint8_t animatedIndex = 3;
912 params[animatedIndex] = JSRef<JSVal>::Make(ToJSValue(isAnimated));
913 targetFunc->Call(JSRef<JSObject>(), argsNum, params);
914 }
915
FireNavigationModeChange(NG::NavigationMode mode)916 void JSNavigationStack::FireNavigationModeChange(NG::NavigationMode mode)
917 {
918 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
919 JSRef<JSFunc> modeFunc;
920 if (!CheckAndGetInterceptionFunc("modeChange", modeFunc)) {
921 return;
922 }
923 JSRef<JSVal> params[1];
924 params[0] = JSRef<JSVal>::Make(ToJSValue(static_cast<int32_t>(mode)));
925 modeFunc->Call(JSRef<JSObject>(), 1, params);
926 }
927
CheckAndGetInterceptionFunc(const std::string & name,JSRef<JSFunc> & func)928 bool JSNavigationStack::CheckAndGetInterceptionFunc(const std::string& name, JSRef<JSFunc>& func)
929 {
930 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
931 if (dataSourceObj_->IsEmpty()) {
932 return false;
933 }
934 JSRef<JSVal> delegateProp = dataSourceObj_->GetProperty("interception");
935 if (!delegateProp->IsObject()) {
936 return false;
937 }
938 JSRef<JSObject> delegate = JSRef<JSObject>::Cast(delegateProp);
939 JSRef<JSVal> funcProp = delegate->GetProperty(name.c_str());
940 if (!funcProp->IsFunction()) {
941 return false;
942 }
943 func = JSRef<JSFunc>::Cast(funcProp);
944 return true;
945 }
946
LoadDestinationByBuilder(const std::string & name,const JSRef<JSVal> & param,RefPtr<NG::UINode> & node,RefPtr<NG::NavDestinationGroupNode> & desNode)947 bool JSNavigationStack::LoadDestinationByBuilder(const std::string& name, const JSRef<JSVal>& param,
948 RefPtr<NG::UINode>& node, RefPtr<NG::NavDestinationGroupNode>& desNode)
949 {
950 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
951 if (navDestBuilderFunc_->IsEmpty()) {
952 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "Builder function is empty");
953 return false;
954 }
955 auto builderObj = JSRef<JSObject>::Cast(navDestBuilderFunc_);
956 const int32_t number = builderObj->GetProperty("length")->ToNumber<int32_t>();
957 JSRef<JSVal> params[number];
958 if (number >= 1) {
959 params[0] = JSRef<JSVal>::Make(ToJSValue(name));
960 }
961 if (number >= ARGC_COUNT_TWO) {
962 params[1] = param;
963 }
964 navDestBuilderFunc_->Call(JSRef<JSObject>(), number, params);
965 node = NG::ViewStackProcessor::GetInstance()->Finish();
966 return GetNavDestinationNodeInUINode(node, desNode);
967 }
968
LoadDestination(const std::string & name,const JSRef<JSVal> & param,const WeakPtr<NG::UINode> & customNode,RefPtr<NG::UINode> & node,RefPtr<NG::NavDestinationGroupNode> & desNode)969 int32_t JSNavigationStack::LoadDestination(const std::string& name, const JSRef<JSVal>& param,
970 const WeakPtr<NG::UINode>& customNode, RefPtr<NG::UINode>& node,
971 RefPtr<NG::NavDestinationGroupNode>& desNode)
972 {
973 NG::ScopedViewStackProcessor scopedViewStackProcessor;
974 // execute navdestination attribute builder
975 if (LoadDestinationByBuilder(name, param, node, desNode)) {
976 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "load destination by buildermap");
977 return ERROR_CODE_NO_ERROR;
978 }
979 // deal route config and execute route config builder
980 auto container = Container::Current();
981 auto navigationRoute = container->GetNavigationRoute();
982 if (!navigationRoute->HasLoaded(name)) {
983 int32_t res = navigationRoute->LoadPage(name);
984 if (res != 0) {
985 TAG_LOGE(AceLogTag::ACE_NAVIGATION, "load page failed: %{public}s", name.c_str());
986 return navDestBuilderFunc_->IsEmpty() ? ERROR_CODE_BUILDER_FUNCTION_NOT_REGISTERED
987 : ERROR_CODE_DESTINATION_NOT_FOUND;
988 }
989 }
990 auto parentCustomNode = AceType::DynamicCast<NG::CustomNode>(customNode.Upgrade());
991 CHECK_NULL_RETURN(parentCustomNode, ERROR_CODE_INTERNAL_ERROR);
992 auto thisObjTmp = parentCustomNode->FireThisFunc();
993 CHECK_NULL_RETURN(thisObjTmp, ERROR_CODE_INTERNAL_ERROR);
994 JSRef<JSObject> thisObj = *(JSRef<JSObject>*)(thisObjTmp);
995 auto engine = AceType::DynamicCast<Framework::JsiDeclarativeEngine>(EngineHelper::GetCurrentEngine());
996 CHECK_NULL_RETURN(engine, ERROR_CODE_INTERNAL_ERROR);
997 JSRef<JSObject> wrapBuilder = JSRef<JSObject>::Make(engine->GetNavigationBuilder(name).ToLocal());
998 if (wrapBuilder->IsEmpty()) {
999 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "wrap builder is empty: %{public}s", name.c_str());
1000 return ERROR_CODE_BUILDER_FUNCTION_NOT_REGISTERED;
1001 }
1002 auto builderProp = wrapBuilder->GetProperty("builder");
1003 if (!builderProp->IsFunction()) {
1004 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "get builder failed: %{public}s", name.c_str());
1005 return ERROR_CODE_BUILDER_FUNCTION_NOT_REGISTERED;
1006 }
1007 auto builderObj = JSRef<JSObject>::Cast(builderProp);
1008 const int32_t number = builderObj->GetProperty("length")->ToNumber<int32_t>();
1009 JSRef<JSVal> params[number];
1010 if (number >= 1) {
1011 params[0] = JSRef<JSVal>::Make(ToJSValue(name));
1012 }
1013 if (number >= ARGC_COUNT_TWO) {
1014 params[1] = param;
1015 }
1016 auto builder = JSRef<JSFunc>::Cast(builderProp);
1017 builder->Call(thisObj, number, params);
1018 node = NG::ViewStackProcessor::GetInstance()->Finish();
1019 if (!GetNavDestinationNodeInUINode(node, desNode)) {
1020 return ERROR_CODE_DESTINATION_NOT_FOUND;
1021 }
1022 return ERROR_CODE_NO_ERROR;
1023 }
1024
GetJsIndexFromNativeIndex(int32_t index)1025 int32_t JSNavigationStack::GetJsIndexFromNativeIndex(int32_t index)
1026 {
1027 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, -1);
1028 if (dataSourceObj_->IsEmpty()) {
1029 return -1;
1030 }
1031 auto getIndexFunc = dataSourceObj_->GetProperty("getJsIndexFromNativeIndex");
1032 if (!getIndexFunc->IsFunction()) {
1033 return -1;
1034 }
1035 auto func = JSRef<JSFunc>::Cast(getIndexFunc);
1036 JSRef<JSVal> param = JSRef<JSVal>::Make(ToJSValue(index));
1037 auto res = func->Call(dataSourceObj_, 1, ¶m);
1038 if (res->IsNumber()) {
1039 return res->ToNumber<int32_t>();
1040 }
1041 return -1;
1042 }
1043
MoveIndexToTop(int32_t index)1044 void JSNavigationStack::MoveIndexToTop(int32_t index)
1045 {
1046 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1047 if (dataSourceObj_->IsEmpty()) {
1048 return;
1049 }
1050 auto moveIndexToTopFunc = dataSourceObj_->GetProperty("moveIndexToTop");
1051 if (!moveIndexToTopFunc->IsFunction()) {
1052 return;
1053 }
1054 auto func = JSRef<JSFunc>::Cast(moveIndexToTopFunc);
1055 JSRef<JSVal> param = JSRef<JSVal>::Make(ToJSValue(index));
1056 func->Call(dataSourceObj_, 1, ¶m);
1057 }
1058
UpdatePathInfoIfNeeded(RefPtr<NG::UINode> & uiNode,int32_t index)1059 void JSNavigationStack::UpdatePathInfoIfNeeded(RefPtr<NG::UINode>& uiNode, int32_t index)
1060 {
1061 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1062 bool needUpdate = GetNeedUpdatePathInfo(index);
1063 if (!needUpdate) {
1064 return;
1065 }
1066 SetNeedUpdatePathInfo(index, false);
1067 RefPtr<NG::NavDestinationGroupNode> desNode;
1068 if (!GetNavDestinationNodeInUINode(uiNode, desNode)) {
1069 return;
1070 }
1071 auto pattern = AceType::DynamicCast<NG::NavDestinationPattern>(desNode->GetPattern());
1072 if (!pattern) {
1073 return;
1074 }
1075
1076 auto name = GetNameByIndex(index);
1077 auto param = GetParamByIndex(index);
1078 auto onPop = GetOnPopByIndex(index);
1079 auto isEntry = GetIsEntryByIndex(index);
1080 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "update destination node info, isEntry %{public}d", isEntry);
1081 auto pathInfo = AceType::MakeRefPtr<JSNavPathInfo>(name, param, onPop, isEntry);
1082 pattern->SetNavPathInfo(pathInfo);
1083 }
1084
GetNeedUpdatePathInfo(int32_t index)1085 bool JSNavigationStack::GetNeedUpdatePathInfo(int32_t index)
1086 {
1087 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
1088 auto path = GetJsPathInfo(index);
1089 if (path->IsEmpty()) {
1090 return false;
1091 }
1092 auto needUpdate = path->GetProperty("needUpdate");
1093 if (!needUpdate->IsBoolean()) {
1094 return false;
1095 }
1096 return needUpdate->ToBoolean();
1097 }
1098
SetNeedUpdatePathInfo(int32_t index,bool need)1099 void JSNavigationStack::SetNeedUpdatePathInfo(int32_t index, bool need)
1100 {
1101 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1102 auto path = GetJsPathInfo(index);
1103 if (path->IsEmpty()) {
1104 return;
1105 }
1106 path->SetProperty<bool>("needUpdate", need);
1107 }
1108
RecoveryNavigationStack()1109 void JSNavigationStack::RecoveryNavigationStack()
1110 {
1111 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1112 navPathList_ = preNavPathList_;
1113 if (dataSourceObj_->IsEmpty()) {
1114 return;
1115 }
1116 JSRef<JSArray> pathArray = JSRef<JSArray>::New();
1117 for (int32_t index = 0; index < static_cast<int32_t>(navPathList_.size()); index++) {
1118 auto node = navPathList_[index].second;
1119 auto navDestinationGroupNode = AceType::DynamicCast<NG::NavDestinationGroupNode>(
1120 NG::NavigationGroupNode::GetNavDestinationNode(node));
1121 if (!navDestinationGroupNode) {
1122 continue;
1123 }
1124 auto pattern = AceType::DynamicCast<NG::NavDestinationPattern>(navDestinationGroupNode->GetPattern());
1125 if (!pattern) {
1126 continue;
1127 }
1128 auto context = pattern->GetNavDestinationContext();
1129 if (!context) {
1130 continue;
1131 }
1132 JSRef<JSObject> item = CreatePathInfoWithNecessaryProperty(context);
1133 pathArray->SetValueAt(index, item);
1134 }
1135 dataSourceObj_->SetPropertyObject("pathArray", pathArray);
1136 }
1137
NeedBuildNewInstance(int32_t index)1138 bool JSNavigationStack::NeedBuildNewInstance(int32_t index)
1139 {
1140 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
1141 auto pathInfo = GetJsPathInfo(index);
1142 if (pathInfo->IsEmpty()) {
1143 return false;
1144 }
1145 auto needBuildNewInstance = pathInfo->GetProperty("needBuildNewInstance");
1146 if (!needBuildNewInstance->IsBoolean()) {
1147 return false;
1148 }
1149 return needBuildNewInstance->ToBoolean();
1150 }
1151
SetNeedBuildNewInstance(int32_t index,bool need)1152 void JSNavigationStack::SetNeedBuildNewInstance(int32_t index, bool need)
1153 {
1154 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1155 auto pathInfo = GetJsPathInfo(index);
1156 if (pathInfo->IsEmpty()) {
1157 return;
1158 }
1159 pathInfo->SetProperty<bool>("needBuildNewInstance", need);
1160 }
1161
GetJsPathArray()1162 JSRef<JSArray> JSNavigationStack::GetJsPathArray()
1163 {
1164 if (dataSourceObj_->IsEmpty()) {
1165 return JSRef<JSArray>();
1166 }
1167 auto objArray = dataSourceObj_->GetProperty("pathArray");
1168 if (!objArray->IsArray()) {
1169 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "navPathArray invalid!");
1170 return JSRef<JSArray>();
1171 }
1172 return JSRef<JSArray>::Cast(objArray);
1173 }
1174
GetJsPathInfo(int32_t index)1175 JSRef<JSObject> JSNavigationStack::GetJsPathInfo(int32_t index)
1176 {
1177 auto navPathArray = GetJsPathArray();
1178 int32_t len = static_cast<int32_t>(navPathArray->Length());
1179 if (index < 0 || index >= len) {
1180 return JSRef<JSObject>();
1181 }
1182 auto pathInfo = navPathArray->GetValueAt(index);
1183 if (!pathInfo->IsObject()) {
1184 return JSRef<JSObject>();
1185 }
1186 return JSRef<JSObject>::Cast(pathInfo);
1187 }
1188
CreatePathInfoWithNecessaryProperty(const RefPtr<NG::NavDestinationContext> & context)1189 JSRef<JSObject> JSNavigationStack::CreatePathInfoWithNecessaryProperty(
1190 const RefPtr<NG::NavDestinationContext>& context)
1191 {
1192 auto pathInfo = JSRef<JSObject>::New();
1193 CHECK_NULL_RETURN(context, pathInfo);
1194 auto jsPathInfo = AceType::DynamicCast<JSNavPathInfo>(context->GetNavPathInfo());
1195 CHECK_NULL_RETURN(jsPathInfo, pathInfo);
1196
1197 pathInfo->SetProperty<std::string>("name", jsPathInfo->GetName());
1198 pathInfo->SetProperty<int32_t>("index", context->GetIndex());
1199 pathInfo->SetProperty<std::string>("navDestinationId", std::to_string(context->GetNavDestinationId()));
1200 pathInfo->SetProperty<bool>("isEntry", jsPathInfo->GetIsEntry());
1201 pathInfo->SetPropertyObject("param", jsPathInfo->GetParam());
1202 pathInfo->SetPropertyObject("onPop", jsPathInfo->GetOnPop());
1203 return pathInfo;
1204 }
1205
GetStringifyParamByIndex(int32_t index) const1206 std::string JSNavigationStack::GetStringifyParamByIndex(int32_t index) const
1207 {
1208 auto env = GetNapiEnv();
1209 if (!env) {
1210 return JS_STRINGIFIED_UNDEFINED;
1211 }
1212 napi_handle_scope scope = nullptr;
1213 napi_open_handle_scope(env, &scope);
1214 if (scope == nullptr) {
1215 return JS_STRINGIFIED_UNDEFINED;
1216 }
1217 if (dataSourceObj_->IsEmpty()) {
1218 napi_close_handle_scope(env, scope);
1219 return JS_STRINGIFIED_UNDEFINED;
1220 }
1221 napi_value navPathStack = JsConverter::ConvertJsValToNapiValue(dataSourceObj_);
1222 napi_value getParamByIndex;
1223 napi_get_named_property(env, navPathStack, "getParamByIndex", &getParamByIndex);
1224 napi_value napiIndex;
1225 napi_create_int32(env, index, &napiIndex);
1226 napi_value param;
1227 napi_call_function(env, navPathStack, getParamByIndex, 1, &napiIndex, ¶m);
1228
1229 napi_value globalValue;
1230 napi_get_global(env, &globalValue);
1231 napi_value jsonClass;
1232 napi_get_named_property(env, globalValue, "JSON", &jsonClass);
1233 napi_value stringifyFunc;
1234 napi_get_named_property(env, jsonClass, "stringify", &stringifyFunc);
1235 napi_value stringifyParam;
1236 if (napi_call_function(env, jsonClass, stringifyFunc, 1, ¶m, &stringifyParam) != napi_ok) {
1237 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "Can not stringify current param!");
1238 napi_get_and_clear_last_exception(env, &stringifyParam);
1239 napi_close_handle_scope(env, scope);
1240 return JS_STRINGIFIED_UNDEFINED;
1241 }
1242 size_t len = 0;
1243 napi_get_value_string_utf8(env, stringifyParam, nullptr, 0, &len);
1244 std::unique_ptr<char[]> paramChar = std::make_unique<char[]>(len + 1);
1245 napi_get_value_string_utf8(env, stringifyParam, paramChar.get(), len + 1, &len);
1246 napi_close_handle_scope(env, scope);
1247 return paramChar.get();
1248 }
1249
SetPathArray(const std::vector<NG::NavdestinationRecoveryInfo> & navdestinationsInfo)1250 void JSNavigationStack::SetPathArray(const std::vector<NG::NavdestinationRecoveryInfo>& navdestinationsInfo)
1251 {
1252 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1253 JSRef<JSArray> newPathArray = JSRef<JSArray>::New();
1254 for (int32_t index = 0; index < static_cast<int32_t>(navdestinationsInfo.size()); ++index) {
1255 auto infoName = navdestinationsInfo[index].name;
1256 auto infoParam = navdestinationsInfo[index].param;
1257 auto infoMode = navdestinationsInfo[index].mode;
1258
1259 JSRef<JSObject> navPathInfo = JSRef<JSObject>::New();
1260 navPathInfo->SetProperty<std::string>("name", infoName);
1261 if (!infoParam.empty() && infoParam != JS_STRINGIFIED_UNDEFINED) {
1262 navPathInfo->SetPropertyObject("param", JSRef<JSObject>::New()->ToJsonObject(infoParam.c_str()));
1263 }
1264 navPathInfo->SetProperty<bool>("fromRecovery", true);
1265 navPathInfo->SetProperty<int32_t>("mode", infoMode);
1266 newPathArray->SetValueAt(index, navPathInfo);
1267 }
1268 dataSourceObj_->SetPropertyObject("pathArray", newPathArray);
1269 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "Set navPathArray by recovery info success");
1270 }
1271
IsFromRecovery(int32_t index)1272 bool JSNavigationStack::IsFromRecovery(int32_t index)
1273 {
1274 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
1275 auto pathInfo = GetJsPathInfo(index);
1276 if (pathInfo->IsEmpty()) {
1277 return false;
1278 }
1279 auto fromRecovery = pathInfo->GetProperty("fromRecovery");
1280 if (!fromRecovery->IsBoolean()) {
1281 return false;
1282 }
1283 return fromRecovery->ToBoolean();
1284 }
1285
SetFromRecovery(int32_t index,bool fromRecovery)1286 void JSNavigationStack::SetFromRecovery(int32_t index, bool fromRecovery)
1287 {
1288 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1289 auto pathInfo = GetJsPathInfo(index);
1290 if (pathInfo->IsEmpty()) {
1291 return;
1292 }
1293 pathInfo->SetProperty<bool>("fromRecovery", fromRecovery);
1294 }
1295
GetRecoveredDestinationMode(int32_t index)1296 int32_t JSNavigationStack::GetRecoveredDestinationMode(int32_t index)
1297 {
1298 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, 0);
1299 auto pathInfo = GetJsPathInfo(index);
1300 if (pathInfo->IsEmpty()) {
1301 return INVALID_DESTINATION_MODE;
1302 }
1303 auto mode = pathInfo->GetProperty("mode");
1304 if (!mode->IsNumber()) {
1305 return INVALID_DESTINATION_MODE;
1306 }
1307 return mode->ToNumber<int32_t>();
1308 }
1309
ExecutePopCallback(const RefPtr<NG::UINode> & uiNode,uint64_t navDestinationId,const JSRef<JSVal> & param)1310 bool JSNavigationStack::ExecutePopCallback(const RefPtr<NG::UINode>& uiNode,
1311 uint64_t navDestinationId, const JSRef<JSVal>& param)
1312 {
1313 auto navDestinationNode = AceType::DynamicCast<NG::NavDestinationGroupNode>(
1314 NG::NavigationGroupNode::GetNavDestinationNode(uiNode));
1315 CHECK_NULL_RETURN(navDestinationNode, false);
1316 auto pattern = navDestinationNode->GetPattern<NG::NavDestinationPattern>();
1317 CHECK_NULL_RETURN(pattern, false);
1318 if (pattern->GetNavDestinationId() != navDestinationId) {
1319 return false;
1320 }
1321 auto navPathInfo = AceType::DynamicCast<JSNavPathInfo>(pattern->GetNavPathInfo());
1322 CHECK_NULL_RETURN(navPathInfo, false);
1323 auto callback = navPathInfo->GetNavDestinationPopCallback();
1324 if (callback->IsEmpty()) {
1325 return false;
1326 }
1327 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "fire onPop callback: %{public}s", pattern->GetName().c_str());
1328 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
1329 JSRef<JSVal> params[1];
1330 params[0] = param;
1331 callback->Call(JSRef<JSObject>(), 1, params);
1332 return true;
1333 }
1334 } // namespace OHOS::Ace::Framework
1335