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 }
722
OnAttachToParent(RefPtr<NG::NavigationStack> parent)723 void JSNavigationStack::OnAttachToParent(RefPtr<NG::NavigationStack> parent)
724 {
725 auto parentStack = AceType::DynamicCast<JSNavigationStack>(parent);
726 if (!parentStack) {
727 return;
728 }
729
730 SetJSParentStack(JSRef<JSVal>::Cast(parentStack->GetDataSourceObj()));
731 }
732
OnDetachFromParent()733 void JSNavigationStack::OnDetachFromParent()
734 {
735 JSRef<JSVal> undefined(JSVal::Undefined());
736 SetJSParentStack(undefined);
737 }
738
SetJSParentStack(JSRef<JSVal> parent)739 void JSNavigationStack::SetJSParentStack(JSRef<JSVal> parent)
740 {
741 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
742 if (dataSourceObj_->IsEmpty()) {
743 return;
744 }
745
746 auto property = dataSourceObj_->GetProperty(JS_NAV_PATH_STACK_SETPARENT_FUNC);
747 if (!property->IsFunction()) {
748 return;
749 }
750
751 auto func = JSRef<JSFunc>::Cast(property);
752 JSRef<JSVal> params[1];
753 params[0] = parent;
754 func->Call(dataSourceObj_, 1, params);
755 }
756
RemoveInvalidPage(const JSRef<JSObject> & info)757 void JSNavigationStack::RemoveInvalidPage(const JSRef<JSObject>& info)
758 {
759 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
760 if (dataSourceObj_->IsEmpty()) {
761 return;
762 }
763 auto removeInvalidPage = dataSourceObj_->GetProperty("removeInvalidPage");
764 if (removeInvalidPage->IsFunction()) {
765 auto func = JSRef<JSFunc>::Cast(removeInvalidPage);
766 auto pathName = info->GetProperty("name");
767 auto param = info->GetProperty("param");
768 JSRef<JSVal> params[ARGC_COUNT_TWO] = { pathName, param };
769 func->Call(dataSourceObj_, ARGC_COUNT_TWO, params);
770 }
771 }
772
SaveNodeToPreBuildList(const std::string & name,const JSRef<JSVal> & param,RefPtr<NG::UINode> & node)773 void JSNavigationStack::SaveNodeToPreBuildList(const std::string& name, const JSRef<JSVal>& param,
774 RefPtr<NG::UINode>& node)
775 {
776 preBuildNodeList_.emplace_back(name, param, node, GetSize() - 1);
777 }
778
GetNodeFromPreBuildList(int32_t index,const std::string & name,const JSRef<JSVal> & param,RefPtr<NG::UINode> & node)779 bool JSNavigationStack::GetNodeFromPreBuildList(int32_t index, const std::string& name,
780 const JSRef<JSVal>& param, RefPtr<NG::UINode>& node)
781 {
782 auto isJsObjEqual = [](const JSRef<JSVal>& objLeft, const JSRef<JSVal>& objRight) {
783 return (objLeft->IsEmpty() && objRight->IsEmpty()) ||
784 (objLeft->GetLocalHandle()->IsStrictEquals(objLeft->GetEcmaVM(), objRight->GetLocalHandle()));
785 };
786 for (auto it = preBuildNodeList_.begin(); it != preBuildNodeList_.end(); ++it) {
787 if (it->name == name && isJsObjEqual(it->param, param) && it->index == index) {
788 node = it->uiNode;
789 preBuildNodeList_.erase(it);
790 return true;
791 }
792 }
793 return false;
794 }
795
ClearPreBuildNodeList()796 void JSNavigationStack::ClearPreBuildNodeList()
797 {
798 preBuildNodeList_.clear();
799 }
800
CheckNavDestinationExists(const JSRef<JSObject> & navPathInfo)801 int32_t JSNavigationStack::CheckNavDestinationExists(const JSRef<JSObject>& navPathInfo)
802 {
803 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, ERROR_CODE_DESTINATION_NOT_FOUND);
804 auto pathName = navPathInfo->GetProperty("name");
805 auto param = navPathInfo->GetProperty("param");
806 JSRef<JSVal> params[ARGC_COUNT_TWO] = { pathName, param };
807 auto name = pathName->ToString();
808 RefPtr<NG::UINode> node;
809 auto navigationNode = AceType::DynamicCast<NG::NavigationGroupNode>(navigationNode_.Upgrade());
810 CHECK_NULL_RETURN(navigationNode, ERROR_CODE_INTERNAL_ERROR);
811 auto navigationPattern = AceType::DynamicCast<NG::NavigationPattern>(navigationNode->GetPattern());
812 CHECK_NULL_RETURN(navigationPattern, ERROR_CODE_INTERNAL_ERROR);
813 RefPtr<NG::NavDestinationGroupNode> desNode;
814 int32_t errorCode = LoadDestination(name, param, navigationPattern->GetParentCustomNode(),
815 node, desNode);
816 if (errorCode != ERROR_CODE_NO_ERROR) {
817 return errorCode;
818 }
819 auto pattern = AceType::DynamicCast<NG::NavDestinationPattern>(desNode->GetPattern());
820 if (pattern) {
821 auto onPop = navPathInfo->GetProperty("onPop");
822 auto isEntryVal = navPathInfo->GetProperty("isEntry");
823 bool isEntry = isEntryVal->IsBoolean() ? isEntryVal->ToBoolean() : false;
824 auto pathInfo = AceType::MakeRefPtr<JSNavPathInfo>(name, param, onPop, isEntry);
825 pattern->SetName(name);
826 pattern->SetIndex(GetSize() - 1);
827 pattern->SetNavPathInfo(pathInfo);
828 pattern->SetNavigationStack(WeakClaim(this));
829 }
830 SaveNodeToPreBuildList(name, param, node);
831 return ERROR_CODE_NO_ERROR;
832 }
833
DumpStackInfo() const834 std::vector<std::string> JSNavigationStack::DumpStackInfo() const
835 {
836 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, {});
837 std::vector<std::string> dumpInfos;
838 for (size_t i = 0; i < navPathList_.size(); ++i) {
839 const auto& name = navPathList_[i].first;
840 std::string info = "[" + std::to_string(i) + "]{ name: \"" + name + "\"";
841 std::string param = ConvertParamToString(GetParamByIndex(i));
842 if (param.length() > 0) {
843 info += ", param: " + param;
844 }
845 info += " }";
846 dumpInfos.push_back(std::move(info));
847 }
848 return dumpInfos;
849 }
850
FireNavigationInterception(bool isBefore,const RefPtr<NG::NavDestinationContext> & from,const RefPtr<NG::NavDestinationContext> & to,NG::NavigationOperation operation,bool isAnimated)851 void JSNavigationStack::FireNavigationInterception(bool isBefore, const RefPtr<NG::NavDestinationContext>& from,
852 const RefPtr<NG::NavDestinationContext>& to, NG::NavigationOperation operation, bool isAnimated)
853 {
854 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
855 std::string targetName = isBefore ? "willShow" : "didShow";
856 JSRef<JSFunc> targetFunc;
857 if (!CheckAndGetInterceptionFunc(targetName, targetFunc)) {
858 return;
859 }
860 const uint8_t argsNum = 4;
861 JSRef<JSVal> params[argsNum];
862 auto preDestination = AceType::DynamicCast<NG::NavDestinationContext>(from);
863 if (!preDestination) {
864 params[0] = JSRef<JSVal>::Make(ToJSValue("navBar"));
865 } else if (preDestination->GetIsEmpty()) {
866 params[0] = JSRef<JSObject>::New();
867 } else {
868 JSRef<JSObject> preObj = JSClass<JSNavDestinationContext>::NewInstance();
869 auto preProxy = Referenced::Claim(preObj->Unwrap<JSNavDestinationContext>());
870 preProxy->SetNavDestinationContext(from);
871 params[0] = preObj;
872 }
873
874 auto topDestination = AceType::DynamicCast<NG::NavDestinationContext>(to);
875 if (!topDestination) {
876 params[1] = JSRef<JSVal>::Make(ToJSValue("navBar"));
877 } else if (topDestination->GetIsEmpty()) {
878 params[1] = JSRef<JSObject>::New();
879 } else {
880 JSRef<JSObject> topObj = JSClass<JSNavDestinationContext>::NewInstance();
881 auto topProxy = Referenced::Claim(topObj->Unwrap<JSNavDestinationContext>());
882 topProxy->SetNavDestinationContext(to);
883 params[1] = topObj;
884 }
885 const uint8_t operationIndex = 2;
886 params[operationIndex] = JSRef<JSVal>::Make(ToJSValue(static_cast<int32_t>(operation)));
887 const uint8_t animatedIndex = 3;
888 params[animatedIndex] = JSRef<JSVal>::Make(ToJSValue(isAnimated));
889 targetFunc->Call(JSRef<JSObject>(), argsNum, params);
890 }
891
FireNavigationModeChange(NG::NavigationMode mode)892 void JSNavigationStack::FireNavigationModeChange(NG::NavigationMode mode)
893 {
894 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
895 JSRef<JSFunc> modeFunc;
896 if (!CheckAndGetInterceptionFunc("modeChange", modeFunc)) {
897 return;
898 }
899 JSRef<JSVal> params[1];
900 params[0] = JSRef<JSVal>::Make(ToJSValue(static_cast<int32_t>(mode)));
901 modeFunc->Call(JSRef<JSObject>(), 1, params);
902 }
903
CheckAndGetInterceptionFunc(const std::string & name,JSRef<JSFunc> & func)904 bool JSNavigationStack::CheckAndGetInterceptionFunc(const std::string& name, JSRef<JSFunc>& func)
905 {
906 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
907 if (dataSourceObj_->IsEmpty()) {
908 return false;
909 }
910 JSRef<JSVal> delegateProp = dataSourceObj_->GetProperty("interception");
911 if (!delegateProp->IsObject()) {
912 return false;
913 }
914 JSRef<JSObject> delegate = JSRef<JSObject>::Cast(delegateProp);
915 JSRef<JSVal> funcProp = delegate->GetProperty(name.c_str());
916 if (!funcProp->IsFunction()) {
917 return false;
918 }
919 func = JSRef<JSFunc>::Cast(funcProp);
920 return true;
921 }
922
LoadDestinationByBuilder(const std::string & name,const JSRef<JSVal> & param,RefPtr<NG::UINode> & node,RefPtr<NG::NavDestinationGroupNode> & desNode)923 bool JSNavigationStack::LoadDestinationByBuilder(const std::string& name, const JSRef<JSVal>& param,
924 RefPtr<NG::UINode>& node, RefPtr<NG::NavDestinationGroupNode>& desNode)
925 {
926 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
927 if (navDestBuilderFunc_->IsEmpty()) {
928 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "Builder function is empty");
929 return false;
930 }
931 auto builderObj = JSRef<JSObject>::Cast(navDestBuilderFunc_);
932 const int32_t number = builderObj->GetProperty("length")->ToNumber<int32_t>();
933 JSRef<JSVal> params[number];
934 if (number >= 1) {
935 params[0] = JSRef<JSVal>::Make(ToJSValue(name));
936 }
937 if (number >= ARGC_COUNT_TWO) {
938 params[1] = param;
939 }
940 navDestBuilderFunc_->Call(JSRef<JSObject>(), number, params);
941 node = NG::ViewStackProcessor::GetInstance()->Finish();
942 return GetNavDestinationNodeInUINode(node, desNode);
943 }
944
LoadDestination(const std::string & name,const JSRef<JSVal> & param,const WeakPtr<NG::UINode> & customNode,RefPtr<NG::UINode> & node,RefPtr<NG::NavDestinationGroupNode> & desNode)945 int32_t JSNavigationStack::LoadDestination(const std::string& name, const JSRef<JSVal>& param,
946 const WeakPtr<NG::UINode>& customNode, RefPtr<NG::UINode>& node,
947 RefPtr<NG::NavDestinationGroupNode>& desNode)
948 {
949 NG::ScopedViewStackProcessor scopedViewStackProcessor;
950 // execute navdestination attribute builder
951 if (LoadDestinationByBuilder(name, param, node, desNode)) {
952 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "load destination by buildermap");
953 return ERROR_CODE_NO_ERROR;
954 }
955 // deal route config and execute route config builder
956 auto container = Container::Current();
957 auto navigationRoute = container->GetNavigationRoute();
958 if (!navigationRoute->HasLoaded(name)) {
959 int32_t res = navigationRoute->LoadPage(name);
960 if (res != 0) {
961 TAG_LOGE(AceLogTag::ACE_NAVIGATION, "load page failed: %{public}s", name.c_str());
962 return navDestBuilderFunc_->IsEmpty() ? ERROR_CODE_BUILDER_FUNCTION_NOT_REGISTERED
963 : ERROR_CODE_DESTINATION_NOT_FOUND;
964 }
965 }
966 auto parentCustomNode = AceType::DynamicCast<NG::CustomNode>(customNode.Upgrade());
967 CHECK_NULL_RETURN(parentCustomNode, ERROR_CODE_INTERNAL_ERROR);
968 auto thisObjTmp = parentCustomNode->FireThisFunc();
969 CHECK_NULL_RETURN(thisObjTmp, ERROR_CODE_INTERNAL_ERROR);
970 JSRef<JSObject> thisObj = *(JSRef<JSObject>*)(thisObjTmp);
971 auto engine = AceType::DynamicCast<Framework::JsiDeclarativeEngine>(EngineHelper::GetCurrentEngine());
972 CHECK_NULL_RETURN(engine, ERROR_CODE_INTERNAL_ERROR);
973 JSRef<JSObject> wrapBuilder = JSRef<JSObject>::Make(engine->GetNavigationBuilder(name).ToLocal());
974 if (wrapBuilder->IsEmpty()) {
975 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "wrap builder is empty: %{public}s", name.c_str());
976 return ERROR_CODE_BUILDER_FUNCTION_NOT_REGISTERED;
977 }
978 auto builderProp = wrapBuilder->GetProperty("builder");
979 if (!builderProp->IsFunction()) {
980 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "get builder failed: %{public}s", name.c_str());
981 return ERROR_CODE_BUILDER_FUNCTION_NOT_REGISTERED;
982 }
983 auto builderObj = JSRef<JSObject>::Cast(builderProp);
984 const int32_t number = builderObj->GetProperty("length")->ToNumber<int32_t>();
985 JSRef<JSVal> params[number];
986 if (number >= 1) {
987 params[0] = JSRef<JSVal>::Make(ToJSValue(name));
988 }
989 if (number >= ARGC_COUNT_TWO) {
990 params[1] = param;
991 }
992 auto builder = JSRef<JSFunc>::Cast(builderProp);
993 builder->Call(thisObj, number, params);
994 node = NG::ViewStackProcessor::GetInstance()->Finish();
995 if (!GetNavDestinationNodeInUINode(node, desNode)) {
996 return ERROR_CODE_DESTINATION_NOT_FOUND;
997 }
998 return ERROR_CODE_NO_ERROR;
999 }
1000
GetJsIndexFromNativeIndex(int32_t index)1001 int32_t JSNavigationStack::GetJsIndexFromNativeIndex(int32_t index)
1002 {
1003 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, -1);
1004 if (dataSourceObj_->IsEmpty()) {
1005 return -1;
1006 }
1007 auto getIndexFunc = dataSourceObj_->GetProperty("getJsIndexFromNativeIndex");
1008 if (!getIndexFunc->IsFunction()) {
1009 return -1;
1010 }
1011 auto func = JSRef<JSFunc>::Cast(getIndexFunc);
1012 JSRef<JSVal> param = JSRef<JSVal>::Make(ToJSValue(index));
1013 auto res = func->Call(dataSourceObj_, 1, ¶m);
1014 if (res->IsNumber()) {
1015 return res->ToNumber<int32_t>();
1016 }
1017 return -1;
1018 }
1019
MoveIndexToTop(int32_t index)1020 void JSNavigationStack::MoveIndexToTop(int32_t index)
1021 {
1022 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1023 if (dataSourceObj_->IsEmpty()) {
1024 return;
1025 }
1026 auto moveIndexToTopFunc = dataSourceObj_->GetProperty("moveIndexToTop");
1027 if (!moveIndexToTopFunc->IsFunction()) {
1028 return;
1029 }
1030 auto func = JSRef<JSFunc>::Cast(moveIndexToTopFunc);
1031 JSRef<JSVal> param = JSRef<JSVal>::Make(ToJSValue(index));
1032 func->Call(dataSourceObj_, 1, ¶m);
1033 }
1034
UpdatePathInfoIfNeeded(RefPtr<NG::UINode> & uiNode,int32_t index)1035 void JSNavigationStack::UpdatePathInfoIfNeeded(RefPtr<NG::UINode>& uiNode, int32_t index)
1036 {
1037 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1038 bool needUpdate = GetNeedUpdatePathInfo(index);
1039 if (!needUpdate) {
1040 return;
1041 }
1042 SetNeedUpdatePathInfo(index, false);
1043 RefPtr<NG::NavDestinationGroupNode> desNode;
1044 if (!GetNavDestinationNodeInUINode(uiNode, desNode)) {
1045 return;
1046 }
1047 auto pattern = AceType::DynamicCast<NG::NavDestinationPattern>(desNode->GetPattern());
1048 if (!pattern) {
1049 return;
1050 }
1051
1052 auto name = GetNameByIndex(index);
1053 auto param = GetParamByIndex(index);
1054 auto onPop = GetOnPopByIndex(index);
1055 auto isEntry = GetIsEntryByIndex(index);
1056 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "update destination node info, isEntry %{public}d", isEntry);
1057 auto pathInfo = AceType::MakeRefPtr<JSNavPathInfo>(name, param, onPop, isEntry);
1058 pattern->SetNavPathInfo(pathInfo);
1059 }
1060
GetNeedUpdatePathInfo(int32_t index)1061 bool JSNavigationStack::GetNeedUpdatePathInfo(int32_t index)
1062 {
1063 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
1064 auto path = GetJsPathInfo(index);
1065 if (path->IsEmpty()) {
1066 return false;
1067 }
1068 auto needUpdate = path->GetProperty("needUpdate");
1069 if (!needUpdate->IsBoolean()) {
1070 return false;
1071 }
1072 return needUpdate->ToBoolean();
1073 }
1074
SetNeedUpdatePathInfo(int32_t index,bool need)1075 void JSNavigationStack::SetNeedUpdatePathInfo(int32_t index, bool need)
1076 {
1077 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1078 auto path = GetJsPathInfo(index);
1079 if (path->IsEmpty()) {
1080 return;
1081 }
1082 path->SetProperty<bool>("needUpdate", need);
1083 }
1084
RecoveryNavigationStack()1085 void JSNavigationStack::RecoveryNavigationStack()
1086 {
1087 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1088 navPathList_ = preNavPathList_;
1089 if (dataSourceObj_->IsEmpty()) {
1090 return;
1091 }
1092 JSRef<JSArray> pathArray = JSRef<JSArray>::New();
1093 for (int32_t index = 0; index < static_cast<int32_t>(navPathList_.size()); index++) {
1094 auto node = navPathList_[index].second;
1095 auto navDestinationGroupNode = AceType::DynamicCast<NG::NavDestinationGroupNode>(
1096 NG::NavigationGroupNode::GetNavDestinationNode(node));
1097 if (!navDestinationGroupNode) {
1098 continue;
1099 }
1100 auto pattern = AceType::DynamicCast<NG::NavDestinationPattern>(navDestinationGroupNode->GetPattern());
1101 if (!pattern) {
1102 continue;
1103 }
1104 auto context = pattern->GetNavDestinationContext();
1105 if (!context) {
1106 continue;
1107 }
1108 JSRef<JSObject> item = CreatePathInfoWithNecessaryProperty(context);
1109 pathArray->SetValueAt(index, item);
1110 }
1111 dataSourceObj_->SetPropertyObject("pathArray", pathArray);
1112 }
1113
NeedBuildNewInstance(int32_t index)1114 bool JSNavigationStack::NeedBuildNewInstance(int32_t index)
1115 {
1116 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
1117 auto pathInfo = GetJsPathInfo(index);
1118 if (pathInfo->IsEmpty()) {
1119 return false;
1120 }
1121 auto needBuildNewInstance = pathInfo->GetProperty("needBuildNewInstance");
1122 if (!needBuildNewInstance->IsBoolean()) {
1123 return false;
1124 }
1125 return needBuildNewInstance->ToBoolean();
1126 }
1127
SetNeedBuildNewInstance(int32_t index,bool need)1128 void JSNavigationStack::SetNeedBuildNewInstance(int32_t index, bool need)
1129 {
1130 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1131 auto pathInfo = GetJsPathInfo(index);
1132 if (pathInfo->IsEmpty()) {
1133 return;
1134 }
1135 pathInfo->SetProperty<bool>("needBuildNewInstance", need);
1136 }
1137
GetJsPathArray()1138 JSRef<JSArray> JSNavigationStack::GetJsPathArray()
1139 {
1140 if (dataSourceObj_->IsEmpty()) {
1141 return JSRef<JSArray>();
1142 }
1143 auto objArray = dataSourceObj_->GetProperty("pathArray");
1144 if (!objArray->IsArray()) {
1145 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "navPathArray invalid!");
1146 return JSRef<JSArray>();
1147 }
1148 return JSRef<JSArray>::Cast(objArray);
1149 }
1150
GetJsPathInfo(int32_t index)1151 JSRef<JSObject> JSNavigationStack::GetJsPathInfo(int32_t index)
1152 {
1153 auto navPathArray = GetJsPathArray();
1154 int32_t len = static_cast<int32_t>(navPathArray->Length());
1155 if (index < 0 || index >= len) {
1156 return JSRef<JSObject>();
1157 }
1158 auto pathInfo = navPathArray->GetValueAt(index);
1159 if (!pathInfo->IsObject()) {
1160 return JSRef<JSObject>();
1161 }
1162 return JSRef<JSObject>::Cast(pathInfo);
1163 }
1164
CreatePathInfoWithNecessaryProperty(const RefPtr<NG::NavDestinationContext> & context)1165 JSRef<JSObject> JSNavigationStack::CreatePathInfoWithNecessaryProperty(
1166 const RefPtr<NG::NavDestinationContext>& context)
1167 {
1168 auto pathInfo = JSRef<JSObject>::New();
1169 CHECK_NULL_RETURN(context, pathInfo);
1170 auto jsPathInfo = AceType::DynamicCast<JSNavPathInfo>(context->GetNavPathInfo());
1171 CHECK_NULL_RETURN(jsPathInfo, pathInfo);
1172
1173 pathInfo->SetProperty<std::string>("name", jsPathInfo->GetName());
1174 pathInfo->SetProperty<int32_t>("index", context->GetIndex());
1175 pathInfo->SetProperty<std::string>("navDestinationId", std::to_string(context->GetNavDestinationId()));
1176 pathInfo->SetProperty<bool>("isEntry", jsPathInfo->GetIsEntry());
1177 pathInfo->SetPropertyObject("param", jsPathInfo->GetParam());
1178 pathInfo->SetPropertyObject("onPop", jsPathInfo->GetOnPop());
1179 return pathInfo;
1180 }
1181
GetStringifyParamByIndex(int32_t index) const1182 std::string JSNavigationStack::GetStringifyParamByIndex(int32_t index) const
1183 {
1184 auto env = GetNapiEnv();
1185 if (!env) {
1186 return JS_STRINGIFIED_UNDEFINED;
1187 }
1188 napi_handle_scope scope = nullptr;
1189 napi_open_handle_scope(env, &scope);
1190 if (scope == nullptr) {
1191 return JS_STRINGIFIED_UNDEFINED;
1192 }
1193 if (dataSourceObj_->IsEmpty()) {
1194 napi_close_handle_scope(env, scope);
1195 return JS_STRINGIFIED_UNDEFINED;
1196 }
1197 napi_value navPathStack = JsConverter::ConvertJsValToNapiValue(dataSourceObj_);
1198 napi_value getParamByIndex;
1199 napi_get_named_property(env, navPathStack, "getParamByIndex", &getParamByIndex);
1200 napi_value napiIndex;
1201 napi_create_int32(env, index, &napiIndex);
1202 napi_value param;
1203 napi_call_function(env, navPathStack, getParamByIndex, 1, &napiIndex, ¶m);
1204
1205 napi_value globalValue;
1206 napi_get_global(env, &globalValue);
1207 napi_value jsonClass;
1208 napi_get_named_property(env, globalValue, "JSON", &jsonClass);
1209 napi_value stringifyFunc;
1210 napi_get_named_property(env, jsonClass, "stringify", &stringifyFunc);
1211 napi_value stringifyParam;
1212 if (napi_call_function(env, jsonClass, stringifyFunc, 1, ¶m, &stringifyParam) != napi_ok) {
1213 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "Can not stringify current param!");
1214 napi_get_and_clear_last_exception(env, &stringifyParam);
1215 napi_close_handle_scope(env, scope);
1216 return JS_STRINGIFIED_UNDEFINED;
1217 }
1218 size_t len = 0;
1219 napi_get_value_string_utf8(env, stringifyParam, nullptr, 0, &len);
1220 std::unique_ptr<char[]> paramChar = std::make_unique<char[]>(len + 1);
1221 napi_get_value_string_utf8(env, stringifyParam, paramChar.get(), len + 1, &len);
1222 napi_close_handle_scope(env, scope);
1223 return paramChar.get();
1224 }
1225
SetPathArray(const std::vector<NG::NavdestinationRecoveryInfo> & navdestinationsInfo)1226 void JSNavigationStack::SetPathArray(const std::vector<NG::NavdestinationRecoveryInfo>& navdestinationsInfo)
1227 {
1228 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1229 JSRef<JSArray> newPathArray = JSRef<JSArray>::New();
1230 for (int32_t index = 0; index < static_cast<int32_t>(navdestinationsInfo.size()); ++index) {
1231 auto infoName = navdestinationsInfo[index].name;
1232 auto infoParam = navdestinationsInfo[index].param;
1233 auto infoMode = navdestinationsInfo[index].mode;
1234
1235 JSRef<JSObject> navPathInfo = JSRef<JSObject>::New();
1236 navPathInfo->SetProperty<std::string>("name", infoName);
1237 if (!infoParam.empty() && infoParam != JS_STRINGIFIED_UNDEFINED) {
1238 navPathInfo->SetPropertyObject("param", JSRef<JSObject>::New()->ToJsonObject(infoParam.c_str()));
1239 }
1240 navPathInfo->SetProperty<bool>("fromRecovery", true);
1241 navPathInfo->SetProperty<int32_t>("mode", infoMode);
1242 newPathArray->SetValueAt(index, navPathInfo);
1243 }
1244 dataSourceObj_->SetPropertyObject("pathArray", newPathArray);
1245 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "Set navPathArray by recovery info success");
1246 }
1247
IsFromRecovery(int32_t index)1248 bool JSNavigationStack::IsFromRecovery(int32_t index)
1249 {
1250 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, false);
1251 auto pathInfo = GetJsPathInfo(index);
1252 if (pathInfo->IsEmpty()) {
1253 return false;
1254 }
1255 auto fromRecovery = pathInfo->GetProperty("fromRecovery");
1256 if (!fromRecovery->IsBoolean()) {
1257 return false;
1258 }
1259 return fromRecovery->ToBoolean();
1260 }
1261
SetFromRecovery(int32_t index,bool fromRecovery)1262 void JSNavigationStack::SetFromRecovery(int32_t index, bool fromRecovery)
1263 {
1264 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_);
1265 auto pathInfo = GetJsPathInfo(index);
1266 if (pathInfo->IsEmpty()) {
1267 return;
1268 }
1269 pathInfo->SetProperty<bool>("fromRecovery", fromRecovery);
1270 }
1271
GetRecoveredDestinationMode(int32_t index)1272 int32_t JSNavigationStack::GetRecoveredDestinationMode(int32_t index)
1273 {
1274 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext_, 0);
1275 auto pathInfo = GetJsPathInfo(index);
1276 if (pathInfo->IsEmpty()) {
1277 return INVALID_DESTINATION_MODE;
1278 }
1279 auto mode = pathInfo->GetProperty("mode");
1280 if (!mode->IsNumber()) {
1281 return INVALID_DESTINATION_MODE;
1282 }
1283 return mode->ToNumber<int32_t>();
1284 }
1285 } // namespace OHOS::Ace::Framework
1286