1 /*
2  * Copyright (c) 2021-2022 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 <cstdint>
17 #include <memory>
18 #include <string>
19 
20 #include "interfaces/napi/kits/utils/napi_utils.h"
21 #include "js_native_api.h"
22 #include "js_native_api_types.h"
23 #include "napi/native_api.h"
24 #include "napi/native_engine/native_value.h"
25 #include "napi/native_node_api.h"
26 
27 #include "core/common/ace_engine.h"
28 #include "frameworks/base/log/log.h"
29 #include "frameworks/bridge/common/utils/engine_helper.h"
30 #include "frameworks/bridge/js_frontend/engine/common/js_engine.h"
31 
32 namespace OHOS::Ace::Napi {
33 const char EN_ALERT_APPROVE[] = "enableAlertBeforeBackPage:ok";
34 const char EN_ALERT_REJECT[] = "enableAlertBeforeBackPage:fail cancel";
35 const char DIS_ALERT_SUCCESS[] = "disableAlertBeforeBackPage:ok";
36 
37 static constexpr size_t ARGC_WITH_MODE = 2;
38 static constexpr size_t ARGC_WITH_ROUTER_PARAMTER = 2;
39 static constexpr size_t ARGC_WITH_MODE_AND_CALLBACK = 3;
40 static constexpr uint32_t STANDARD = 0;
41 static constexpr uint32_t SINGLE = 1;
42 static constexpr uint32_t INVALID = 2;
43 static constexpr uint32_t RESULT_ARRAY_INDEX_INDEX = 0;
44 static constexpr uint32_t RESULT_ARRAY_NAME_INDEX = 1;
45 static constexpr uint32_t RESULT_ARRAY_PATH_INDEX = 2;
46 static constexpr uint32_t RESULT_ARRAY_LENGTH = 3;
47 
ParseUri(napi_env env,napi_value uriNApi,std::string & uriString)48 static void ParseUri(napi_env env, napi_value uriNApi, std::string& uriString)
49 {
50     if (uriNApi != nullptr) {
51         size_t uriLen = 0;
52         napi_get_value_string_utf8(env, uriNApi, nullptr, 0, &uriLen);
53         std::unique_ptr<char[]> uri = std::make_unique<char[]>(uriLen + 1);
54         napi_get_value_string_utf8(env, uriNApi, uri.get(), uriLen + 1, &uriLen);
55         uriString = uri.get();
56     }
57 }
58 
ParseParams(napi_env env,napi_value params,std::string & paramsString)59 static void ParseParams(napi_env env, napi_value params, std::string& paramsString)
60 {
61     if (params == nullptr) {
62         return;
63     }
64     napi_value globalValue;
65     napi_get_global(env, &globalValue);
66     napi_value jsonValue;
67     napi_get_named_property(env, globalValue, "JSON", &jsonValue);
68     napi_value stringifyValue;
69     napi_get_named_property(env, jsonValue, "stringify", &stringifyValue);
70     napi_value funcArgv[1] = { params };
71     napi_value returnValue;
72     if (napi_call_function(env, jsonValue, stringifyValue, 1, funcArgv, &returnValue) != napi_ok) {
73         TAG_LOGE(AceLogTag::ACE_ROUTER,
74             "Router parse param failed, probably caused by invalid format of JSON object 'params'");
75     }
76     size_t len = 0;
77     napi_get_value_string_utf8(env, returnValue, nullptr, 0, &len);
78     std::unique_ptr<char[]> paramsChar = std::make_unique<char[]>(len + 1);
79     napi_get_value_string_utf8(env, returnValue, paramsChar.get(), len + 1, &len);
80     paramsString = paramsChar.get();
81 }
82 
ParseJSONParams(napi_env env,const std::string & paramsStr)83 static napi_value ParseJSONParams(napi_env env, const std::string& paramsStr)
84 {
85     napi_value globalValue;
86     napi_get_global(env, &globalValue);
87     napi_value jsonValue;
88     napi_get_named_property(env, globalValue, "JSON", &jsonValue);
89     napi_value parseValue;
90     napi_get_named_property(env, jsonValue, "parse", &parseValue);
91 
92     napi_value paramsNApi;
93     napi_create_string_utf8(env, paramsStr.c_str(), NAPI_AUTO_LENGTH, &paramsNApi);
94     napi_value funcArgv[1] = { paramsNApi };
95     napi_value result;
96     napi_call_function(env, jsonValue, parseValue, 1, funcArgv, &result);
97 
98     napi_valuetype valueType = napi_undefined;
99     napi_typeof(env, result, &valueType);
100     if (valueType != napi_object) {
101         return nullptr;
102     }
103 
104     return result;
105 }
106 
ParseRecoverable(napi_env env,napi_value recoverableNApi,bool & recoverable)107 static void ParseRecoverable(napi_env env, napi_value recoverableNApi, bool& recoverable)
108 {
109     if (recoverableNApi == nullptr) {
110         return;
111     }
112     napi_get_value_bool(env, recoverableNApi, &recoverable);
113 }
114 
115 struct RouterAsyncContext {
116     napi_env env = nullptr;
117     napi_ref callbackSuccess = nullptr;
118     napi_ref callbackFail = nullptr;
119     napi_ref callbackComplete = nullptr;
120     int32_t callbackType = 0;
121     std::string keyForUrl;
122     std::string paramsString;
123     std::string uriString;
124     bool recoverable = true;
125     uint32_t mode = STANDARD;
126     napi_deferred deferred = nullptr;
127     napi_ref callbackRef = nullptr;
128     int32_t callbackCode = 0;
129     std::string callbackMsg;
130     int32_t instanceId = -1;
~RouterAsyncContextOHOS::Ace::Napi::RouterAsyncContext131     ~RouterAsyncContext()
132     {
133         if (callbackRef) {
134             napi_delete_reference(env, callbackRef);
135         }
136         if (callbackSuccess) {
137             napi_delete_reference(env, callbackSuccess);
138         }
139         if (callbackFail) {
140             napi_delete_reference(env, callbackFail);
141         }
142         if (callbackComplete) {
143             napi_delete_reference(env, callbackComplete);
144         }
145     }
146 };
147 
148 using RouterFunc = std::function<void(const std::string&, const std::string&, int32_t)>;
149 
CommonRouterProcess(napi_env env,napi_callback_info info,const RouterFunc & callback)150 static void CommonRouterProcess(napi_env env, napi_callback_info info, const RouterFunc& callback)
151 {
152     size_t requireArgc = 1;
153     size_t argc = ARGC_WITH_MODE;
154     napi_value argv[ARGC_WITH_MODE] = { 0 };
155     napi_value thisVar = nullptr;
156     void* data = nullptr;
157     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
158     if (argc < requireArgc) {
159         return;
160     }
161     napi_value uriNApi = nullptr;
162     napi_value params = nullptr;
163     napi_valuetype valueType = napi_undefined;
164     napi_typeof(env, argv[0], &valueType);
165     if (valueType == napi_object) {
166         napi_get_named_property(env, argv[0], "url", &uriNApi);
167         napi_typeof(env, uriNApi, &valueType);
168         if (valueType != napi_string) {
169             return;
170         }
171         napi_get_named_property(env, argv[0], "params", &params);
172     }
173     std::string paramsString;
174     ParseParams(env, params, paramsString);
175     std::string uriString;
176     napi_typeof(env, uriNApi, &valueType);
177     if (valueType == napi_string) {
178         ParseUri(env, uriNApi, uriString);
179     }
180 
181     uint32_t mode = INVALID;
182     napi_typeof(env, argv[1], &valueType);
183     if (argc == ARGC_WITH_MODE && valueType == napi_number) {
184         napi_get_value_uint32(env, argv[1], &mode);
185     }
186     callback(uriString, paramsString, mode);
187 }
188 
JSRouterPush(napi_env env,napi_callback_info info)189 static napi_value JSRouterPush(napi_env env, napi_callback_info info)
190 {
191     auto callback = [](const std::string& uri, const std::string& params, uint32_t mode) {
192         auto delegate = EngineHelper::GetCurrentDelegateSafely();
193         if (!delegate) {
194             return;
195         }
196         if (mode == INVALID) {
197             delegate->Push(uri, params);
198         } else {
199             delegate->PushWithMode(uri, params, mode);
200         }
201     };
202     CommonRouterProcess(env, info, callback);
203     return nullptr;
204 }
205 
JSRouterReplace(napi_env env,napi_callback_info info)206 static napi_value JSRouterReplace(napi_env env, napi_callback_info info)
207 {
208     auto callback = [](const std::string& uri, const std::string& params, uint32_t mode) {
209         auto delegate = EngineHelper::GetCurrentDelegateSafely();
210         if (!delegate) {
211             return;
212         }
213         if (mode == INVALID) {
214             delegate->Replace(uri, params);
215         } else {
216             delegate->ReplaceWithMode(uri, params, mode);
217         }
218     };
219     CommonRouterProcess(env, info, callback);
220     return nullptr;
221 }
222 
ParseParamWithCallback(napi_env env,std::shared_ptr<RouterAsyncContext> asyncContext,const size_t argc,napi_value * argv,napi_value * result)223 bool ParseParamWithCallback(napi_env env, std::shared_ptr<RouterAsyncContext> asyncContext, const size_t argc,
224     napi_value* argv, napi_value* result)
225 {
226     asyncContext->env = env;
227     for (size_t i = 0; i < argc; i++) {
228         napi_valuetype valueType = napi_undefined;
229         napi_typeof(env, argv[i], &valueType);
230         if (i == 0) {
231             if (valueType != napi_object) {
232                 NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
233                 return false;
234             }
235             napi_value uriNApi = nullptr;
236             napi_value params = nullptr;
237             napi_value recoverable = nullptr;
238             napi_get_named_property(env, argv[i], asyncContext->keyForUrl.c_str(), &uriNApi);
239             napi_typeof(env, uriNApi, &valueType);
240             if (valueType != napi_string) {
241                 NapiThrow(env, "The type of the url parameter is not string.", ERROR_CODE_PARAM_INVALID);
242                 return false;
243             }
244             ParseUri(env, uriNApi, asyncContext->uriString);
245             napi_get_named_property(env, argv[i], "params", &params);
246             ParseParams(env, params, asyncContext->paramsString);
247             napi_get_named_property(env, argv[i], "recoverable", &recoverable);
248             ParseRecoverable(env, recoverable, asyncContext->recoverable);
249         } else if (valueType == napi_number) {
250             napi_get_value_uint32(env, argv[i], &asyncContext->mode);
251         } else if (valueType == napi_function) {
252             napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
253         } else {
254             NapiThrow(env, "The type of parameters is incorrect.", ERROR_CODE_PARAM_INVALID);
255             return false;
256         }
257     }
258 
259     if (asyncContext->callbackRef == nullptr) {
260         if (argc > ARGC_WITH_MODE) {
261             NapiThrow(env, "The largest number of parameters is 2 in Promise.", ERROR_CODE_PARAM_INVALID);
262             return false;
263         }
264         napi_create_promise(env, &asyncContext->deferred, result);
265     }
266     return true;
267 }
268 
TriggerCallback(std::shared_ptr<RouterAsyncContext> asyncContext)269 void TriggerCallback(std::shared_ptr<RouterAsyncContext> asyncContext)
270 {
271     napi_handle_scope scope = nullptr;
272     napi_open_handle_scope(asyncContext->env, &scope);
273     if (scope == nullptr) {
274         return;
275     }
276 
277     if (asyncContext->callbackCode == ERROR_CODE_NO_ERROR) {
278         napi_value result = nullptr;
279         napi_get_undefined(asyncContext->env, &result);
280         if (asyncContext->deferred) {
281             napi_resolve_deferred(asyncContext->env, asyncContext->deferred, result);
282         } else {
283             napi_value callback = nullptr;
284             napi_get_reference_value(asyncContext->env, asyncContext->callbackRef, &callback);
285             napi_value ret;
286             napi_call_function(asyncContext->env, nullptr, callback, 1, &result, &ret);
287         }
288     } else {
289         napi_value code = nullptr;
290         std::string strCode = std::to_string(asyncContext->callbackCode);
291         napi_create_string_utf8(asyncContext->env, strCode.c_str(), strCode.length(), &code);
292 
293         napi_value msg = nullptr;
294         std::string strMsg = ErrorToMessage(asyncContext->callbackCode) + asyncContext->callbackMsg;
295         napi_create_string_utf8(asyncContext->env, strMsg.c_str(), strMsg.length(), &msg);
296 
297         napi_value error = nullptr;
298         napi_create_error(asyncContext->env, code, msg, &error);
299         if (asyncContext->deferred) {
300             napi_reject_deferred(asyncContext->env, asyncContext->deferred, error);
301         } else {
302             napi_value callback = nullptr;
303             napi_get_reference_value(asyncContext->env, asyncContext->callbackRef, &callback);
304             napi_value ret;
305             napi_call_function(asyncContext->env, nullptr, callback, 1, &error, &ret);
306         }
307     }
308     napi_close_handle_scope(asyncContext->env, scope);
309 }
310 
311 using ErrorCallback = std::function<void(const std::string&, int32_t)>;
312 using RouterWithCallbackFunc = std::function<void(std::shared_ptr<RouterAsyncContext>, const ErrorCallback&)>;
313 
CommonRouterWithCallbackProcess(napi_env env,napi_callback_info info,const RouterWithCallbackFunc & callback,const std::string & keyForUrl)314 static napi_value CommonRouterWithCallbackProcess(
315     napi_env env, napi_callback_info info, const RouterWithCallbackFunc& callback, const std::string& keyForUrl)
316 {
317     napi_value result = nullptr;
318     napi_get_undefined(env, &result);
319     size_t requireArgc = 1;
320     size_t argc = ARGC_WITH_MODE_AND_CALLBACK;
321     napi_value argv[ARGC_WITH_MODE_AND_CALLBACK] = { 0 };
322     napi_value thisVar = nullptr;
323     void* data = nullptr;
324     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
325     if (argc < requireArgc) {
326         NapiThrow(
327             env, "The number of parameters must be greater than or equal to 1.", ERROR_CODE_PARAM_INVALID);
328         return result;
329     } else if (argc > ARGC_WITH_MODE_AND_CALLBACK) {
330         NapiThrow(env, "The largest number of parameters is 3.", ERROR_CODE_PARAM_INVALID);
331         return result;
332     }
333 
334     auto asyncContext = std::make_shared<RouterAsyncContext>();
335     asyncContext->keyForUrl = keyForUrl;
336     if (!ParseParamWithCallback(env, asyncContext, argc, argv, &result)) {
337         return result;
338     }
339 
340     auto errorCallback = [asyncContext](const std::string& message, int32_t errCode) mutable {
341         if (!asyncContext) {
342             return;
343         }
344         asyncContext->callbackCode = errCode;
345         asyncContext->callbackMsg = message;
346         TriggerCallback(asyncContext);
347         asyncContext = nullptr;
348     };
349     callback(asyncContext, errorCallback);
350     return result;
351 }
352 
JSRouterPushWithCallback(napi_env env,napi_callback_info info)353 static napi_value JSRouterPushWithCallback(napi_env env, napi_callback_info info)
354 {
355     auto callback = [](std::shared_ptr<RouterAsyncContext> context, const ErrorCallback& errorCallback) {
356         auto delegate = EngineHelper::GetCurrentDelegateSafely();
357         auto defaultDelegate = EngineHelper::GetDefaultDelegate();
358         if (!delegate && !defaultDelegate) {
359             NapiThrow(context->env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
360             return;
361         }
362         if (delegate) {
363             delegate->PushWithCallback(context->uriString, context->paramsString,
364                 context->recoverable, errorCallback, context->mode);
365         } else {
366             defaultDelegate->PushWithCallback(context->uriString, context->paramsString,
367                 context->recoverable, errorCallback, context->mode);
368         }
369     };
370     return CommonRouterWithCallbackProcess(env, info, callback, "url");
371 }
372 
JSRouterReplaceWithCallback(napi_env env,napi_callback_info info)373 static napi_value JSRouterReplaceWithCallback(napi_env env, napi_callback_info info)
374 {
375     auto callback = [](std::shared_ptr<RouterAsyncContext> context, const ErrorCallback& errorCallback) {
376         auto delegate = EngineHelper::GetCurrentDelegateSafely();
377         auto defaultDelegate = EngineHelper::GetDefaultDelegate();
378         if (!delegate && !defaultDelegate) {
379             NapiThrow(context->env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
380             return;
381         }
382         if (delegate) {
383             delegate->ReplaceWithCallback(context->uriString, context->paramsString,
384                 context->recoverable, errorCallback, context->mode);
385         } else {
386             defaultDelegate->ReplaceWithCallback(context->uriString, context->paramsString,
387                 context->recoverable, errorCallback, context->mode);
388         }
389     };
390     return CommonRouterWithCallbackProcess(env, info, callback, "url");
391 }
392 
JSPushNamedRoute(napi_env env,napi_callback_info info)393 static napi_value JSPushNamedRoute(napi_env env, napi_callback_info info)
394 {
395     auto callback = [](std::shared_ptr<RouterAsyncContext> context, const ErrorCallback& errorCallback) {
396         auto delegate = EngineHelper::GetCurrentDelegateSafely();
397         if (!delegate) {
398             NapiThrow(context->env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
399             return;
400         }
401         delegate->PushNamedRoute(context->uriString, context->paramsString,
402             context->recoverable, errorCallback, context->mode);
403     };
404     return CommonRouterWithCallbackProcess(env, info, callback, "name");
405 }
406 
JSReplaceNamedRoute(napi_env env,napi_callback_info info)407 static napi_value JSReplaceNamedRoute(napi_env env, napi_callback_info info)
408 {
409     auto callback = [](std::shared_ptr<RouterAsyncContext> context, const ErrorCallback& errorCallback) {
410         auto delegate = EngineHelper::GetCurrentDelegateSafely();
411         if (!delegate) {
412             NapiThrow(context->env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
413             return;
414         }
415         delegate->ReplaceNamedRoute(context->uriString, context->paramsString,
416             context->recoverable, errorCallback, context->mode);
417     };
418     return CommonRouterWithCallbackProcess(env, info, callback, "name");
419 }
420 
JsBackToIndex(napi_env env,napi_callback_info info)421 static napi_value JsBackToIndex(napi_env env, napi_callback_info info)
422 {
423     size_t argc = ARGC_WITH_ROUTER_PARAMTER;
424     napi_value argv[ARGC_WITH_ROUTER_PARAMTER] = { nullptr };
425     napi_value thisVar = nullptr;
426     void* data = nullptr;
427     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
428 
429     auto delegate = EngineHelper::GetCurrentDelegateSafely();
430     if (!delegate) {
431         NapiThrow(env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
432         return nullptr;
433     }
434     std::string paramsString;
435     int32_t routeIndex = 0;
436     napi_valuetype valueType = napi_undefined;
437     napi_typeof(env, argv[0], &valueType);
438     if (valueType == napi_number) {
439         napi_get_value_int32(env, argv[0], &routeIndex);
440     } else {
441         TAG_LOGE(AceLogTag::ACE_ROUTER, "Index is not of type number");
442         return nullptr;
443     }
444     napi_typeof(env, argv[1], &valueType);
445     if (valueType == napi_object) {
446         ParseParams(env, argv[1], paramsString);
447     }
448     delegate->BackToIndex(routeIndex, paramsString);
449     return nullptr;
450 }
451 
JSRouterBack(napi_env env,napi_callback_info info)452 static napi_value JSRouterBack(napi_env env, napi_callback_info info)
453 {
454     size_t argc = ARGC_WITH_ROUTER_PARAMTER;
455     napi_value argv[ARGC_WITH_ROUTER_PARAMTER] = { nullptr };
456     napi_value thisVar = nullptr;
457     void* data = nullptr;
458     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
459 
460     napi_valuetype valueType = napi_undefined;
461     napi_typeof(env, argv[0], &valueType);
462     if (argc == ARGC_WITH_ROUTER_PARAMTER || valueType == napi_number) {
463         return JsBackToIndex(env, info);
464     }
465     auto delegate = EngineHelper::GetCurrentDelegateSafely();
466     if (!delegate) {
467         NapiThrow(env, "UI execution context not found.", ERROR_CODE_PARAM_INVALID);
468         return nullptr;
469     }
470     std::string uriString = "";
471     std::string paramsString = "";
472     napi_value uriNApi = nullptr;
473     napi_value params = nullptr;
474     if (valueType == napi_object) {
475         napi_get_named_property(env, argv[0], "url", &uriNApi);
476         napi_typeof(env, uriNApi, &valueType);
477         if (valueType == napi_undefined) {
478             napi_get_named_property(env, argv[0], "path", &uriNApi);
479             napi_typeof(env, uriNApi, &valueType);
480         }
481         if (valueType == napi_string) {
482             ParseUri(env, uriNApi, uriString);
483         }
484 
485         napi_get_named_property(env, argv[0], "params", &params);
486         napi_typeof(env, params, &valueType);
487         if (valueType == napi_object) {
488             ParseParams(env, params, paramsString);
489         }
490     }
491     delegate->Back(uriString, paramsString);
492     return nullptr;
493 }
494 
JSRouterClear(napi_env env,napi_callback_info info)495 static napi_value JSRouterClear(napi_env env, napi_callback_info info)
496 {
497     auto delegate = EngineHelper::GetCurrentDelegateSafely();
498     if (!delegate) {
499         NapiThrow(env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
500         return nullptr;
501     }
502     delegate->Clear();
503     return nullptr;
504 }
505 
JSRouterGetLength(napi_env env,napi_callback_info info)506 static napi_value JSRouterGetLength(napi_env env, napi_callback_info info)
507 {
508     auto delegate = EngineHelper::GetCurrentDelegateSafely();
509     if (!delegate) {
510         NapiThrow(env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
511         return nullptr;
512     }
513     int32_t routeNumber = delegate->GetStackSize();
514     napi_value routeNApiNum = nullptr;
515     napi_create_int32(env, routeNumber, &routeNApiNum);
516     napi_value result = nullptr;
517     napi_coerce_to_string(env, routeNApiNum, &result);
518     return result;
519 }
520 
JSRouterGetState(napi_env env,napi_callback_info info)521 static napi_value JSRouterGetState(napi_env env, napi_callback_info info)
522 {
523     int32_t routeIndex = 0;
524     std::string routeName;
525     std::string routePath;
526     auto delegate = EngineHelper::GetCurrentDelegateSafely();
527     if (!delegate) {
528         NapiThrow(env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
529         return nullptr;
530     }
531     delegate->GetState(routeIndex, routeName, routePath);
532     size_t routeNameLen = routeName.length();
533     size_t routePathLen = routePath.length();
534 
535     std::string paramsStr = delegate->GetParams();
536     napi_value params = paramsStr.empty() ? nullptr : ParseJSONParams(env, paramsStr);
537     napi_value resultArray[RESULT_ARRAY_LENGTH] = { 0 };
538     napi_create_int32(env, routeIndex, &resultArray[RESULT_ARRAY_INDEX_INDEX]);
539     napi_create_string_utf8(env, routeName.c_str(), routeNameLen, &resultArray[RESULT_ARRAY_NAME_INDEX]);
540     napi_create_string_utf8(env, routePath.c_str(), routePathLen, &resultArray[RESULT_ARRAY_PATH_INDEX]);
541 
542     napi_value result = nullptr;
543     napi_create_object(env, &result);
544     napi_set_named_property(env, result, "index", resultArray[RESULT_ARRAY_INDEX_INDEX]);
545     napi_set_named_property(env, result, "name", resultArray[RESULT_ARRAY_NAME_INDEX]);
546     napi_set_named_property(env, result, "path", resultArray[RESULT_ARRAY_PATH_INDEX]);
547     napi_set_named_property(env, result, "params", params);
548     return result;
549 }
550 
JSGetStateByIndex(napi_env env,napi_callback_info info)551 static napi_value JSGetStateByIndex(napi_env env, napi_callback_info info)
552 {
553     size_t argc = 1;
554     napi_value argv = nullptr;
555     napi_value thisVar = nullptr;
556     void* data = nullptr;
557     napi_get_cb_info(env, info, &argc, &argv, &thisVar, &data);
558 
559     int32_t routeIndex = 0;
560     std::string routeName;
561     std::string routePath;
562     std::string routeParams;
563     napi_valuetype valueType = napi_undefined;
564     napi_typeof(env, argv, &valueType);
565     if (valueType == napi_number) {
566         napi_get_value_int32(env, argv, &routeIndex);
567     }
568     auto delegate = EngineHelper::GetCurrentDelegateSafely();
569     if (!delegate) {
570         NapiThrow(env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
571         return nullptr;
572     }
573 
574     delegate->GetRouterStateByIndex(routeIndex, routeName, routePath, routeParams);
575     if (routeName.empty()) {
576         napi_value undefined;
577         napi_get_undefined(env, &undefined);
578         return undefined;
579     }
580     size_t routeNameLen = routeName.length();
581     size_t routePathLen = routePath.length();
582 
583     napi_value resultArray[RESULT_ARRAY_LENGTH] = { 0 };
584     napi_create_int32(env, routeIndex, &resultArray[RESULT_ARRAY_INDEX_INDEX]);
585     napi_create_string_utf8(env, routeName.c_str(), routeNameLen, &resultArray[RESULT_ARRAY_NAME_INDEX]);
586     napi_create_string_utf8(env, routePath.c_str(), routePathLen, &resultArray[RESULT_ARRAY_PATH_INDEX]);
587 
588     napi_value parsedParams = nullptr;
589     if (!routeParams.empty()) {
590         parsedParams = ParseJSONParams(env, routeParams);
591     } else {
592         napi_create_object(env, &parsedParams);
593     }
594 
595     napi_value result = nullptr;
596     napi_create_object(env, &result);
597     napi_set_named_property(env, result, "index", resultArray[RESULT_ARRAY_INDEX_INDEX]);
598     napi_set_named_property(env, result, "name", resultArray[RESULT_ARRAY_NAME_INDEX]);
599     napi_set_named_property(env, result, "path", resultArray[RESULT_ARRAY_PATH_INDEX]);
600     napi_set_named_property(env, result, "params", parsedParams);
601     return result;
602 }
603 
JSGetStateByUrl(napi_env env,napi_callback_info info)604 static napi_value JSGetStateByUrl(napi_env env, napi_callback_info info)
605 {
606     size_t argc = 1;
607     napi_value argv = nullptr;
608     napi_value thisVar = nullptr;
609     void* data = nullptr;
610     napi_get_cb_info(env, info, &argc, &argv, &thisVar, &data);
611 
612     auto delegate = EngineHelper::GetCurrentDelegateSafely();
613     if (!delegate) {
614         NapiThrow(env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
615         return nullptr;
616     }
617     std::string uriString;
618     napi_valuetype valueType = napi_undefined;
619     napi_typeof(env, argv, &valueType);
620     if (valueType == napi_string) {
621         ParseUri(env, argv, uriString);
622     }
623     std::vector<Framework::StateInfo> stateArray;
624     delegate->GetRouterStateByUrl(uriString, stateArray);
625 
626     napi_value result = nullptr;
627     napi_create_array(env, &result);
628     int32_t index = 0;
629     for (const auto& info : stateArray) {
630         napi_value pageObj = nullptr;
631         napi_create_object(env, &pageObj);
632         int32_t routeIndex = info.index;
633         std::string routeName = info.name;
634         std::string routePath = info.path;
635         std::string routeParams = info.params;
636         napi_value indexValue = nullptr;
637         napi_value nameValue = nullptr;
638         napi_value pathValue = nullptr;
639 
640         napi_create_int32(env, routeIndex, &indexValue);
641         napi_create_string_utf8(env, routeName.c_str(), NAPI_AUTO_LENGTH, &nameValue);
642         napi_create_string_utf8(env, routePath.c_str(), NAPI_AUTO_LENGTH, &pathValue);
643         napi_value parsedParams = nullptr;
644         if (!routeParams.empty()) {
645             parsedParams = ParseJSONParams(env, routeParams);
646         } else {
647             napi_create_object(env, &parsedParams);
648         }
649         napi_set_named_property(env, pageObj, "index", indexValue);
650         napi_set_named_property(env, pageObj, "name", nameValue);
651         napi_set_named_property(env, pageObj, "path", pathValue);
652         napi_set_named_property(env, pageObj, "params", parsedParams);
653         napi_set_element(env, result, index++, pageObj);
654     }
655     return result;
656 }
657 
CallBackToJSTread(std::shared_ptr<RouterAsyncContext> context)658 void CallBackToJSTread(std::shared_ptr<RouterAsyncContext> context)
659 {
660     auto container = AceEngine::Get().GetContainer(context->instanceId);
661     if (!container) {
662         return;
663     }
664 
665     auto taskExecutor = container->GetTaskExecutor();
666     if (!taskExecutor) {
667         return;
668     }
669     taskExecutor->PostTask(
670         [context]() {
671             napi_handle_scope scope = nullptr;
672             napi_open_handle_scope(context->env, &scope);
673             if (scope == nullptr) {
674                 return;
675             }
676 
677             napi_value result = nullptr;
678             napi_value callback = nullptr;
679             napi_value ret = nullptr;
680             if (Framework::AlertState(context->callbackType) == Framework::AlertState::USER_CONFIRM) {
681                 if (context->callbackSuccess) {
682                     napi_create_string_utf8(context->env, EN_ALERT_APPROVE, NAPI_AUTO_LENGTH, &result);
683                     napi_value argv[1] = { result };
684                     napi_get_reference_value(context->env, context->callbackSuccess, &callback);
685                     napi_call_function(context->env, nullptr, callback, 1, argv, &ret);
686                 }
687                 if (context->callbackComplete) {
688                     napi_get_reference_value(context->env, context->callbackComplete, &callback);
689                     napi_call_function(context->env, nullptr, callback, 0, nullptr, &ret);
690                 }
691             }
692             if (Framework::AlertState(context->callbackType) == Framework::AlertState::USER_CANCEL) {
693                 if (context->callbackFail) {
694                     napi_create_string_utf8(context->env, EN_ALERT_REJECT, NAPI_AUTO_LENGTH, &result);
695                     napi_value argv[1] = { result };
696                     napi_get_reference_value(context->env, context->callbackFail, &callback);
697                     napi_call_function(context->env, nullptr, callback, 1, argv, &ret);
698                 }
699                 if (context->callbackComplete) {
700                     napi_get_reference_value(context->env, context->callbackComplete, &callback);
701                     napi_call_function(context->env, nullptr, callback, 0, nullptr, &ret);
702                 }
703             }
704 
705             napi_close_handle_scope(context->env, scope);
706         },
707         TaskExecutor::TaskType::JS, "ArkUIRouterAlertCallback");
708 }
709 
JSRouterEnableAlertBeforeBackPage(napi_env env,napi_callback_info info)710 static napi_value JSRouterEnableAlertBeforeBackPage(napi_env env, napi_callback_info info)
711 {
712     size_t argc = 1;
713     napi_value argv = nullptr;
714     napi_value thisVar = nullptr;
715     void* data = nullptr;
716     napi_get_cb_info(env, info, &argc, &argv, &thisVar, &data);
717 
718     napi_valuetype valueType = napi_undefined;
719     napi_typeof(env, argv, &valueType);
720     if (valueType != napi_object) {
721         NapiThrow(env, "The type of the parameter is not object.", ERROR_CODE_PARAM_INVALID);
722         return nullptr;
723     }
724 
725     napi_value messageNapi = nullptr;
726     std::unique_ptr<char[]> messageChar;
727     napi_get_named_property(env, argv, "message", &messageNapi);
728     napi_typeof(env, messageNapi, &valueType);
729     if (valueType == napi_string) {
730         size_t length = 0;
731         napi_get_value_string_utf8(env, messageNapi, nullptr, 0, &length);
732         messageChar = std::make_unique<char[]>(length + 1);
733         napi_get_value_string_utf8(env, messageNapi, messageChar.get(), length + 1, &length);
734     } else {
735         NapiThrow(env, "The type of the message is not string.", ERROR_CODE_PARAM_INVALID);
736         return nullptr;
737     }
738 
739     auto delegate = EngineHelper::GetCurrentDelegateSafely();
740     if (!delegate) {
741         NapiThrow(env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
742         return nullptr;
743     }
744 
745     auto context = std::make_shared<RouterAsyncContext>();
746     context->env = env;
747     context->instanceId = Container::CurrentIdSafely();
748     napi_value successFunc = nullptr;
749     napi_value failFunc = nullptr;
750     napi_value completeFunc = nullptr;
751     napi_get_named_property(env, argv, "success", &successFunc);
752     napi_get_named_property(env, argv, "cancel", &failFunc);
753     napi_get_named_property(env, argv, "complete", &completeFunc);
754     bool isNeedCallBack = false;
755     napi_typeof(env, successFunc, &valueType);
756     if (valueType == napi_function) {
757         napi_create_reference(env, successFunc, 1, &context->callbackSuccess);
758         isNeedCallBack = true;
759     }
760 
761     napi_typeof(env, failFunc, &valueType);
762     if (valueType == napi_function) {
763         napi_create_reference(env, failFunc, 1, &context->callbackFail);
764         isNeedCallBack = true;
765     }
766     napi_typeof(env, completeFunc, &valueType);
767     if (valueType == napi_function) {
768         napi_create_reference(env, completeFunc, 1, &context->callbackComplete);
769         isNeedCallBack = true;
770     }
771 
772     auto dilogCallback = [context, isNeedCallBack](int32_t callbackType) mutable {
773         if (context && isNeedCallBack) {
774             if (Framework::AlertState(callbackType) == Framework::AlertState::RECOVERY) {
775                 context = nullptr;
776                 return;
777             }
778             context->callbackType = callbackType;
779             CallBackToJSTread(context);
780         }
781     };
782     delegate->EnableAlertBeforeBackPage(messageChar.get(), std::move(dilogCallback));
783 
784     return nullptr;
785 }
786 
JSRouterDisableAlertBeforeBackPage(napi_env env,napi_callback_info info)787 static napi_value JSRouterDisableAlertBeforeBackPage(napi_env env, napi_callback_info info)
788 {
789     auto delegate = EngineHelper::GetCurrentDelegateSafely();
790     if (delegate) {
791         delegate->DisableAlertBeforeBackPage();
792     } else {
793         NapiThrow(env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
794         return nullptr;
795     }
796 
797     size_t argc = 1;
798     napi_value argv = nullptr;
799     napi_value thisVar = nullptr;
800     void* data = nullptr;
801     napi_get_cb_info(env, info, &argc, &argv, &thisVar, &data);
802     napi_valuetype valueType = napi_undefined;
803     napi_typeof(env, argv, &valueType);
804     if (valueType == napi_object) {
805         napi_value successFunc = nullptr;
806         napi_value completeFunc = nullptr;
807         napi_get_named_property(env, argv, "success", &successFunc);
808         napi_get_named_property(env, argv, "complete", &completeFunc);
809 
810         napi_value result = nullptr;
811         napi_value ret = nullptr;
812         napi_create_string_utf8(env, DIS_ALERT_SUCCESS, NAPI_AUTO_LENGTH, &result);
813         napi_value argv[1] = { result };
814 
815         napi_typeof(env, successFunc, &valueType);
816         if (valueType == napi_function) {
817             napi_call_function(env, nullptr, successFunc, 1, argv, &ret);
818         }
819         napi_typeof(env, completeFunc, &valueType);
820         if (valueType == napi_function) {
821             napi_call_function(env, nullptr, completeFunc, 1, argv, &ret);
822         }
823     }
824     return nullptr;
825 }
826 
JSRouterGetParams(napi_env env,napi_callback_info info)827 static napi_value JSRouterGetParams(napi_env env, napi_callback_info info)
828 {
829     auto delegate = EngineHelper::GetCurrentDelegateSafely();
830     if (!delegate) {
831         NapiThrow(env, "UI execution context not found.", ERROR_CODE_INTERNAL_ERROR);
832         return nullptr;
833     }
834 
835     std::string paramsStr = delegate->GetParams();
836     if (paramsStr.empty()) {
837         return nullptr;
838     }
839 
840     napi_value result = ParseJSONParams(env, paramsStr);
841     return result;
842 }
843 
RouterExport(napi_env env,napi_value exports)844 static napi_value RouterExport(napi_env env, napi_value exports)
845 {
846     napi_value routerMode = nullptr;
847     napi_create_object(env, &routerMode);
848     napi_value prop = nullptr;
849     napi_create_uint32(env, STANDARD, &prop);
850     napi_set_named_property(env, routerMode, "Standard", prop);
851     napi_create_uint32(env, SINGLE, &prop);
852     napi_set_named_property(env, routerMode, "Single", prop);
853 
854     napi_property_descriptor routerDesc[] = {
855         DECLARE_NAPI_FUNCTION("push", JSRouterPush),
856         DECLARE_NAPI_FUNCTION("pushUrl", JSRouterPushWithCallback),
857         DECLARE_NAPI_FUNCTION("replace", JSRouterReplace),
858         DECLARE_NAPI_FUNCTION("replaceUrl", JSRouterReplaceWithCallback),
859         DECLARE_NAPI_FUNCTION("back", JSRouterBack),
860         DECLARE_NAPI_FUNCTION("clear", JSRouterClear),
861         DECLARE_NAPI_FUNCTION("getLength", JSRouterGetLength),
862         DECLARE_NAPI_FUNCTION("getState", JSRouterGetState),
863         DECLARE_NAPI_FUNCTION("getStateByIndex", JSGetStateByIndex),
864         DECLARE_NAPI_FUNCTION("getStateByUrl", JSGetStateByUrl),
865         DECLARE_NAPI_FUNCTION("enableAlertBeforeBackPage", JSRouterEnableAlertBeforeBackPage),
866         DECLARE_NAPI_FUNCTION("enableBackPageAlert", JSRouterEnableAlertBeforeBackPage),
867         DECLARE_NAPI_FUNCTION("showAlertBeforeBackPage", JSRouterEnableAlertBeforeBackPage),
868         DECLARE_NAPI_FUNCTION("disableAlertBeforeBackPage", JSRouterDisableAlertBeforeBackPage),
869         DECLARE_NAPI_FUNCTION("hideAlertBeforeBackPage", JSRouterDisableAlertBeforeBackPage),
870         DECLARE_NAPI_FUNCTION("getParams", JSRouterGetParams),
871         DECLARE_NAPI_FUNCTION("pushNamedRoute", JSPushNamedRoute),
872         DECLARE_NAPI_FUNCTION("replaceNamedRoute", JSReplaceNamedRoute),
873         DECLARE_NAPI_PROPERTY("RouterMode", routerMode),
874     };
875     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(routerDesc) / sizeof(routerDesc[0]), routerDesc));
876 
877     return exports;
878 }
879 
880 static napi_module routerModule = {
881     .nm_version = 1,
882     .nm_flags = 0,
883     .nm_filename = nullptr,
884     .nm_register_func = RouterExport,
885     .nm_modname = "router",
886     .nm_priv = ((void*)0),
887     .reserved = { 0 },
888 };
889 
RouterRegister()890 extern "C" __attribute__((constructor)) void RouterRegister()
891 {
892     napi_module_register(&routerModule);
893 }
894 
895 } // namespace OHOS::Ace::Napi
896