* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "selection_js_utils.h"
#include "selection_errors.h"
namespace OHOS {
namespace SelectionFwk {
constexpr int32_t STR_MAX_LENGTH = 4096;
constexpr size_t STR_TAIL_LENGTH = 1;
constexpr size_t ARGC_MAX = 6;
const std::map<int32_t, int32_t> JsUtils::ERROR_CODE_MAP = {
{ ErrorCode::ERROR_PARAMETER_CHECK_FAILED, EXCEPTION_PARAMCHECK },
{ ErrorCode::ERROR_SELECTION_SERVICE, EXCEPTION_SELECTION_SERVICE },
{ ErrorCode::ERROR_PANEL_DESTROYED, EXCEPTION_PANEL_DESTROYED },
{ ErrorCode::ERROR_INVALID_OPERATION, EXCEPTION_INVALID_OPERATION }
};
const std::map<int32_t, int32_t> JsUtils::SERVICE_ERROR_CODE_MAP = {
{ SelectionServiceError::INVALID_DATA, EXCEPTION_SELECTION_SERVICE },
{ SelectionServiceError::NOT_SYSTEM_APP_ERROR, EXCEPTION_NOT_SYSTEM_APP },
{ SelectionServiceError::INVALID_TIMING, EXCEPTION_INVALID_TIMING },
{ SelectionServiceError::CANNOT_GET_CONTENT, EXCEPTION_CANNOT_GET_CONTENT },
{ SelectionServiceError::CONTENT_OUT_OF_RANGE, EXCEPTION_CONTENT_OUT_OF_RANGE },
{ SelectionServiceError::GET_CONTENT_TIMEOUT, EXCEPTION_GET_CONTENT_TIMEOUT },
};
const std::map<int32_t, std::string> JsUtils::ERROR_CODE_CONVERT_MESSAGE_MAP = {
{ EXCEPTION_PARAMCHECK, "The parameters check fails." },
{ EXCEPTION_SELECTION_SERVICE, "Selection service exception." },
{ EXCEPTION_NOT_SYSTEM_APP, "Permission denied. Called by non-system application."},
{ EXCEPTION_PANEL_DESTROYED, "This selection window has been destroyed." },
{ EXCEPTION_INVALID_OPERATION, "The application calling the API does not match the application \
selected in the system settings." },
{ EXCEPTION_TOO_FREQUENT, "The interface is called too frequently." },
{ EXCEPTION_INVALID_TIMING, "The interface is called at the wrong time." },
{ EXCEPTION_CANNOT_GET_CONTENT, "The current application is prohibited from accessing content." },
{ EXCEPTION_CONTENT_OUT_OF_RANGE, "The length of selected content is out of range." },
{ EXCEPTION_GET_CONTENT_TIMEOUT, "Getting the selected content times out." },
};
const std::map<int32_t, std::string> JsUtils::PARAMETER_TYPE = {
{ TYPE_UNDEFINED, "napi_undefine." },
{ TYPE_NULL, "napi_null." },
{ TYPE_BOOLEAN, "napi_boolean." },
{ TYPE_NUMBER, "napi_number." },
{ TYPE_STRING, "napi_string." },
{ TYPE_SYMBOL, "napi_symbol." },
{ TYPE_OBJECT, "napi_object." },
{ TYPE_FUNCTION, "napi_function." },
{ TYPE_EXTERNAL, "napi_external." },
{ TYPE_BIGINT, "napi_bigint." },
{ TYPE_ARRAY_BUFFER, "ArrayBuffer." },
{ TYPE_ARRAY, "napi_array." },
};
void JsUtils::ThrowException(napi_env env, int32_t err, const std::string &msg, TypeCode type)
{
std::string errMsg = ToMessage(err);
napi_value error;
napi_value code;
napi_value message;
if (type == TypeCode::TYPE_NONE) {
errMsg = errMsg + " " + msg;
SELECTION_HILOGE("THROW_ERROR message: %{public}s!", errMsg.c_str());
} else {
auto iter = PARAMETER_TYPE.find(type);
if (iter != PARAMETER_TYPE.end()) {
errMsg = errMsg + "The type of " + msg + " must be " + iter->second;
SELECTION_HILOGE("THROW_ERROR message: %{public}s!", errMsg.c_str());
}
}
NAPI_CALL_RETURN_VOID(env, napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &message));
NAPI_CALL_RETURN_VOID(env, napi_create_error(env, nullptr, message, &error));
NAPI_CALL_RETURN_VOID(env, napi_create_int32(env, err, &code));
NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, error, "code", code));
NAPI_CALL_RETURN_VOID(env, napi_throw(env, error));
}
napi_value JsUtils::ToError(napi_env env, int32_t code, const std::string &msg)
{
SELECTION_HILOGD("ToError start");
napi_value errorObj;
NAPI_CALL(env, napi_create_object(env, &errorObj));
napi_value errorCode = nullptr;
NAPI_CALL(env, napi_create_int32(env, Convert(code), &errorCode));
napi_value errorMessage = nullptr;
std::string errMsg = ToMessage(Convert(code)) + " " + msg;
NAPI_CALL(env, napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &errorMessage));
NAPI_CALL(env, napi_set_named_property(env, errorObj, "code", errorCode));
NAPI_CALL(env, napi_set_named_property(env, errorObj, "message", errorMessage));
SELECTION_HILOGD("ToError end");
return errorObj;
}
int32_t JsUtils::Convert(int32_t code)
{
SELECTION_HILOGD("Convert start.");
auto iter = ERROR_CODE_MAP.find(code);
if (iter != ERROR_CODE_MAP.end()) {
SELECTION_HILOGD("ErrorCode: %{public}d", iter->second);
return iter->second;
}
SELECTION_HILOGD("Convert end.");
return ERROR_CODE_QUERY_FAILED;
}
int32_t JsUtils::ConvertServiceErrorToJs(int32_t errCode)
{
SELECTION_HILOGD("ConvertServiceErrorToJs start.");
auto iter = SERVICE_ERROR_CODE_MAP.find(errCode);
if (iter != SERVICE_ERROR_CODE_MAP.end()) {
SELECTION_HILOGD("ErrorCode: %{public}d", iter->second);
return iter->second;
}
SELECTION_HILOGD("ConvertServiceErrorToJs end.");
return EXCEPTION_SELECTION_SERVICE;
}
const std::string JsUtils::ToMessage(int32_t code)
{
SELECTION_HILOGD("ToMessage start");
auto iter = ERROR_CODE_CONVERT_MESSAGE_MAP.find(code);
if (iter != ERROR_CODE_CONVERT_MESSAGE_MAP.end()) {
SELECTION_HILOGD("ErrorMessage: %{public}s", (iter->second).c_str());
return iter->second;
}
return "error is out of definition.";
}
bool JsUtils::Equals(napi_env env, napi_value value, napi_ref copy, std::thread::id threadId)
{
if (copy == nullptr) {
return value == nullptr;
}
if (threadId != std::this_thread::get_id()) {
SELECTION_HILOGD("napi_value can not be compared");
return false;
}
napi_value copyValue = nullptr;
napi_get_reference_value(env, copy, ©Value);
bool isEquals = false;
napi_strict_equals(env, value, copyValue, &isEquals);
SELECTION_HILOGD("value compare result: %{public}d", isEquals);
return isEquals;
}
void *JsUtils::GetNativeSelf(napi_env env, napi_callback_info info)
{
size_t argc = ARGC_MAX;
void *native = nullptr;
napi_value self = nullptr;
napi_value argv[ARGC_MAX] = { nullptr };
napi_status status = napi_invalid_arg;
napi_get_cb_info(env, info, &argc, argv, &self, nullptr);
CHECK_RETURN((self != nullptr && argc <= ARGC_MAX), "napi_get_cb_info failed!", nullptr);
status = napi_unwrap(env, self, &native);
CHECK_RETURN((status == napi_ok && native != nullptr), "napi_unwrap failed!", nullptr);
return native;
}
napi_status JsUtils::GetValue(napi_env env, napi_value in, int32_t &out)
{
SELECTION_HILOGD("napi_value -> int32_t ");
napi_valuetype type = napi_undefined;
napi_status status = napi_typeof(env, in, &type);
CHECK_RETURN((status == napi_ok) && (type == napi_number), "invalid type", napi_generic_failure);
return napi_get_value_int32(env, in, &out);
}
napi_status JsUtils::GetValue(napi_env env, napi_value in, uint32_t &out)
{
napi_valuetype type = napi_undefined;
napi_status status = napi_typeof(env, in, &type);
CHECK_RETURN((status == napi_ok) && (type == napi_number), "invalid type", napi_generic_failure);
return napi_get_value_uint32(env, in, &out);
}
napi_status JsUtils::GetValue(napi_env env, napi_value in, bool &out)
{
napi_valuetype type = napi_undefined;
napi_status status = napi_typeof(env, in, &type);
CHECK_RETURN((status == napi_ok) && (type == napi_boolean), "invalid type", napi_generic_failure);
return napi_get_value_bool(env, in, &out);
}
napi_status JsUtils::GetValue(napi_env env, napi_value in, double &out)
{
napi_valuetype type = napi_undefined;
napi_status status = napi_typeof(env, in, &type);
CHECK_RETURN((status == napi_ok) && (type == napi_number), "invalid double type", napi_generic_failure);
return napi_get_value_double(env, in, &out);
}
napi_status JsUtils::GetValue(napi_env env, napi_value in, std::string &out)
{
SELECTION_HILOGD("JsUtils get string value in.");
napi_valuetype type = napi_undefined;
napi_status status = napi_typeof(env, in, &type);
CHECK_RETURN((status == napi_ok) && (type == napi_string), "invalid type", napi_generic_failure);
size_t maxLen = STR_MAX_LENGTH;
status = napi_get_value_string_utf8(env, in, NULL, 0, &maxLen);
if (maxLen <= 0) {
return status;
}
SELECTION_HILOGD("napi_value -> std::string get length %{public}zu", maxLen);
char *buf = new (std::nothrow) char[maxLen + STR_TAIL_LENGTH];
if (buf != nullptr) {
size_t len = 0;
status = napi_get_value_string_utf8(env, in, buf, maxLen + STR_TAIL_LENGTH, &len);
if (status == napi_ok) {
buf[len] = 0;
out = std::string(buf);
}
delete[] buf;
} else {
status = napi_generic_failure;
}
return status;
}
napi_status JsUtils::GetValue(napi_env env, napi_value in, const std::string &type, napi_value &out)
{
napi_valuetype valueType = napi_undefined;
napi_status status = napi_typeof(env, in, &valueType);
if ((status == napi_ok) && (valueType == napi_object)) {
status = napi_get_named_property(env, in, type.c_str(), &out);
return status;
}
return napi_generic_failure;
}
napi_status JsUtils::GetValue(napi_env env, const std::string &in, napi_value &out)
{
return napi_create_string_utf8(env, in.c_str(), in.size(), &out);
}
napi_value JsUtils::GetValue(napi_env env, const std::vector<uint8_t> &in)
{
void *data = nullptr;
napi_value arrayBuffer = nullptr;
size_t length = in.size();
NAPI_CALL(env, napi_create_arraybuffer(env, length, &data, &arrayBuffer));
CHECK_RETURN(length != 0, "Data size is 0.", arrayBuffer);
if (memcpy_s(data, length, reinterpret_cast<const void *>(in.data()), length) != 0) {
return nullptr;
}
return arrayBuffer;
}
napi_status JsUtils::GetValue(napi_env env, napi_value in, std::vector<uint8_t> &out)
{
size_t length = 0;
void *data = nullptr;
auto status = napi_get_arraybuffer_info(env, in, &data, &length);
if (status != napi_ok) {
SELECTION_HILOGE("Get ArrayBuffer info failed!");
return status;
}
if (data == nullptr && length == 0) {
SELECTION_HILOGE("Empty ArrayBuffer.");
out.clear();
return napi_ok;
}
if (data == nullptr) {
SELECTION_HILOGE("ArrayBuffer data is nullptr!");
return napi_generic_failure;
}
SELECTION_HILOGD("ArrayBuffer data size: %{public}zu.", length);
out.assign(reinterpret_cast<const uint8_t *>(data), reinterpret_cast<const uint8_t *>(data) + length);
return napi_ok;
}
}
}