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, &param);
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, &param);
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, &param);
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, &param, &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