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