1 /*
2  * Copyright (c) 2021-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 "napi_utils.h"
17 #include "core/common/resource/resource_manager.h"
18 
19 namespace OHOS::Ace::Napi {
20 using namespace OHOS::Ace;
21 namespace {
22 
23 const std::regex RESOURCE_APP_STRING_PLACEHOLDER(R"(\%((\d+)(\$)){0,1}([dsf]))", std::regex::icase);
24 constexpr int32_t NAPI_BUF_LENGTH = 256;
25 constexpr int32_t UNKNOWN_RESOURCE_ID = -1;
26 constexpr char BUNDLE_NAME[] = "bundleName";
27 std::vector<std::string> RESOURCE_HEADS = { "app", "sys" };
28 } // namespace
29 
30 static const std::unordered_map<int32_t, std::string> ERROR_CODE_TO_MSG {
31     { ERROR_CODE_PERMISSION_DENIED, "Permission denied. " },
32     { ERROR_CODE_PARAM_INVALID, "Parameter error. " },
33     { ERROR_CODE_SYSTEMCAP_ERROR, "Capability not supported. " },
34     { ERROR_CODE_INTERNAL_ERROR, "Internal error. " },
35     { ERROR_CODE_URI_ERROR, "Uri error. " },
36     { ERROR_CODE_PAGE_STACK_FULL, "Page stack error. " },
37     { ERROR_CODE_URI_ERROR_LITE, "Uri error. " },
38     { ERROR_CODE_DIALOG_CONTENT_ERROR, "Dialog content error. " },
39     { ERROR_CODE_DIALOG_CONTENT_ALREADY_EXIST, "Dialog content already exist. " },
40     { ERROR_CODE_DIALOG_CONTENT_NOT_FOUND, "Dialog content not found. " },
41     { ERROR_CODE_TOAST_NOT_FOUND, "Toast not found. " }
42 };
43 
NapiThrow(napi_env env, const std::string& message, int32_t errCode)44 void NapiThrow(napi_env env, const std::string& message, int32_t errCode)
45 {
46     napi_value code = nullptr;
47     std::string strCode = std::to_string(errCode);
48     napi_create_string_utf8(env, strCode.c_str(), strCode.length(), &code);
49 
50     napi_value msg = nullptr;
51     auto iter = ERROR_CODE_TO_MSG.find(errCode);
52     std::string strMsg = (iter != ERROR_CODE_TO_MSG.end() ? iter->second : "") + message;
53     LOGE("napi throw errCode %d strMsg %s", errCode, strMsg.c_str());
54     napi_create_string_utf8(env, strMsg.c_str(), strMsg.length(), &msg);
55 
56     napi_value error = nullptr;
57     napi_create_error(env, code, msg, &error);
58     napi_throw(env, error);
59 }
60 
ReplaceHolder(std::string& originStr, const std::vector<std::string>& params, uint32_t containCount)61 void ReplaceHolder(std::string& originStr, const std::vector<std::string>& params, uint32_t containCount)
62 {
63     auto size = static_cast<uint32_t>(params.size());
64     if (containCount == size) {
65         return;
66     }
67     std::string::const_iterator start = originStr.begin();
68     std::string::const_iterator end = originStr.end();
69     std::smatch matches;
70     bool shortHolderType = false;
71     bool firstMatch = true;
72     uint32_t searchTime = 0;
73     while (std::regex_search(start, end, matches, RESOURCE_APP_STRING_PLACEHOLDER)) {
74         std::string pos = matches[2];
75         std::string type = matches[4];
76         if (firstMatch) {
77             firstMatch = false;
78             shortHolderType = pos.length() == 0;
79         } else {
80             if (static_cast<uint32_t>(shortHolderType) ^ ((uint32_t)(pos.length() == 0))) {
81                 LOGE("wrong place holder,stop parse string");
82                 return;
83             }
84         }
85 
86         std::string replaceContentStr;
87         std::string::size_type index = 0;
88         if (shortHolderType) {
89             index = static_cast<std::string::size_type>(searchTime + containCount);
90         } else {
91             int32_t indexTmp = StringUtils::StringToInt(pos) + static_cast<int32_t>(containCount) - 1;
92             if (indexTmp >= 0) {
93                 index = static_cast<std::string::size_type>(indexTmp);
94             } else {
95                 LOGE("indexTmp err:%{public}d", indexTmp);
96             }
97         }
98         if (static_cast<uint32_t>(index) < size) {
99             replaceContentStr = params[index];
100         } else {
101             LOGE("index = %{public}d size = %{public}d", static_cast<uint32_t>(index), size);
102         }
103         originStr.replace(matches[0].first - originStr.begin(), matches[0].length(), replaceContentStr);
104         start = originStr.begin() + matches.prefix().length() + replaceContentStr.length();
105         end = originStr.end();
106         searchTime++;
107     }
108 }
109 
GetParamLen(napi_env env, napi_value param)110 size_t GetParamLen(napi_env env, napi_value param)
111 {
112     size_t buffSize = 0;
113     napi_status status = napi_get_value_string_utf8(env, param, nullptr, 0, &buffSize);
114     if (status != napi_ok || buffSize == 0) {
115         return 0;
116     }
117     return buffSize;
118 }
119 
NapiStringToString(napi_env env, napi_value value, std::string& retStr)120 bool NapiStringToString(napi_env env, napi_value value, std::string& retStr)
121 {
122     size_t ret = 0;
123     napi_valuetype valueType = napi_undefined;
124     napi_typeof(env, value, &valueType);
125     if (valueType != napi_string) {
126         return false;
127     }
128     if (GetParamLen(env, value) == 0) {
129         return false;
130     }
131     size_t valueLen = GetParamLen(env, value) + 1;
132     std::unique_ptr<char[]> buffer = std::make_unique<char[]>(valueLen);
133     napi_get_value_string_utf8(env, value, buffer.get(), valueLen, &ret);
134     retStr = buffer.get();
135     return true;
136 }
137 
GetNapiString(napi_env env, napi_value value, std::string& retStr, napi_valuetype& valueType)138 bool GetNapiString(napi_env env, napi_value value, std::string& retStr, napi_valuetype& valueType)
139 {
140     if (NapiStringToString(env, value, retStr)) {
141         return true;
142     }
143     napi_typeof(env, value, &valueType);
144     if (valueType == napi_object) {
145         ResourceInfo recv;
146         if (ParseResourceParam(env, value, recv)) {
147             ParseString(recv, retStr);
148             return true;
149         }
150     }
151     return false;
152 }
153 
GetThemeConstants(const std::optional<std::string>& bundleName = std::nullopt, const std::optional<std::string>& moduleName = std::nullopt)154 RefPtr<ThemeConstants> GetThemeConstants(const std::optional<std::string>& bundleName = std::nullopt,
155     const std::optional<std::string>& moduleName = std::nullopt)
156 {
157     auto container = Container::Current();
158     if (!container) {
159         LOGW("container is null");
160         return nullptr;
161     }
162     auto pipelineContext = container->GetPipelineContext();
163     if (!pipelineContext) {
164         LOGE("pipelineContext is null!");
165         return nullptr;
166     }
167     auto themeManager = pipelineContext->GetThemeManager();
168     if (!themeManager) {
169         LOGE("themeManager is null!");
170         return nullptr;
171     }
172     if (bundleName.has_value() && moduleName.has_value()) {
173         return themeManager->GetThemeConstants(bundleName.value_or(""), moduleName.value_or(""));
174     }
175     return themeManager->GetThemeConstants();
176 }
177 
CreateResourceWrapper(const ResourceInfo& info)178 RefPtr<ResourceWrapper> CreateResourceWrapper(const ResourceInfo& info)
179 {
180     auto bundleName = info.bundleName;
181     auto moduleName = info.moduleName;
182 
183     RefPtr<ResourceAdapter> resourceAdapter = nullptr;
184     RefPtr<ThemeConstants> themeConstants = nullptr;
185     if (SystemProperties::GetResourceDecoupling()) {
186         if (bundleName.has_value() && moduleName.has_value()) {
187             auto resourceObject = AceType::MakeRefPtr<ResourceObject>(bundleName.value_or(""), moduleName.value_or(""));
188             resourceAdapter = ResourceManager::GetInstance().GetOrCreateResourceAdapter(resourceObject);
189         } else {
190             resourceAdapter = ResourceManager::GetInstance().GetResourceAdapter();
191         }
192         if (!resourceAdapter) {
193             return nullptr;
194         }
195     } else {
196         themeConstants = GetThemeConstants(info.bundleName, info.moduleName);
197         if (!themeConstants) {
198             return nullptr;
199         }
200     }
201     auto resourceWrapper = AceType::MakeRefPtr<ResourceWrapper>(themeConstants, resourceAdapter);
202     return resourceWrapper;
203 }
204 
CreateNapiString(napi_env env, const std::string& rawStr)205 napi_value CreateNapiString(napi_env env, const std::string& rawStr)
206 {
207     napi_value retVal = nullptr;
208     napi_create_string_utf8(env, rawStr.c_str(), rawStr.length(), &retVal);
209     return retVal;
210 }
211 
ConvertResourceType(const std::string& typeName, ResourceType& resType)212 bool ConvertResourceType(const std::string& typeName, ResourceType& resType)
213 {
214     static const std::unordered_map<std::string, ResourceType> resTypeMap {
215         { "color", ResourceType::COLOR },
216         { "media", ResourceType::MEDIA },
217         { "float", ResourceType::FLOAT },
218         { "string", ResourceType::STRING },
219         { "plural", ResourceType::PLURAL },
220         { "pattern", ResourceType::PATTERN },
221         { "boolean", ResourceType::BOOLEAN },
222         { "integer", ResourceType::INTEGER },
223         { "strarray", ResourceType::STRARRAY },
224         { "intarray", ResourceType::INTARRAY },
225     };
226     auto it = resTypeMap.find(typeName);
227     if (it == resTypeMap.end()) {
228         return false;
229     }
230     resType = it->second;
231     return true;
232 }
233 
ParseDollarResource( napi_env env, napi_value value, ResourceType& resType, std::string& resName, std::string& moduleName)234 bool ParseDollarResource(
235     napi_env env, napi_value value, ResourceType& resType, std::string& resName, std::string& moduleName)
236 {
237     napi_valuetype valueType = napi_undefined;
238     napi_typeof(env, value, &valueType);
239     if (valueType != napi_string) {
240         return false;
241     }
242     std::string resPath;
243     if (!GetNapiString(env, value, resPath, valueType)) {
244         return false;
245     }
246     std::vector<std::string> tokens;
247     StringUtils::StringSplitter(resPath, '.', tokens);
248     // $r format like app.xxx.xxx, has 3 paragraph
249     if (static_cast<int32_t>(tokens.size()) != 3) {
250         return false;
251     }
252     std::string maybeModuleName = tokens[0];
253     // [*] or app/hsp at least has 3 chars
254     if (maybeModuleName.size() < 3) {
255         return false;
256     }
257     char begin = *maybeModuleName.begin();
258     char end = maybeModuleName.at(maybeModuleName.size() - 1);
259     bool headCheckPass = false;
260     if (begin == '[' && end == ']') {
261         // moduleName not include 2 brackets
262         moduleName = maybeModuleName.substr(1, maybeModuleName.size() - 2);
263         headCheckPass = true;
264     }
265     if (std::find(RESOURCE_HEADS.begin(), RESOURCE_HEADS.end(), tokens[0]) == RESOURCE_HEADS.end() && !headCheckPass) {
266         return false;
267     }
268     if (!ConvertResourceType(tokens[1], resType)) {
269         return false;
270     }
271     resName = resPath;
272     return true;
273 }
274 
PreFixEmptyBundleName(napi_env env, napi_value value)275 void PreFixEmptyBundleName(napi_env env, napi_value value)
276 {
277     napi_value bundleNameNApi = nullptr;
278     if (napi_get_named_property(env, value, BUNDLE_NAME, &bundleNameNApi) != napi_ok) {
279         return;
280     }
281     std::string bundleName;
282     NapiStringToString(env, bundleNameNApi, bundleName);
283     if (bundleName.empty()) {
284         auto container = Container::CurrentSafely();
285         CHECK_NULL_VOID(container);
286         bundleName = container->GetBundleName();
287         bundleNameNApi = CreateNapiString(env, bundleName);
288         napi_set_named_property(env, value, BUNDLE_NAME, bundleNameNApi);
289     }
290 }
291 
CheckResourceStruct(napi_env env, napi_value value)292 ResourceStruct CheckResourceStruct(napi_env env, napi_value value)
293 {
294     napi_value idNApi = nullptr;
295     napi_valuetype valueType = napi_undefined;
296     napi_typeof(env, value, &valueType);
297     if (valueType != napi_object) {
298         return ResourceStruct::CONSTANT;
299     }
300     if (napi_get_named_property(env, value, "id", &idNApi) != napi_ok) {
301         return ResourceStruct::CONSTANT;
302     }
303     napi_typeof(env, idNApi, &valueType);
304     if (valueType == napi_string) {
305         return ResourceStruct::DYNAMIC_V1;
306     }
307     if (valueType == napi_number) {
308         int32_t id = 0;
309         napi_get_value_int32(env, idNApi, &id);
310         if (id == UNKNOWN_RESOURCE_ID) {
311             return ResourceStruct::DYNAMIC_V2;
312         }
313     }
314     return ResourceStruct::CONSTANT;
315 }
316 
CompleteResourceParam(napi_env env, napi_value value)317 void CompleteResourceParam(napi_env env, napi_value value)
318 {
319     PreFixEmptyBundleName(env, value);
320     ResourceStruct resourceStruct = CheckResourceStruct(env, value);
321     switch (resourceStruct) {
322         case ResourceStruct::CONSTANT:
323             return;
324         case ResourceStruct::DYNAMIC_V1:
325             CompleteResourceParamV1(env, value);
326             return;
327         case ResourceStruct::DYNAMIC_V2:
328             CompleteResourceParamV2(env, value);
329             return;
330         default:
331             return;
332     }
333 }
334 
CompleteResourceParamV1(napi_env env, napi_value value)335 void CompleteResourceParamV1(napi_env env, napi_value value)
336 {
337     napi_value idNApi = nullptr;
338     napi_valuetype valueType = napi_undefined;
339     napi_typeof(env, value, &valueType);
340     if (valueType != napi_object) {
341         return;
342     }
343     if (napi_get_named_property(env, value, "id", &idNApi) != napi_ok) {
344         return;
345     }
346     std::string resName;
347     std::string moduleName;
348     ResourceType resType;
349     if (!ParseDollarResource(env, idNApi, resType, resName, moduleName)) {
350         return;
351     }
352     bool hasProperty = false;
353     napi_value typeIdNApi = nullptr;
354     napi_value resourceIdNApi = nullptr;
355     napi_value typeKeyNApi = CreateNapiString(env, "type");
356     napi_value defaultNameNApi = CreateNapiString(env, "");
357     napi_value bundleNameKeyNApi = CreateNapiString(env, "bundleName");
358     napi_value moduleNameKeyNApi = CreateNapiString(env, "moduleName");
359     napi_create_int32(env, UNKNOWN_RESOURCE_ID, &resourceIdNApi);
360     napi_create_int32(env, static_cast<uint32_t>(resType), &typeIdNApi);
361     ModifyResourceParam(env, value, resType, resName);
362     napi_set_property(env, value, typeKeyNApi, typeIdNApi);
363     napi_set_property(env, value, CreateNapiString(env, "id"), resourceIdNApi);
364     napi_has_property(env, value, bundleNameKeyNApi, &hasProperty);
365     if (!hasProperty) {
366         napi_set_property(env, value, bundleNameKeyNApi, defaultNameNApi);
367     }
368     napi_has_property(env, value, moduleNameKeyNApi, &hasProperty);
369     if (!hasProperty) {
370         napi_set_property(env, value, moduleNameKeyNApi, defaultNameNApi);
371     }
372 }
373 
CompleteResourceParamV2(napi_env env, napi_value value)374 void CompleteResourceParamV2(napi_env env, napi_value value)
375 {
376     napi_value paramsNApi = nullptr;
377     if (napi_get_named_property(env, value, "params", &paramsNApi) != napi_ok) {
378         return;
379     }
380     bool isArray = false;
381     napi_is_array(env, paramsNApi, &isArray);
382     if (!isArray) {
383         return;
384     }
385     uint32_t paramCount = 0;
386     napi_get_array_length(env, paramsNApi, &paramCount);
387     if (paramCount <= 0) {
388         return;
389     }
390     napi_value resNameNApi = nullptr;
391     napi_get_element(env, paramsNApi, 0, &resNameNApi);
392     std::string resName;
393     std::string moduleName;
394     ResourceType resType;
395     if (!ParseDollarResource(env, resNameNApi, resType, resName, moduleName)) {
396         return;
397     }
398     napi_value typeIdNApi = nullptr;
399     napi_value typeKeyNApi = CreateNapiString(env, "type");
400     napi_create_int32(env, static_cast<uint32_t>(resType), &typeIdNApi);
401     napi_set_property(env, value, typeKeyNApi, typeIdNApi);
402     if (!moduleName.empty()) {
403         napi_value moduleNameNApi = CreateNapiString(env, moduleName);
404         napi_value moduleNameKeyNApi = CreateNapiString(env, "moduleName");
405         napi_set_property(env, value, moduleNameKeyNApi, moduleNameNApi);
406     }
407 }
408 
ModifyResourceParam(napi_env env, napi_value value, const ResourceType& resType, const std::string& resName)409 void ModifyResourceParam(napi_env env, napi_value value, const ResourceType& resType, const std::string& resName)
410 {
411     // raw input : {"id":"app.xxx.xxx","params":[],"moduleName":"xxx","bundleName":"xxx"}
412     // modified output : {"id":-1, "params":["app.xxx.xxx"],"type":xxxx,"moduleName":"xxx","bundleName":"xxx"}
413     napi_value paramsNApi = nullptr;
414     napi_get_named_property(env, value, "params", &paramsNApi);
415     bool isArray = false;
416     if (napi_is_array(env, paramsNApi, &isArray) != napi_ok) {
417         return;
418     }
419     if (!isArray) {
420         return;
421     }
422     uint32_t paramCount = 0;
423     bool hasProperty = false;
424     napi_get_array_length(env, paramsNApi, &paramCount);
425     napi_value typeKeyNApi = CreateNapiString(env, "type");
426     napi_value resNameNApi = CreateNapiString(env, resName);
427     if (resType == ResourceType::PLURAL || resType == ResourceType::STRING) {
428         std::vector<napi_value> tmpParams;
429         for (uint32_t i = 0; i < paramCount; i++) {
430             napi_value param = nullptr;
431             napi_get_element(env, paramsNApi, i, &param);
432             tmpParams.insert(tmpParams.end(), param);
433         }
434         napi_set_element(env, paramsNApi, 0, resNameNApi);
435         uint32_t paramIndex = 1;
436         napi_has_property(env, value, typeKeyNApi, &hasProperty);
437         if (hasProperty) {
438             napi_value firstParam = nullptr;
439             napi_get_property(env, value, typeKeyNApi, &firstParam);
440             napi_set_element(env, paramsNApi, paramIndex, firstParam);
441             paramIndex++;
442         }
443         for (auto tmpParam : tmpParams) {
444             napi_set_element(env, paramsNApi, paramIndex, tmpParam);
445             paramIndex++;
446         }
447     } else {
448         napi_set_element(env, paramsNApi, 0, resNameNApi);
449     }
450 }
451 
ParseCurveInfo(const std::string& curveString, std::string& curveTypeString, std::vector<float>& curveValue)452 void ParseCurveInfo(const std::string& curveString, std::string& curveTypeString, std::vector<float>& curveValue)
453 {
454     if (curveString.back() != ')') {
455         return;
456     }
457     std::string::size_type leftEmbracePosition = curveString.find_last_of('(');
458     if (leftEmbracePosition == std::string::npos) {
459         return;
460     }
461     curveTypeString = curveString.substr(0, leftEmbracePosition);
462     auto params = curveString.substr(leftEmbracePosition + 1, curveString.length() - leftEmbracePosition - 2);
463     if (curveTypeString.empty() || params.empty()) {
464         return;
465     }
466     std::vector<std::string> paramsVector;
467     StringUtils::StringSplitter(params, ',', paramsVector);
468     for (auto& param : paramsVector) {
469         Framework::RemoveHeadTailSpace(param);
470         if (param == "true" || param == "start") {
471             param = "1.000000";
472         }
473         if (param == "false" || param == "end") {
474             param = "0.000000";
475         }
476         errno = 0;
477         char* end = nullptr;
478         float value = strtof(param.c_str(), &end);
479         if (end == param.c_str() || errno == ERANGE) {
480             LOGW("%{public}s can not be converted to float or is out of range.", param.c_str());
481         }
482         curveValue.emplace_back(value);
483     }
484 }
485 
ParseCurve(napi_env env, napi_value value, std::string& curveTypeString, std::vector<float>& curveValue)486 napi_value ParseCurve(napi_env env, napi_value value, std::string& curveTypeString, std::vector<float>& curveValue)
487 {
488     CHECK_NULL_RETURN(value, nullptr);
489     napi_valuetype valueType = napi_undefined;
490     napi_typeof(env, value, &valueType);
491     NAPI_ASSERT(env, valueType == napi_object || valueType == napi_string, "The type of curve is incorrect");
492     if (valueType == napi_object) {
493         napi_value curveObjectNApi = nullptr;
494         napi_get_named_property(env, value, "__curveString", &curveObjectNApi);
495         value = curveObjectNApi;
496     }
497 
498     size_t paramLen = 0;
499     napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &paramLen);
500     NAPI_ASSERT(env, paramLen > 0 && paramLen < NAPI_BUF_LENGTH && status == napi_ok, "paramLen error");
501     char params[NAPI_BUF_LENGTH] = { 0 };
502     status = napi_get_value_string_utf8(env, value, params, paramLen + 1, &paramLen);
503     NAPI_ASSERT(env, status == napi_ok, "Parse curve failed");
504 
505     RefPtr<Curve> curve;
506     const std::string domAnimationDefaultCurveString = "ease-in-out";
507     if (params[0] == '\0') {
508         curve = Framework::CreateCurve(domAnimationDefaultCurveString);
509     } else {
510         curve = Framework::CreateCurve(params);
511     }
512     std::string curveString = curve->ToString();
513     ParseCurveInfo(curveString, curveTypeString, curveValue);
514     return nullptr;
515 }
516 
GetValueType(napi_env env, napi_value value)517 napi_valuetype GetValueType(napi_env env, napi_value value)
518 {
519     if (value == nullptr) {
520         return napi_undefined;
521     }
522 
523     napi_valuetype valueType = napi_undefined;
524     NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), napi_undefined);
525     return valueType;
526 }
527 
GetStringFromValueUtf8(napi_env env, napi_value value)528 std::optional<std::string> GetStringFromValueUtf8(napi_env env, napi_value value)
529 {
530     static constexpr size_t maxLength = 2048;
531     if (GetValueType(env, value) != napi_string) {
532         return std::nullopt;
533     }
534 
535     size_t paramLen = 0;
536     napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &paramLen);
537     if (paramLen == 0 || paramLen > maxLength || status != napi_ok) {
538         return std::nullopt;
539     }
540     char params[maxLength] = { 0 };
541     status = napi_get_value_string_utf8(env, value, params, paramLen + 1, &paramLen);
542     if (status != napi_ok) {
543         return std::nullopt;
544     }
545     return params;
546 }
547 
GetIntProperty(napi_env env, napi_value value, const std::string& key, int32_t& result)548 bool GetIntProperty(napi_env env, napi_value value, const std::string& key, int32_t& result)
549 {
550     CHECK_NULL_RETURN(value, false);
551     napi_valuetype valueType = napi_undefined;
552     napi_value propertyNApi = nullptr;
553     napi_get_named_property(env, value, key.c_str(), &propertyNApi);
554     if (valueType != napi_number) {
555         LOGE("The type of property is incorrect");
556         return false;
557     }
558     int32_t property = 0;
559     napi_status status = napi_get_value_int32(env, propertyNApi, &property);
560     if (status != napi_ok) {
561         LOGE("Get property failed");
562         return false;
563     }
564     return true;
565 }
566 
CompleteColorAlphaIfIncomplete(uint32_t origin)567 static uint32_t CompleteColorAlphaIfIncomplete(uint32_t origin)
568 {
569     constexpr uint32_t colorAlphaOffset = 24;
570     constexpr uint32_t colorAlphaDefaultValue = 0xFF000000;
571     uint32_t result = origin;
572     if ((origin >> colorAlphaOffset) == 0) {
573         result = origin | colorAlphaDefaultValue;
574     }
575     return result;
576 }
577 
ParseColorFromResourceObject(napi_env env, napi_value value, Color& colorResult)578 bool ParseColorFromResourceObject(napi_env env, napi_value value, Color& colorResult)
579 {
580     ResourceInfo resourceInfo;
581     if (!ParseResourceParam(env, value, resourceInfo)) {
582         LOGE("Parse color from resource failed");
583         return false;
584     }
585     auto themeConstants = GetThemeConstants(resourceInfo.bundleName, resourceInfo.moduleName);
586     if (themeConstants == nullptr) {
587         LOGE("themeConstants is nullptr");
588         return false;
589     }
590     if (resourceInfo.type == static_cast<int32_t>(ResourceType::STRING)) {
591         auto colorString = themeConstants->GetString(resourceInfo.type);
592         return Color::ParseColorString(colorString, colorResult);
593     }
594     if (resourceInfo.type == static_cast<int32_t>(ResourceType::INTEGER)) {
595         auto colorInt = themeConstants->GetInt(resourceInfo.type);
596         colorResult = Color(CompleteColorAlphaIfIncomplete(colorInt));
597         return true;
598     }
599     colorResult = themeConstants->GetColor(resourceInfo.resId);
600     return true;
601 }
602 
ParseColor(napi_env env, napi_value value, Color& result)603 bool ParseColor(napi_env env, napi_value value, Color& result)
604 {
605     napi_valuetype valueType = GetValueType(env, value);
606     if (valueType != napi_number && valueType != napi_string && valueType != napi_object) {
607         return false;
608     }
609     if (valueType == napi_number) {
610         int32_t colorId = 0;
611         napi_get_value_int32(env, value, &colorId);
612         result = Color(CompleteColorAlphaIfIncomplete(static_cast<uint32_t>(colorId)));
613         return true;
614     }
615     if (valueType == napi_string) {
616         std::optional<std::string> colorString = GetStringFromValueUtf8(env, value);
617         if (!colorString.has_value()) {
618             LOGE("Parse color from string failed");
619         }
620         return Color::ParseColorString(colorString.value(), result);
621     }
622 
623     return ParseColorFromResourceObject(env, value, result);
624 }
625 
ParseResourceParam(napi_env env, napi_value value, ResourceInfo& info)626 bool ParseResourceParam(napi_env env, napi_value value, ResourceInfo& info)
627 {
628     CompleteResourceParam(env, value);
629     napi_value idNApi = nullptr;
630     napi_value typeNApi = nullptr;
631     napi_value paramsNApi = nullptr;
632     napi_value bundleNameNApi = nullptr;
633     napi_value moduleNameNApi = nullptr;
634     napi_valuetype valueType = napi_undefined;
635     napi_typeof(env, value, &valueType);
636     if (valueType == napi_object) {
637         napi_get_named_property(env, value, "id", &idNApi);
638         napi_get_named_property(env, value, "type", &typeNApi);
639         napi_get_named_property(env, value, "params", &paramsNApi);
640         napi_get_named_property(env, value, "bundleName", &bundleNameNApi);
641         napi_get_named_property(env, value, "moduleName", &moduleNameNApi);
642     } else {
643         return false;
644     }
645 
646     napi_typeof(env, idNApi, &valueType);
647     if (valueType == napi_number) {
648         napi_get_value_int32(env, idNApi, &info.resId);
649     }
650 
651     napi_typeof(env, typeNApi, &valueType);
652     if (valueType == napi_number) {
653         napi_get_value_int32(env, typeNApi, &info.type);
654     }
655 
656     bool isArray = false;
657     if (napi_is_array(env, paramsNApi, &isArray) != napi_ok) {
658         return false;
659     }
660 
661     if (!isArray) {
662         return false;
663     }
664 
665     uint32_t arrayLength = 0;
666     napi_get_array_length(env, paramsNApi, &arrayLength);
667 
668     for (uint32_t i = 0; i < arrayLength; i++) {
669         size_t ret = 0;
670         napi_value indexValue = nullptr;
671         napi_get_element(env, paramsNApi, i, &indexValue);
672         napi_typeof(env, indexValue, &valueType);
673         if (valueType == napi_string) {
674             size_t strLen = GetParamLen(env, indexValue) + 1;
675             std::unique_ptr<char[]> indexStr = std::make_unique<char[]>(strLen);
676             napi_get_value_string_utf8(env, indexValue, indexStr.get(), strLen, &ret);
677             info.params.emplace_back(indexStr.get());
678         } else if (valueType == napi_number) {
679             int32_t num;
680             napi_get_value_int32(env, indexValue, &num);
681             info.params.emplace_back(std::to_string(num));
682         }
683     }
684 
685     napi_typeof(env, bundleNameNApi, &valueType);
686     if (valueType == napi_string) {
687         size_t ret = 0;
688         size_t strLen = GetParamLen(env, bundleNameNApi) + 1;
689         std::unique_ptr<char[]> bundleNameStr = std::make_unique<char[]>(strLen);
690         napi_get_value_string_utf8(env, bundleNameNApi, bundleNameStr.get(), strLen, &ret);
691         info.bundleName = bundleNameStr.get();
692     }
693 
694     napi_typeof(env, moduleNameNApi, &valueType);
695     if (valueType == napi_string) {
696         size_t ret = 0;
697         size_t strLen = GetParamLen(env, moduleNameNApi) + 1;
698         std::unique_ptr<char[]> moduleNameStr = std::make_unique<char[]>(strLen);
699         napi_get_value_string_utf8(env, moduleNameNApi, moduleNameStr.get(), strLen, &ret);
700         info.moduleName = moduleNameStr.get();
701     }
702 
703     return true;
704 }
705 
DimensionToString(Dimension dimension)706 std::string DimensionToString(Dimension dimension)
707 {
708     static const int32_t unitsNum = 6;
709     static const int32_t percentIndex = 3;
710     static const int32_t percentUnit = 100;
711     static std::array<std::string, unitsNum> units = { "px", "vp", "fp", "%", "lpx", "auto" };
712     auto unit = dimension.Unit();
713     auto value = dimension.Value();
714     if (unit == DimensionUnit::NONE) {
715         return StringUtils::DoubleToString(value).append("none");
716     }
717     if (units[static_cast<int>(unit)] == units[percentIndex]) {
718         return StringUtils::DoubleToString(value * percentUnit).append(units[static_cast<int>(unit)]);
719     }
720     return StringUtils::DoubleToString(value).append(units[static_cast<int>(unit)]);
721 }
722 
ParseString(const ResourceInfo& info, std::string& result)723 bool ParseString(const ResourceInfo& info, std::string& result)
724 {
725     auto resourceWrapper = CreateResourceWrapper(info);
726     if (info.type == static_cast<int>(ResourceType::PLURAL)) {
727         std::string pluralResults;
728         if (info.resId == UNKNOWN_RESOURCE_ID) {
729             auto count = StringUtils::StringToInt(info.params[1]);
730             pluralResults = resourceWrapper->GetPluralStringByName(info.params[0], count);
731             ReplaceHolder(pluralResults, info.params, 2); // plural holder in index 2
732         } else {
733             auto count = StringUtils::StringToInt(info.params[0]);
734             pluralResults = resourceWrapper->GetPluralString(info.resId, count);
735             ReplaceHolder(pluralResults, info.params, 1);
736         }
737         result = pluralResults;
738         return true;
739     }
740     if (info.type == static_cast<int>(ResourceType::RAWFILE)) {
741         auto fileName = info.params[0];
742         result = resourceWrapper->GetRawfile(fileName);
743         return true;
744     }
745     if (info.type == static_cast<int>(ResourceType::FLOAT)) {
746         if (info.resId == UNKNOWN_RESOURCE_ID) {
747             result = DimensionToString(resourceWrapper->GetDimensionByName(info.params[0]));
748         } else {
749             result = DimensionToString(resourceWrapper->GetDimension(info.resId));
750         }
751         return true;
752     }
753     if (info.type == static_cast<int>(ResourceType::STRING)) {
754         std::string originStr;
755         if (info.resId == UNKNOWN_RESOURCE_ID) {
756             originStr = resourceWrapper->GetStringByName(info.params[0]);
757             ReplaceHolder(originStr, info.params, 1);
758         } else {
759             originStr = resourceWrapper->GetString(info.resId);
760             ReplaceHolder(originStr, info.params, 0);
761         }
762         result = originStr;
763         return true;
764     }
765     if (info.type == static_cast<int>(ResourceType::COLOR)) {
766         result = resourceWrapper->GetColor(info.resId).ColorToString();
767         return true;
768     }
769     if (info.type == static_cast<int>(ResourceType::INTEGER)) {
770         result = std::to_string(resourceWrapper->GetInt(info.resId));
771         return true;
772     }
773     return true;
774 }
775 
ErrorToMessage(int32_t code)776 std::string ErrorToMessage(int32_t code)
777 {
778     auto iter = ERROR_CODE_TO_MSG.find(code);
779     return (iter != ERROR_CODE_TO_MSG.end()) ? iter->second : "";
780 }
781 
GetSingleParam(napi_env env, napi_callback_info info, napi_value* argv, napi_valuetype& valueType)782 bool GetSingleParam(napi_env env, napi_callback_info info, napi_value* argv, napi_valuetype& valueType)
783 {
784     size_t argc = 1;
785     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
786     if (argc != 1) {
787         return false;
788     }
789     napi_typeof(env, argv[0], &valueType);
790     return true;
791 }
792 
793 // (Color | number | string | undifened)
GetOptionalColor(napi_env env, napi_value argv, napi_valuetype& valueType)794 std::optional<Color> GetOptionalColor(napi_env env, napi_value argv, napi_valuetype& valueType)
795 {
796     if (valueType == napi_number) {
797         uint32_t num;
798         uint32_t alpha = 0xff000000;
799         napi_get_value_uint32(env, argv, &num);
800         if ((num & alpha) == 0) {
801             num |= alpha;
802         }
803         return Color(num);
804     } else if (valueType == napi_string) {
805         std::string str;
806         bool result = GetNapiString(env, argv, str, valueType);
807         Color color;
808         if (!result || !Color::ParseColorString(str, color)) {
809             return std::nullopt;
810         }
811         return color;
812     } else {
813         return std::nullopt;
814     }
815 }
816 
ParseIntegerToString(const ResourceInfo& info, std::string& result)817 bool ParseIntegerToString(const ResourceInfo& info, std::string& result)
818 {
819     auto resourceWrapper = CreateResourceWrapper(info);
820     if (info.type == static_cast<int>(ResourceType::INTEGER)) {
821         if (info.resId == UNKNOWN_RESOURCE_ID) {
822             result = std::to_string(resourceWrapper->GetIntByName(info.params[0]));
823         } else {
824             result = std::to_string(resourceWrapper->GetInt(info.resId));
825         }
826         return true;
827     }
828     return true;
829 }
830 
HasProperty(napi_env env, napi_value value, const std::string& targetStr)831 bool HasProperty(napi_env env, napi_value value, const std::string& targetStr)
832 {
833     bool hasProperty = false;
834     napi_has_named_property(env, value, targetStr.c_str(), &hasProperty);
835     return hasProperty;
836 }
837 
GetReturnObject(napi_env env, std::string callbackString)838 napi_value GetReturnObject(napi_env env, std::string callbackString)
839 {
840     napi_value result = nullptr;
841     napi_value returnObj = nullptr;
842     napi_create_object(env, &returnObj);
843     napi_create_string_utf8(env, callbackString.c_str(), NAPI_AUTO_LENGTH, &result);
844     napi_set_named_property(env, returnObj, "errMsg", result);
845     return returnObj;
846 }
847 
ParseNapiDimension(napi_env env, CalcDimension& result, napi_value napiValue, DimensionUnit defaultUnit)848 bool ParseNapiDimension(napi_env env, CalcDimension& result, napi_value napiValue, DimensionUnit defaultUnit)
849 {
850     napi_valuetype valueType = napi_undefined;
851     napi_typeof(env, napiValue, &valueType);
852     if (valueType == napi_number) {
853         double value = 0;
854         napi_get_value_double(env, napiValue, &value);
855         result.SetUnit(defaultUnit);
856         result.SetValue(value);
857         return true;
858     } else if (valueType == napi_string) {
859         std::string valueString;
860         if (!GetNapiString(env, napiValue, valueString, valueType)) {
861             return false;
862         }
863         result = StringUtils::StringToCalcDimension(valueString, false, defaultUnit);
864         return true;
865     } else if (valueType == napi_object) {
866         ResourceInfo recv;
867         std::string parameterStr;
868         if (!ParseResourceParam(env, napiValue, recv)) {
869             return false;
870         }
871         if (!ParseString(recv, parameterStr)) {
872             return false;
873         }
874         result = StringUtils::StringToDimensionWithUnit(parameterStr, defaultUnit);
875         return true;
876     }
877     return false;
878 }
879 
ParseNapiDimensionNG( napi_env env, CalcDimension& result, napi_value napiValue, DimensionUnit defaultUnit, bool isSupportPercent)880 bool ParseNapiDimensionNG(
881     napi_env env, CalcDimension& result, napi_value napiValue, DimensionUnit defaultUnit, bool isSupportPercent)
882 {
883     napi_valuetype valueType = napi_undefined;
884     napi_typeof(env, napiValue, &valueType);
885     if (valueType == napi_number) {
886         double value = 0;
887         napi_get_value_double(env, napiValue, &value);
888 
889         result.SetUnit(defaultUnit);
890         result.SetValue(value);
891         return true;
892     } else if (valueType == napi_string) {
893         std::string valueString;
894         if (!GetNapiString(env, napiValue, valueString, valueType)) {
895             return false;
896         }
897         if (valueString.back() == '%' && !isSupportPercent) {
898             return false;
899         }
900         return StringUtils::StringToCalcDimensionNG(valueString, result, false, defaultUnit);
901     } else if (valueType == napi_object) {
902         ResourceInfo recv;
903         std::string parameterStr;
904         if (!ParseResourceParam(env, napiValue, recv)) {
905             return false;
906         }
907         if (!ParseString(recv, parameterStr)) {
908             return false;
909         }
910         if (!ParseIntegerToString(recv, parameterStr)) {
911             return false;
912         }
913         result = StringUtils::StringToDimensionWithUnit(parameterStr, defaultUnit);
914         return true;
915     }
916     return false;
917 }
918 
ParseNapiColor(napi_env env, napi_value value, Color& result)919 bool ParseNapiColor(napi_env env, napi_value value, Color& result)
920 {
921     napi_valuetype valueType = GetValueType(env, value);
922     if (valueType != napi_number && valueType != napi_string && valueType != napi_object) {
923         return false;
924     }
925     if (valueType == napi_number) {
926         int32_t colorId = 0;
927         napi_get_value_int32(env, value, &colorId);
928         constexpr uint32_t colorAlphaOffset = 24;
929         constexpr uint32_t colorAlphaDefaultValue = 0xFF000000;
930         auto origin = static_cast<uint32_t>(colorId);
931         uint32_t alphaResult = origin;
932         if ((origin >> colorAlphaOffset) == 0) {
933             alphaResult = origin | colorAlphaDefaultValue;
934         }
935         result = Color(alphaResult);
936         return true;
937     }
938     if (valueType == napi_string) {
939         std::optional<std::string> colorString = GetStringFromValueUtf8(env, value);
940         if (!colorString.has_value()) {
941             LOGE("Parse color from string failed");
942             return false;
943         }
944         return Color::ParseColorString(colorString.value(), result);
945     }
946 
947     return ParseColorFromResourceObject(env, value, result);
948 }
949 
ParseStyle(napi_env env, napi_value value, std::optional<BorderStyle>& style)950 bool ParseStyle(napi_env env, napi_value value, std::optional<BorderStyle>& style)
951 {
952     napi_valuetype valueType = GetValueType(env, value);
953     if (valueType != napi_number) {
954         return false;
955     }
956     int32_t num;
957     napi_get_value_int32(env, value, &num);
958     style = static_cast<BorderStyle>(num);
959     if (style < BorderStyle::SOLID || style > BorderStyle::NONE) {
960         return false;
961     }
962     return true;
963 }
964 
ParseShadowColorStrategy(napi_env env, napi_value value, ShadowColorStrategy& strategy)965 bool ParseShadowColorStrategy(napi_env env, napi_value value, ShadowColorStrategy& strategy)
966 {
967     napi_valuetype valueType = GetValueType(env, value);
968     if (valueType == napi_string) {
969         std::optional<std::string> colorStr = GetStringFromValueUtf8(env, value);
970         if (colorStr.has_value()) {
971             if (colorStr->compare("average") == 0) {
972                 strategy = ShadowColorStrategy::AVERAGE;
973                 return true;
974             } else if (colorStr->compare("primary") == 0) {
975                 strategy = ShadowColorStrategy::PRIMARY;
976                 return true;
977             }
978         }
979     }
980     return false;
981 }
982 } // namespace OHOS::Ace::Napi
983