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 
20 namespace OHOS {
21 namespace MiscServices {
22 constexpr int32_t STR_MAX_LENGTH = 4096;
23 constexpr size_t STR_TAIL_LENGTH = 1;
24 constexpr size_t ARGC_MAX = 6;
25 const 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 
67 const 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 
87 const 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 
ThrowException(napi_env env,int32_t err,const std::string & msg,TypeCode type)100 void 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 
ToError(napi_env env,int32_t code,const std::string & msg)123 napi_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 
Convert(int32_t code)139 int32_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_HILOGE("ErrorCode: %{public}d", iter->second);
145         return iter->second;
146     }
147     IMSA_HILOGD("Convert end.");
148     return ERROR_CODE_QUERY_FAILED;
149 }
150 
ToMessage(int32_t code)151 const 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_HILOGI("ErrorMessage: %{public}s", (iter->second).c_str());
157         return iter->second;
158     }
159     return "error is out of definition.";
160 }
161 
Equals(napi_env env,napi_value value,napi_ref copy,std::thread::id threadId)162 bool 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 
GetNativeSelf(napi_env env,napi_callback_info info)182 void *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 
GetValue(napi_env env,napi_value in,int32_t & out)197 napi_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 */
GetValue(napi_env env,napi_value in,uint32_t & out)206 napi_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 
GetValue(napi_env env,napi_value in,bool & out)214 napi_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 
GetValue(napi_env env,napi_value in,double & out)222 napi_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 */
GetValue(napi_env env,napi_value in,std::string & out)231 napi_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> */
GetValue(napi_env env,napi_value in,std::unordered_map<std::string,PrivateDataValue> & out)260 napi_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 
GetValue(napi_env env,napi_value in,PrivateDataValue & out)299 napi_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 
GetValue(napi_env env,napi_value in,const std::string & type,napi_value & out)325 napi_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 */
GetValue(napi_env env,napi_value in,PanelInfo & out)337 napi_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 
GetValue(napi_env env,const std::vector<InputWindowInfo> & in)360 napi_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 
GetValue(napi_env env,const InputWindowInfo & in)377 napi_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 
GetValue(napi_env env,const std::string & in,napi_value & out)405 napi_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 
GetJsPrivateCommand(napi_env env,const std::unordered_map<std::string,PrivateDataValue> & in)410 napi_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
439