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