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