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