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
29namespace OHOS {
30namespace NWeb {
31namespace {
32bool 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
39bool 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_status status = napi_create_arraybuffer(env, msgArr.size(), &arrayData, &dst);
44    if (status != napi_ok) {
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
54bool 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
61bool 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
68bool 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
75bool 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
87bool 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
106bool 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
125bool 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
144bool 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
164napi_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
171napi_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
178bool 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
194bool 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
210bool 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
226bool 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
242bool 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
267bool 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
286bool 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
301bool 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
324bool 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
347bool 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
370bool 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
393bool 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
407bool 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
422bool 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
452bool IsFormatStringOfLength(const std::string &str)
453{
454    std::regex pattern("^\\d+(px|vp|%)?$");
455    return std::regex_match(str, pattern);
456}
457
458bool 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
466bool NapiParseUtils::ParseJsLengthStringToInt(const std::string &input, PixelUnit &type, int32_t &value)
467{
468    if (input.empty()) {
469        return false;
470    }
471    if (!IsFormatStringOfLength(input)) {
472        return false;
473    }
474    if (IsNumberOfLength(input)) {
475        value = std::stoi(input);
476        type = PixelUnit::VP;
477        return true;
478    }
479    if (input.back() == '%') {
480        std::string trans = input.substr(0, input.length() - 1);
481        if (IsNumberOfLength(trans)) {
482            value = std::stoi(trans);
483            type = PixelUnit::PERCENTAGE;
484            return true;
485        }
486        return false;
487    }
488    if (input.length() < INTEGER_THREE) {
489        return false;
490    }
491    std::string lastTwo = input.substr(input.length() - INTEGER_TWO);
492    std::string trans = input.substr(0, input.length() - INTEGER_TWO);
493    if (!IsNumberOfLength(trans)) {
494        return false;
495    }
496    if (lastTwo == "px") {
497        value = std::stoi(trans);
498        type = PixelUnit::PX;
499        return true;
500    } else if (lastTwo == "vp") {
501        value = std::stoi(trans);
502        type = PixelUnit::VP;
503        return true;
504    }
505    return false;
506}
507
508ErrCode NapiParseUtils::ConstructStringFlowbuf(napi_env env, napi_value argv, int& fd, size_t& scriptLength)
509{
510    napi_valuetype valueType = napi_undefined;
511    napi_typeof(env, argv, &valueType);
512    if (valueType != napi_string) {
513        WVLOG_E("Not a valid napi string");
514        return NWebError::PARAM_CHECK_ERROR;
515    }
516
517    napi_get_value_string_utf8(env, argv, nullptr, 0, &scriptLength);
518    if (scriptLength + 1 > MAX_FLOWBUF_DATA_SIZE) {
519        WVLOG_E("String length is too long");
520        return NWebError::PARAM_CHECK_ERROR;
521    }
522
523    // get ashmem
524    auto flowbufferAdapter = OhosAdapterHelper::GetInstance().CreateFlowbufferAdapter();
525    if (!flowbufferAdapter) {
526        WVLOG_E("Create flowbuffer adapter failed");
527        return NWebError::NEW_OOM;
528    }
529    auto ashmem = flowbufferAdapter->CreateAshmem(scriptLength + 1, PROT_READ | PROT_WRITE, fd);
530    if (!ashmem) {
531        return NWebError::NEW_OOM;
532    }
533
534    // write to ashmem
535    size_t jsStringLength = 0;
536    napi_get_value_string_utf8(env, argv, static_cast<char*>(ashmem), scriptLength + 1, &jsStringLength);
537    if (jsStringLength != scriptLength) {
538        close(fd);
539        WVLOG_E("Write js string failed, the length values are different");
540        return NWebError::PARAM_CHECK_ERROR;
541    }
542    return NWebError::NO_ERROR;
543}
544
545ErrCode NapiParseUtils::ConstructArrayBufFlowbuf(napi_env env, napi_value argv, int& fd, size_t& scriptLength)
546{
547    bool isArrayBuffer = false;
548    if (napi_ok != napi_is_arraybuffer(env, argv, &isArrayBuffer) || !isArrayBuffer) {
549        WVLOG_E("Not a valid napi ArrayBuffer");
550        return NWebError::PARAM_CHECK_ERROR;
551    }
552
553    char *arrBuf = nullptr;
554    napi_get_arraybuffer_info(env, argv, (void**)&arrBuf, &scriptLength);
555    if (!arrBuf) {
556        WVLOG_E("Get arrayBuffer info failed");
557        return NWebError::PARAM_CHECK_ERROR;
558    }
559
560    if (scriptLength + 1 > MAX_FLOWBUF_DATA_SIZE) {
561        WVLOG_E("Arraybuffer length is too long");
562        return NWebError::PARAM_CHECK_ERROR;
563    }
564
565    // get ashmem
566    auto flowbufferAdapter = OhosAdapterHelper::GetInstance().CreateFlowbufferAdapter();
567    if (!flowbufferAdapter) {
568        WVLOG_E("Create flowbuffer adapter failed");
569        return NWebError::NEW_OOM;
570    }
571    auto ashmem = flowbufferAdapter->CreateAshmem(scriptLength + 1, PROT_READ | PROT_WRITE, fd);
572    if (!ashmem) {
573        return NWebError::NEW_OOM;
574    }
575
576    // write to ashmem
577    if (memcpy_s(ashmem, scriptLength + 1, arrBuf, scriptLength) != EOK) {
578        WVLOG_E("ConstructArrayBufFlowbuf, memory copy failed");
579        return NWebError::NEW_OOM;
580    }
581    static_cast<char*>(ashmem)[scriptLength] = '\0';
582    return NWebError::NO_ERROR;
583}
584} // namespace NWeb
585} // namespace OHOS
586