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
22namespace OHOS::Ace::Napi {
23const char EN_ALERT_APPROVE[] = "enableAlertBeforeBackPage:ok";
24const char EN_ALERT_REJECT[] = "enableAlertBeforeBackPage:fail cancel";
25const char DIS_ALERT_SUCCESS[] = "disableAlertBeforeBackPage:ok";
26
27static constexpr size_t ARGC_WITH_MODE = 2;
28static constexpr size_t ARGC_WITH_ROUTER_PARAMTER = 2;
29static constexpr size_t ARGC_WITH_MODE_AND_CALLBACK = 3;
30static constexpr uint32_t STANDARD = 0;
31static constexpr uint32_t SINGLE = 1;
32static constexpr uint32_t INVALID = 2;
33static constexpr uint32_t RESULT_ARRAY_INDEX_INDEX = 0;
34static constexpr uint32_t RESULT_ARRAY_NAME_INDEX = 1;
35static constexpr uint32_t RESULT_ARRAY_PATH_INDEX = 2;
36static constexpr uint32_t RESULT_ARRAY_LENGTH = 3;
37
38static 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
49static 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
73static 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
97static 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
105struct 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;
121    ~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
138using RouterFunc = std::function<void(const std::string&, const std::string&, int32_t)>;
139
140static 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
179static 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
196static 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
213bool 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
259void 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
301using ErrorCallback = std::function<void(const std::string&, int32_t)>;
302using RouterWithCallbackFunc = std::function<void(std::shared_ptr<RouterAsyncContext>, const ErrorCallback&)>;
303
304static 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
343static 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
363static 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
383static 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
397static 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
411static 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
442static 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
485static 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
496static 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
511static 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
541static 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
594static 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
648void 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
700static 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
777static 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
817static 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
832static 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
868static 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
878extern "C" __attribute__((constructor)) void RouterRegister()
879{
880    napi_module_register(&routerModule);
881}
882
883} // namespace OHOS::Ace::Napi
884