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 #include "napi_common_want.h"
16 #include "paste_data_record.h"
17 #include "pasteboard_common.h"
18 
19 #include "pasteboard_hilog.h"
20 #include "pasteboard_js_err.h"
21 
22 namespace OHOS {
23 namespace MiscServicesNapi {
24 using namespace OHOS::MiscServices;
25 const size_t ARGC_TYPE_SET2 = 2;
26 constexpr size_t STR_TAIL_LENGTH = 1;
27 constexpr int32_t MIMETYPE_MAX_SIZE = 1024;
28 
GetCallbackErrorValue(napi_env env,int32_t errorCode)29 napi_value GetCallbackErrorValue(napi_env env, int32_t errorCode)
30 {
31     napi_value result = nullptr;
32     napi_value eCode = nullptr;
33     NAPI_CALL(env, napi_create_int32(env, errorCode, &eCode));
34     NAPI_CALL(env, napi_create_object(env, &result));
35     NAPI_CALL(env, napi_set_named_property(env, result, "code", eCode));
36     return result;
37 }
38 
SetCallback(const napi_env & env,const napi_ref & callbackIn,const napi_value * results)39 void SetCallback(const napi_env &env, const napi_ref &callbackIn, const napi_value *results)
40 {
41     if (results == nullptr) {
42         return;
43     }
44     napi_value callback = nullptr;
45     napi_value resultout = nullptr;
46     napi_get_reference_value(env, callbackIn, &callback);
47     napi_call_function(env, nullptr, callback, ARGC_TYPE_SET2, results, &resultout);
48 }
49 
NapiGetNull(napi_env env)50 napi_value NapiGetNull(napi_env env)
51 {
52     napi_value result = nullptr;
53     napi_get_null(env, &result);
54     return result;
55 }
56 
CreateNapiNumber(napi_env env,int32_t num)57 napi_value CreateNapiNumber(napi_env env, int32_t num)
58 {
59     napi_value value = nullptr;
60     napi_create_int32(env, num, &value);
61     return value;
62 }
63 
CreateNapiString(napi_env env,std::string str)64 napi_value CreateNapiString(napi_env env, std::string str)
65 {
66     napi_value value = nullptr;
67     napi_create_string_utf8(env, str.c_str(), NAPI_AUTO_LENGTH, &value);
68     return value;
69 }
70 
71 /* napi_value <-> std::string */
GetValue(napi_env env,napi_value in,std::string & out)72 bool GetValue(napi_env env, napi_value in, std::string &out)
73 {
74     napi_valuetype type = napi_undefined;
75     NAPI_CALL_BASE(env, napi_typeof(env, in, &type), false);
76     NAPI_ASSERT_BASE(env, type == napi_string, "Wrong argument type. String expected.", false);
77 
78     size_t len = 0;
79     NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, in, nullptr, 0, &len), false);
80     if (len < 0) {
81         return false;
82     }
83 
84     size_t length = 0;
85     out.resize(len + STR_TAIL_LENGTH, 0);
86     NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, in, out.data(), len + STR_TAIL_LENGTH, &length), false);
87     out.resize(len);
88 
89     return true;
90 }
91 
92 /* napi_value <-> std::set<Pattern> */
GetValue(napi_env env,napi_value in,std::set<MiscServices::Pattern> & out)93 bool GetValue(napi_env env, napi_value in, std::set<MiscServices::Pattern> &out)
94 {
95     bool isArray = false;
96     NAPI_CALL_BASE(env, napi_is_array(env, in, &isArray), false);
97     if (!isArray) {
98         PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "Wrong argument type. pattern/uint32 array expected.");
99         return false;
100     }
101 
102     uint32_t len = 0;
103     napi_status status = napi_get_array_length(env, in, &len);
104     if (status != napi_ok || len == 0) {
105         PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "napi_get_array_length status = %{public}d, len = %{public}d",
106             status, len);
107         return false;
108     }
109 
110     for (uint32_t i = 0; i < len; i++) {
111         napi_value element;
112         napi_status status = napi_get_element(env, in, i, &element);
113         if (status != napi_ok) {
114             PASTEBOARD_HILOGE(
115                 PASTEBOARD_MODULE_JS_NAPI, "napi_get_element%{public}d err status = %{public}d", i, status);
116             return false;
117         }
118         uint32_t pattern;
119         status = napi_get_value_uint32(env, element, &pattern);
120         if (status != napi_ok) {
121             PASTEBOARD_HILOGE(
122                 PASTEBOARD_MODULE_JS_NAPI, "napi_get_value_uint32 err status = %{public}d", status);
123             return false;
124         }
125         if (pattern >= static_cast<uint32_t>(Pattern::PatternCount)) {
126             PASTEBOARD_HILOGE(
127                 PASTEBOARD_MODULE_JS_NAPI, "Unsurportted pattern value: %{public}d", pattern);
128             return false;
129         }
130         out.insert(static_cast<Pattern>(pattern));
131     }
132     return true;
133 }
134 
135 /* napi_value <-> std::set<Pattern> */
SetValue(napi_env env,std::set<Pattern> & in,napi_value & result)136 napi_status SetValue(napi_env env, std::set<Pattern> &in, napi_value &result)
137 {
138     napi_status status = napi_create_array_with_length(env, in.size(), &result);
139     if (status != napi_ok) {
140         PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "napi_create_array_with_length error status = %{public}d", status);
141         return status;
142     }
143     int i = 0;
144     for (auto &pattern : in) {
145         napi_value element;
146         status = napi_create_uint32(env, static_cast<uint32_t>(pattern), &element);
147         if (status != napi_ok) {
148             PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "napi_create_uint32 error status = %{public}d", status);
149             return status;
150         }
151         status = napi_set_element(env, result, i, element);
152         if (status != napi_ok) {
153             PASTEBOARD_HILOGE(
154                 PASTEBOARD_MODULE_JS_NAPI, "napi_set_element %{public}d err status = %{public}d", i, status);
155             return status;
156         }
157         ++i;
158     }
159     return status;
160 }
161 
SetValue(napi_env env,const std::vector<std::string> & in,napi_value & out)162 napi_status SetValue(napi_env env, const std::vector<std::string> &in, napi_value &out)
163 {
164     PASTEBOARD_HILOGD(PASTEBOARD_MODULE_JS_NAPI, "napi_value <- std::vector<std::string>");
165     napi_status status = napi_create_array_with_length(env, in.size(), &out);
166     if (status != napi_ok) {
167         PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "create array failed, status=%{public}d", status);
168         return status;
169     }
170     int index = 0;
171     for (auto &item : in) {
172         napi_value element = nullptr;
173         status = napi_create_string_utf8(env, item.c_str(), item.size(), &element);
174         if (status != napi_ok) {
175             PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "napi_create_string_utf8 error status = %{public}d", status);
176             return status;
177         }
178         status = napi_set_element(env, out, index++, element);
179         if (status != napi_ok) {
180             PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "napi_set_element failed, status=%{public}d", status);
181             return status;
182         }
183     }
184     return status;
185 }
186 
CheckArgsType(napi_env env,napi_value in,napi_valuetype expectedType,const char * message)187 bool CheckArgsType(napi_env env, napi_value in, napi_valuetype expectedType, const char *message)
188 {
189     napi_valuetype type = napi_undefined;
190     NAPI_CALL_BASE(env, napi_typeof(env, in, &type), false);
191     int32_t errCode = static_cast<int32_t>(JSErrorCode::INVALID_PARAMETERS);
192     if (type != expectedType) {
193         napi_throw_error(env, std::to_string(errCode).c_str(), message);
194         return false;
195     }
196     return true;
197 }
198 
CheckExpression(napi_env env,bool flag,MiscServices::JSErrorCode errCode,const char * message)199 bool CheckExpression(napi_env env, bool flag, MiscServices::JSErrorCode errCode, const char *message)
200 {
201     if (!flag) {
202         NAPI_CALL_BASE(
203             env, napi_throw_error(env, std::to_string(static_cast<int32_t>(errCode)).c_str(), message), false);
204         return false;
205     }
206     return true;
207 }
208 
209 // Check Parameters of CreateData, CreateRecord and AddRecord
CheckArgs(napi_env env,napi_value * argv,size_t argc,std::string & mimeType)210 bool CheckArgs(napi_env env, napi_value *argv, size_t argc, std::string &mimeType)
211 {
212     // 2: CreateRecord, CreateRecord and AddRecord has 2 args.
213     if (!CheckExpression(env, argc >= ARGC_TYPE_SET2, JSErrorCode::INVALID_PARAMETERS,
214         "Parameter error. The number of arguments cannot be less than two.")) {
215         return false;
216     }
217 
218     bool ret = CheckArgsMimeType(env, argv[0], mimeType);
219     if (!ret) {
220         return false;
221     }
222 
223     if (mimeType == MIMETYPE_TEXT_URI || mimeType == MIMETYPE_TEXT_PLAIN || mimeType == MIMETYPE_TEXT_HTML) {
224         if (!CheckArgsType(env, argv[1], napi_string, "Parameter error. The type of mimeType must be string.")) {
225             return false;
226         }
227     } else if (mimeType == MIMETYPE_PIXELMAP) {
228         if (!CheckExpression(env, Media::PixelMapNapi::GetPixelMap(env, argv[1]) != nullptr,
229             JSErrorCode::INVALID_PARAMETERS, "Parameter error. Actual mimeType is not mimetype_pixelmap.")) {
230             return false;
231         }
232     } else if (mimeType == MIMETYPE_TEXT_WANT) {
233         AAFwk::Want want;
234         ret = OHOS::AppExecFwk::UnwrapWant(env, argv[1], want);
235         if (!CheckExpression(env, ret, JSErrorCode::INVALID_PARAMETERS,
236             "Parameter error. Actual mimeType is not mimetype_text_want.")) {
237             return false;
238         }
239     } else {
240         bool isArrayBuffer = false;
241         NAPI_CALL_BASE(env, napi_is_arraybuffer(env, argv[1], &isArrayBuffer), false);
242         if (!CheckExpression(env, isArrayBuffer, JSErrorCode::INVALID_PARAMETERS,
243             "Parameter error. The mimeType is not an arraybuffer.")) {
244             return false;
245         }
246     }
247     return true;
248 }
249 
CheckArgsMimeType(napi_env env,napi_value in,std::string & mimeType)250 bool CheckArgsMimeType(napi_env env, napi_value in, std::string &mimeType)
251 {
252     bool ret = CheckArgsType(env, in, napi_string, "Parameter error. The type of mimeType must be string.");
253     if (!ret) {
254         PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "Wrong argument type. String expected.");
255         return false;
256     }
257     ret = GetValue(env, in, mimeType);
258     if (!ret) {
259         PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "GetValue failed");
260         return false;
261     }
262     if (!CheckExpression(
263         env, mimeType != "", JSErrorCode::INVALID_PARAMETERS, "Parameter error. mimeType cannot be empty.") ||
264         !CheckExpression(env, mimeType.size() <= MIMETYPE_MAX_SIZE, JSErrorCode::INVALID_PARAMETERS,
265         "Parameter error. The length of mimeType cannot be greater than 1024 bytes.")) {
266         return false;
267     }
268     return true;
269 }
270 
CheckArgsArray(napi_env env,napi_value in,std::vector<std::string> & mimeTypes)271 bool CheckArgsArray(napi_env env, napi_value in, std::vector<std::string> &mimeTypes)
272 {
273     napi_valuetype type = napi_undefined;
274     NAPI_CALL_BASE(env, napi_typeof(env, in, &type), false);
275     int32_t errCode = static_cast<int32_t>(JSErrorCode::INVALID_PARAMETERS);
276     if (type != napi_object) {
277         napi_throw_error(env, std::to_string(errCode).c_str(), "Wrong argument type. Object expected.");
278         return false;
279     }
280 
281     bool isArray = false;
282     NAPI_CALL_BASE(env, napi_is_array(env, in, &isArray), false);
283     if (!isArray) {
284         return false;
285     }
286 
287     uint32_t length = 0;
288     NAPI_CALL_BASE(env, napi_get_array_length(env, in, &length), false);
289     napi_value element;
290     for (uint32_t i = 0; i < length; i++) {
291         NAPI_CALL_BASE(env, napi_get_element(env, in, i, &element), false);
292         std::string mimeType;
293         if (!GetValue(env, element, mimeType)) {
294             return false;
295         }
296         mimeTypes.emplace_back(mimeType);
297     }
298 
299     return true;
300 }
301 
CheckArgsFunc(napi_env env,napi_value in,napi_ref & provider)302 bool CheckArgsFunc(napi_env env, napi_value in, napi_ref &provider)
303 {
304     napi_valuetype type = napi_undefined;
305     NAPI_CALL_BASE(env, napi_typeof(env, in, &type), false);
306     int32_t errCode = static_cast<int32_t>(JSErrorCode::INVALID_PARAMETERS);
307     if (type != napi_function) {
308         napi_throw_error(env, std::to_string(errCode).c_str(), "Wrong argument type. function expected.");
309         return false;
310     }
311 
312     NAPI_CALL_BASE(env, napi_create_reference(env, in, 1, &provider), false);
313 
314     return true;
315 }
316 
CheckArgsVector(napi_env env,napi_value in,std::shared_ptr<std::vector<std::pair<std::string,std::shared_ptr<EntryValue>>>> result)317 bool CheckArgsVector(napi_env env, napi_value in,
318     std::shared_ptr<std::vector<std::pair<std::string, std::shared_ptr<EntryValue>>>> result)
319 {
320     napi_valuetype valueType = napi_undefined;
321     NAPI_CALL_BASE(env, napi_typeof(env, in, &valueType), false);
322     if (!CheckExpression(env, valueType == napi_object, JSErrorCode::INVALID_PARAMETERS,
323         "Parameter error. When there is only one parameter, it must be a Record.")) {
324         return false;
325     }
326 
327     napi_value typeValueMap = nullptr;
328     NAPI_CALL_BASE(env, napi_get_property_names(env, in, &typeValueMap), false);
329     uint32_t length = 0;
330     NAPI_CALL_BASE(env, napi_get_array_length(env, typeValueMap, &length), false);
331 
332     PASTEBOARD_HILOGD(PASTEBOARD_MODULE_JS_NAPI, "length = %{public}u", length);
333     for (uint32_t i = 0; i < length; i++) {
334         napi_value mimeTypeNapi = nullptr;
335         NAPI_CALL_BASE(env, napi_get_element(env, typeValueMap, i, &mimeTypeNapi), false);
336         std::string mimeType;
337         bool ret = CheckArgsMimeType(env, mimeTypeNapi, mimeType);
338         if (!ret) {
339             return false;
340         }
341         napi_value value = nullptr;
342         std::shared_ptr<EntryValue> entryValue = std::make_shared<EntryValue>();
343         NAPI_CALL_BASE(env, napi_get_property(env, in, mimeTypeNapi, &value), false);
344         if (!GetNativeValue(env, mimeType, value, *entryValue)) {
345             PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "GetNativeValue failed");
346             return false;
347         }
348         result->emplace_back(std::make_pair(mimeType, entryValue));
349     }
350     return true;
351 }
352 
ConvertEntryValue(napi_env env,napi_value * result,std::string & mimeType,std::shared_ptr<PasteDataEntry> value)353 napi_status ConvertEntryValue(napi_env env, napi_value *result, std::string &mimeType,
354     std::shared_ptr<PasteDataEntry> value)
355 {
356     if (value == nullptr) {
357         PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "failed to find dataEntry");
358         return napi_generic_failure;
359     }
360     if (mimeType == MIMETYPE_TEXT_URI) {
361         std::shared_ptr<Uri> uri = value->ConvertToUri();
362         if (uri == nullptr) {
363             return napi_generic_failure;
364         }
365         std::string str = uri->ToString();
366         return napi_create_string_utf8(env, str.c_str(), str.size(), result);
367     } else if (mimeType == MIMETYPE_TEXT_PLAIN) {
368         std::shared_ptr<std::string> str = value->ConvertToPlianText();
369         if (str == nullptr) {
370             return napi_generic_failure;
371         }
372         return napi_create_string_utf8(env, str->c_str(), str->size(), result);
373     } else if (mimeType == MIMETYPE_TEXT_HTML) {
374         std::shared_ptr<std::string> str = value->ConvertToHtml();
375         if (str == nullptr) {
376             return napi_generic_failure;
377         }
378         return napi_create_string_utf8(env, str->c_str(), str->size(), result);
379     } else if (mimeType == MIMETYPE_PIXELMAP) {
380         std::shared_ptr<Media::PixelMap> pixelMap = value->ConvertToPixelMap();
381         if (!CheckExpression(env, pixelMap != nullptr,
382             JSErrorCode::INVALID_PARAMETERS, "Parameter error. pixelMap get failed")) {
383             return napi_generic_failure;
384         }
385         *result = Media::PixelMapNapi::CreatePixelMap(env, pixelMap);
386         return napi_ok;
387     } else if (mimeType == MIMETYPE_TEXT_WANT) {
388         std::shared_ptr<AAFwk::Want> want = value->ConvertToWant();
389         if (!CheckExpression(env, want != nullptr,
390             JSErrorCode::INVALID_PARAMETERS, "Parameter error. want get failed")) {
391             return napi_generic_failure;
392         }
393         *result = AppExecFwk::WrapWant(env, *want);
394         return napi_ok;
395     } else {
396         std::shared_ptr<MineCustomData> customData = value->ConvertToCustomData();
397         if (customData == nullptr) {
398             return napi_generic_failure;
399         }
400         auto itemData = customData->GetItemData();
401         auto item = itemData.find(mimeType);
402         if (item == itemData.end()) {
403             return napi_generic_failure;
404         }
405         std::vector<uint8_t> dataArray = item->second;
406         void *data = nullptr;
407         size_t len = dataArray.size();
408         NAPI_CALL_BASE(env, napi_create_arraybuffer(env, len, &data, result), napi_generic_failure);
409         if (memcpy_s(data, len, reinterpret_cast<const void *>(dataArray.data()), len) != 0) {
410             PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "memcpy_s failed");
411             return napi_generic_failure;
412         }
413         return napi_ok;
414     }
415 }
416 
GetNativeValue(napi_env env,std::string type,napi_value valueNapi,EntryValue & value)417 bool GetNativeValue(napi_env env, std::string type, napi_value valueNapi, EntryValue &value)
418 {
419     bool isArrayBuffer = false;
420     NAPI_CALL_BASE(env, napi_is_arraybuffer(env, valueNapi, &isArrayBuffer), false);
421     if (isArrayBuffer) {
422         void *data = nullptr;
423         size_t dataLen = 0;
424         NAPI_CALL_BASE(env, napi_get_arraybuffer_info(env, valueNapi, &data, &dataLen), false);
425         value = std::vector<uint8_t>(reinterpret_cast<uint8_t *>(data), reinterpret_cast<uint8_t *>(data) + dataLen);
426         return true;
427     }
428 
429     napi_status status;
430     napi_valuetype valueType = napi_undefined;
431     status = napi_typeof(env, valueNapi, &valueType);
432     NAPI_ASSERT_BASE(env, status == napi_ok,
433         "Parameter error: parameter value type must be ValueType", false);
434     if (valueType == napi_object) {
435         if (type == MIMETYPE_PIXELMAP) {
436             value = std::shared_ptr<OHOS::Media::PixelMap>(nullptr);
437         } else if (type == MIMETYPE_TEXT_WANT) {
438             value = std::shared_ptr<OHOS::AAFwk::Want>(nullptr);
439         } else {
440             PASTEBOARD_HILOGE(PASTEBOARD_MODULE_JS_NAPI, "Parameter error: error ValueType");
441             value = nullptr;
442         }
443     } else if (valueType == napi_string) {
444         value = std::string();
445     } else if (valueType == napi_number) {
446         value = double();
447     } else if (valueType == napi_boolean) {
448         value = bool();
449     } else if (valueType == napi_undefined) {
450         value = std::monostate();
451     } else if (valueType == napi_null) {
452         value = nullptr;
453     }
454     std::visit([&](auto &value) { status = NapiDataUtils::GetValue(env, valueNapi, value); }, value);
455     NAPI_ASSERT_BASE(env, status == napi_ok, "get unifiedRecord failed", false);
456     return true;
457 }
458 } // namespace MiscServicesNapi
459 } // namespace OHOS