1 /*
2  * Copyright (c) 2022 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_parse_utils.h"
17 
18 #include <sys/mman.h>
19 #include <unistd.h>
20 #include <regex>
21 
22 #include "nweb.h"
23 #include "nweb_log.h"
24 #include "ohos_adapter_helper.h"
25 #include "securec.h"
26 
27 #define MAX_FLOWBUF_DATA_SIZE 52428800 /* 50 MB */
28 
29 namespace OHOS {
30 namespace NWeb {
31 namespace {
ConvertToNapiHandlerOfString(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)32 bool ConvertToNapiHandlerOfString(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
33 {
34     std::string msgStr = src->GetString();
35     napi_create_string_utf8(env, msgStr.c_str(), msgStr.length(), &dst);
36     return true;
37 }
38 
ConvertToNapiHandlerOfBinary(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)39 bool ConvertToNapiHandlerOfBinary(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
40 {
41     std::vector<uint8_t> msgArr = src->GetBinary();
42     void *arrayData = nullptr;
43     napi_create_arraybuffer(env, msgArr.size(), &arrayData, &dst);
44     if (arrayData == nullptr) {
45         WVLOG_E("Create arraybuffer failed");
46         return false;
47     }
48     for (size_t i = 0; i < msgArr.size(); ++i) {
49         *(uint8_t*)((uint8_t*)arrayData + i) = msgArr[i];
50     }
51     return true;
52 }
53 
ConvertToNapiHandlerOfBoolean(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)54 bool ConvertToNapiHandlerOfBoolean(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
55 {
56     bool value = src->GetBoolean();
57     napi_get_boolean(env, value, &dst);
58     return true;
59 }
60 
ConvertToNapiHandlerOfInteger(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)61 bool ConvertToNapiHandlerOfInteger(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
62 {
63     int64_t value = src->GetInt64();
64     napi_create_int64(env, value, &dst);
65     return true;
66 }
67 
ConvertToNapiHandlerOfDouble(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)68 bool ConvertToNapiHandlerOfDouble(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
69 {
70     double value = src->GetDouble();
71     napi_create_double(env, value, &dst);
72     return true;
73 }
74 
ConvertToNapiHandlerOfError(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)75 bool ConvertToNapiHandlerOfError(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
76 {
77     std::string errorName = src->GetErrName();
78     std::string errorMsg = src->GetErrName() + ": " + src->GetErrMsg();
79     napi_value name = nullptr;
80     napi_value message = nullptr;
81     napi_create_string_utf8(env, errorName.c_str(), errorName.length(), &name);
82     napi_create_string_utf8(env, errorMsg.c_str(), errorMsg.length(), &message);
83     napi_create_error(env, name, message, &dst);
84     return true;
85 }
86 
ConvertToNapiHandlerOfStringArray(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)87 bool ConvertToNapiHandlerOfStringArray(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
88 {
89     std::vector<std::string> values = src->GetStringArray();
90     napi_create_array(env, &dst);
91     bool isArray = false;
92     if (napi_is_array(env, dst, &isArray) != napi_ok || !isArray) {
93         WVLOG_E("Create array failed");
94         return false;
95     }
96 
97     int32_t index = 0;
98     for (auto value : values) {
99         napi_value element = nullptr;
100         napi_create_string_utf8(env, value.c_str(), value.length(), &element);
101         napi_set_element(env, dst, index++, element);
102     }
103     return true;
104 }
105 
ConvertToNapiHandlerOfBooleanArray(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)106 bool ConvertToNapiHandlerOfBooleanArray(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
107 {
108     std::vector<bool> values = src->GetBooleanArray();
109     napi_create_array(env, &dst);
110     bool isArray = false;
111     if (napi_is_array(env, dst, &isArray) != napi_ok || !isArray) {
112         WVLOG_E("Create array failed");
113         return false;
114     }
115 
116     int32_t index = 0;
117     for (auto value : values) {
118         napi_value element = nullptr;
119         napi_get_boolean(env, value, &element);
120         napi_set_element(env, dst, index++, element);
121     }
122     return true;
123 }
124 
ConvertToNapiHandlerOfDoubleArray(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)125 bool ConvertToNapiHandlerOfDoubleArray(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
126 {
127     std::vector<double> values = src->GetDoubleArray();
128     napi_create_array(env, &dst);
129     bool isArray = false;
130     if (napi_is_array(env, dst, &isArray) != napi_ok || !isArray) {
131         WVLOG_E("Create array failed");
132         return false;
133     }
134 
135     int32_t index = 0;
136     for (auto value : values) {
137         napi_value element = nullptr;
138         napi_create_double(env, value, &element);
139         napi_set_element(env, dst, index++, element);
140     }
141     return true;
142 }
143 
ConvertToNapiHandlerOfInt64Array(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)144 bool ConvertToNapiHandlerOfInt64Array(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
145 {
146     std::vector<int64_t> values = src->GetInt64Array();
147     napi_create_array(env, &dst);
148     bool isArray = false;
149     if (napi_is_array(env, dst, &isArray) != napi_ok || !isArray) {
150         WVLOG_E("Create array failed");
151         return false;
152     }
153 
154     int32_t index = 0;
155     for (auto value : values) {
156         napi_value element = nullptr;
157         napi_create_int64(env, value, &element);
158         napi_set_element(env, dst, index++, element);
159     }
160     return true;
161 }
162 } // namespace
163 
CreateEnumConstructor(napi_env env,napi_callback_info info)164 napi_value NapiParseUtils::CreateEnumConstructor(napi_env env, napi_callback_info info)
165 {
166     napi_value arg = nullptr;
167     napi_get_cb_info(env, info, nullptr, nullptr, &arg, nullptr);
168     return arg;
169 }
170 
ToInt32Value(napi_env env,int32_t number)171 napi_value NapiParseUtils::ToInt32Value(napi_env env, int32_t number)
172 {
173     napi_value result = nullptr;
174     napi_create_int32(env, number, &result);
175     return result;
176 }
177 
ParseInt32(napi_env env,napi_value argv,int32_t & outValue)178 bool NapiParseUtils::ParseInt32(napi_env env, napi_value argv, int32_t& outValue)
179 {
180     napi_valuetype valueType = napi_undefined;
181 
182     napi_typeof(env, argv, &valueType);
183     if (valueType != napi_number) {
184         return false;
185     }
186 
187     int32_t number = 0;
188     napi_get_value_int32(env, argv, &number);
189     outValue = number;
190 
191     return true;
192 }
193 
ParseUint32(napi_env env,napi_value argv,uint32_t & outValue)194 bool NapiParseUtils::ParseUint32(napi_env env, napi_value argv, uint32_t& outValue)
195 {
196     napi_valuetype valueType = napi_undefined;
197 
198     napi_typeof(env, argv, &valueType);
199     if (valueType != napi_number) {
200         return false;
201     }
202 
203     uint32_t number = 0;
204     napi_get_value_uint32(env, argv, &number);
205     outValue = number;
206 
207     return true;
208 }
209 
ParseUint64(napi_env env,napi_value argv,uint64_t & outValue,bool * lossless)210 bool NapiParseUtils::ParseUint64(napi_env env, napi_value argv, uint64_t& outValue, bool *lossless)
211 {
212     napi_valuetype valueType = napi_undefined;
213 
214     napi_typeof(env, argv, &valueType);
215     if (valueType != napi_bigint) {
216         return false;
217     }
218 
219     uint64_t number = 0;
220     napi_get_value_bigint_uint64(env, argv, &number, lossless);
221     outValue = number;
222 
223     return true;
224 }
225 
ParseInt64(napi_env env,napi_value argv,int64_t & outValue)226 bool NapiParseUtils::ParseInt64(napi_env env, napi_value argv, int64_t& outValue)
227 {
228     napi_valuetype valueType = napi_undefined;
229 
230     napi_typeof(env, argv, &valueType);
231     if (valueType != napi_number) {
232         return false;
233     }
234 
235     int64_t number = 0;
236     napi_get_value_int64(env, argv, &number);
237     outValue = number;
238 
239     return true;
240 }
241 
ParseString(napi_env env,napi_value argv,std::string & outValue)242 bool NapiParseUtils::ParseString(napi_env env, napi_value argv, std::string& outValue)
243 {
244     size_t bufferSize = 0;
245     napi_valuetype valueType = napi_undefined;
246 
247     napi_typeof(env, argv, &valueType);
248     if (valueType != napi_string) {
249         WVLOG_E("Not a valid napi string");
250         return false;
251     }
252     napi_get_value_string_utf8(env, argv, nullptr, 0, &bufferSize);
253     if (bufferSize + 1 > UINT_MAX) {
254         WVLOG_E("String length is too long");
255         return false;
256     }
257     size_t jsStringLength = 0;
258     outValue.resize(bufferSize);
259     napi_get_value_string_utf8(env, argv, outValue.data(), bufferSize + 1, &jsStringLength);
260     if (jsStringLength != bufferSize) {
261         WVLOG_E("The length values obtained twice are different");
262         return false;
263     }
264     return true;
265 }
266 
ParseArrayBuffer(napi_env env,napi_value argv,std::string & outValue)267 bool NapiParseUtils::ParseArrayBuffer(napi_env env, napi_value argv, std::string& outValue)
268 {
269     bool isArrayBuffer = false;
270     if (napi_ok != napi_is_arraybuffer(env, argv, &isArrayBuffer) || !isArrayBuffer) {
271         WVLOG_E("Not a valid napi ArrayBuffer");
272         return false;
273     }
274 
275     char *arrBuf = nullptr;
276     size_t byteLength = 0;
277     napi_get_arraybuffer_info(env, argv, (void**)&arrBuf, &byteLength);
278     if (!arrBuf) {
279         WVLOG_E("Get arrayBuffer info failed");
280         return false;
281     }
282     outValue = std::string(arrBuf, byteLength);
283     return true;
284 }
285 
ParseBoolean(napi_env env,napi_value argv,bool & outValue)286 bool NapiParseUtils::ParseBoolean(napi_env env, napi_value argv, bool& outValue)
287 {
288     napi_valuetype valueType = napi_null;
289 
290     napi_typeof(env, argv, &valueType);
291     if (valueType != napi_boolean) {
292         return false;
293     }
294 
295     bool boolValue;
296     napi_get_value_bool(env, argv, &boolValue);
297     outValue = boolValue;
298     return true;
299 }
300 
ParseStringArray(napi_env env,napi_value argv,std::vector<std::string> & outValue)301 bool NapiParseUtils::ParseStringArray(napi_env env, napi_value argv, std::vector<std::string>& outValue)
302 {
303     bool isArray = false;
304     napi_is_array(env, argv, &isArray);
305     if (!isArray) {
306         return false;
307     }
308 
309     uint32_t arrLen = 0;
310     napi_get_array_length(env, argv, &arrLen);
311     for (uint32_t i = 0; i < arrLen; ++i) {
312         napi_value item = nullptr;
313         napi_get_element(env, argv, i, &item);
314 
315         std::string str;
316         if (ParseString(env, item, str)) {
317             outValue.push_back(str);
318         }
319     }
320 
321     return true;
322 }
323 
ParseInt64Array(napi_env env,napi_value argv,std::vector<int64_t> & outValue)324 bool NapiParseUtils::ParseInt64Array(napi_env env, napi_value argv, std::vector<int64_t>& outValue)
325 {
326     bool isArray = false;
327     napi_is_array(env, argv, &isArray);
328     if (!isArray) {
329         return false;
330     }
331 
332     uint32_t arrLen = 0;
333     napi_get_array_length(env, argv, &arrLen);
334     for (uint32_t i = 0; i < arrLen; ++i) {
335         napi_value item = nullptr;
336         napi_get_element(env, argv, i, &item);
337 
338         int64_t value;
339         if (ParseInt64(env, item, value)) {
340             outValue.push_back(value);
341         }
342     }
343 
344     return true;
345 }
346 
ParseBooleanArray(napi_env env,napi_value argv,std::vector<bool> & outValue)347 bool NapiParseUtils::ParseBooleanArray(napi_env env, napi_value argv, std::vector<bool>& outValue)
348 {
349     bool isArray = false;
350     napi_is_array(env, argv, &isArray);
351     if (!isArray) {
352         return false;
353     }
354 
355     uint32_t arrLen = 0;
356     napi_get_array_length(env, argv, &arrLen);
357     for (uint32_t i = 0; i < arrLen; ++i) {
358         napi_value item = nullptr;
359         napi_get_element(env, argv, i, &item);
360 
361         bool value;
362         if (ParseBoolean(env, item, value)) {
363             outValue.push_back(value);
364         }
365     }
366 
367     return true;
368 }
369 
ParseDoubleArray(napi_env env,napi_value argv,std::vector<double> & outValue)370 bool NapiParseUtils::ParseDoubleArray(napi_env env, napi_value argv, std::vector<double>& outValue)
371 {
372     bool isArray = false;
373     napi_is_array(env, argv, &isArray);
374     if (!isArray) {
375         return false;
376     }
377 
378     uint32_t arrLen = 0;
379     napi_get_array_length(env, argv, &arrLen);
380     for (uint32_t i = 0; i < arrLen; ++i) {
381         napi_value item = nullptr;
382         napi_get_element(env, argv, i, &item);
383 
384         double value;
385         if (ParseDouble(env, item, value)) {
386             outValue.push_back(value);
387         }
388     }
389 
390     return true;
391 }
392 
ParseFloat(napi_env env,napi_value argv,float & outValue)393 bool NapiParseUtils::ParseFloat(napi_env env, napi_value argv, float& outValue)
394 {
395     napi_valuetype valueType = napi_undefined;
396     napi_typeof(env, argv, &valueType);
397     if (valueType != napi_number) {
398         return false;
399     }
400 
401     double value;
402     napi_get_value_double(env, argv, &value);
403     outValue = static_cast<float>(value);
404     return true;
405 }
406 
ParseDouble(napi_env env,napi_value argv,double & outValue)407 bool NapiParseUtils::ParseDouble(napi_env env, napi_value argv, double& outValue)
408 {
409     napi_valuetype valueType = napi_undefined;
410     napi_typeof(env, argv, &valueType);
411     if (valueType != napi_number) {
412         return false;
413     }
414 
415     double value;
416     napi_get_value_double(env, argv, &value);
417     outValue = value;
418     return true;
419 }
420 
421 //static
ConvertNWebToNapiValue(napi_env env,std::shared_ptr<NWebMessage> src,napi_value & dst)422 bool NapiParseUtils::ConvertNWebToNapiValue(napi_env env, std::shared_ptr<NWebMessage> src, napi_value& dst)
423 {
424     if (!src) {
425         WVLOG_E("src is nullptr");
426         return false;
427     }
428     NWebValue::Type type = src->GetType();
429     using ConvertNWebToNapiValueHandler = std::function<bool(napi_env, std::shared_ptr<NWebMessage>, napi_value&)>;
430     static const std::unordered_map<NWebValue::Type, ConvertNWebToNapiValueHandler> functionMap = {
431         { NWebValue::Type::STRING, ConvertToNapiHandlerOfString },
432         { NWebValue::Type::BINARY, ConvertToNapiHandlerOfBinary },
433         { NWebValue::Type::BOOLEAN, ConvertToNapiHandlerOfBoolean },
434         { NWebValue::Type::INTEGER, ConvertToNapiHandlerOfInteger },
435         { NWebValue::Type::DOUBLE, ConvertToNapiHandlerOfDouble },
436         { NWebValue::Type::ERROR, ConvertToNapiHandlerOfError },
437         { NWebValue::Type::STRINGARRAY, ConvertToNapiHandlerOfStringArray },
438         { NWebValue::Type::BOOLEANARRAY, ConvertToNapiHandlerOfBooleanArray },
439         { NWebValue::Type::DOUBLEARRAY, ConvertToNapiHandlerOfDoubleArray },
440         { NWebValue::Type::INT64ARRAY, ConvertToNapiHandlerOfInt64Array }
441     };
442     auto it = functionMap.find(type);
443     if (it == functionMap.end()) {
444         WVLOG_E("This type not support");
445         std::string msgStr = "This type not support";
446         napi_create_string_utf8(env, msgStr.c_str(), msgStr.length(), &dst);
447         return true;
448     }
449     return it->second(env, src, dst);
450 }
451 
IsFormatStringOfLength(const std::string & str)452 bool IsFormatStringOfLength(const std::string &str)
453 {
454     std::regex pattern("^\\d+(px|vp|%)?$");
455     return std::regex_match(str, pattern);
456 }
457 
IsNumberOfLength(const std::string & value)458 bool IsNumberOfLength(const std::string &value)
459 {
460     if (value.empty()) {
461         return false;
462     }
463     return std::all_of(value.begin(), value.end(), [](char i) { return isdigit(i); });
464 }
465 
ParseJsLengthStringToInt(const std::string & input,PixelUnit & type,int32_t & value)466 bool NapiParseUtils::ParseJsLengthStringToInt(const std::string &input, PixelUnit &type, int32_t &value)
467 {
468     if (input.empty() || input.size() >= MAX_STRING_TO_INT32_LENGTH) {
469         return false;
470     }
471     if (!IsFormatStringOfLength(input)) {
472         return false;
473     }
474     if (IsNumberOfLength(input)) {
475         try {
476             value = std::stoi(input);
477         } catch (std::out_of_range&) {
478             WVLOG_E("input trans failed: out of range");
479             value = 0;
480         }
481         type = PixelUnit::VP;
482         return true;
483     }
484     if (input.back() == '%') {
485         std::string trans = input.substr(0, input.length() - 1);
486         if (IsNumberOfLength(trans)) {
487             try {
488                 value = std::stoi(trans);
489             } catch (std::out_of_range&) {
490                 WVLOG_E("input trans failed: out of range");
491                 value = 0;
492             }
493             type = PixelUnit::PERCENTAGE;
494             return true;
495         }
496         return false;
497     }
498     if (input.length() < INTEGER_THREE) {
499         return false;
500     }
501     std::string lastTwo = input.substr(input.length() - INTEGER_TWO);
502     std::string trans = input.substr(0, input.length() - INTEGER_TWO);
503     if (!IsNumberOfLength(trans)) {
504         return false;
505     }
506     if (lastTwo == "px") {
507         try {
508             value = std::stoi(trans);
509         } catch (std::out_of_range&) {
510             WVLOG_E("input trans failed: out of range");
511             value = 0;
512         }
513         type = PixelUnit::PX;
514         return true;
515     } else if (lastTwo == "vp") {
516         try {
517             value = std::stoi(trans);
518         } catch (std::out_of_range&) {
519             WVLOG_E("input trans failed: out of range");
520             value = 0;
521         }
522         type = PixelUnit::VP;
523         return true;
524     }
525     return false;
526 }
527 
ConstructStringFlowbuf(napi_env env,napi_value argv,int & fd,size_t & scriptLength)528 ErrCode NapiParseUtils::ConstructStringFlowbuf(napi_env env, napi_value argv, int& fd, size_t& scriptLength)
529 {
530     napi_valuetype valueType = napi_undefined;
531     napi_typeof(env, argv, &valueType);
532     if (valueType != napi_string) {
533         WVLOG_E("Not a valid napi string");
534         return NWebError::PARAM_CHECK_ERROR;
535     }
536 
537     napi_get_value_string_utf8(env, argv, nullptr, 0, &scriptLength);
538     if (scriptLength + 1 > MAX_FLOWBUF_DATA_SIZE) {
539         WVLOG_E("String length is too long");
540         return NWebError::PARAM_CHECK_ERROR;
541     }
542 
543     // get ashmem
544     auto flowbufferAdapter = OhosAdapterHelper::GetInstance().CreateFlowbufferAdapter();
545     if (!flowbufferAdapter) {
546         WVLOG_E("Create flowbuffer adapter failed");
547         return NWebError::NEW_OOM;
548     }
549     auto ashmem = flowbufferAdapter->CreateAshmem(scriptLength + 1, PROT_READ | PROT_WRITE, fd);
550     if (!ashmem) {
551         return NWebError::NEW_OOM;
552     }
553 
554     // write to ashmem
555     size_t jsStringLength = 0;
556     napi_get_value_string_utf8(env, argv, static_cast<char*>(ashmem), scriptLength + 1, &jsStringLength);
557     if (jsStringLength != scriptLength) {
558         close(fd);
559         WVLOG_E("Write js string failed, the length values are different");
560         return NWebError::PARAM_CHECK_ERROR;
561     }
562     return NWebError::NO_ERROR;
563 }
564 
ConstructArrayBufFlowbuf(napi_env env,napi_value argv,int & fd,size_t & scriptLength)565 ErrCode NapiParseUtils::ConstructArrayBufFlowbuf(napi_env env, napi_value argv, int& fd, size_t& scriptLength)
566 {
567     bool isArrayBuffer = false;
568     if (napi_ok != napi_is_arraybuffer(env, argv, &isArrayBuffer) || !isArrayBuffer) {
569         WVLOG_E("Not a valid napi ArrayBuffer");
570         return NWebError::PARAM_CHECK_ERROR;
571     }
572 
573     char *arrBuf = nullptr;
574     napi_get_arraybuffer_info(env, argv, (void**)&arrBuf, &scriptLength);
575     if (!arrBuf) {
576         WVLOG_E("Get arrayBuffer info failed");
577         return NWebError::PARAM_CHECK_ERROR;
578     }
579 
580     if (scriptLength + 1 > MAX_FLOWBUF_DATA_SIZE) {
581         WVLOG_E("Arraybuffer length is too long");
582         return NWebError::PARAM_CHECK_ERROR;
583     }
584 
585     // get ashmem
586     auto flowbufferAdapter = OhosAdapterHelper::GetInstance().CreateFlowbufferAdapter();
587     if (!flowbufferAdapter) {
588         WVLOG_E("Create flowbuffer adapter failed");
589         return NWebError::NEW_OOM;
590     }
591     auto ashmem = flowbufferAdapter->CreateAshmem(scriptLength + 1, PROT_READ | PROT_WRITE, fd);
592     if (!ashmem) {
593         return NWebError::NEW_OOM;
594     }
595 
596     // write to ashmem
597     if (memcpy_s(ashmem, scriptLength + 1, arrBuf, scriptLength) != EOK) {
598         WVLOG_E("ConstructArrayBufFlowbuf, memory copy failed");
599         return NWebError::NEW_OOM;
600     }
601     static_cast<char*>(ashmem)[scriptLength] = '\0';
602     return NWebError::NO_ERROR;
603 }
604 } // namespace NWeb
605 } // namespace OHOS
606