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 int32_t ARGC_ONE = 1;
37 using ErrorCallback = std::function<void(const std::string&, int32_t)>;
38
39 struct NavgationAsyncContext {
40 napi_env env = nullptr;
41 napi_deferred deferred = nullptr;
42 std::string pathName;
43 JSRef<JSVal> param;
44 JSRef<JSObject> navPathInfo;
45 };
46
ErrorToMessage(int32_t code)47 std::string ErrorToMessage(int32_t code)
48 {
49 switch (code) {
50 case ERROR_CODE_INTERNAL_ERROR:
51 return "Internal error.";
52 case ERROR_CODE_DESTINATION_NOT_FOUND:
53 return "NavDestination not found.";
54 case ERROR_CODE_BUILDER_FUNCTION_NOT_REGISTERED:
55 return "Builder function not registered.";
56 case ERROR_CODE_PARAM_INVALID:
57 return "Paramter error.";
58 default:
59 return "Error code is not supported.";
60 }
61 }
62
ProcessPromiseCallback(std::shared_ptr<NavgationAsyncContext> asyncContext,int32_t callbackCode)63 void ProcessPromiseCallback(std::shared_ptr<NavgationAsyncContext> asyncContext, int32_t callbackCode)
64 {
65 CHECK_NULL_VOID(asyncContext);
66 CHECK_NULL_VOID(asyncContext->env);
67 CHECK_NULL_VOID(asyncContext->deferred);
68 napi_handle_scope scope = nullptr;
69 auto status = napi_open_handle_scope(asyncContext->env, &scope);
70 if (status != napi_ok) {
71 return;
72 }
73 CHECK_NULL_VOID(scope);
74 if (callbackCode == ERROR_CODE_NO_ERROR) {
75 napi_value result = nullptr;
76 napi_get_undefined(asyncContext->env, &result);
77 napi_resolve_deferred(asyncContext->env, asyncContext->deferred, result);
78 } else {
79 napi_value code = nullptr;
80 napi_create_int32(asyncContext->env, callbackCode, &code);
81
82 napi_value msg = nullptr;
83 std::string strMsg = ErrorToMessage(callbackCode);
84 napi_create_string_utf8(asyncContext->env, strMsg.c_str(), strMsg.length(), &msg);
85
86 napi_value error = nullptr;
87 napi_create_error(asyncContext->env, code, msg, &error);
88 napi_reject_deferred(asyncContext->env, asyncContext->deferred, error);
89 }
90 napi_close_handle_scope(asyncContext->env, scope);
91 }
92
ReturnPromise(const JSCallbackInfo & info,napi_value result)93 void ReturnPromise(const JSCallbackInfo& info, napi_value result)
94 {
95 CHECK_NULL_VOID(result);
96 auto jsPromise = JsConverter::ConvertNapiValueToJsVal(result);
97 if (!jsPromise->IsObject()) {
98 return;
99 }
100 info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
101 }
102 }
103
JSBind(BindingTarget globalObj)104 void JSNavPathStack::JSBind(BindingTarget globalObj)
105 {
106 JSClass<JSNavPathStack>::Declare("NativeNavPathStack");
107 JSClass<JSNavPathStack>::Method("onStateChanged", &JSNavPathStack::OnStateChanged);
108 JSClass<JSNavPathStack>::CustomMethod("onPopCallback", &JSNavPathStack::OnPopCallback);
109 JSClass<JSNavPathStack>::CustomMethod("onPushDestination", &JSNavPathStack::OnPushDestination);
110 JSClass<JSNavPathStack>::Bind(globalObj, &JSNavPathStack::Constructor, &JSNavPathStack::Destructor);
111 }
112
Constructor(const JSCallbackInfo & info)113 void JSNavPathStack::Constructor(const JSCallbackInfo& info)
114 {
115 auto stack = Referenced::MakeRefPtr<JSNavPathStack>();
116 stack->IncRefCount();
117 info.SetReturnValue(Referenced::RawPtr(stack));
118 }
119
Destructor(JSNavPathStack * stack)120 void JSNavPathStack::Destructor(JSNavPathStack* stack)
121 {
122 if (stack != nullptr) {
123 stack->DecRefCount();
124 }
125 }
126
CreateNewNavPathStackJSObject()127 JSRef<JSObject> JSNavPathStack::CreateNewNavPathStackJSObject()
128 {
129 JSRef<JSObject> empty;
130 auto engine = EngineHelper::GetCurrentEngine();
131 CHECK_NULL_RETURN(engine, empty);
132 NativeEngine* nativeEngine = engine->GetNativeEngine();
133 CHECK_NULL_RETURN(nativeEngine, empty);
134 auto env = reinterpret_cast<napi_env>(nativeEngine);
135
136 napi_value global;
137 napi_status ret = napi_get_global(env, &global);
138 if (ret != napi_ok) {
139 return empty;
140 }
141 napi_value constructor;
142 ret = napi_get_named_property(env, global, JS_NAV_PATH_STACK_CLASS_NAME, &constructor);
143 if (ret != napi_ok) {
144 return empty;
145 }
146
147 napi_value obj;
148 ret = napi_new_instance(env, constructor, 0, nullptr, &obj);
149 if (ret != napi_ok) {
150 return empty;
151 }
152
153 JSRef<JSVal> value = JsConverter::ConvertNapiValueToJsVal(obj);
154 if (!value->IsObject()) {
155 return empty;
156 }
157
158 return JSRef<JSObject>::Cast(value);
159 }
160
SetNativeNavPathStack(JSRef<JSObject> jsStack,JSRef<JSObject> nativeStack)161 void JSNavPathStack::SetNativeNavPathStack(JSRef<JSObject> jsStack, JSRef<JSObject> nativeStack)
162 {
163 if (jsStack->IsEmpty() || nativeStack->IsEmpty()) {
164 return;
165 }
166
167 auto property = jsStack->GetProperty(JS_SET_NATIVESTACK_FUNC);
168 if (!property->IsFunction()) {
169 return;
170 }
171
172 auto setNativeStackFunc = JSRef<JSFunc>::Cast(property);
173 JSRef<JSVal> params[1];
174 params[0] = JSRef<JSVal>::Cast(nativeStack);
175 setNativeStackFunc->Call(jsStack, 1, params);
176 }
177
OnPushDestination(const JSCallbackInfo & info)178 void JSNavPathStack::OnPushDestination(const JSCallbackInfo& info)
179 {
180 ContainerScope scope(containerCurrentId_);
181 auto engine = EngineHelper::GetCurrentEngine();
182 CHECK_NULL_VOID(engine);
183 NativeEngine* nativeEngine = engine->GetNativeEngine();
184 CHECK_NULL_VOID(nativeEngine);
185
186 auto asyncContext = std::make_shared<NavgationAsyncContext>();
187 asyncContext->env = reinterpret_cast<napi_env>(nativeEngine);
188 napi_value result = nullptr;
189 napi_create_promise(asyncContext->env, &asyncContext->deferred, &result);
190 if (info.Length() != ARGC_ONE || !info[0]->IsObject()) {
191 ProcessPromiseCallback(asyncContext, ERROR_CODE_INTERNAL_ERROR);
192 ReturnPromise(info, result);
193 return;
194 }
195 asyncContext->navPathInfo = JSRef<JSObject>::Cast(info[0]);
196 if (!(asyncContext->navPathInfo->GetProperty("name")->IsString())) {
197 ProcessPromiseCallback(asyncContext, ERROR_CODE_PARAM_INVALID);
198 ReturnPromise(info, result);
199 return;
200 }
201
202 auto context = PipelineContext::GetCurrentContext();
203 if (context == nullptr) {
204 ProcessPromiseCallback(asyncContext, ERROR_CODE_INTERNAL_ERROR);
205 ReturnPromise(info, result);
206 return;
207 }
208
209 auto asyncTask = [asyncContext, weakStack = WeakClaim(this)]() {
210 CHECK_NULL_VOID(asyncContext);
211 auto stack = weakStack.Upgrade();
212 if (stack == nullptr || stack->checkNavDestinationExistsFunc_ == nullptr) {
213 ProcessPromiseCallback(asyncContext, ERROR_CODE_INTERNAL_ERROR);
214 return;
215 }
216 auto errorCode = stack->checkNavDestinationExistsFunc_(asyncContext->navPathInfo);
217 ProcessPromiseCallback(asyncContext, errorCode);
218 };
219
220 context->PostAsyncEvent(asyncTask, "ArkUINavigationPushDestination", TaskExecutor::TaskType::JS);
221 ReturnPromise(info, result);
222 }
223
CheckIsValid(JSValueWrapper object)224 bool JSNavPathStack::CheckIsValid(JSValueWrapper object)
225 {
226 auto engine = EngineHelper::GetCurrentEngine();
227 CHECK_NULL_RETURN(engine, false);
228 NativeEngine* nativeEngine = engine->GetNativeEngine();
229 CHECK_NULL_RETURN(nativeEngine, false);
230 auto env = reinterpret_cast<napi_env>(nativeEngine);
231
232 napi_value global;
233 napi_status ret = napi_get_global(env, &global);
234 if (ret != napi_ok) {
235 return false;
236 }
237 napi_value constructor;
238 ret = napi_get_named_property(env, global, JS_NAV_PATH_STACK_CLASS_NAME, &constructor);
239 if (ret != napi_ok) {
240 return false;
241 }
242 bool isInstance = false;
243 ScopeRAII scope(reinterpret_cast<napi_env>(nativeEngine));
244 napi_value stack = nativeEngine->ValueToNapiValue(object);
245 napi_instanceof(env, stack, constructor, &isInstance);
246 return isInstance;
247 }
248
OnPopCallback(const JSCallbackInfo & info)249 void JSNavPathStack::OnPopCallback(const JSCallbackInfo& info)
250 {
251 if (info.Length() < 1) {
252 return;
253 }
254 if (onPopCallback_) {
255 onPopCallback_(info[0]);
256 }
257 }
258 } // namespace OHOS::Ace::Framework
259