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