• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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_nav_path_stack.h"
17 
18 #include "base/log/ace_scoring_log.h"
19 #include "base/memory/ace_type.h"
20 #include "base/utils/utils.h"
21 #include "bridge/common/utils/engine_helper.h"
22 #include "bridge/declarative_frontend/engine/functions/js_function.h"
23 #include "bridge/declarative_frontend/engine/js_converter.h"
24 #include "bridge/declarative_frontend/engine/js_ref_ptr.h"
25 #include "bridge/declarative_frontend/engine/js_types.h"
26 #include "bridge/declarative_frontend/jsview/js_utils.h"
27 #include "core/components_ng/pattern/navigation/navigation_pattern.h"
28 #include "core/components_ng/pattern/navigation/navigation_group_node.h"
29 #include "core/pipeline_ng/pipeline_context.h"
30 #include "interfaces/napi/kits/utils/napi_utils.h"
31 
32 namespace OHOS::Ace::Framework {
33 namespace {
34 constexpr char JS_NAV_PATH_STACK_CLASS_NAME[] = "NavPathStack";
35 constexpr char JS_SET_NATIVESTACK_FUNC[] = "setNativeStack";
36 constexpr char JS_NAV_PATH_ARRAY_NAME[] = "pathArray";
37 constexpr char JS_NAV_PATH_ANIMATE_NAME[] = "animated";
38 constexpr char JS_NAV_PATH_ISREPLACE_NAME[] = "isReplace";
39 
40 constexpr char JS_NAV_PATH_ISFORCESET_NAME[] = "isForceSet";
41 constexpr char JS_NAV_PATH_NAME_NAME[] = "name";
42 constexpr char JS_NAV_PATH_PARAM_NAME[] = "param";
43 constexpr char JS_NAV_PATH_ONPOP_NAME[] = "onPop";
44 constexpr char JS_NAV_PATH_ISENTRY_NAME[] = "isEntry";
45 constexpr char JS_NAV_PATH_NAVDESTINATIONID_NAME[] = "navDestinationId";
46 constexpr int32_t ARGC_COUNT_THREE = 3;
47 }
48 
JSBind(BindingTarget globalObj)49 void JSNavPathStack::JSBind(BindingTarget globalObj)
50 {
51     JSClass<JSNavPathStack>::Declare("NativeNavPathStack");
52     JSClass<JSNavPathStack>::Method("onStateChanged", &JSNavPathStack::OnStateChanged);
53     JSClass<JSNavPathStack>::CustomMethod("onPopCallback", &JSNavPathStack::OnPopCallback);
54     JSClass<JSNavPathStack>::CustomMethod("getPathStack", &JSNavPathStack::GetPathStack);
55     JSClass<JSNavPathStack>::CustomMethod("setPathStack", &JSNavPathStack::SetPathStack);
56     JSClass<JSNavPathStack>::Bind(globalObj, &JSNavPathStack::Constructor, &JSNavPathStack::Destructor);
57 }
58 
Constructor(const JSCallbackInfo & info)59 void JSNavPathStack::Constructor(const JSCallbackInfo& info)
60 {
61     auto stack = Referenced::MakeRefPtr<JSNavPathStack>();
62     stack->IncRefCount();
63     info.SetReturnValue(Referenced::RawPtr(stack));
64 }
65 
Destructor(JSNavPathStack * stack)66 void JSNavPathStack::Destructor(JSNavPathStack* stack)
67 {
68     if (stack != nullptr) {
69         stack->DecRefCount();
70     }
71 }
72 
CreateNewNavPathStackJSObject()73 JSRef<JSObject> JSNavPathStack::CreateNewNavPathStackJSObject()
74 {
75     JSRef<JSObject> empty;
76     auto engine = EngineHelper::GetCurrentEngine();
77     CHECK_NULL_RETURN(engine, empty);
78     NativeEngine* nativeEngine = engine->GetNativeEngine();
79     CHECK_NULL_RETURN(nativeEngine, empty);
80     auto env = reinterpret_cast<napi_env>(nativeEngine);
81 
82     napi_value global;
83     napi_status ret = napi_get_global(env, &global);
84     if (ret != napi_ok) {
85         return empty;
86     }
87     napi_value constructor;
88     ret = napi_get_named_property(env, global, JS_NAV_PATH_STACK_CLASS_NAME, &constructor);
89     if (ret != napi_ok) {
90         return empty;
91     }
92 
93     napi_value obj;
94     ret = napi_new_instance(env, constructor, 0, nullptr, &obj);
95     if (ret != napi_ok) {
96         return empty;
97     }
98 
99     JSRef<JSVal> value = JsConverter::ConvertNapiValueToJsVal(obj);
100     if (!value->IsObject()) {
101         return empty;
102     }
103 
104     return JSRef<JSObject>::Cast(value);
105 }
106 
SetNativeNavPathStack(JSRef<JSObject> jsStack,JSRef<JSObject> nativeStack)107 void JSNavPathStack::SetNativeNavPathStack(JSRef<JSObject> jsStack, JSRef<JSObject> nativeStack)
108 {
109     if (jsStack->IsEmpty() || nativeStack->IsEmpty()) {
110         return;
111     }
112 
113     auto property = jsStack->GetProperty(JS_SET_NATIVESTACK_FUNC);
114     if (!property->IsFunction()) {
115         return;
116     }
117 
118     auto setNativeStackFunc = JSRef<JSFunc>::Cast(property);
119     JSRef<JSVal> params[1];
120     params[0] = JSRef<JSVal>::Cast(nativeStack);
121     setNativeStackFunc->Call(jsStack, 1, params);
122 }
123 
CheckIsValid(JSValueWrapper object)124 bool JSNavPathStack::CheckIsValid(JSValueWrapper object)
125 {
126     auto engine = EngineHelper::GetCurrentEngine();
127     CHECK_NULL_RETURN(engine, false);
128     NativeEngine* nativeEngine = engine->GetNativeEngine();
129     CHECK_NULL_RETURN(nativeEngine, false);
130     auto env = reinterpret_cast<napi_env>(nativeEngine);
131 
132     napi_value global;
133     napi_status ret = napi_get_global(env, &global);
134     if (ret != napi_ok) {
135         return false;
136     }
137     napi_value constructor;
138     ret = napi_get_named_property(env, global, JS_NAV_PATH_STACK_CLASS_NAME, &constructor);
139     if (ret != napi_ok) {
140         return false;
141     }
142     bool isInstance = false;
143     ScopeRAII scope(reinterpret_cast<napi_env>(nativeEngine));
144     napi_value stack = nativeEngine->ValueToNapiValue(object);
145     napi_instanceof(env, stack, constructor, &isInstance);
146     return isInstance;
147 }
148 
GetPathStack(const JSCallbackInfo & info)149 void JSNavPathStack::GetPathStack(const JSCallbackInfo& info)
150 {
151     JSRef<JSArray> empty;
152     if (info.Length() < 1) {
153         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "invalid input length");
154         info.SetReturnValue(empty);
155         return;
156     }
157     auto navPathStack = JSRef<JSObject>::Cast(info[0]);
158     if (navPathStack->IsEmpty() || navPathStack->IsUndefined()) {
159         info.SetReturnValue(empty);
160         return;
161     }
162     auto pathArrayValue = navPathStack->GetProperty(JS_NAV_PATH_ARRAY_NAME);
163     if (pathArrayValue->IsEmpty() || !pathArrayValue->IsArray()) {
164         info.SetReturnValue(empty);
165         return;
166     }
167     auto pathArray = JSRef<JSArray>::Cast(pathArrayValue);
168     auto arrayLength = pathArray->Length();
169     JSRef<JSArray> copyInfo = JSRef<JSArray>::New(arrayLength);
170     for (size_t index = 0; index < arrayLength; index++) {
171         CopyPathInfo(pathArray, copyInfo, index);
172     }
173     info.SetReturnValue(std::move(copyInfo));
174     return;
175 }
176 
CopyPathInfo(const JSRef<JSArray> & origin,JSRef<JSArray> & dest,size_t index)177 void JSNavPathStack::CopyPathInfo(const JSRef<JSArray>& origin, JSRef<JSArray>& dest, size_t index)
178 {
179     auto originObj = JSRef<JSObject>::Cast(origin->GetValueAt(index));
180     if (originObj->IsEmpty() || originObj->IsUndefined()) {
181         return;
182     }
183     auto dstObj = JSRef<JSObject>::New();
184     auto name = originObj->GetProperty(JS_NAV_PATH_NAME_NAME);
185     auto param = originObj->GetProperty(JS_NAV_PATH_PARAM_NAME);
186     auto onPop = originObj->GetProperty(JS_NAV_PATH_ONPOP_NAME);
187     auto isEntry = originObj->GetProperty(JS_NAV_PATH_ISENTRY_NAME);
188     auto id = originObj->GetProperty(JS_NAV_PATH_NAVDESTINATIONID_NAME);
189     dstObj->SetPropertyObject(JS_NAV_PATH_NAME_NAME, name);
190     dstObj->SetPropertyObject(JS_NAV_PATH_PARAM_NAME, param);
191     dstObj->SetPropertyObject(JS_NAV_PATH_ONPOP_NAME, onPop);
192     dstObj->SetPropertyObject(JS_NAV_PATH_ISENTRY_NAME, isEntry);
193     dstObj->SetPropertyObject(JS_NAV_PATH_NAVDESTINATIONID_NAME, id);
194     dest->SetValueAt(index, dstObj);
195 }
196 
SetPathStack(const JSCallbackInfo & info)197 void JSNavPathStack::SetPathStack(const JSCallbackInfo& info)
198 {
199     if (info.Length() < ARGC_COUNT_THREE) {
200         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "invalid input length");
201         return;
202     }
203     auto navPathStack = JSRef<JSObject>::Cast(info[0]);
204     auto setPathArray = JSRef<JSArray>::Cast(info[1]);
205     auto animated = info[2];
206     if (navPathStack->IsEmpty() || navPathStack->IsUndefined()) {
207         TAG_LOGE(AceLogTag::ACE_NAVIGATION, "navpathStack is invalid");
208         return;
209     }
210     auto curPathArrayValue = navPathStack->GetProperty(JS_NAV_PATH_ARRAY_NAME);
211     if (curPathArrayValue->IsEmpty() || !curPathArrayValue->IsArray()) {
212         TAG_LOGE(AceLogTag::ACE_NAVIGATION, "current navpathInfo is invalid");
213         return;
214     }
215     auto curPathArray = JSRef<JSArray>::Cast(curPathArrayValue);
216     if (setPathArray->IsEmpty() || !setPathArray->IsArray()) {
217         TAG_LOGW(AceLogTag::ACE_NAVIGATION, "invalid pathInfo is set");
218         return;
219     }
220     auto setPathLength = setPathArray->Length();
221 
222     JSRef<JSArray> newPathArray = JSRef<JSArray>::New(setPathLength);
223     for (size_t index = 0; index < setPathLength; index++) {
224         /**
225          * step1. copy user set path info.
226          * step2. check if name and id is valid or not.
227          * step3. if valid, find existing node that name and id is same, and update param info;
228          *  otherwise, set id as undefined.
229          * step4. set isForceSet flag as true.
230          */
231         auto setPathInfo = JSRef<JSObject>::Cast(setPathArray->GetValueAt(index));
232         JSRef<JSObject> pathInfo = JSRef<JSObject>::Make(setPathInfo.Get());
233         auto navDestinationId = setPathInfo->GetProperty(JS_NAV_PATH_NAVDESTINATIONID_NAME);
234         auto name = setPathInfo->GetProperty(JS_NAV_PATH_NAME_NAME);
235         if (!navDestinationId->IsEmpty() && navDestinationId->IsString() &&
236             !name->IsEmpty() && name->IsString()) {
237             auto navIdStr = navDestinationId->ToString();
238             auto nameStr = name->ToString();
239             bool findInCurArray = FindNavInfoInPreArray(pathInfo, curPathArray, navIdStr, nameStr);
240             if (findInCurArray) {
241                 auto param = setPathInfo->GetProperty(JS_NAV_PATH_PARAM_NAME);
242                 auto onPop = setPathInfo->GetProperty(JS_NAV_PATH_ONPOP_NAME);
243                 auto isEntry = setPathInfo->GetProperty(JS_NAV_PATH_ISENTRY_NAME);
244                 pathInfo->SetPropertyObject(JS_NAV_PATH_PARAM_NAME, param);
245                 pathInfo->SetPropertyObject(JS_NAV_PATH_ONPOP_NAME, onPop);
246                 pathInfo->SetPropertyObject(JS_NAV_PATH_ISENTRY_NAME, isEntry);
247             }
248         }
249         pathInfo->SetProperty<bool>(JS_NAV_PATH_ISFORCESET_NAME, true);
250         newPathArray->SetValueAt(index, pathInfo);
251     }
252     navPathStack->SetPropertyObject(JS_NAV_PATH_ARRAY_NAME, std::move(newPathArray));
253     bool finalAnimate = true;
254     if (!animated->IsEmpty() && animated->IsBoolean()) {
255         finalAnimate = animated->ToBoolean();
256     }
257     navPathStack->SetProperty<bool>(JS_NAV_PATH_ANIMATE_NAME, finalAnimate);
258     navPathStack->SetProperty<uint32_t>(JS_NAV_PATH_ISREPLACE_NAME, 0);
259     TAG_LOGI(AceLogTag::ACE_NAVIGATION, "set path stack, new size :%{public}zu, animated %{public}d",
260         setPathLength, finalAnimate);
261     OnStateChanged();
262 }
263 
FindNavInfoInPreArray(JSRef<JSObject> & destInfo,JSRef<JSArray> & originArray,std::string & navIdStr,std::string & nameStr)264 bool JSNavPathStack::FindNavInfoInPreArray(
265     JSRef<JSObject>& destInfo, JSRef<JSArray>& originArray, std::string& navIdStr, std::string& nameStr)
266 {
267     auto curPathLength = originArray->Length();
268     for (size_t i = 0; i < curPathLength; i++) {
269         auto curPathInfo = JSRef<JSObject>::Cast(originArray->GetValueAt(i));
270         auto curNavDestinationId = curPathInfo->GetProperty(JS_NAV_PATH_NAVDESTINATIONID_NAME);
271         std::string curNavIdStr;
272         if (!curNavDestinationId->IsEmpty() && curNavDestinationId->IsString()) {
273             curNavIdStr = curNavDestinationId->ToString();
274         }
275         auto curName = curPathInfo->GetProperty(JS_NAV_PATH_NAME_NAME);
276         std::string curNameStr;
277         if (!curName->IsEmpty() && curName->IsString()) {
278             curNameStr = curName->ToString();
279         }
280         if (navIdStr == curNavIdStr && nameStr == curNameStr) {
281             destInfo = JSRef<JSObject>::Make(curPathInfo.Get());
282             return true;
283         }
284     }
285     destInfo->SetPropertyObject(JS_NAV_PATH_NAVDESTINATIONID_NAME, JsiValue::Undefined());
286     return false;
287 }
288 
OnPopCallback(const JSCallbackInfo & info)289 void JSNavPathStack::OnPopCallback(const JSCallbackInfo& info)
290 {
291     if (info.Length() < 1) {
292         return;
293     }
294     if (onPopCallback_) {
295         onPopCallback_(info[0]);
296     }
297 }
298 } // namespace OHOS::Ace::Framework
299