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