1/*
2 * Copyright (c) 2022-2023 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 "js_utils.h"
17
18#include "js_util.h"
19
20namespace OHOS {
21namespace MiscServices {
22constexpr int32_t STR_MAX_LENGTH = 4096;
23constexpr size_t STR_TAIL_LENGTH = 1;
24constexpr size_t ARGC_MAX = 6;
25const std::map<int32_t, int32_t> JsUtils::ERROR_CODE_MAP = {
26    { ErrorCode::ERROR_CONTROLLER_INVOKING_FAILED, EXCEPTION_CONTROLLER },
27    { ErrorCode::ERROR_STATUS_PERMISSION_DENIED, EXCEPTION_PERMISSION },
28    { ErrorCode::ERROR_STATUS_SYSTEM_PERMISSION, EXCEPTION_SYSTEM_PERMISSION },
29    { ErrorCode::ERROR_REMOTE_CLIENT_DIED, EXCEPTION_IMCLIENT },
30    { ErrorCode::ERROR_CLIENT_NOT_FOUND, EXCEPTION_IMCLIENT },
31    { ErrorCode::ERROR_CLIENT_NULL_POINTER, EXCEPTION_IMCLIENT },
32    { ErrorCode::ERROR_CLIENT_NOT_FOCUSED, EXCEPTION_IMCLIENT },
33    { ErrorCode::ERROR_CLIENT_NOT_EDITABLE, EXCEPTION_IMCLIENT },
34    { ErrorCode::ERROR_CLIENT_NOT_BOUND, EXCEPTION_DETACHED },
35    { ErrorCode::ERROR_CLIENT_ADD_FAILED, EXCEPTION_IMCLIENT },
36    { ErrorCode::ERROR_NULL_POINTER, EXCEPTION_IMMS },
37    { ErrorCode::ERROR_BAD_PARAMETERS, EXCEPTION_IMMS },
38    { ErrorCode::ERROR_SERVICE_START_FAILED, EXCEPTION_IMMS },
39    { ErrorCode::ERROR_IME_START_FAILED, EXCEPTION_IMMS },
40    { ErrorCode::ERROR_KBD_SHOW_FAILED, EXCEPTION_IMMS },
41    { ErrorCode::ERROR_KBD_HIDE_FAILED, EXCEPTION_IMMS },
42    { ErrorCode::ERROR_IME_NOT_STARTED, EXCEPTION_IMMS },
43    { ErrorCode::ERROR_EX_NULL_POINTER, EXCEPTION_IMMS },
44    { ErrorCode::ERROR_PERSIST_CONFIG, EXCEPTION_CONFPERSIST },
45    { ErrorCode::ERROR_PACKAGE_MANAGER, EXCEPTION_PACKAGEMANAGER },
46    { ErrorCode::ERROR_EX_UNSUPPORTED_OPERATION, EXCEPTION_IMMS },
47    { ErrorCode::ERROR_EX_SERVICE_SPECIFIC, EXCEPTION_IMMS },
48    { ErrorCode::ERROR_EX_PARCELABLE, EXCEPTION_IMMS },
49    { ErrorCode::ERROR_EX_ILLEGAL_ARGUMENT, EXCEPTION_IMMS },
50    { ErrorCode::ERROR_EX_ILLEGAL_STATE, EXCEPTION_IMMS },
51    { ErrorCode::ERROR_IME_START_INPUT_FAILED, EXCEPTION_IMMS },
52    { ErrorCode::ERROR_NOT_IME, EXCEPTION_IME },
53    { ErrorCode::ERROR_IME, EXCEPTION_IMENGINE },
54    { ErrorCode::ERROR_PARAMETER_CHECK_FAILED, EXCEPTION_PARAMCHECK },
55    { ErrorCode::ERROR_NOT_DEFAULT_IME, EXCEPTION_DEFAULTIME },
56    { ErrorCode::ERROR_ENABLE_IME, EXCEPTION_IMMS },
57    { ErrorCode::ERROR_NOT_CURRENT_IME, EXCEPTION_IMMS },
58    { ErrorCode::ERROR_PANEL_NOT_FOUND, EXCEPTION_PANEL_NOT_FOUND },
59    { ErrorCode::ERROR_WINDOW_MANAGER, EXCEPTION_WINDOW_MANAGER },
60    { ErrorCode::ERROR_GET_TEXT_CONFIG, EXCEPTION_IMCLIENT },
61    { ErrorCode::ERROR_INVALID_PRIVATE_COMMAND_SIZE, EXCEPTION_PARAMCHECK },
62    { ErrorCode::ERROR_TEXT_LISTENER_ERROR, EXCEPTION_IMCLIENT },
63    { ErrorCode::ERROR_TEXT_PREVIEW_NOT_SUPPORTED, EXCEPTION_TEXT_PREVIEW_NOT_SUPPORTED },
64    { ErrorCode::ERROR_INVALID_RANGE, EXCEPTION_PARAMCHECK },
65};
66
67const std::map<int32_t, std::string> JsUtils::ERROR_CODE_CONVERT_MESSAGE_MAP = {
68    { EXCEPTION_PERMISSION, "the permissions check fails." },
69    { EXCEPTION_SYSTEM_PERMISSION, "not system application." },
70    { EXCEPTION_PARAMCHECK, "the parameters check fails." },
71    { EXCEPTION_UNSUPPORTED, "call unsupported api." },
72    { EXCEPTION_PACKAGEMANAGER, "package manager error." },
73    { EXCEPTION_IMENGINE, "input method engine error." },
74    { EXCEPTION_IMCLIENT, "input method client error." },
75    { EXCEPTION_IME, "not an input method extension." },
76    { EXCEPTION_CONFPERSIST, "configuration persisting error." },
77    { EXCEPTION_CONTROLLER, "input method controller error." },
78    { EXCEPTION_SETTINGS, "input method settings extension error." },
79    { EXCEPTION_IMMS, "input method manager service error." },
80    { EXCEPTION_DETACHED, "input method not attached." },
81    { EXCEPTION_DEFAULTIME, "not default input method configured by system." },
82    { EXCEPTION_TEXT_PREVIEW_NOT_SUPPORTED, "text preview is not supported." },
83    { EXCEPTION_PANEL_NOT_FOUND, "soft keyboard panel doesn't exist." },
84    { EXCEPTION_WINDOW_MANAGER, "window manager service error." },
85};
86
87const std::map<int32_t, std::string> JsUtils::PARAMETER_TYPE = {
88    { TYPE_UNDEFINED, "napi_undefine." },
89    { TYPE_NULL, "napi_null." },
90    { TYPE_BOOLEAN, "napi_boolean." },
91    { TYPE_NUMBER, "napi_number." },
92    { TYPE_STRING, "napi_string." },
93    { TYPE_SYMBOL, "napi_symbol." },
94    { TYPE_OBJECT, "napi_object." },
95    { TYPE_FUNCTION, "napi_function." },
96    { TYPE_EXTERNAL, "napi_external." },
97    { TYPE_BIGINT, "napi_bigint." },
98};
99
100void JsUtils::ThrowException(napi_env env, int32_t err, const std::string &msg, TypeCode type)
101{
102    std::string errMsg = ToMessage(err);
103    napi_value error;
104    napi_value code;
105    napi_value message;
106    if (type == TypeCode::TYPE_NONE) {
107        errMsg = errMsg + " " + msg;
108        IMSA_HILOGE("THROW_ERROR message: %{public}s!", errMsg.c_str());
109    } else {
110        auto iter = PARAMETER_TYPE.find(type);
111        if (iter != PARAMETER_TYPE.end()) {
112            errMsg = errMsg + "The type of " + msg + " must be " + iter->second;
113            IMSA_HILOGE("THROW_ERROR message: %{public}s!", errMsg.c_str());
114        }
115    }
116    NAPI_CALL_RETURN_VOID(env, napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &message));
117    NAPI_CALL_RETURN_VOID(env, napi_create_error(env, nullptr, message, &error));
118    NAPI_CALL_RETURN_VOID(env, napi_create_int32(env, err, &code));
119    NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, error, "code", code));
120    NAPI_CALL_RETURN_VOID(env, napi_throw(env, error));
121}
122
123napi_value JsUtils::ToError(napi_env env, int32_t code, const std::string &msg)
124{
125    IMSA_HILOGD("ToError start");
126    napi_value errorObj;
127    NAPI_CALL(env, napi_create_object(env, &errorObj));
128    napi_value errorCode = nullptr;
129    NAPI_CALL(env, napi_create_int32(env, Convert(code), &errorCode));
130    napi_value errorMessage = nullptr;
131    std::string errMsg = ToMessage(Convert(code)) + " " + msg;
132    NAPI_CALL(env, napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &errorMessage));
133    NAPI_CALL(env, napi_set_named_property(env, errorObj, "code", errorCode));
134    NAPI_CALL(env, napi_set_named_property(env, errorObj, "message", errorMessage));
135    IMSA_HILOGD("ToError end");
136    return errorObj;
137}
138
139int32_t JsUtils::Convert(int32_t code)
140{
141    IMSA_HILOGD("Convert start.");
142    auto iter = ERROR_CODE_MAP.find(code);
143    if (iter != ERROR_CODE_MAP.end()) {
144        IMSA_HILOGD("ErrorCode: %{public}d", iter->second);
145        return iter->second;
146    }
147    IMSA_HILOGD("Convert end.");
148    return ERROR_CODE_QUERY_FAILED;
149}
150
151const std::string JsUtils::ToMessage(int32_t code)
152{
153    IMSA_HILOGD("ToMessage start");
154    auto iter = ERROR_CODE_CONVERT_MESSAGE_MAP.find(code);
155    if (iter != ERROR_CODE_CONVERT_MESSAGE_MAP.end()) {
156        IMSA_HILOGD("ErrorMessage: %{public}s", (iter->second).c_str());
157        return iter->second;
158    }
159    return "error is out of definition.";
160}
161
162bool JsUtils::Equals(napi_env env, napi_value value, napi_ref copy, std::thread::id threadId)
163{
164    if (copy == nullptr) {
165        return value == nullptr;
166    }
167
168    if (threadId != std::this_thread::get_id()) {
169        IMSA_HILOGD("napi_value can not be compared");
170        return false;
171    }
172
173    napi_value copyValue = nullptr;
174    napi_get_reference_value(env, copy, &copyValue);
175
176    bool isEquals = false;
177    napi_strict_equals(env, value, copyValue, &isEquals);
178    IMSA_HILOGD("value compare result: %{public}d", isEquals);
179    return isEquals;
180}
181
182void *JsUtils::GetNativeSelf(napi_env env, napi_callback_info info)
183{
184    size_t argc = ARGC_MAX;
185    void *native = nullptr;
186    napi_value self = nullptr;
187    napi_value argv[ARGC_MAX] = { nullptr };
188    napi_status status = napi_invalid_arg;
189    napi_get_cb_info(env, info, &argc, argv, &self, nullptr);
190    CHECK_RETURN((self != nullptr && argc <= ARGC_MAX), "napi_get_cb_info failed!", nullptr);
191
192    status = napi_unwrap(env, self, &native);
193    CHECK_RETURN((status == napi_ok && native != nullptr), "napi_unwrap failed!", nullptr);
194    return native;
195}
196
197napi_status JsUtils::GetValue(napi_env env, napi_value in, int32_t &out)
198{
199    napi_valuetype type = napi_undefined;
200    napi_status status = napi_typeof(env, in, &type);
201    CHECK_RETURN((status == napi_ok) && (type == napi_number), "invalid type", napi_generic_failure);
202    return napi_get_value_int32(env, in, &out);
203}
204
205/* napi_value <-> uint32_t */
206napi_status JsUtils::GetValue(napi_env env, napi_value in, uint32_t &out)
207{
208    napi_valuetype type = napi_undefined;
209    napi_status status = napi_typeof(env, in, &type);
210    CHECK_RETURN((status == napi_ok) && (type == napi_number), "invalid type", napi_generic_failure);
211    return napi_get_value_uint32(env, in, &out);
212}
213
214napi_status JsUtils::GetValue(napi_env env, napi_value in, bool &out)
215{
216    napi_valuetype type = napi_undefined;
217    napi_status status = napi_typeof(env, in, &type);
218    CHECK_RETURN((status == napi_ok) && (type == napi_boolean), "invalid type", napi_generic_failure);
219    return napi_get_value_bool(env, in, &out);
220}
221
222napi_status JsUtils::GetValue(napi_env env, napi_value in, double &out)
223{
224    napi_valuetype type = napi_undefined;
225    napi_status status = napi_typeof(env, in, &type);
226    CHECK_RETURN((status == napi_ok) && (type == napi_number), "invalid double type", napi_generic_failure);
227    return napi_get_value_double(env, in, &out);
228}
229
230/* napi_value <-> std::string */
231napi_status JsUtils::GetValue(napi_env env, napi_value in, std::string &out)
232{
233    IMSA_HILOGD("JsUtils get string value in.");
234    napi_valuetype type = napi_undefined;
235    napi_status status = napi_typeof(env, in, &type);
236    CHECK_RETURN((status == napi_ok) && (type == napi_string), "invalid type", napi_generic_failure);
237
238    size_t maxLen = STR_MAX_LENGTH;
239    status = napi_get_value_string_utf8(env, in, NULL, 0, &maxLen);
240    if (maxLen <= 0) {
241        return status;
242    }
243    IMSA_HILOGD("napi_value -> std::string get length %{public}zu", maxLen);
244    char *buf = new (std::nothrow) char[maxLen + STR_TAIL_LENGTH];
245    if (buf != nullptr) {
246        size_t len = 0;
247        status = napi_get_value_string_utf8(env, in, buf, maxLen + STR_TAIL_LENGTH, &len);
248        if (status == napi_ok) {
249            buf[len] = 0;
250            out = std::string(buf);
251        }
252        delete[] buf;
253    } else {
254        status = napi_generic_failure;
255    }
256    return status;
257}
258
259/* napi_value <-> std::unordered_map<string, string> */
260napi_status JsUtils::GetValue(napi_env env, napi_value in, std::unordered_map<std::string, PrivateDataValue> &out)
261{
262    napi_valuetype type = napi_undefined;
263    napi_status status = napi_typeof(env, in, &type);
264    PARAM_CHECK_RETURN(env, type != napi_undefined, "param is undefined.", TYPE_NONE, napi_generic_failure);
265
266    napi_value keys = nullptr;
267    napi_get_property_names(env, in, &keys);
268    uint32_t arrLen = 0;
269    status = napi_get_array_length(env, keys, &arrLen);
270    if (status != napi_ok) {
271        IMSA_HILOGE("napi_get_array_length error");
272        return status;
273    }
274    // 5 means max private command count.
275    PARAM_CHECK_RETURN(env, arrLen <= 5 && arrLen > 0, "privateCommand must more than 0 and less than 5.", TYPE_NONE,
276        napi_generic_failure);
277    IMSA_HILOGD("length : %{public}u", arrLen);
278    for (size_t iter = 0; iter < arrLen; ++iter) {
279        napi_value key = nullptr;
280        status = napi_get_element(env, keys, iter, &key);
281        CHECK_RETURN(status == napi_ok, "napi_get_element error", status);
282
283        napi_value value = nullptr;
284        status = napi_get_property(env, in, key, &value);
285        CHECK_RETURN(status == napi_ok, "napi_get_property error", status);
286
287        std::string keyStr;
288        status = GetValue(env, key, keyStr);
289        CHECK_RETURN(status == napi_ok, "GetValue keyStr error", status);
290
291        PrivateDataValue privateCommand;
292        status = GetValue(env, value, privateCommand);
293        CHECK_RETURN(status == napi_ok, "GetValue privateCommand error", status);
294        out.emplace(keyStr, privateCommand);
295    }
296    return status;
297}
298
299napi_status JsUtils::GetValue(napi_env env, napi_value in, PrivateDataValue &out)
300{
301    napi_valuetype valueType = napi_undefined;
302    napi_status status = napi_typeof(env, in, &valueType);
303    CHECK_RETURN(status == napi_ok, "napi_typeof error", napi_generic_failure);
304    if (valueType == napi_string) {
305        std::string privateDataStr;
306        status = GetValue(env, in, privateDataStr);
307        CHECK_RETURN(status == napi_ok, "GetValue napi_string error", napi_generic_failure);
308        out.emplace<std::string>(privateDataStr);
309    } else if (valueType == napi_boolean) {
310        bool privateDataBool = false;
311        status = GetValue(env, in, privateDataBool);
312        CHECK_RETURN(status == napi_ok, "GetValue napi_boolean error", napi_generic_failure);
313        out.emplace<bool>(privateDataBool);
314    } else if (valueType == napi_number) {
315        int32_t privateDataInt = 0;
316        status = GetValue(env, in, privateDataInt);
317        CHECK_RETURN(status == napi_ok, "GetValue napi_number error", napi_generic_failure);
318        out.emplace<int32_t>(privateDataInt);
319    } else {
320        PARAM_CHECK_RETURN(env, false, "value type must be string | boolean | number", TYPE_NONE, napi_generic_failure);
321    }
322    return status;
323}
324
325napi_status JsUtils::GetValue(napi_env env, napi_value in, const std::string &type, napi_value &out)
326{
327    napi_valuetype valueType = napi_undefined;
328    napi_status status = napi_typeof(env, in, &valueType);
329    if ((status == napi_ok) && (valueType == napi_object)) {
330        status = napi_get_named_property(env, in, type.c_str(), &out);
331        return status;
332    }
333    return napi_generic_failure;
334}
335
336/* napi_value <-> PanelInfo */
337napi_status JsUtils::GetValue(napi_env env, napi_value in, PanelInfo &out)
338{
339    IMSA_HILOGD("napi_value -> PanelInfo ");
340    napi_value propType = nullptr;
341    napi_status status = napi_get_named_property(env, in, "type", &propType);
342    CHECK_RETURN((status == napi_ok), "no property type ", status);
343    int32_t panelType = 0;
344    status = GetValue(env, propType, panelType);
345    CHECK_RETURN((status == napi_ok), "no value of type ", status);
346
347    // PanelFlag is optional, defaults to FLG_FIXED when empty.
348    int32_t panelFlag = static_cast<int32_t>(PanelFlag::FLG_FIXED);
349    napi_value panelFlagObj = nullptr;
350    status = napi_get_named_property(env, in, "flag", &panelFlagObj);
351    if (status == napi_ok) {
352        JsUtils::GetValue(env, panelFlagObj, panelFlag);
353    }
354
355    out.panelType = PanelType(panelType);
356    out.panelFlag = PanelFlag(panelFlag);
357    return napi_ok;
358}
359
360napi_value JsUtils::GetValue(napi_env env, const std::vector<InputWindowInfo> &in)
361{
362    napi_value array = nullptr;
363    uint32_t index = 0;
364    napi_create_array(env, &array);
365    if (array == nullptr) {
366        IMSA_HILOGE("create array failed");
367        return array;
368    }
369    for (const auto &info : in) {
370        napi_value jsInfo = GetValue(env, info);
371        napi_set_element(env, array, index, jsInfo);
372        ++index;
373    }
374    return array;
375}
376
377napi_value JsUtils::GetValue(napi_env env, const InputWindowInfo &in)
378{
379    napi_value info = nullptr;
380    napi_create_object(env, &info);
381
382    napi_value name = nullptr;
383    napi_create_string_utf8(env, in.name.c_str(), in.name.size(), &name);
384    napi_set_named_property(env, info, "name", name);
385
386    napi_value left = nullptr;
387    napi_create_int32(env, in.left, &left);
388    napi_set_named_property(env, info, "left", left);
389
390    napi_value top = nullptr;
391    napi_create_int32(env, in.top, &top);
392    napi_set_named_property(env, info, "top", top);
393
394    napi_value width = nullptr;
395    napi_create_uint32(env, in.width, &width);
396    napi_set_named_property(env, info, "width", width);
397
398    napi_value height = nullptr;
399    napi_create_uint32(env, in.height, &height);
400    napi_set_named_property(env, info, "height", height);
401
402    return info;
403}
404
405napi_status JsUtils::GetValue(napi_env env, const std::string &in, napi_value &out)
406{
407    return napi_create_string_utf8(env, in.c_str(), in.size(), &out);
408}
409
410napi_value JsUtils::GetJsPrivateCommand(napi_env env, const std::unordered_map<std::string, PrivateDataValue> &in)
411{
412    napi_value jsPrivateCommand = nullptr;
413    NAPI_CALL(env, napi_create_object(env, &jsPrivateCommand));
414    for (const auto &iter : in) {
415        size_t idx = iter.second.index();
416        napi_value value = nullptr;
417        if (idx == static_cast<size_t>(PrivateDataValueType::VALUE_TYPE_STRING)) {
418            auto stringValue = std::get_if<std::string>(&iter.second);
419            if (stringValue != nullptr) {
420                NAPI_CALL(env, napi_create_string_utf8(env, (*stringValue).c_str(), (*stringValue).size(), &value));
421            }
422        } else if (idx == static_cast<size_t>(PrivateDataValueType::VALUE_TYPE_BOOL)) {
423            auto boolValue = std::get_if<bool>(&iter.second);
424            if (boolValue != nullptr) {
425                NAPI_CALL(env, napi_get_boolean(env, *boolValue, &value));
426            }
427        } else if (idx == static_cast<size_t>(PrivateDataValueType::VALUE_TYPE_NUMBER)) {
428            auto numberValue = std::get_if<int32_t>(&iter.second);
429            if (numberValue != nullptr) {
430                NAPI_CALL(env, napi_create_int32(env, *numberValue, &value));
431            }
432        }
433        NAPI_CALL(env, napi_set_named_property(env, jsPrivateCommand, iter.first.c_str(), value));
434    }
435    return jsPrivateCommand;
436}
437} // namespace MiscServices
438} // namespace OHOS