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", ¶msNApi);
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