• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "js_native_api_types.h"
18 
19 namespace OHOS::Ace::Napi {
20 namespace {
21 
22 enum class ResourceType : uint32_t {
23     COLOR = 10001,
24     FLOAT,
25     STRING,
26     PLURAL,
27     BOOLEAN,
28     INTARRAY,
29     INTEGER,
30     PATTERN,
31     STRARRAY,
32     MEDIA = 20000,
33     RAWFILE = 30000
34 };
35 
36 const std::regex RESOURCE_APP_STRING_PLACEHOLDER(R"(\%((\d+)(\$)){0,1}([dsf]))", std::regex::icase);
37 
38 } // namespace
39 
40 static const std::unordered_map<int32_t, std::string> ERROR_CODE_TO_MSG {
41     { Framework::ERROR_CODE_PERMISSION_DENIED, "Permission denied. " },
42     { Framework::ERROR_CODE_PARAM_INVALID, "Parameter error. " },
43     { Framework::ERROR_CODE_SYSTEMCAP_ERROR, "Capability not supported. " },
44     { Framework::ERROR_CODE_INTERNAL_ERROR, "Internal error. " },
45     { Framework::ERROR_CODE_URI_ERROR, "Uri error. " },
46     { Framework::ERROR_CODE_PAGE_STACK_FULL, "Page stack error. " },
47     { Framework::ERROR_CODE_URI_ERROR_LITE, "Uri error. " }
48 };
49 
NapiThrow(napi_env env,const std::string & message,int32_t errCode)50 void NapiThrow(napi_env env, const std::string& message, int32_t errCode)
51 {
52     napi_value code = nullptr;
53     std::string strCode = std::to_string(errCode);
54     napi_create_string_utf8(env, strCode.c_str(), strCode.length(), &code);
55 
56     napi_value msg = nullptr;
57     auto iter = ERROR_CODE_TO_MSG.find(errCode);
58     std::string strMsg = (iter != ERROR_CODE_TO_MSG.end() ? iter->second : "") + message;
59     LOGE("napi throw errCode %d strMsg %s", errCode, strMsg.c_str());
60     napi_create_string_utf8(env, strMsg.c_str(), strMsg.length(), &msg);
61 
62     napi_value error = nullptr;
63     napi_create_error(env, code, msg, &error);
64     napi_throw(env, error);
65 }
66 
ReplaceHolder(std::string & originStr,const std::vector<std::string> & params,int32_t containCount)67 void ReplaceHolder(std::string& originStr, const std::vector<std::string>& params, int32_t containCount)
68 {
69     auto size = static_cast<int32_t>(params.size());
70     if (containCount == size) {
71         return;
72     }
73     std::string::const_iterator start = originStr.begin();
74     std::string::const_iterator end = originStr.end();
75     std::smatch matches;
76     bool shortHolderType = false;
77     bool firstMatch = true;
78     int searchTime = 0;
79     while (std::regex_search(start, end, matches, RESOURCE_APP_STRING_PLACEHOLDER)) {
80         std::string pos = matches[2];
81         std::string type = matches[4];
82         if (firstMatch) {
83             firstMatch = false;
84             shortHolderType = pos.length() == 0;
85         } else {
86             if (static_cast<uint32_t>(shortHolderType) ^ ((uint32_t)(pos.length() == 0))) {
87                 LOGE("wrong place holder,stop parse string");
88                 return;
89             }
90         }
91 
92         std::string replaceContentStr;
93         std::string::size_type index;
94         if (shortHolderType) {
95             index = static_cast<uint32_t>(searchTime + containCount);
96         } else {
97             index = static_cast<uint32_t>(StringUtils::StringToInt(pos) - 1 + containCount);
98         }
99         replaceContentStr = params[index];
100 
101         originStr.replace(matches[0].first - originStr.begin(), matches[0].length(), replaceContentStr);
102         start = originStr.begin() + matches.prefix().length() + replaceContentStr.length();
103         end = originStr.end();
104         searchTime++;
105     }
106 }
107 
GetParamLen(napi_value param)108 size_t GetParamLen(napi_value param)
109 {
110     auto nativeValue = reinterpret_cast<NativeValue*>(param);
111     auto resultValue = nativeValue->ToString();
112     auto nativeString = reinterpret_cast<NativeString*>(resultValue->GetInterface(NativeString::INTERFACE_ID));
113     size_t len = nativeString->GetLength();
114     return len;
115 }
116 
GetNapiString(napi_env env,napi_value value,std::string & retStr,napi_valuetype & valueType)117 bool GetNapiString(napi_env env, napi_value value, std::string& retStr, napi_valuetype& valueType)
118 {
119     size_t ret = 0;
120     napi_typeof(env, value, &valueType);
121     if (valueType == napi_string) {
122         size_t valueLen = GetParamLen(value) + 1;
123         std::unique_ptr<char[]> buffer = std::make_unique<char[]>(valueLen);
124         napi_get_value_string_utf8(env, value, buffer.get(), valueLen, &ret);
125         retStr = buffer.get();
126         return true;
127     }
128     if (valueType == napi_object) {
129         ResourceInfo recv;
130         if (ParseResourceParam(env, value, recv)) {
131             ParseString(recv, retStr);
132             return true;
133         }
134     }
135     return false;
136 }
137 
GetThemeConstants(const std::optional<std::string> & bundleName=std::nullopt,const std::optional<std::string> & moduleName=std::nullopt)138 RefPtr<ThemeConstants> GetThemeConstants(const std::optional<std::string>& bundleName = std::nullopt,
139     const std::optional<std::string>& moduleName = std::nullopt)
140 {
141     auto container = Container::Current();
142     if (!container) {
143         LOGW("container is null");
144         return nullptr;
145     }
146     auto pipelineContext = container->GetPipelineContext();
147     if (!pipelineContext) {
148         LOGE("pipelineContext is null!");
149         return nullptr;
150     }
151     auto themeManager = pipelineContext->GetThemeManager();
152     if (!themeManager) {
153         LOGE("themeManager is null!");
154         return nullptr;
155     }
156     if (bundleName.has_value() && moduleName.has_value()) {
157         return themeManager->GetThemeConstants(bundleName.value_or(""), moduleName.value_or(""));
158     }
159     return themeManager->GetThemeConstants();
160 }
161 
ParseResourceParam(napi_env env,napi_value value,ResourceInfo & info)162 bool ParseResourceParam(napi_env env, napi_value value, ResourceInfo& info)
163 {
164     napi_value idNApi = nullptr;
165     napi_value typeNApi = nullptr;
166     napi_value paramsNApi = nullptr;
167     napi_value bundleNameNApi = nullptr;
168     napi_value moduleNameNApi = nullptr;
169     napi_valuetype valueType = napi_undefined;
170     napi_typeof(env, value, &valueType);
171     if (valueType == napi_object) {
172         napi_get_named_property(env, value, "id", &idNApi);
173         napi_get_named_property(env, value, "type", &typeNApi);
174         napi_get_named_property(env, value, "params", &paramsNApi);
175         napi_get_named_property(env, value, "bundleName", &bundleNameNApi);
176         napi_get_named_property(env, value, "moduleName", &moduleNameNApi);
177     } else {
178         return false;
179     }
180 
181     napi_typeof(env, idNApi, &valueType);
182     if (valueType == napi_number) {
183         napi_get_value_int32(env, idNApi, &info.resId);
184     }
185 
186     napi_typeof(env, typeNApi, &valueType);
187     if (valueType == napi_number) {
188         napi_get_value_int32(env, typeNApi, &info.type);
189     }
190 
191     bool isArray = false;
192     if (napi_is_array(env, paramsNApi, &isArray) != napi_ok) {
193         return false;
194     }
195 
196     if (!isArray) {
197         return false;
198     }
199 
200     uint32_t arrayLength = 0;
201     napi_get_array_length(env, paramsNApi, &arrayLength);
202 
203     for (uint32_t i = 0; i < arrayLength; i++) {
204         size_t ret = 0;
205         napi_value indexValue = nullptr;
206         napi_get_element(env, paramsNApi, i, &indexValue);
207         napi_typeof(env, indexValue, &valueType);
208         if (valueType == napi_string) {
209             size_t strLen = GetParamLen(indexValue) + 1;
210             std::unique_ptr<char[]> indexStr = std::make_unique<char[]>(strLen);
211             napi_get_value_string_utf8(env, indexValue, indexStr.get(), strLen, &ret);
212             info.params.emplace_back(indexStr.get());
213         } else if (valueType == napi_number) {
214             int32_t num;
215             napi_get_value_int32(env, indexValue, &num);
216             info.params.emplace_back(std::to_string(num));
217         }
218     }
219 
220     napi_typeof(env, bundleNameNApi, &valueType);
221     if (valueType == napi_string) {
222         size_t ret = 0;
223         size_t strLen = GetParamLen(bundleNameNApi) + 1;
224         std::unique_ptr<char[]> bundleNameStr = std::make_unique<char[]>(strLen);
225         napi_get_value_string_utf8(env, bundleNameNApi, bundleNameStr.get(), strLen, &ret);
226         info.bundleName = bundleNameStr.get();
227     }
228 
229     napi_typeof(env, moduleNameNApi, &valueType);
230     if (valueType == napi_string) {
231         size_t ret = 0;
232         size_t strLen = GetParamLen(moduleNameNApi) + 1;
233         std::unique_ptr<char[]> moduleNameStr = std::make_unique<char[]>(strLen);
234         napi_get_value_string_utf8(env, moduleNameNApi, moduleNameStr.get(), strLen, &ret);
235         info.moduleName = moduleNameStr.get();
236     }
237 
238     return true;
239 }
240 
DimensionToString(Dimension dimension)241 std::string DimensionToString(Dimension dimension)
242 {
243     static const int32_t unitsNum = 6;
244     static const int32_t percentIndex = 3;
245     static const int32_t percentUnit = 100;
246     static std::array<std::string, unitsNum> units = { "px", "vp", "fp", "%", "lpx", "auto" };
247     auto unit = dimension.Unit();
248     auto value = dimension.Value();
249     if (unit == DimensionUnit::NONE) {
250         return StringUtils::DoubleToString(value).append("none");
251     }
252     if (units[static_cast<int>(unit)] == units[percentIndex]) {
253         return StringUtils::DoubleToString(value * percentUnit).append(units[static_cast<int>(unit)]);
254     }
255     return StringUtils::DoubleToString(value).append(units[static_cast<int>(unit)]);
256 }
257 
ParseString(const ResourceInfo & info,std::string & result)258 bool ParseString(const ResourceInfo& info, std::string& result)
259 {
260     auto themeConstants = GetThemeConstants(info.bundleName, info.moduleName);
261     if (!themeConstants) {
262         LOGE("themeConstants is nullptr");
263         return false;
264     }
265 
266     if (info.type == static_cast<int>(ResourceType::PLURAL)) {
267         auto count = StringUtils::StringToDouble(info.params[0]);
268         auto pluralResults = themeConstants->GetStringArray(info.resId);
269         auto pluralChoice = Localization::GetInstance()->PluralRulesFormat(count);
270         auto iter = std::find(pluralResults.begin(), pluralResults.end(), pluralChoice);
271         std::string originStr;
272         if (iter != pluralResults.end() && ++iter != pluralResults.end()) {
273             originStr = *iter;
274         }
275         ReplaceHolder(originStr, info.params, 1);
276         result = originStr;
277     } else if (info.type == static_cast<int>(ResourceType::RAWFILE)) {
278         auto fileName = info.params[0];
279         result = themeConstants->GetRawfile(fileName);
280     } else if (info.type == static_cast<int>(ResourceType::FLOAT)) {
281         result = DimensionToString(themeConstants->GetDimension(info.resId));
282     } else {
283         auto originStr = themeConstants->GetString(info.resId);
284         ReplaceHolder(originStr, info.params, 0);
285         result = originStr;
286     }
287     return true;
288 }
289 
ErrorToMessage(int32_t code)290 std::string ErrorToMessage(int32_t code)
291 {
292     auto iter = ERROR_CODE_TO_MSG.find(code);
293     return (iter != ERROR_CODE_TO_MSG.end()) ? iter->second : "";
294 }
295 
296 } // namespace OHOS::Ace::Napi
297