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("onPushDestination", &JSNavPathStack::OnPushDestination);
109 JSClass<JSNavPathStack>::Bind(globalObj, &JSNavPathStack::Constructor, &JSNavPathStack::Destructor);
110 }
111
Constructor(const JSCallbackInfo & info)112 void JSNavPathStack::Constructor(const JSCallbackInfo& info)
113 {
114 auto stack = Referenced::MakeRefPtr<JSNavPathStack>();
115 stack->IncRefCount();
116 info.SetReturnValue(Referenced::RawPtr(stack));
117 }
118
Destructor(JSNavPathStack * stack)119 void JSNavPathStack::Destructor(JSNavPathStack* stack)
120 {
121 if (stack != nullptr) {
122 stack->DecRefCount();
123 }
124 }
125
CreateNewNavPathStackJSObject()126 JSRef<JSObject> JSNavPathStack::CreateNewNavPathStackJSObject()
127 {
128 JSRef<JSObject> empty;
129 auto engine = EngineHelper::GetCurrentEngine();
130 CHECK_NULL_RETURN(engine, empty);
131 NativeEngine* nativeEngine = engine->GetNativeEngine();
132 CHECK_NULL_RETURN(nativeEngine, empty);
133 auto env = reinterpret_cast<napi_env>(nativeEngine);
134
135 napi_value global;
136 napi_status ret = napi_get_global(env, &global);
137 if (ret != napi_ok) {
138 return empty;
139 }
140 napi_value constructor;
141 ret = napi_get_named_property(env, global, JS_NAV_PATH_STACK_CLASS_NAME, &constructor);
142 if (ret != napi_ok) {
143 return empty;
144 }
145
146 napi_value obj;
147 ret = napi_new_instance(env, constructor, 0, nullptr, &obj);
148 if (ret != napi_ok) {
149 return empty;
150 }
151
152 JSRef<JSVal> value = JsConverter::ConvertNapiValueToJsVal(obj);
153 if (!value->IsObject()) {
154 return empty;
155 }
156
157 return JSRef<JSObject>::Cast(value);
158 }
159
SetNativeNavPathStack(JSRef<JSObject> jsStack,JSRef<JSObject> nativeStack)160 void JSNavPathStack::SetNativeNavPathStack(JSRef<JSObject> jsStack, JSRef<JSObject> nativeStack)
161 {
162 if (jsStack->IsEmpty() || nativeStack->IsEmpty()) {
163 return;
164 }
165
166 auto property = jsStack->GetProperty(JS_SET_NATIVESTACK_FUNC);
167 if (!property->IsFunction()) {
168 return;
169 }
170
171 auto setNativeStackFunc = JSRef<JSFunc>::Cast(property);
172 JSRef<JSVal> params[1];
173 params[0] = JSRef<JSVal>::Cast(nativeStack);
174 setNativeStackFunc->Call(jsStack, 1, params);
175 }
176
OnPushDestination(const JSCallbackInfo & info)177 void JSNavPathStack::OnPushDestination(const JSCallbackInfo& info)
178 {
179 ContainerScope scope(containerCurrentId_);
180 auto engine = EngineHelper::GetCurrentEngine();
181 CHECK_NULL_VOID(engine);
182 NativeEngine* nativeEngine = engine->GetNativeEngine();
183 CHECK_NULL_VOID(nativeEngine);
184
185 auto asyncContext = std::make_shared<NavgationAsyncContext>();
186 asyncContext->env = reinterpret_cast<napi_env>(nativeEngine);
187 napi_value result = nullptr;
188 napi_create_promise(asyncContext->env, &asyncContext->deferred, &result);
189 if (info.Length() != ARGC_ONE || !info[0]->IsObject()) {
190 ProcessPromiseCallback(asyncContext, ERROR_CODE_INTERNAL_ERROR);
191 ReturnPromise(info, result);
192 return;
193 }
194 asyncContext->navPathInfo = JSRef<JSObject>::Cast(info[0]);
195 if (!(asyncContext->navPathInfo->GetProperty("name")->IsString())) {
196 ProcessPromiseCallback(asyncContext, ERROR_CODE_PARAM_INVALID);
197 ReturnPromise(info, result);
198 return;
199 }
200
201 auto context = PipelineContext::GetCurrentContext();
202 if (context == nullptr) {
203 ProcessPromiseCallback(asyncContext, ERROR_CODE_INTERNAL_ERROR);
204 ReturnPromise(info, result);
205 return;
206 }
207
208 auto asyncTask = [asyncContext, weakStack = WeakClaim(this)]() {
209 CHECK_NULL_VOID(asyncContext);
210 auto stack = weakStack.Upgrade();
211 if (stack == nullptr || stack->checkNavDestinationExistsFunc_ == nullptr) {
212 ProcessPromiseCallback(asyncContext, ERROR_CODE_INTERNAL_ERROR);
213 return;
214 }
215 auto errorCode = stack->checkNavDestinationExistsFunc_(asyncContext->navPathInfo);
216 ProcessPromiseCallback(asyncContext, errorCode);
217 };
218
219 context->PostAsyncEvent(asyncTask, "ArkUINavigationPushDestination", TaskExecutor::TaskType::JS);
220 ReturnPromise(info, result);
221 }
222
CheckIsValid(JSValueWrapper object)223 bool JSNavPathStack::CheckIsValid(JSValueWrapper object)
224 {
225 auto engine = EngineHelper::GetCurrentEngine();
226 CHECK_NULL_RETURN(engine, false);
227 NativeEngine* nativeEngine = engine->GetNativeEngine();
228 CHECK_NULL_RETURN(nativeEngine, false);
229 auto env = reinterpret_cast<napi_env>(nativeEngine);
230
231 napi_value global;
232 napi_status ret = napi_get_global(env, &global);
233 if (ret != napi_ok) {
234 return false;
235 }
236 napi_value constructor;
237 ret = napi_get_named_property(env, global, JS_NAV_PATH_STACK_CLASS_NAME, &constructor);
238 if (ret != napi_ok) {
239 return false;
240 }
241 bool isInstance = false;
242 ScopeRAII scope(reinterpret_cast<napi_env>(nativeEngine));
243 napi_value stack = nativeEngine->ValueToNapiValue(object);
244 napi_instanceof(env, stack, constructor, &isInstance);
245 return isInstance;
246 }
247 } // namespace OHOS::Ace::Framework
248