• 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 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