• 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>::CustomMethod("isHomeName", &JSNavPathStack::IsHomeName);
57     JSClass<JSNavPathStack>::Bind(globalObj, &JSNavPathStack::Constructor, &JSNavPathStack::Destructor);
58 }
59 
Constructor(const JSCallbackInfo & info)60 void JSNavPathStack::Constructor(const JSCallbackInfo& info)
61 {
62     auto stack = Referenced::MakeRefPtr<JSNavPathStack>();
63     stack->IncRefCount();
64     info.SetReturnValue(Referenced::RawPtr(stack));
65 }
66 
Destructor(JSNavPathStack * stack)67 void JSNavPathStack::Destructor(JSNavPathStack* stack)
68 {
69     if (stack != nullptr) {
70         stack->DecRefCount();
71     }
72 }
73 
CreateNewNavPathStackJSObject()74 JSRef<JSObject> JSNavPathStack::CreateNewNavPathStackJSObject()
75 {
76     JSRef<JSObject> empty;
77     auto engine = EngineHelper::GetCurrentEngine();
78     CHECK_NULL_RETURN(engine, empty);
79     NativeEngine* nativeEngine = engine->GetNativeEngine();
80     CHECK_NULL_RETURN(nativeEngine, empty);
81     auto env = reinterpret_cast<napi_env>(nativeEngine);
82 
83     napi_value global;
84     napi_status ret = napi_get_global(env, &global);
85     if (ret != napi_ok) {
86         return empty;
87     }
88     napi_value constructor;
89     ret = napi_get_named_property(env, global, JS_NAV_PATH_STACK_CLASS_NAME, &constructor);
90     if (ret != napi_ok) {
91         return empty;
92     }
93 
94     napi_value obj;
95     ret = napi_new_instance(env, constructor, 0, nullptr, &obj);
96     if (ret != napi_ok) {
97         return empty;
98     }
99 
100     JSRef<JSVal> value = JsConverter::ConvertNapiValueToJsVal(obj);
101     if (!value->IsObject()) {
102         return empty;
103     }
104 
105     return JSRef<JSObject>::Cast(value);
106 }
107 
SetNativeNavPathStack(JSRef<JSObject> jsStack,JSRef<JSObject> nativeStack)108 void JSNavPathStack::SetNativeNavPathStack(JSRef<JSObject> jsStack, JSRef<JSObject> nativeStack)
109 {
110     if (jsStack->IsEmpty() || nativeStack->IsEmpty()) {
111         return;
112     }
113 
114     auto property = jsStack->GetProperty(JS_SET_NATIVESTACK_FUNC);
115     if (!property->IsFunction()) {
116         return;
117     }
118 
119     auto setNativeStackFunc = JSRef<JSFunc>::Cast(property);
120     JSRef<JSVal> params[1];
121     params[0] = JSRef<JSVal>::Cast(nativeStack);
122     setNativeStackFunc->Call(jsStack, 1, params);
123 }
124 
CheckIsValid(JSValueWrapper object)125 bool JSNavPathStack::CheckIsValid(JSValueWrapper object)
126 {
127     auto engine = EngineHelper::GetCurrentEngine();
128     CHECK_NULL_RETURN(engine, false);
129     NativeEngine* nativeEngine = engine->GetNativeEngine();
130     CHECK_NULL_RETURN(nativeEngine, false);
131     auto env = reinterpret_cast<napi_env>(nativeEngine);
132 
133     napi_value global;
134     napi_status ret = napi_get_global(env, &global);
135     if (ret != napi_ok) {
136         return false;
137     }
138     napi_value constructor;
139     ret = napi_get_named_property(env, global, JS_NAV_PATH_STACK_CLASS_NAME, &constructor);
140     if (ret != napi_ok) {
141         return false;
142     }
143     bool isInstance = false;
144     ScopeRAII scope(reinterpret_cast<napi_env>(nativeEngine));
145     napi_value stack = nativeEngine->ValueToNapiValue(object);
146     napi_instanceof(env, stack, constructor, &isInstance);
147     return isInstance;
148 }
149 
GetPathStack(const JSCallbackInfo & info)150 void JSNavPathStack::GetPathStack(const JSCallbackInfo& info)
151 {
152     JSRef<JSArray> empty;
153     if (info.Length() < 1) {
154         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "invalid input length");
155         info.SetReturnValue(empty);
156         return;
157     }
158     if (!info[0]->IsObject()) {
159         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "invalid input params navPathStack");
160         info.SetReturnValue(empty);
161         return;
162     }
163     auto navPathStack = JSRef<JSObject>::Cast(info[0]);
164     if (navPathStack->IsEmpty() || navPathStack->IsUndefined()) {
165         info.SetReturnValue(empty);
166         return;
167     }
168     auto pathArrayValue = navPathStack->GetProperty(JS_NAV_PATH_ARRAY_NAME);
169     if (pathArrayValue->IsEmpty() || !pathArrayValue->IsArray()) {
170         info.SetReturnValue(empty);
171         return;
172     }
173     auto pathArray = JSRef<JSArray>::Cast(pathArrayValue);
174     auto arrayLength = pathArray->Length();
175     JSRef<JSArray> copyInfo = JSRef<JSArray>::New(arrayLength);
176     for (size_t index = 0; index < arrayLength; index++) {
177         CopyPathInfo(pathArray, copyInfo, index);
178     }
179     info.SetReturnValue(std::move(copyInfo));
180     return;
181 }
182 
CopyPathInfo(const JSRef<JSArray> & origin,JSRef<JSArray> & dest,size_t index)183 void JSNavPathStack::CopyPathInfo(const JSRef<JSArray>& origin, JSRef<JSArray>& dest, size_t index)
184 {
185     auto indexedValue = origin->GetValueAt(index);
186     if (!indexedValue->IsObject()) {
187         return;
188     }
189     auto originObj = JSRef<JSObject>::Cast(indexedValue);
190     if (originObj->IsEmpty() || originObj->IsUndefined()) {
191         return;
192     }
193     auto dstObj = JSRef<JSObject>::New();
194     auto name = originObj->GetProperty(JS_NAV_PATH_NAME_NAME);
195     auto param = originObj->GetProperty(JS_NAV_PATH_PARAM_NAME);
196     auto onPop = originObj->GetProperty(JS_NAV_PATH_ONPOP_NAME);
197     auto isEntry = originObj->GetProperty(JS_NAV_PATH_ISENTRY_NAME);
198     auto id = originObj->GetProperty(JS_NAV_PATH_NAVDESTINATIONID_NAME);
199     dstObj->SetPropertyObject(JS_NAV_PATH_NAME_NAME, name);
200     dstObj->SetPropertyObject(JS_NAV_PATH_PARAM_NAME, param);
201     dstObj->SetPropertyObject(JS_NAV_PATH_ONPOP_NAME, onPop);
202     dstObj->SetPropertyObject(JS_NAV_PATH_ISENTRY_NAME, isEntry);
203     dstObj->SetPropertyObject(JS_NAV_PATH_NAVDESTINATIONID_NAME, id);
204     dest->SetValueAt(index, dstObj);
205 }
206 
SetPathStack(const JSCallbackInfo & info)207 void JSNavPathStack::SetPathStack(const JSCallbackInfo& info)
208 {
209     if (info.Length() < ARGC_COUNT_THREE) {
210         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "invalid input length");
211         return;
212     }
213     if (!info[0]->IsObject()) {
214         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "invalid input params navPathStack");
215         return;
216     }
217     auto navPathStack = JSRef<JSObject>::Cast(info[0]);
218     if (!info[1]->IsArray()) {
219         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "invalid input params navPathArray");
220         return;
221     }
222     auto setPathArray = JSRef<JSArray>::Cast(info[1]);
223     auto animated = info[2];
224     if (navPathStack->IsEmpty() || navPathStack->IsUndefined()) {
225         TAG_LOGE(AceLogTag::ACE_NAVIGATION, "navpathStack is invalid");
226         return;
227     }
228     auto curPathArrayValue = navPathStack->GetProperty(JS_NAV_PATH_ARRAY_NAME);
229     if (curPathArrayValue->IsEmpty() || !curPathArrayValue->IsArray()) {
230         TAG_LOGE(AceLogTag::ACE_NAVIGATION, "current navpathInfo is invalid");
231         return;
232     }
233     auto curPathArray = JSRef<JSArray>::Cast(curPathArrayValue);
234     if (setPathArray->IsEmpty() || !setPathArray->IsArray()) {
235         TAG_LOGW(AceLogTag::ACE_NAVIGATION, "invalid pathInfo is set");
236         return;
237     }
238     auto setPathLength = setPathArray->Length();
239 
240     JSRef<JSArray> newPathArray = JSRef<JSArray>::New(setPathLength);
241     for (size_t index = 0; index < setPathLength; index++) {
242         /**
243          * step1. copy user set path info.
244          * step2. check if name and id is valid or not.
245          * step3. if valid, find existing node that name and id is same, and update param info;
246          *  otherwise, set id as undefined.
247          * step4. set isForceSet flag as true.
248          */
249         auto indexedValue = setPathArray->GetValueAt(index);
250         if (!indexedValue->IsObject()) {
251             TAG_LOGI(AceLogTag::ACE_NAVIGATION, "value at index %{public}zu in set navPathArray is invalid", index);
252             continue;
253         }
254         auto setPathInfo = JSRef<JSObject>::Cast(indexedValue);
255         JSRef<JSObject> pathInfo = JSRef<JSObject>::Make(setPathInfo.Get());
256         auto navDestinationId = setPathInfo->GetProperty(JS_NAV_PATH_NAVDESTINATIONID_NAME);
257         auto name = setPathInfo->GetProperty(JS_NAV_PATH_NAME_NAME);
258         if (!navDestinationId->IsEmpty() && navDestinationId->IsString() &&
259             !name->IsEmpty() && name->IsString()) {
260             auto navIdStr = navDestinationId->ToString();
261             auto nameStr = name->ToString();
262             bool findInCurArray = FindNavInfoInPreArray(pathInfo, curPathArray, navIdStr, nameStr);
263             if (findInCurArray) {
264                 auto param = setPathInfo->GetProperty(JS_NAV_PATH_PARAM_NAME);
265                 auto onPop = setPathInfo->GetProperty(JS_NAV_PATH_ONPOP_NAME);
266                 auto isEntry = setPathInfo->GetProperty(JS_NAV_PATH_ISENTRY_NAME);
267                 pathInfo->SetPropertyObject(JS_NAV_PATH_PARAM_NAME, param);
268                 pathInfo->SetPropertyObject(JS_NAV_PATH_ONPOP_NAME, onPop);
269                 pathInfo->SetPropertyObject(JS_NAV_PATH_ISENTRY_NAME, isEntry);
270             }
271         }
272         pathInfo->SetProperty<bool>(JS_NAV_PATH_ISFORCESET_NAME, true);
273         newPathArray->SetValueAt(index, pathInfo);
274     }
275     navPathStack->SetPropertyObject(JS_NAV_PATH_ARRAY_NAME, std::move(newPathArray));
276     bool finalAnimate = true;
277     if (!animated->IsEmpty() && animated->IsBoolean()) {
278         finalAnimate = animated->ToBoolean();
279     }
280     navPathStack->SetProperty<bool>(JS_NAV_PATH_ANIMATE_NAME, finalAnimate);
281     navPathStack->SetProperty<uint32_t>(JS_NAV_PATH_ISREPLACE_NAME, 0);
282     TAG_LOGI(AceLogTag::ACE_NAVIGATION, "set path stack, new size :%{public}zu, animated %{public}d",
283         setPathLength, finalAnimate);
284     OnStateChanged();
285 }
286 
IsHomeName(const JSCallbackInfo & info)287 void JSNavPathStack::IsHomeName(const JSCallbackInfo& info)
288 {
289     if (info.Length() < 1 || !info[0]->IsString() || !isHomeNameCallback_) {
290         info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(false)));
291         return;
292     }
293     auto name = info[0]->ToString();
294     bool isHomeName = isHomeNameCallback_(name);
295     info.SetReturnValue(JSRef<JSVal>::Make(ToJSValue(isHomeName)));
296 }
297 
FindNavInfoInPreArray(JSRef<JSObject> & destInfo,JSRef<JSArray> & originArray,std::string & navIdStr,std::string & nameStr)298 bool JSNavPathStack::FindNavInfoInPreArray(
299     JSRef<JSObject>& destInfo, JSRef<JSArray>& originArray, std::string& navIdStr, std::string& nameStr)
300 {
301     auto curPathLength = originArray->Length();
302     for (size_t i = 0; i < curPathLength; i++) {
303         auto indexedValue = originArray->GetValueAt(i);
304         if (!indexedValue->IsObject()) {
305             TAG_LOGI(AceLogTag::ACE_NAVIGATION, "value at index %{public}zu in pre navPathArray is invalid", i);
306             continue;
307         }
308         auto curPathInfo = JSRef<JSObject>::Cast(indexedValue);
309         auto curNavDestinationId = curPathInfo->GetProperty(JS_NAV_PATH_NAVDESTINATIONID_NAME);
310         std::string curNavIdStr;
311         if (!curNavDestinationId->IsEmpty() && curNavDestinationId->IsString()) {
312             curNavIdStr = curNavDestinationId->ToString();
313         }
314         auto curName = curPathInfo->GetProperty(JS_NAV_PATH_NAME_NAME);
315         std::string curNameStr;
316         if (!curName->IsEmpty() && curName->IsString()) {
317             curNameStr = curName->ToString();
318         }
319         if (navIdStr == curNavIdStr && nameStr == curNameStr) {
320             destInfo = JSRef<JSObject>::Make(curPathInfo.Get());
321             return true;
322         }
323     }
324     destInfo->SetPropertyObject(JS_NAV_PATH_NAVDESTINATIONID_NAME, JsiValue::Undefined());
325     return false;
326 }
327 
OnPopCallback(const JSCallbackInfo & info)328 void JSNavPathStack::OnPopCallback(const JSCallbackInfo& info)
329 {
330     if (info.Length() < 1) {
331         return;
332     }
333     if (onPopCallback_) {
334         onPopCallback_(info[0]);
335     }
336 }
337 } // namespace OHOS::Ace::Framework
338