1 /*
2  * Copyright (c) 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 "core/components_ng/common_napi_utils/common_napi_utils.h"
17 
18 #include <cstddef>
19 
20 #include "napi/native_api.h"
21 #include "napi/native_node_api.h"
22 #include "securec.h"
23 
24 #include "base/json/json_util.h"
25 #include "core/common/card_scope.h"
26 #include "core/common/container.h"
27 
28 namespace OHOS::Ace {
29 namespace {
30 constexpr uint32_t COLOR_ALPHA_OFFSET = 24;
31 constexpr uint32_t COLOR_ALPHA_VALUE = 0xFF000000;
32 constexpr uint32_t ERROR_COLOR_ID = -1;
33 
34 enum class ResourceType : uint32_t {
35     COLOR = 10001,
36     FLOAT,
37     STRING,
38     PLURAL,
39     BOOLEAN,
40     INTARRAY,
41     INTEGER,
42     PATTERN,
43     STRARRAY,
44     MEDIA = 20000,
45     RAWFILE = 30000
46 };
47 } // namespace
48 
NapiAsyncEvnet(napi_env env,napi_value callback)49 NapiAsyncEvnet::NapiAsyncEvnet(napi_env env, napi_value callback)
50 {
51     env_ = env;
52     napi_create_reference(env_, callback, 1, &ref_);
53 }
54 
~NapiAsyncEvnet()55 NapiAsyncEvnet::~NapiAsyncEvnet()
56 {
57     napi_delete_reference(env_, ref_);
58 }
59 
Call(int32_t argc,napi_value * argv)60 napi_value NapiAsyncEvnet::Call(int32_t argc, napi_value* argv)
61 {
62     napi_value result = nullptr;
63     napi_handle_scope scope;
64     napi_open_handle_scope(env_, &scope);
65     if (scope == nullptr) {
66         napi_close_handle_scope(env_, scope);
67         return result;
68     }
69     napi_value callback = nullptr;
70     napi_get_reference_value(env_, ref_, &callback);
71     napi_value undefined = nullptr;
72     napi_get_undefined(env_, &undefined);
73     napi_call_function(env_, undefined, callback, argc, argv, &result);
74     napi_close_handle_scope(env_, scope);
75     return result;
76 }
77 
GetEnv()78 napi_env NapiAsyncEvnet::GetEnv()
79 {
80     return env_;
81 }
82 
CreateInt32(napi_env env,int32_t code)83 napi_value CommonNapiUtils::CreateInt32(napi_env env, int32_t code)
84 {
85     napi_value value = nullptr;
86     if (napi_create_int32(env, code, &value) != napi_ok) {
87         return nullptr;
88     }
89     return value;
90 }
91 
GetCInt32(napi_value value,napi_env env)92 int32_t CommonNapiUtils::GetCInt32(napi_value value, napi_env env)
93 {
94     int32_t num;
95     napi_get_value_int32(env, value, &num);
96     return num;
97 }
98 
GetCInt64(napi_value value,napi_env env)99 int64_t CommonNapiUtils::GetCInt64(napi_value value, napi_env env)
100 {
101     int64_t num;
102     napi_get_value_int64(env, value, &num);
103     return num;
104 }
105 
CreateBoolean(napi_env env,bool value)106 napi_value CommonNapiUtils::CreateBoolean(napi_env env, bool value)
107 {
108     napi_value jsValue = nullptr;
109     NAPI_CALL(env, napi_get_boolean(env, value, &jsValue));
110     return jsValue;
111 }
112 
GetBool(napi_env env,napi_value value)113 bool CommonNapiUtils::GetBool(napi_env env, napi_value value)
114 {
115     bool boolValue = false;
116     napi_status ret = napi_get_value_bool(env, value, &boolValue);
117     if (ret == napi_ok) {
118         return boolValue;
119     }
120     return false;
121 }
122 
CreateDouble(napi_env env,double value)123 napi_value CommonNapiUtils::CreateDouble(napi_env env, double value)
124 {
125     napi_value jsValue = nullptr;
126     NAPI_CALL(env, napi_create_double(env, value, &jsValue));
127     return jsValue;
128 }
129 
GetDouble(napi_env env,napi_value value)130 double CommonNapiUtils::GetDouble(napi_env env, napi_value value)
131 {
132     double numberValue = 0;
133     napi_status ret = napi_get_value_double(env, value, &numberValue);
134     if (ret == napi_ok) {
135         return numberValue;
136     }
137     return 0;
138 }
139 
GetCString(napi_value value,napi_env env,char * buffer,size_t bufSize)140 size_t CommonNapiUtils::GetCString(napi_value value, napi_env env, char* buffer, size_t bufSize)
141 {
142     size_t valueLength;
143     napi_get_value_string_utf8(env, value, buffer, bufSize, &valueLength);
144     return valueLength;
145 }
146 
CreateStringUtf8(napi_env env,const std::string & str)147 napi_value CommonNapiUtils::CreateStringUtf8(napi_env env, const std::string& str)
148 {
149     napi_value value = nullptr;
150     if (napi_create_string_utf8(env, str.c_str(), strlen(str.c_str()), &value) != napi_ok) {
151         return nullptr;
152     }
153     return value;
154 }
155 
GetStringFromValueUtf8(napi_env env,napi_value value)156 std::string CommonNapiUtils::GetStringFromValueUtf8(napi_env env, napi_value value)
157 {
158     static constexpr size_t max_length = 2048;
159     if (GetValueType(env, value) != napi_string) {
160         return {};
161     }
162 
163     std::string result;
164     size_t stringLength = 0;
165     NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, nullptr, 0, &stringLength), result);
166     if (stringLength == 0 || stringLength > max_length) {
167         return result;
168     }
169 
170     auto deleter = [](char* s) { free(reinterpret_cast<void*>(s)); };
171     char* strTmp = static_cast<char*>(malloc(stringLength + 1));
172     if (strTmp == nullptr) {
173         return result;
174     }
175     std::unique_ptr<char, decltype(deleter)> str(strTmp, deleter);
176     if (memset_s(str.get(), stringLength + 1, 0, stringLength + 1) != EOK) {
177         return result;
178     }
179     size_t length = 0;
180     NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, str.get(), stringLength + 1, &length), result);
181     if (length > 0) {
182         result.append(str.get(), length);
183     }
184     return result;
185 }
186 
CreateNull(napi_env env)187 napi_value CommonNapiUtils::CreateNull(napi_env env)
188 {
189     napi_value jsNull = nullptr;
190     NAPI_CALL(env, napi_get_null(env, &jsNull));
191     return jsNull;
192 }
193 
CreateUndefined(napi_env env)194 napi_value CommonNapiUtils::CreateUndefined(napi_env env)
195 {
196     napi_value undefined = nullptr;
197     NAPI_CALL(env, napi_get_undefined(env, &undefined));
198     return undefined;
199 }
200 
GetValueType(napi_env env,napi_value value)201 napi_valuetype CommonNapiUtils::GetValueType(napi_env env, napi_value value)
202 {
203     if (value == nullptr) {
204         return napi_undefined;
205     }
206 
207     napi_valuetype valueType = napi_undefined;
208     NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), napi_undefined);
209     return valueType;
210 }
211 
CreateObject(napi_env env)212 napi_value CommonNapiUtils::CreateObject(napi_env env)
213 {
214     napi_value object = nullptr;
215     NAPI_CALL(env, napi_create_object(env, &object));
216     return object;
217 }
218 
DefineProperties(napi_env env,napi_value object,const std::initializer_list<napi_property_descriptor> & properties)219 void CommonNapiUtils::DefineProperties(
220     napi_env env, napi_value object, const std::initializer_list<napi_property_descriptor>& properties)
221 {
222     napi_property_descriptor descriptors[properties.size()];
223     std::copy(properties.begin(), properties.end(), descriptors);
224 
225     (void)napi_define_properties(env, object, properties.size(), descriptors);
226 }
227 
DefineClass(napi_env env,napi_value exports,const std::initializer_list<napi_property_descriptor> & properties,const std::string & className)228 void CommonNapiUtils::DefineClass(napi_env env, napi_value exports,
229     const std::initializer_list<napi_property_descriptor>& properties, const std::string& className)
230 {
231     auto constructor = [](napi_env env, napi_callback_info info) -> napi_value {
232         napi_value thisVal = nullptr;
233         NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVal, nullptr));
234 
235         return thisVal;
236     };
237 
238     napi_value jsConstructor = nullptr;
239 
240     napi_property_descriptor descriptors[properties.size()];
241     std::copy(properties.begin(), properties.end(), descriptors);
242 
243     NAPI_CALL_RETURN_VOID(env, napi_define_class(env, className.c_str(), NAPI_AUTO_LENGTH, constructor, nullptr,
244                                    properties.size(), descriptors, &jsConstructor));
245 
246     SetNamedProperty(env, exports, className, jsConstructor);
247 }
248 
SetNamedProperty(napi_env env,napi_value object,const std::string & propertyName,napi_value value)249 void CommonNapiUtils::SetNamedProperty(
250     napi_env env, napi_value object, const std::string& propertyName, napi_value value)
251 {
252     if (GetValueType(env, object) != napi_object) {
253         return;
254     }
255 
256     napi_set_named_property(env, object, propertyName.c_str(), value);
257 }
258 
GetNamedProperty(napi_env env,napi_value object,const std::string & propertyName)259 napi_value CommonNapiUtils::GetNamedProperty(napi_env env, napi_value object, const std::string& propertyName)
260 {
261     if (GetValueType(env, object) != napi_object) {
262         return CreateUndefined(env);
263     }
264 
265     napi_value value = nullptr;
266     NAPI_CALL(env, napi_get_named_property(env, object, propertyName.c_str(), &value));
267     return value;
268 }
269 
HasNamedProperty(napi_env env,napi_value object,const std::string & propertyName)270 bool CommonNapiUtils::HasNamedProperty(napi_env env, napi_value object, const std::string& propertyName)
271 {
272     if (GetValueType(env, object) != napi_object) {
273         return false;
274     }
275 
276     bool hasProperty = false;
277     NAPI_CALL_BASE(env, napi_has_named_property(env, object, propertyName.c_str(), &hasProperty), false);
278     return hasProperty;
279 }
280 
GetPropertyNames(napi_env env,napi_value object,std::vector<std::string> & nameList)281 bool CommonNapiUtils::GetPropertyNames(napi_env env, napi_value object, std::vector<std::string>& nameList)
282 {
283     napi_value names = nullptr;
284     NAPI_CALL_BASE(env, napi_get_property_names(env, object, &names), false);
285     uint32_t length = 0;
286     NAPI_CALL_BASE(env, napi_get_array_length(env, names, &length), false);
287     for (uint32_t index = 0; index < length; ++index) {
288         napi_value name = nullptr;
289         if (napi_get_element(env, names, index, &name) != napi_ok) {
290             continue;
291         }
292         if (GetValueType(env, name) != napi_string) {
293             continue;
294         }
295         nameList.emplace_back(GetStringFromValueUtf8(env, name));
296     }
297     return true;
298 }
299 
IsArray(napi_env env,napi_value value)300 bool CommonNapiUtils::IsArray(napi_env env, napi_value value)
301 {
302     bool isArray = false;
303     napi_status ret = napi_is_array(env, value, &isArray);
304     if (ret == napi_ok) {
305         return isArray;
306     }
307     return false;
308 }
309 
CreateArray(napi_env env)310 napi_value CommonNapiUtils::CreateArray(napi_env env)
311 {
312     napi_value value = nullptr;
313     NAPI_CALL(env, napi_create_array(env, &value));
314     return value;
315 }
316 
SetSelementToArray(napi_env env,napi_value array,int index,napi_value value)317 void CommonNapiUtils::SetSelementToArray(napi_env env, napi_value array, int index, napi_value value)
318 {
319     napi_set_element(env, array, index, value);
320 }
321 
ColorAlphaAdapt(uint32_t origin)322 uint32_t ColorAlphaAdapt(uint32_t origin)
323 {
324     uint32_t result = origin;
325     if ((origin >> COLOR_ALPHA_OFFSET) == 0) {
326         result = origin | COLOR_ALPHA_VALUE;
327     }
328     return result;
329 }
330 
GetThemeConstants(napi_env env,napi_value value)331 RefPtr<ThemeConstants> CommonNapiUtils::GetThemeConstants(napi_env env, napi_value value)
332 {
333     napi_value jsBundleName = CommonNapiUtils::GetNamedProperty(env, value, "bundleName");
334     napi_value jsModuleName = CommonNapiUtils::GetNamedProperty(env, value, "moduleName");
335     std::string bundleName = CommonNapiUtils::GetStringFromValueUtf8(env, jsBundleName);
336     std::string moduleName = CommonNapiUtils::GetStringFromValueUtf8(env, jsModuleName);
337 
338     auto cardId = CardScope::CurrentId();
339     if (cardId != INVALID_CARD_ID) {
340         auto container = Container::Current();
341         auto weak = container->GetCardPipeline(cardId);
342         auto cardPipelineContext = weak.Upgrade();
343         CHECK_NULL_RETURN(cardPipelineContext, nullptr);
344         auto cardThemeManager = cardPipelineContext->GetThemeManager();
345         CHECK_NULL_RETURN(cardThemeManager, nullptr);
346         return cardThemeManager->GetThemeConstants(bundleName, moduleName);
347     }
348 
349     auto container = Container::Current();
350     CHECK_NULL_RETURN(container, nullptr);
351     auto pipelineContext = container->GetPipelineContext();
352     CHECK_NULL_RETURN(pipelineContext, nullptr);
353     auto themeManager = pipelineContext->GetThemeManager();
354     CHECK_NULL_RETURN(themeManager, nullptr);
355     return themeManager->GetThemeConstants(bundleName, moduleName);
356 }
357 
PutJsonValue(napi_env env,napi_value value,std::string & key)358 std::unique_ptr<JsonValue> CommonNapiUtils::PutJsonValue(napi_env env, napi_value value, std::string& key)
359 {
360     auto result = JsonUtil::Create(true);
361     napi_valuetype valueType = CommonNapiUtils::GetValueType(env, value);
362     switch (valueType) {
363         case napi_boolean: {
364             bool boolValue = CommonNapiUtils::GetBool(env, value);
365             result->Put(key.c_str(), boolValue);
366             break;
367         }
368         case napi_number: {
369             int32_t intValue = CommonNapiUtils::GetCInt32(value, env);
370             result->Put(key.c_str(), intValue);
371             break;
372         }
373         case napi_string: {
374             std::string stringValue = CommonNapiUtils::GetStringFromValueUtf8(env, value);
375             result->Put(key.c_str(), stringValue.c_str());
376             break;
377         }
378         default:
379             break;
380     }
381     return result;
382 }
383 
ParseColorFromResource(napi_env env,napi_value value,Color & colorResult)384 bool CommonNapiUtils::ParseColorFromResource(napi_env env, napi_value value, Color& colorResult)
385 {
386     auto themeConstants = GetThemeConstants(env, value);
387     CHECK_NULL_RETURN(themeConstants, false);
388 
389     napi_value jsColorId = CommonNapiUtils::GetNamedProperty(env, value, "id");
390     napi_value jsParams = CommonNapiUtils::GetNamedProperty(env, value, "params");
391     uint32_t colorId = CommonNapiUtils::GetCInt32(jsColorId, env);
392     if (!CommonNapiUtils::IsArray(env, jsParams)) {
393         return false;
394     }
395     if (colorId == ERROR_COLOR_ID) {
396         uint32_t length;
397         napi_get_array_length(env, jsParams, &length);
398         auto jsonArray = JsonUtil::CreateArray(true);
399         for (uint32_t i = 0; i < length; i++) {
400             napi_value elementValue;
401             napi_get_element(env, jsParams, i, &elementValue);
402             std::string key = std::to_string(i);
403             jsonArray->Put(key.c_str(), PutJsonValue(env, elementValue, key));
404         }
405         const char* jsonKey = std::to_string(0).c_str();
406         std::string colorName = jsonArray->GetValue(jsonKey)->GetValue(jsonKey)->ToString();
407         colorResult = themeConstants->GetColorByName(colorName);
408         return true;
409     }
410     napi_value jsType = GetNamedProperty(env, value, "type");
411     napi_valuetype valueType = GetValueType(env, jsType);
412     if (valueType != napi_null && valueType == napi_number &&
413         valueType == static_cast<uint32_t>(ResourceType::STRING)) {
414         auto value = themeConstants->GetString(CommonNapiUtils::GetCInt32(jsType, env));
415         return Color::ParseColorString(value, colorResult);
416     }
417     if (valueType != napi_null && valueType == napi_number &&
418         valueType == static_cast<uint32_t>(ResourceType::INTEGER)) {
419         auto value = themeConstants->GetInt(CommonNapiUtils::GetCInt32(jsType, env));
420         colorResult = Color(ColorAlphaAdapt(value));
421         return true;
422     }
423     colorResult = themeConstants->GetColor(colorId);
424     return true;
425 }
426 
ParseColor(napi_env env,napi_value value,Color & result)427 bool CommonNapiUtils::ParseColor(napi_env env, napi_value value, Color& result)
428 {
429     napi_valuetype valueType = CommonNapiUtils::GetValueType(env, value);
430     if (valueType != napi_number && valueType != napi_string && valueType != napi_object) {
431         return false;
432     }
433     if (valueType == napi_number) {
434         int32_t colorId = CommonNapiUtils::GetCInt32(value, env);
435         result = Color(ColorAlphaAdapt((uint32_t)colorId));
436         return true;
437     }
438     if (valueType == napi_string) {
439         std::string colorString = CommonNapiUtils::GetStringFromValueUtf8(env, value);
440         return Color::ParseColorString(colorString, result);
441     }
442     return ParseColorFromResource(env, value, result);
443 }
444 
GetDimensionResult(napi_env env,napi_value value,CalcDimension & result)445 bool CommonNapiUtils::GetDimensionResult(napi_env env, napi_value value, CalcDimension& result)
446 {
447     napi_valuetype valueType = GetValueType(env, value);
448     if (valueType == napi_number) {
449         double radius = GetDouble(env, value);
450         result = CalcDimension(radius, DimensionUnit::VP);
451         return true;
452     }
453     if (valueType == napi_string) {
454         std::string dimensionString = GetStringFromValueUtf8(env, value);
455         result = StringUtils::StringToCalcDimension(dimensionString, false, DimensionUnit::VP);
456         return true;
457     }
458     auto themeConstants = GetThemeConstants(env, value);
459     CHECK_NULL_RETURN(themeConstants, false);
460 
461     napi_value jsDimensionId = GetNamedProperty(env, value, "id");
462     napi_value jsParams = GetNamedProperty(env, value, "params");
463     uint32_t dimensionId = GetCInt32(jsDimensionId, env);
464     if (!IsArray(env, jsParams)) {
465         return false;
466     }
467     if (dimensionId == ERROR_COLOR_ID) {
468         uint32_t length;
469         napi_get_array_length(env, jsParams, &length);
470         auto jsonArray = JsonUtil::CreateArray(true);
471         for (uint32_t i = 0; i < length; i++) {
472             napi_value elementValue;
473             napi_get_element(env, jsParams, i, &elementValue);
474             std::string key = std::to_string(i);
475             jsonArray->Put(key.c_str(), PutJsonValue(env, elementValue, key));
476         }
477         const char* jsonKey = std::to_string(0).c_str();
478         std::string dimensionName = jsonArray->GetValue(jsonKey)->GetValue(jsonKey)->ToString();
479         result = themeConstants->GetDimensionByName(dimensionName);
480         return true;
481     }
482 
483     napi_value jsType = GetNamedProperty(env, value, "type");
484     napi_valuetype temp = GetValueType(env, jsType);
485     uint32_t type = GetCInt32(jsType, env);
486     if (temp != napi_null && temp == napi_number && type == static_cast<uint32_t>(ResourceType::STRING)) {
487         auto dimensionValue = themeConstants->GetString(dimensionId);
488         result = StringUtils::StringToCalcDimension(dimensionValue, false, DimensionUnit::VP);
489         return true;
490     }
491     if (temp != napi_null && temp == napi_number && type == static_cast<uint32_t>(ResourceType::INTEGER)) {
492         auto dimensionValue = std::to_string(themeConstants->GetInt(dimensionId));
493         result = StringUtils::StringToDimensionWithUnit(dimensionValue, DimensionUnit::VP);
494         return true;
495     }
496 
497     result = themeConstants->GetDimension(dimensionId);
498     return true;
499 }
500 } // namespace OHOS::Ace
501