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