1 /*
2 * Copyright (c) 2024 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 "ext_napi_utils.h"
17 #include <memory>
18 #include <cstddef>
19
20 #include "napi/native_api.h"
21 #include "napi/native_node_api.h"
22 #include "securec.h"
23 #include "frameworks/base/json/json_util.h"
24 #include "frameworks/core/common/card_scope.h"
25 #include "frameworks/core/common/container.h"
26 #include "core/pipeline/pipeline_base.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
NapiAsyncEvent(napi_env env,napi_value callback)48 NapiAsyncEvent::NapiAsyncEvent(napi_env env, napi_value callback)
49 {
50 env_ = env;
51 napi_create_reference(env_, callback, 1, &ref_);
52 }
53
~NapiAsyncEvent()54 NapiAsyncEvent::~NapiAsyncEvent()
55 {
56 napi_delete_reference(env_, ref_);
57 }
58
Call(int32_t argc,napi_value * argv)59 napi_value NapiAsyncEvent::Call(int32_t argc, napi_value* argv)
60 {
61 napi_value result = nullptr;
62 napi_handle_scope scope;
63 napi_open_handle_scope(env_, &scope);
64 if (scope == nullptr) {
65 napi_close_handle_scope(env_, scope);
66 return result;
67 }
68 napi_value callback = nullptr;
69 napi_get_reference_value(env_, ref_, &callback);
70 napi_value undefined = nullptr;
71 napi_get_undefined(env_, &undefined);
72 napi_call_function(env_, undefined, callback, argc, argv, &result);
73 napi_close_handle_scope(env_, scope);
74 return result;
75 }
76
GetEnv()77 napi_env NapiAsyncEvent::GetEnv()
78 {
79 return env_;
80 }
81
CreateInt32(napi_env env,int32_t code)82 napi_value ExtNapiUtils::CreateInt32(napi_env env, int32_t code)
83 {
84 napi_value value = nullptr;
85 if (napi_create_int32(env, code, &value) != napi_ok) {
86 return nullptr;
87 }
88 return value;
89 }
90
GetCInt32(napi_env env,napi_value value)91 int32_t ExtNapiUtils::GetCInt32(napi_env env, napi_value value)
92 {
93 int32_t num = 0;
94 napi_get_value_int32(env, value, &num);
95 return num;
96 }
97
GetCInt64(napi_env env,napi_value value)98 int64_t ExtNapiUtils::GetCInt64(napi_env env, napi_value value)
99 {
100 int64_t num = 0;
101 napi_get_value_int64(env, value, &num);
102 return num;
103 }
104
GetDouble(napi_env env,napi_value value)105 double ExtNapiUtils::GetDouble(napi_env env, napi_value value)
106 {
107 double numberValue = 0;
108 napi_status ret = napi_get_value_double(env, value, &numberValue);
109 if (ret == napi_ok) {
110 return numberValue;
111 }
112 return 0;
113 }
114
CreateNull(napi_env env)115 napi_value ExtNapiUtils::CreateNull(napi_env env)
116 {
117 napi_value jsNull = nullptr;
118 NAPI_CALL(env, napi_get_null(env, &jsNull));
119 return jsNull;
120 }
121
CreateObject(napi_env env)122 napi_value ExtNapiUtils::CreateObject(napi_env env)
123 {
124 napi_value object = nullptr;
125 NAPI_CALL(env, napi_create_object(env, &object));
126 return object;
127 }
128
CreateDouble(napi_env env,double value)129 napi_value ExtNapiUtils::CreateDouble(napi_env env, double value)
130 {
131 napi_value jsValue = nullptr;
132 NAPI_CALL(env, napi_create_double(env, value, &jsValue));
133 return jsValue;
134 }
135
CreateFunction(napi_env env,const char * utf8name,size_t length,napi_callback cb,void * data)136 napi_value ExtNapiUtils::CreateFunction(napi_env env,
137 const char* utf8name, size_t length,
138 napi_callback cb,
139 void* data)
140 {
141 napi_value jsfuncValue = nullptr;
142 napi_create_function(env, utf8name, length, cb, data, &jsfuncValue);
143 return jsfuncValue;
144 }
145
GetBool(napi_env env,napi_value value)146 bool ExtNapiUtils::GetBool(napi_env env, napi_value value)
147 {
148 bool boolValue = false;
149 napi_status ret = napi_get_value_bool(env, value, &boolValue);
150 if (ret == napi_ok) {
151 return boolValue;
152 }
153 return false;
154 }
155
GetValueType(napi_env env,napi_value value)156 napi_valuetype ExtNapiUtils::GetValueType(napi_env env, napi_value value)
157 {
158 if (value == nullptr) {
159 return napi_undefined;
160 }
161
162 napi_valuetype valueType = napi_undefined;
163 NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), napi_undefined);
164 return valueType;
165 }
166
GetStringFromValueUtf8(napi_env env,napi_value value)167 std::string ExtNapiUtils::GetStringFromValueUtf8(napi_env env, napi_value value)
168 {
169 static constexpr size_t max_length = 2048;
170 if (GetValueType(env, value) != napi_string) {
171 return {};
172 }
173
174 std::string result;
175 size_t stringLength = 0;
176 NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, nullptr, 0, &stringLength), result);
177 if (stringLength == 0 || stringLength > max_length) {
178 return result;
179 }
180
181 auto deleter = [](char* s) { free(reinterpret_cast<void*>(s)); };
182 char* strTmp = static_cast<char*>(malloc(stringLength + 1));
183 if (strTmp == nullptr) {
184 return result;
185 }
186 std::unique_ptr<char, decltype(deleter)> str(strTmp, deleter);
187 if (memset_s(str.get(), stringLength + 1, 0, stringLength + 1) != EOK) {
188 return result;
189 }
190 size_t length = 0;
191 NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, str.get(), stringLength + 1, &length), result);
192 if (length > 0) {
193 result.append(str.get(), length);
194 }
195 return result;
196 }
197
CheckTypeForNapiValue(napi_env env,napi_value param,napi_valuetype expectType)198 bool ExtNapiUtils::CheckTypeForNapiValue(napi_env env, napi_value param, napi_valuetype expectType)
199 {
200 napi_valuetype valueType = napi_undefined;
201
202 if (napi_typeof(env, param, &valueType) != napi_ok) {
203 return false;
204 }
205
206 return valueType == expectType;
207 }
208
ParseLengthMetrics(napi_env env,napi_value param,CalcDimension & result)209 bool ExtNapiUtils::ParseLengthMetrics(napi_env env, napi_value param, CalcDimension& result)
210 {
211 if (CheckTypeForNapiValue(env, param, napi_object)) {
212 napi_value jsValue = GetNamedProperty(env, param, "value");
213 napi_value jsUnit = GetNamedProperty(env, param, "unit");
214 double value = 0;
215 int32_t unit = static_cast<int32_t>(DimensionUnit::VP);
216 if (CheckTypeForNapiValue(env, jsValue, napi_number) && CheckTypeForNapiValue(env, jsUnit, napi_number) &&
217 napi_get_value_double(env, jsValue, &value) == napi_ok &&
218 napi_get_value_int32(env, jsUnit, &unit) == napi_ok && GreatOrEqual(value, 0.0f)) {
219 result = CalcDimension(value, static_cast<DimensionUnit>(unit));
220 return true;
221 }
222 }
223 return false;
224 }
225
GetNamedProperty(napi_env env,napi_value object,const std::string & propertyName)226 napi_value ExtNapiUtils::GetNamedProperty(napi_env env, napi_value object, const std::string& propertyName)
227 {
228 if (GetValueType(env, object) != napi_object) {
229 return CreateUndefined(env);
230 }
231
232 napi_value value = nullptr;
233 NAPI_CALL(env, napi_get_named_property(env, object, propertyName.c_str(), &value));
234 return value;
235 }
236
IsArray(napi_env env,napi_value value)237 bool ExtNapiUtils::IsArray(napi_env env, napi_value value)
238 {
239 bool isArray = false;
240 napi_status ret = napi_is_array(env, value, &isArray);
241 if (ret == napi_ok) {
242 return isArray;
243 }
244 return false;
245 }
246
CreateUndefined(napi_env env)247 napi_value ExtNapiUtils::CreateUndefined(napi_env env)
248 {
249 napi_value undefined = nullptr;
250 NAPI_CALL(env, napi_get_undefined(env, &undefined));
251 return undefined;
252 }
253
ColorAlphaAdapt(uint32_t origin)254 uint32_t ColorAlphaAdapt(uint32_t origin)
255 {
256 uint32_t result = origin;
257 if ((origin >> COLOR_ALPHA_OFFSET) == 0) {
258 result = origin | COLOR_ALPHA_VALUE;
259 }
260 return result;
261 }
262
GetThemeConstants(napi_env env,napi_value value)263 RefPtr<ThemeConstants> ExtNapiUtils::GetThemeConstants(napi_env env, napi_value value)
264 {
265 napi_value jsBundleName = ExtNapiUtils::GetNamedProperty(env, value, "bundleName");
266 napi_value jsModuleName = ExtNapiUtils::GetNamedProperty(env, value, "moduleName");
267 std::string bundleName = ExtNapiUtils::GetStringFromValueUtf8(env, jsBundleName);
268 std::string moduleName = ExtNapiUtils::GetStringFromValueUtf8(env, jsModuleName);
269
270 auto cardId = CardScope::CurrentId();
271 if (cardId != INVALID_CARD_ID) {
272 auto container = Container::Current();
273 CHECK_NULL_RETURN(container, nullptr);
274 auto weak = container->GetCardPipeline(cardId);
275 auto cardPipelineContext = weak.Upgrade();
276 CHECK_NULL_RETURN(cardPipelineContext, nullptr);
277 auto cardThemeManager = cardPipelineContext->GetThemeManager();
278 CHECK_NULL_RETURN(cardThemeManager, nullptr);
279 return cardThemeManager->GetThemeConstants(bundleName, moduleName);
280 }
281
282 auto container = Container::CurrentSafely();
283 CHECK_NULL_RETURN(container, nullptr);
284 auto pipelineContext = container->GetPipelineContext();
285 CHECK_NULL_RETURN(pipelineContext, nullptr);
286 auto themeManager = pipelineContext->GetThemeManager();
287 CHECK_NULL_RETURN(themeManager, nullptr);
288 return themeManager->GetThemeConstants(bundleName, moduleName);
289 }
290
ParseColor(napi_env env,napi_value value,Color & result)291 bool ExtNapiUtils::ParseColor(napi_env env, napi_value value, Color& result)
292 {
293 napi_valuetype valueType = ExtNapiUtils::GetValueType(env, value);
294 if (valueType != napi_number && valueType != napi_string && valueType != napi_object) {
295 return false;
296 }
297 if (valueType == napi_number) {
298 int32_t colorId = ExtNapiUtils::GetCInt32(env, value);
299 result = Color(ColorAlphaAdapt(static_cast<uint32_t>(colorId)));
300 return true;
301 }
302 if (valueType == napi_string) {
303 std::string colorString = ExtNapiUtils::GetStringFromValueUtf8(env, value);
304 return Color::ParseColorString(colorString, result);
305 }
306 return ParseColorFromResource(env, value, result);
307 }
308
ParseColorFromResource(napi_env env,napi_value value,Color & colorResult)309 bool ExtNapiUtils::ParseColorFromResource(napi_env env, napi_value value, Color& colorResult)
310 {
311 auto themeConstants = GetThemeConstants(env, value);
312 CHECK_NULL_RETURN(themeConstants, false);
313
314 napi_value jsColorId = ExtNapiUtils::GetNamedProperty(env, value, "id");
315 napi_value jsParams = ExtNapiUtils::GetNamedProperty(env, value, "params");
316 uint32_t colorId = static_cast<uint32_t>(ExtNapiUtils::GetCInt32(env, jsColorId));
317 if (!ExtNapiUtils::IsArray(env, jsParams)) {
318 return false;
319 }
320 if (colorId == ERROR_COLOR_ID) {
321 uint32_t length;
322 napi_get_array_length(env, jsParams, &length);
323 auto jsonArray = JsonUtil::CreateArray(true);
324 for (uint32_t i = 0; i < length; i++) {
325 napi_value elementValue;
326 napi_get_element(env, jsParams, i, &elementValue);
327 std::string key = std::to_string(i);
328 jsonArray->Put(key.c_str(), PutJsonValue(env, elementValue, key));
329 }
330 std::string strKey = std::to_string(0);
331 std::string colorName = jsonArray->GetValue(strKey.c_str())->GetValue(strKey.c_str())->ToString();
332 colorResult = themeConstants->GetColorByName(colorName);
333 return true;
334 }
335 napi_value jsType = GetNamedProperty(env, value, "type");
336 napi_valuetype valueType = GetValueType(env, jsType);
337 if (valueType != napi_null && valueType == napi_number &&
338 static_cast<uint32_t>(valueType) == static_cast<uint32_t>(ResourceType::STRING)) {
339 auto value = themeConstants->GetString(ExtNapiUtils::GetCInt32(env, jsType));
340 return Color::ParseColorString(value, colorResult);
341 }
342 if (valueType != napi_null && valueType == napi_number &&
343 static_cast<uint32_t>(valueType) == static_cast<uint32_t>(ResourceType::INTEGER)) {
344 auto value = themeConstants->GetInt(ExtNapiUtils::GetCInt32(env, jsType));
345 colorResult = Color(ColorAlphaAdapt(value));
346 return true;
347 }
348 colorResult = themeConstants->GetColor(colorId);
349 return true;
350 }
351
SetNamedProperty(napi_env env,napi_value object,const std::string & propertyName,napi_value value)352 void ExtNapiUtils::SetNamedProperty(napi_env env, napi_value object, const std::string& propertyName, napi_value value)
353 {
354 if (GetValueType(env, object) != napi_object) {
355 return;
356 }
357
358 napi_set_named_property(env, object, propertyName.c_str(), value);
359 }
360
PutJsonValue(napi_env env,napi_value value,std::string & key)361 std::unique_ptr<JsonValue> ExtNapiUtils::PutJsonValue(napi_env env, napi_value value, std::string& key)
362 {
363 auto result = JsonUtil::Create(true);
364 napi_valuetype valueType = ExtNapiUtils::GetValueType(env, value);
365 switch (valueType) {
366 case napi_boolean: {
367 bool boolValue = ExtNapiUtils::GetBool(env, value);
368 result->Put(key.c_str(), boolValue);
369 break;
370 }
371 case napi_number: {
372 int32_t intValue = ExtNapiUtils::GetCInt32(env, value);
373 result->Put(key.c_str(), intValue);
374 break;
375 }
376 case napi_string: {
377 std::string stringValue = ExtNapiUtils::GetStringFromValueUtf8(env, value);
378 result->Put(key.c_str(), stringValue.c_str());
379 break;
380 }
381 default:
382 break;
383 }
384 return result;
385 }
386
ParseColorMetrics(napi_env env,napi_value param,Color & result)387 bool ExtNapiUtils::ParseColorMetrics(napi_env env, napi_value param, Color& result)
388 {
389 if (CheckTypeForNapiValue(env, param, napi_object)) {
390 napi_value jsToNumeric = GetNamedProperty(env, param, "toNumeric");
391 napi_value jsColor;
392 uint32_t colorVal = 0;
393 if (CheckTypeForNapiValue(env, jsToNumeric, napi_function) &&
394 napi_call_function(env, param, jsToNumeric, 0, nullptr, &jsColor) == napi_ok &&
395 CheckTypeForNapiValue(env, jsColor, napi_number) &&
396 napi_get_value_uint32(env, jsColor, &colorVal) == napi_ok) {
397 result.SetValue(colorVal);
398 return true;
399 }
400 }
401 return false;
402 }
403 } // namespace OHOS::Ace
404