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