1 /*
2  * Copyright (c) 2021 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_storage.h"
17 
18 #include <cerrno>
19 #include <cmath>
20 #include <limits>
21 
22 #include "js_common_utils.h"
23 #include "napi_async_call.h"
24 #include "preferences.h"
25 #include "preferences_errno.h"
26 #include "preferences_value.h"
27 
28 using namespace OHOS::NativePreferences;
29 using namespace OHOS::PreferencesJsKit;
30 
31 namespace OHOS {
32 namespace StorageJsKit {
33 #define MAX_KEY_LENGTH Preferences::MAX_KEY_LENGTH
34 #define MAX_VALUE_LENGTH Preferences::MAX_VALUE_LENGTH
35 
36 struct StorageAysncContext : public BaseContext {
37     std::string key;
38     PreferencesValue defValue = PreferencesValue(static_cast<int>(0));
39     std::map<std::string, PreferencesValue> allElements;
40     bool hasKey;
41     std::list<std::string> keysModified;
42     std::vector<std::weak_ptr<PreferencesObserver>> preferencesObservers;
43 
StorageAysncContextOHOS::StorageJsKit::StorageAysncContext44     StorageAysncContext() : hasKey(false)
45     {
46     }
~StorageAysncContextOHOS::StorageJsKit::StorageAysncContext47     virtual ~StorageAysncContext(){};
48 };
49 
50 static __thread napi_ref constructor_;
51 
StorageProxy(std::shared_ptr<OHOS::NativePreferences::Preferences> & value)52 StorageProxy::StorageProxy(std::shared_ptr<OHOS::NativePreferences::Preferences> &value)
53     : value_(value), env_(nullptr), uvQueue_(nullptr)
54 {
55 }
56 
~StorageProxy()57 StorageProxy::~StorageProxy()
58 {
59     for (auto &observer : dataObserver_) {
60         value_->UnRegisterObserver(observer);
61     }
62     dataObserver_.clear();
63 }
64 
Destructor(napi_env env,void * nativeObject,void * finalize_hint)65 void StorageProxy::Destructor(napi_env env, void *nativeObject, void *finalize_hint)
66 {
67     StorageProxy *obj = static_cast<StorageProxy *>(nativeObject);
68     delete obj;
69 }
70 
Init(napi_env env,napi_value exports)71 void StorageProxy::Init(napi_env env, napi_value exports)
72 {
73     napi_property_descriptor descriptors[] = {
74         DECLARE_NAPI_FUNCTION("putSync", SetValueSync),
75         DECLARE_NAPI_FUNCTION("put", SetValue),
76         DECLARE_NAPI_FUNCTION("getSync", GetValueSync),
77         DECLARE_NAPI_FUNCTION("get", GetValue),
78         DECLARE_NAPI_FUNCTION("deleteSync", DeleteSync),
79         DECLARE_NAPI_FUNCTION("delete", Delete),
80         DECLARE_NAPI_FUNCTION("clearSync", ClearSync),
81         DECLARE_NAPI_FUNCTION("clear", Clear),
82         DECLARE_NAPI_FUNCTION("hasSync", HasKeySync),
83         DECLARE_NAPI_FUNCTION("has", HasKey),
84         DECLARE_NAPI_FUNCTION("flushSync", FlushSync),
85         DECLARE_NAPI_FUNCTION("flush", Flush),
86         DECLARE_NAPI_FUNCTION("on", RegisterObserver),
87         DECLARE_NAPI_FUNCTION("off", UnRegisterObserver),
88     };
89     napi_value cons = nullptr;
90     napi_define_class(env, "Storage", NAPI_AUTO_LENGTH, New, nullptr,
91         sizeof(descriptors) / sizeof(napi_property_descriptor), descriptors, &cons);
92 
93     napi_create_reference(env, cons, 1, &constructor_);
94 }
95 
NewInstance(napi_env env,napi_value arg,napi_value * instance)96 napi_status StorageProxy::NewInstance(napi_env env, napi_value arg, napi_value *instance)
97 {
98     napi_status status;
99 
100     const int argc = 1;
101     napi_value argv[argc] = { arg };
102 
103     napi_value cons;
104     status = napi_get_reference_value(env, constructor_, &cons);
105     if (status != napi_ok) {
106         return status;
107     }
108 
109     status = napi_new_instance(env, cons, argc, argv, instance);
110     if (status != napi_ok) {
111         return status;
112     }
113 
114     return napi_ok;
115 }
116 
New(napi_env env,napi_callback_info info)117 napi_value StorageProxy::New(napi_env env, napi_callback_info info)
118 {
119     size_t argc = 1;
120     napi_value args[1] = { 0 };
121     napi_value thiz = nullptr;
122     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
123     if (thiz == nullptr) {
124         LOG_WARN("get this failed");
125         return nullptr;
126     }
127 
128     napi_valuetype valueType = napi_undefined;
129     NAPI_CALL(env, napi_typeof(env, args[0], &valueType));
130     NAPI_ASSERT(env, valueType == napi_string, "input type not string");
131     char *path = new (std::nothrow) char[PATH_MAX];
132     if (path == nullptr) {
133         LOG_ERROR("StorageProxy::New new failed, path is nullptr");
134         return nullptr;
135     }
136     size_t pathLen = 0;
137     napi_status status = napi_get_value_string_utf8(env, args[0], path, PATH_MAX, &pathLen);
138     if (status != napi_ok) {
139         LOG_ERROR("get path failed. ");
140         delete[] path;
141         return nullptr;
142     }
143     // get native object
144     int errCode = 0;
145     std::shared_ptr<OHOS::NativePreferences::Preferences> preference =
146         OHOS::NativePreferences::PreferencesHelper::GetPreferences(path, errCode);
147     delete[] path;
148     NAPI_ASSERT(env, preference != nullptr, "failed to call native");
149     StorageProxy *obj = new (std::nothrow) StorageProxy(preference);
150     if (obj == nullptr) {
151         LOG_ERROR("StorageProxy::New new failed, obj is nullptr");
152         return nullptr;
153     }
154     obj->env_ = env;
155     obj->value_ = std::move(preference);
156     obj->uvQueue_ = std::make_shared<UvQueue>(env);
157     NAPI_CALL(env, napi_wrap(env, thiz, obj, StorageProxy::Destructor, nullptr, nullptr));
158     return thiz;
159 }
160 
CheckNumberType(double input)161 template<typename T> bool CheckNumberType(double input)
162 {
163     if (input > (std::numeric_limits<T>::max)() || input < (std::numeric_limits<T>::min)()) {
164         return false;
165     }
166     return true;
167 }
168 
ParseString(napi_env env,napi_value jsStr,std::string & output,const unsigned int maxLength)169 int ParseString(napi_env env, napi_value jsStr, std::string &output, const unsigned int maxLength)
170 {
171     size_t strBufferSize = 0;
172     napi_status status = napi_get_value_string_utf8(env, jsStr, nullptr, 0, &strBufferSize);
173     if (status != napi_ok) {
174         LOG_ERROR("GetString: get strBufferSize failed, status = %{public}d", status);
175         return ERR;
176     }
177     if (strBufferSize > maxLength) {
178         LOG_ERROR("GetString: string over maximum length.");
179         return ERR;
180     }
181     char *str = new (std::nothrow) char[strBufferSize + 1];
182     if (str == nullptr) {
183         LOG_ERROR("GetString: new failed");
184         return ERR;
185     }
186     size_t valueSize = 0;
187     status = napi_get_value_string_utf8(env, jsStr, str, strBufferSize + 1, &valueSize);
188     if (status != napi_ok) {
189         LOG_ERROR("GetString: get jsVal failed, status = %{public}d", status);
190         delete[] str;
191         return ERR;
192     }
193     str[valueSize] = 0;
194     output = std::string(str);
195     delete[] str;
196     return OK;
197 }
198 
GetValueSync(napi_env env,napi_callback_info info)199 napi_value StorageProxy::GetValueSync(napi_env env, napi_callback_info info)
200 {
201     napi_value thiz = nullptr;
202     size_t argc = 2; // arg count
203     napi_value args[2] = { 0 };
204 
205     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
206     // Check if the number of arguments is 2
207     NAPI_ASSERT(env, argc == 2, "Wrong number of arguments");
208     // get value type
209     napi_valuetype valueType = napi_undefined;
210     NAPI_CALL(env, napi_typeof(env, args[0], &valueType));
211     NAPI_ASSERT(env, valueType == napi_string, "type mismatch for key");
212 
213     // get input key
214     std::string key;
215     NAPI_ASSERT(env, ParseString(env, args[0], key, MAX_KEY_LENGTH) == E_OK, "ParseString failed");
216     StorageProxy *obj = nullptr;
217     NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
218     NAPI_ASSERT(env, (obj != nullptr && obj->value_ != nullptr), "unwrap null native pointer");
219 
220     napi_value output = nullptr;
221     NAPI_CALL(env, napi_typeof(env, args[1], &valueType));
222     if (valueType == napi_number) {
223         double value = 0.0;
224         NAPI_CALL(env, napi_get_value_double(env, args[1], &value));
225         double result = obj->value_->GetDouble(key, value);
226         NAPI_CALL(env, napi_create_double(env, result, &output)); // double
227     } else if (valueType == napi_string) {
228         char *value = new (std::nothrow) char[MAX_VALUE_LENGTH];
229         if (value == nullptr) {
230             LOG_ERROR("StorageProxy::GetValueSync new failed, value is nullptr");
231             return nullptr;
232         }
233         size_t valueSize = 0;
234         napi_get_value_string_utf8(env, args[1], value, MAX_VALUE_LENGTH, &valueSize);
235         // get value
236         std::string result = obj->value_->GetString(key, value);
237         delete[] value;
238         NAPI_CALL(env, napi_create_string_utf8(env, result.c_str(), result.size(), &output));
239     } else if (valueType == napi_boolean) {
240         bool value = false;
241         NAPI_CALL(env, napi_get_value_bool(env, args[1], &value));
242         // get value
243         bool result = obj->value_->GetBool(key, value);
244         NAPI_CALL(env, napi_get_boolean(env, result, &output));
245     } else {
246         NAPI_ASSERT(env, false, "Wrong second parameter type");
247     }
248     return output;
249 }
250 
ParseKey(const napi_env env,const napi_value arg,std::shared_ptr<StorageAysncContext> asyncContext)251 int ParseKey(const napi_env env, const napi_value arg, std::shared_ptr<StorageAysncContext> asyncContext)
252 {
253     napi_valuetype keyType = napi_undefined;
254     napi_typeof(env, arg, &keyType);
255     if (keyType != napi_string) {
256         LOG_ERROR("ParseKey: key type must be string.");
257         std::shared_ptr<JSError> paramError = std::make_shared<ParamTypeError>("The key must be string.");
258         asyncContext->SetError(paramError);
259         return ERR;
260     }
261     size_t keyBufferSize = 0;
262     napi_status status = napi_get_value_string_utf8(env, arg, nullptr, 0, &keyBufferSize);
263     if (status != napi_ok) {
264         LOG_ERROR("ParseKey: get keyBufferSize failed");
265         std::shared_ptr<JSError> paramError = std::make_shared<ParamTypeError>("Failed to get keyBufferSize.");
266         asyncContext->SetError(paramError);
267         return ERR;
268     }
269     if (keyBufferSize > MAX_KEY_LENGTH) {
270         LOG_ERROR("the length of the key is over maximum length.");
271         std::shared_ptr<JSError> paramError = std::make_shared<ParamTypeError>("The key must be less than 1024 bytes.");
272         asyncContext->SetError(paramError);
273         return ERR;
274     }
275     // get input key
276     char *key = new (std::nothrow) char[keyBufferSize + 1];
277     if (key == nullptr) {
278         return ERR;
279     }
280     size_t keySize = 0;
281     status = napi_get_value_string_utf8(env, arg, key, keyBufferSize + 1, &keySize);
282     if (status != napi_ok) {
283         LOG_ERROR("ParseKey: get keySize failed");
284         std::shared_ptr<JSError> paramError = std::make_shared<ParamTypeError>("Failed to get keySize.");
285         asyncContext->SetError(paramError);
286         delete[] key;
287         return ERR;
288     }
289     key[keySize] = 0;
290     asyncContext->key = std::string(key);
291     delete[] key;
292     return OK;
293 }
294 
ParseDefValue(const napi_env env,const napi_value jsVal,std::shared_ptr<StorageAysncContext> asyncContext)295 int ParseDefValue(const napi_env env, const napi_value jsVal, std::shared_ptr<StorageAysncContext> asyncContext)
296 {
297     napi_valuetype valueType = napi_undefined;
298     napi_typeof(env, jsVal, &valueType);
299     if (valueType == napi_number) {
300         double number = 0.0;
301         if (JSUtils::Convert2NativeValue(env, jsVal, number) != E_OK) {
302             LOG_ERROR("ParseDefValue Convert2NativeValue error");
303             std::shared_ptr<JSError> paramError =
304                 std::make_shared<ParamTypeError>("The type of value must be ValueType.");
305             asyncContext->SetError(paramError);
306             return ERR;
307         }
308         asyncContext->defValue = number;
309     } else if (valueType == napi_string) {
310         std::string str;
311         auto ret = JSUtils::Convert2NativeValue(env, jsVal, str);
312         if (ret != E_OK) {
313             LOG_ERROR("ParseDefValue Convert2NativeValue error");
314             if (ret == EXCEED_MAX_LENGTH) {
315                 std::shared_ptr<JSError> paramError =
316                     std::make_shared<ParamTypeError>("The value must be less than 16 * 1024 * 1024 bytes.");
317                 asyncContext->SetError(paramError);
318                 return ERR;
319             }
320             std::shared_ptr<JSError> paramError =
321                 std::make_shared<ParamTypeError>("The type of value must be ValueType.");
322             asyncContext->SetError(paramError);
323             return ERR;
324         }
325         asyncContext->defValue = str;
326     } else if (valueType == napi_boolean) {
327         bool bValue = false;
328         if (JSUtils::Convert2NativeValue(env, jsVal, bValue) != E_OK) {
329             LOG_ERROR("ParseDefValue Convert2NativeValue error");
330             std::shared_ptr<JSError> paramError =
331                 std::make_shared<ParamTypeError>("The type of value must be ValueType.");
332             asyncContext->SetError(paramError);
333             return ERR;
334         }
335         asyncContext->defValue = bValue;
336     } else {
337         LOG_ERROR("Wrong second parameter type");
338         std::shared_ptr<JSError> paramError =
339             std::make_shared<ParamTypeError>("The type of value must be ValueType.");
340         asyncContext->SetError(paramError);
341         return ERR;
342     }
343     return OK;
344 }
345 
GetValue(napi_env env,napi_callback_info info)346 napi_value StorageProxy::GetValue(napi_env env, napi_callback_info info)
347 {
348     LOG_DEBUG("GetValue start");
349     auto context = std::make_shared<StorageAysncContext>();
350     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
351         PRE_CHECK_RETURN_VOID_SET(argc == 2, std::make_shared<ParamNumError>("2 or 3"));
352         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
353         PRE_CHECK_RETURN_VOID(ParseDefValue(env, argv[1], context) == OK);
354         napi_unwrap(env, self, &context->boundObj);
355     };
356     auto exec = [context]() -> int {
357         int errCode = OK;
358         StorageProxy *obj = reinterpret_cast<StorageProxy *>(context->boundObj);
359         if (context->defValue.IsBool()) {
360             bool tmpValue = (bool)obj->value_->GetBool(context->key, context->defValue);
361             context->defValue = PreferencesValue(tmpValue);
362         } else if (context->defValue.IsString()) {
363             std::string tmpValue = obj->value_->GetString(context->key, context->defValue);
364             context->defValue = PreferencesValue(tmpValue);
365         } else if (context->defValue.IsDouble()) {
366             double tmpValue = obj->value_->GetDouble(context->key, context->defValue);
367             context->defValue = PreferencesValue(tmpValue);
368         } else {
369             errCode = ERR;
370         }
371 
372         return errCode;
373     };
374     auto output = [context](napi_env env, napi_value &result) {
375         int errCode = OK;
376         if (context->defValue.IsBool()) {
377             napi_get_boolean(context->env_, context->defValue, &result);
378         } else if (context->defValue.IsString()) {
379             std::string tempStr = (std::string)context->defValue;
380             napi_create_string_utf8(context->env_, tempStr.c_str(), tempStr.size(), &result);
381         } else if (context->defValue.IsDouble()) {
382             napi_create_double(context->env_, context->defValue, &result);
383         } else {
384             errCode = ERR;
385         }
386         PRE_CHECK_RETURN_VOID_SET(errCode == OK, std::make_shared<InnerError>("type error in get value."));
387     };
388     context->SetAction(env, info, input, exec, output);
389 
390     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
391     return AsyncCall::Call(env, context, "GetValue");
392 }
393 
SetValueSync(napi_env env,napi_callback_info info)394 napi_value StorageProxy::SetValueSync(napi_env env, napi_callback_info info)
395 {
396     napi_value thiz = nullptr;
397     size_t argc = 2;
398     napi_value args[2] = { 0 };
399 
400     LOG_DEBUG("SETVALUE");
401     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
402     // Check if the number of arguments is 2
403     NAPI_ASSERT(env, argc == 2, "Wrong number of arguments");
404     // get value type
405     napi_valuetype valueType = napi_undefined;
406     NAPI_CALL(env, napi_typeof(env, args[0], &valueType));
407     NAPI_ASSERT(env, valueType == napi_string, "type mismatch for key");
408 
409     std::string key;
410     NAPI_ASSERT(env, ParseString(env, args[0], key, MAX_KEY_LENGTH) == E_OK, "ParseString failed");
411 
412     StorageProxy *obj = nullptr;
413     NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
414     NAPI_ASSERT(env, (obj != nullptr && obj->value_ != nullptr), "unwrap null native pointer");
415 
416     NAPI_CALL(env, napi_typeof(env, args[1], &valueType));
417     if (valueType == napi_number) {
418         double value = 0.0;
419         NAPI_CALL(env, napi_get_value_double(env, args[1], &value));
420         NAPI_ASSERT(env, obj->value_->PutDouble(key, value) == E_OK, "call PutDouble failed");
421     } else if (valueType == napi_string) {
422         std::string value;
423         NAPI_ASSERT(env, ParseString(env, args[1], value, MAX_VALUE_LENGTH) == E_OK, "ParseString failed");
424         NAPI_ASSERT(env, obj->value_->PutString(key, value) == E_OK, "call PutString failed");
425     } else if (valueType == napi_boolean) {
426         bool value = false;
427         NAPI_CALL(env, napi_get_value_bool(env, args[1], &value));
428         // get value
429         NAPI_ASSERT(env, obj->value_->PutBool(key, value) == E_OK, "call PutBool failed");
430     } else {
431         NAPI_ASSERT(env, false, "Wrong second parameter type");
432     }
433     return nullptr;
434 }
435 
SetValue(napi_env env,napi_callback_info info)436 napi_value StorageProxy::SetValue(napi_env env, napi_callback_info info)
437 {
438     LOG_DEBUG("SetValue start");
439     auto context = std::make_shared<StorageAysncContext>();
440     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
441         PRE_CHECK_RETURN_VOID_SET(argc == 2, std::make_shared<ParamNumError>("2 or 3"));
442         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
443         PRE_CHECK_RETURN_VOID(ParseDefValue(env, argv[1], context) == OK);
444         napi_unwrap(env, self, &context->boundObj);
445     };
446     auto exec = [context]() -> int {
447         int errCode = OK;
448         StorageProxy *obj = reinterpret_cast<StorageProxy *>(context->boundObj);
449         if (context->defValue.IsBool()) {
450             errCode = obj->value_->PutBool(context->key, context->defValue);
451         } else if (context->defValue.IsString()) {
452             errCode = obj->value_->PutString(context->key, context->defValue);
453         } else if (context->defValue.IsDouble()) {
454             errCode = obj->value_->PutDouble(context->key, context->defValue);
455         } else {
456             errCode = ERR;
457         }
458         return errCode;
459     };
460     auto output = [context](napi_env env, napi_value &result) {
461         napi_status status = napi_get_undefined(context->env_, &result);
462         PRE_CHECK_RETURN_VOID_SET(status == napi_ok,
463             std::make_shared<InnerError>("Failed to get undefined when setting value."));
464     };
465     context->SetAction(env, info, input, exec, output);
466 
467     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
468     return AsyncCall::Call(env, context, "SetValue");
469 }
470 
DeleteSync(napi_env env,napi_callback_info info)471 napi_value StorageProxy::DeleteSync(napi_env env, napi_callback_info info)
472 {
473     napi_value thiz = nullptr;
474     size_t argc = 1;
475     napi_value args[1] = { 0 };
476     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
477     NAPI_ASSERT(env, argc == 1, "Wrong number of arguments");
478     // get value type
479     napi_valuetype valueType;
480     NAPI_CALL(env, napi_typeof(env, args[0], &valueType));
481     NAPI_ASSERT(env, valueType == napi_string, "type mismatch for key");
482 
483     std::string key;
484     NAPI_ASSERT(env, ParseString(env, args[0], key, MAX_KEY_LENGTH) == E_OK, "ParseString failed");
485     StorageProxy *obj = nullptr;
486     NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
487     int result = obj->value_->Delete(key);
488     NAPI_ASSERT(env, result == E_OK, "call Delete failed");
489     LOG_DEBUG("Delete");
490     return nullptr;
491 }
492 
Delete(napi_env env,napi_callback_info info)493 napi_value StorageProxy::Delete(napi_env env, napi_callback_info info)
494 {
495     LOG_DEBUG("Delete start");
496     auto context = std::make_shared<StorageAysncContext>();
497     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
498         PRE_CHECK_RETURN_VOID_SET(argc == 1, std::make_shared<ParamNumError>("1 or 2"));
499         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
500         napi_unwrap(env, self, &context->boundObj);
501     };
502     auto exec = [context]() -> int {
503         StorageProxy *obj = reinterpret_cast<StorageProxy *>(context->boundObj);
504         int errCode = obj->value_->Delete(context->key);
505         return errCode;
506     };
507     auto output = [context](napi_env env, napi_value &result) {
508         napi_status status = napi_get_undefined(context->env_, &result);
509         PRE_CHECK_RETURN_VOID_SET(status == napi_ok,
510             std::make_shared<InnerError>("Failed to get undefined when deleting value."));
511     };
512     context->SetAction(env, info, input, exec, output);
513 
514     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
515     return AsyncCall::Call(env, context, "Delete");
516 }
517 
HasKeySync(napi_env env,napi_callback_info info)518 napi_value StorageProxy::HasKeySync(napi_env env, napi_callback_info info)
519 {
520     napi_value thiz = nullptr;
521     size_t argc = 1;
522     napi_value args[1] = { 0 };
523     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
524     NAPI_ASSERT(env, argc == 1, "Wrong number of arguments");
525     // get value type
526     napi_valuetype valueType;
527     NAPI_CALL(env, napi_typeof(env, args[0], &valueType));
528     NAPI_ASSERT(env, valueType == napi_string, "type mismatch for key");
529 
530     std::string key;
531     NAPI_ASSERT(env, ParseString(env, args[0], key, MAX_KEY_LENGTH) == E_OK, "ParseString failed");
532     StorageProxy *obj = nullptr;
533     NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
534     bool result = obj->value_->HasKey(key);
535     napi_value output = nullptr;
536     NAPI_CALL(env, napi_get_boolean(env, result, &output));
537     LOG_DEBUG("HasKey");
538     return output;
539 }
540 
HasKey(napi_env env,napi_callback_info info)541 napi_value StorageProxy::HasKey(napi_env env, napi_callback_info info)
542 {
543     LOG_DEBUG("HasKeySync start");
544     auto context = std::make_shared<StorageAysncContext>();
545     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
546         PRE_CHECK_RETURN_VOID_SET(argc == 1, std::make_shared<ParamNumError>("1 or 2"));
547         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
548         napi_unwrap(env, self, &context->boundObj);
549     };
550     auto exec = [context]() -> int {
551         StorageProxy *obj = reinterpret_cast<StorageProxy *>(context->boundObj);
552         context->hasKey = obj->value_->HasKey(context->key);
553 
554         return OK;
555     };
556     auto output = [context](napi_env env, napi_value &result) {
557         napi_status status = napi_get_boolean(context->env_, context->hasKey, &result);
558         PRE_CHECK_RETURN_VOID_SET(status == napi_ok,
559             std::make_shared<InnerError>("Failed to get boolean when having key."));
560     };
561     context->SetAction(env, info, input, exec, output);
562 
563     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
564     return AsyncCall::Call(env, context, "HasKey");
565 }
566 
Flush(napi_env env,napi_callback_info info)567 napi_value StorageProxy::Flush(napi_env env, napi_callback_info info)
568 {
569     LOG_DEBUG("Flush start");
570     auto context = std::make_shared<StorageAysncContext>();
571     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
572         PRE_CHECK_RETURN_VOID_SET(argc == 0, std::make_shared<ParamNumError>("0 or 1"));
573         napi_unwrap(env, self, &context->boundObj);
574     };
575     auto exec = [context]() -> int {
576         StorageProxy *obj = reinterpret_cast<StorageProxy *>(context->boundObj);
577         return obj->value_->FlushSync();
578     };
579     auto output = [context](napi_env env, napi_value &result) {
580         napi_status status = napi_get_undefined(context->env_, &result);
581         PRE_CHECK_RETURN_VOID_SET(status == napi_ok,
582             std::make_shared<InnerError>("Failed to get undefined when flushing."));
583     };
584     context->SetAction(env, info, input, exec, output);
585 
586     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
587     return AsyncCall::Call(env, context, "Flush");
588 }
589 
FlushSync(napi_env env,napi_callback_info info)590 napi_value StorageProxy::FlushSync(napi_env env, napi_callback_info info)
591 {
592     napi_value thiz = nullptr;
593     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thiz, nullptr));
594     StorageProxy *obj = nullptr;
595     NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
596     int result = obj->value_->FlushSync();
597     napi_value output = nullptr;
598     NAPI_CALL(env, napi_create_int64(env, result, &output));
599     LOG_DEBUG("FlushSync");
600     return output;
601 }
602 
ClearSync(napi_env env,napi_callback_info info)603 napi_value StorageProxy::ClearSync(napi_env env, napi_callback_info info)
604 {
605     napi_value thiz = nullptr;
606     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thiz, nullptr));
607     StorageProxy *obj = nullptr;
608     NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
609     int result = obj->value_->Clear();
610     NAPI_ASSERT(env, result == E_OK, "call Clear failed");
611     LOG_DEBUG("Clear");
612     return nullptr;
613 }
614 
Clear(napi_env env,napi_callback_info info)615 napi_value StorageProxy::Clear(napi_env env, napi_callback_info info)
616 {
617     LOG_DEBUG("Flush start");
618     auto context = std::make_shared<StorageAysncContext>();
619     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
620         PRE_CHECK_RETURN_VOID_SET(argc == 0, std::make_shared<ParamNumError>("0 or 1"));
621         napi_unwrap(env, self, &context->boundObj);
622     };
623     auto exec = [context]() -> int {
624         StorageProxy *obj = reinterpret_cast<StorageProxy *>(context->boundObj);
625         return obj->value_->Clear();
626     };
627     auto output = [context](napi_env env, napi_value &result) {
628         napi_status status = napi_get_undefined(context->env_, &result);
629         PRE_CHECK_RETURN_VOID_SET(status == napi_ok,
630             std::make_shared<InnerError>("Failed to get undefined when clearing."));
631     };
632     context->SetAction(env, info, input, exec, output);
633 
634     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
635     return AsyncCall::Call(env, context, "Clear");
636 }
637 
RegisterObserver(napi_env env,napi_callback_info info)638 napi_value StorageProxy::RegisterObserver(napi_env env, napi_callback_info info)
639 {
640     napi_value thiz = nullptr;
641     size_t argc = 2;
642     napi_value args[2] = { 0 };
643 
644     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
645     napi_valuetype type;
646     NAPI_CALL(env, napi_typeof(env, args[0], &type));
647     NAPI_ASSERT(env, type == napi_string, "type should be 'change'");
648 
649     std::string change;
650     int ret = JSUtils::Convert2NativeValue(env, args[0], change);
651     NAPI_ASSERT(env, ret == OK && change == "change", "type should be 'change'");
652 
653     NAPI_CALL(env, napi_typeof(env, args[1], &type));
654     NAPI_ASSERT(env, type == napi_function, "observer not function type");
655 
656     StorageProxy *obj = nullptr;
657     NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
658     obj->RegisterObserver(args[1]);
659 
660     return nullptr;
661 }
662 
UnRegisterObserver(napi_env env,napi_callback_info info)663 napi_value StorageProxy::UnRegisterObserver(napi_env env, napi_callback_info info)
664 {
665     napi_value thiz = nullptr;
666     size_t argc = 2;
667     napi_value args[2] = { 0 };
668 
669     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
670     napi_valuetype type;
671     NAPI_CALL(env, napi_typeof(env, args[0], &type));
672     NAPI_ASSERT(env, type == napi_string, "key not string type");
673 
674     std::string change;
675     int ret = JSUtils::Convert2NativeValue(env, args[0], change);
676     NAPI_ASSERT(env, ret == OK && change == "change", "type should be 'change'");
677 
678     NAPI_CALL(env, napi_typeof(env, args[1], &type));
679     NAPI_ASSERT(env, type == napi_function, "observer not function type");
680 
681     StorageProxy *obj = nullptr;
682     NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
683     obj->UnRegisterObserver(args[1]);
684 
685     return nullptr;
686 }
687 
HasRegisteredObserver(napi_value callback)688 bool StorageProxy::HasRegisteredObserver(napi_value callback)
689 {
690     std::lock_guard<std::mutex> lck(listMutex_);
691     for (auto &it : dataObserver_) {
692         if (JSUtils::Equals(env_, callback, it->GetCallback())) {
693             LOG_INFO("The observer has already subscribed.");
694             return true;
695         }
696     }
697     return false;
698 }
699 
RegisterObserver(napi_value callback)700 void StorageProxy::RegisterObserver(napi_value callback)
701 {
702     if (!HasRegisteredObserver(callback)) {
703         auto observer = std::make_shared<JSPreferencesObserver>(uvQueue_, callback);
704         value_->RegisterObserver(observer);
705         dataObserver_.push_back(observer);
706         LOG_DEBUG("The observer subscribed success.");
707     }
708 }
709 
UnRegisterObserver(napi_value callback)710 void StorageProxy::UnRegisterObserver(napi_value callback)
711 {
712     std::lock_guard<std::mutex> lck(listMutex_);
713     auto it = dataObserver_.begin();
714     while (it != dataObserver_.end()) {
715         if (!JSUtils::Equals(env_, callback, (*it)->GetCallback())) {
716             ++it;
717             continue; // specified observer and not current iterator
718         }
719         value_->UnRegisterObserver(*it);
720         it = dataObserver_.erase(it);
721         LOG_DEBUG("The observer unsubscribed success.");
722     }
723 }
724 } // namespace StorageJsKit
725 } // namespace OHOS
726