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