• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_preferences.h"
17 
18 #include <algorithm>
19 #include <cerrno>
20 #include <climits>
21 #include <cmath>
22 #include <list>
23 
24 #include "js_sendable_utils.h"
25 #include "napi_async_call.h"
26 #include "napi_preferences_error.h"
27 #include "preferences.h"
28 #include "preferences_errno.h"
29 #include "preferences_value.h"
30 
31 using namespace OHOS::NativePreferences;
32 using namespace OHOS::PreferencesJsKit;
33 namespace OHOS::Sendable::JSPreferences {
34 #define MAX_KEY_LENGTH Preferences::MAX_KEY_LENGTH
35 #define MAX_VALUE_LENGTH Preferences::MAX_VALUE_LENGTH
36 
37 struct PreferencesAysncContext : public BaseContext {
38     std::weak_ptr<Preferences> instance_;
39     std::string key;
40     PreferencesValue defValue = PreferencesValue(static_cast<int64_t>(0));
41     napi_ref inputValueRef = nullptr;
42     std::unordered_map<std::string, PreferencesValue> allElements;
43     bool hasKey = false;
44     std::vector<std::weak_ptr<PreferencesObserver>> preferencesObservers;
45 
PreferencesAysncContextOHOS::Sendable::JSPreferences::PreferencesAysncContext46     PreferencesAysncContext()
47     {
48     }
~PreferencesAysncContextOHOS::Sendable::JSPreferences::PreferencesAysncContext49     virtual ~PreferencesAysncContext(){};
50 };
51 
52 static thread_local napi_ref constructor_ = nullptr;
53 
PreferencesProxy()54 PreferencesProxy::PreferencesProxy()
55 {
56 }
57 
~PreferencesProxy()58 PreferencesProxy::~PreferencesProxy()
59 {
60     std::lock_guard<decltype(mutex_)> lockGuard(mutex_);
61     observers_.clear();
62 }
63 
Destructor(napi_env env,void * nativeObject,void * finalize_hint)64 void PreferencesProxy::Destructor(napi_env env, void *nativeObject, void *finalize_hint)
65 {
66     PreferencesProxy *obj = static_cast<PreferencesProxy *>(nativeObject);
67     if (obj != nullptr) {
68         delete obj;
69     }
70 }
71 
Init(napi_env env,napi_value exports)72 void PreferencesProxy::Init(napi_env env, napi_value exports)
73 {
74     napi_property_descriptor descriptors[] = {
75         DECLARE_NAPI_FUNCTION_WITH_DATA("put", SetValue, ASYNC),
76         DECLARE_NAPI_FUNCTION_WITH_DATA("putSync", SetValue, SYNC),
77         DECLARE_NAPI_FUNCTION_WITH_DATA("get", GetValue, ASYNC),
78         DECLARE_NAPI_FUNCTION_WITH_DATA("getSync", GetValue, SYNC),
79         DECLARE_NAPI_FUNCTION_WITH_DATA("getAll", GetAll, ASYNC),
80         DECLARE_NAPI_FUNCTION_WITH_DATA("getAllSync", GetAll, SYNC),
81         DECLARE_NAPI_FUNCTION_WITH_DATA("delete", Delete, ASYNC),
82         DECLARE_NAPI_FUNCTION_WITH_DATA("deleteSync", Delete, SYNC),
83         DECLARE_NAPI_FUNCTION_WITH_DATA("clear", Clear, ASYNC),
84         DECLARE_NAPI_FUNCTION_WITH_DATA("clearSync", Clear, SYNC),
85         DECLARE_NAPI_FUNCTION_WITH_DATA("has", HasKey, ASYNC),
86         DECLARE_NAPI_FUNCTION_WITH_DATA("hasSync", HasKey, SYNC),
87         DECLARE_NAPI_FUNCTION_WITH_DATA("flush", Flush, ASYNC),
88         DECLARE_NAPI_FUNCTION_WITH_DATA("flushSync", Flush, SYNC),
89         DECLARE_NAPI_FUNCTION("on", RegisterObserver),
90         DECLARE_NAPI_FUNCTION("off", UnregisterObserver),
91     };
92 
93     napi_value cons = nullptr;
94     napi_define_sendable_class(env, "Preferences", NAPI_AUTO_LENGTH, New, nullptr,
95         sizeof(descriptors) / sizeof(napi_property_descriptor), descriptors, nullptr, &cons);
96 
97     napi_create_reference(env, cons, 1, &constructor_);
98 }
99 
NewInstance(napi_env env,std::shared_ptr<OHOS::NativePreferences::Preferences> value,napi_value * instance)100 napi_status PreferencesProxy::NewInstance(
101     napi_env env, std::shared_ptr<OHOS::NativePreferences::Preferences> value, napi_value *instance)
102 {
103     if (value == nullptr) {
104         LOG_ERROR("PreferencesProxy::NewInstance get native preferences is null");
105         return napi_invalid_arg;
106     }
107     napi_value cons;
108     napi_status status = napi_get_reference_value(env, constructor_, &cons);
109     if (status != napi_ok) {
110         return status;
111     }
112 
113     status = napi_new_instance(env, cons, 0, nullptr, instance);
114     if (status != napi_ok) {
115         return status;
116     }
117 
118     PreferencesProxy *obj = new (std::nothrow) PreferencesProxy();
119     if (obj == nullptr) {
120         LOG_ERROR("PreferencesProxy::New new failed, obj is nullptr");
121         return napi_invalid_arg;
122     }
123     obj->SetInstance(value);
124     status = napi_wrap_sendable(env, *instance, obj, PreferencesProxy::Destructor, nullptr);
125     if (status != napi_ok) {
126         delete obj;
127         return status;
128     }
129 
130     return napi_ok;
131 }
132 
New(napi_env env,napi_callback_info info)133 napi_value PreferencesProxy::New(napi_env env, napi_callback_info info)
134 {
135     napi_value thiz = nullptr;
136     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thiz, nullptr));
137     if (thiz == nullptr) {
138         LOG_WARN("get this failed");
139         return nullptr;
140     }
141     return thiz;
142 }
143 
ParseKey(napi_env env,const napi_value arg,std::shared_ptr<PreferencesAysncContext> context)144 int ParseKey(napi_env env, const napi_value arg, std::shared_ptr<PreferencesAysncContext> context)
145 {
146     int32_t rc = Utils::ConvertFromSendable(env, arg, context->key);
147     PRE_CHECK_RETURN_ERR_SET(rc == napi_ok, std::make_shared<ParamTypeError>("The key must be string."));
148     PRE_CHECK_RETURN_ERR_SET(context->key.length() <= MAX_KEY_LENGTH, std::make_shared<ParamTypeError>("The key must "
149                                                                                                        "be less than "
150                                                                                                        "80 bytes."));
151     return OK;
152 }
153 
ParseDefValue(const napi_env env,const napi_value jsVal,std::shared_ptr<PreferencesAysncContext> context)154 int ParseDefValue(const napi_env env, const napi_value jsVal, std::shared_ptr<PreferencesAysncContext> context)
155 {
156     int32_t rc = Utils::ConvertFromSendable(env, jsVal, context->defValue.value_);
157     if (rc == EXCEED_MAX_LENGTH) {
158         PRE_CHECK_RETURN_ERR_SET(rc == napi_ok,
159             std::make_shared<ParamTypeError>("The type of value must be less then 16 * 1024 * 1024 btyes."));
160     }
161     PRE_CHECK_RETURN_ERR_SET(rc == napi_ok, std::make_shared<ParamTypeError>("The type of value must be ValueType."));
162     return OK;
163 }
164 
GetSelfInstance(napi_env env,napi_value self)165 std::pair<PreferencesProxy *, std::weak_ptr<Preferences>> PreferencesProxy::GetSelfInstance(
166     napi_env env, napi_value self)
167 {
168     void *boundObj = nullptr;
169     napi_unwrap_sendable(env, self, &boundObj);
170     if (boundObj != nullptr) {
171         PreferencesProxy *obj = reinterpret_cast<PreferencesProxy *>(boundObj);
172         return { obj, obj->GetInstance() };
173     }
174     return { nullptr, std::weak_ptr<Preferences>() };
175 }
176 
GetAllExecute(napi_env env,std::shared_ptr<PreferencesAysncContext> context,napi_value & result)177 int GetAllExecute(napi_env env, std::shared_ptr<PreferencesAysncContext> context, napi_value &result)
178 {
179     std::vector<napi_property_descriptor> descriptors;
180     descriptors.reserve(context->allElements.size());
181     for (const auto &[key, value] : context->allElements) {
182         descriptors.push_back(napi_property_descriptor(
183             DECLARE_NAPI_DEFAULT_PROPERTY(key.c_str(), Utils::ConvertToSendable(env, value.value_))));
184     }
185     napi_create_sendable_object_with_properties(env, descriptors.size(), descriptors.data(), &result);
186     return OK;
187 }
188 
GetAll(napi_env env,napi_callback_info info)189 napi_value PreferencesProxy::GetAll(napi_env env, napi_callback_info info)
190 {
191     LOG_DEBUG("GetAll start");
192     auto context = std::make_shared<PreferencesAysncContext>();
193     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
194         PRE_CHECK_RETURN_VOID_SET(argc == 0, std::make_shared<ParamNumError>("0 or 1"));
195         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
196         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
197                                                                                              "getting all."));
198     };
199     auto exec = [context]() -> int {
200         auto instance = context->instance_.lock();
201         if (instance == nullptr) {
202             return E_INNER_ERROR;
203         }
204         context->allElements = instance->GetAllDatas();
205         return OK;
206     };
207     auto output = [context](napi_env env, napi_value &result) { GetAllExecute(env, context, result); };
208     context->SetAction(env, info, input, exec, output);
209 
210     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
211     return AsyncCall::Call(env, context, "SendablePreferencesGetAll");
212 }
213 
GetValue(napi_env env,napi_callback_info info)214 napi_value PreferencesProxy::GetValue(napi_env env, napi_callback_info info)
215 {
216     LOG_DEBUG("GetValue start");
217     auto context = std::make_shared<PreferencesAysncContext>();
218     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
219         PRE_CHECK_RETURN_VOID_SET(argc == 2, std::make_shared<ParamNumError>("2 or 3"));
220         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
221         napi_create_reference(env, argv[1], 1, &context->inputValueRef);
222         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
223         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
224                                                                                              "getting value."));
225     };
226     auto exec = [context]() -> int {
227         auto instance = context->instance_.lock();
228         if (instance == nullptr) {
229             return E_INNER_ERROR;
230         }
231         context->defValue = instance->Get(context->key, context->defValue);
232         return OK;
233     };
234     auto output = [context](napi_env env, napi_value &result) {
235         if (context->defValue.IsLong()) {
236             LOG_DEBUG("GetValue get default value.");
237             napi_get_reference_value(env, context->inputValueRef, &result);
238         } else {
239             result = Utils::ConvertToSendable(env, context->defValue.value_);
240         }
241         napi_delete_reference(env, context->inputValueRef);
242         PRE_CHECK_RETURN_VOID_SET(result != nullptr, std::make_shared<InnerError>("Failed to delete reference when "
243                                                                                   "getting value."));
244     };
245     context->SetAction(env, info, input, exec, output);
246 
247     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
248     return AsyncCall::Call(env, context, "SendablePreferencesGetValue");
249 }
250 
SetValue(napi_env env,napi_callback_info info)251 napi_value PreferencesProxy::SetValue(napi_env env, napi_callback_info info)
252 {
253     LOG_DEBUG("SetValue start");
254     auto context = std::make_shared<PreferencesAysncContext>();
255     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
256         PRE_CHECK_RETURN_VOID_SET(argc == 2, std::make_shared<ParamNumError>("2 or 3"));
257         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
258         PRE_CHECK_RETURN_VOID(ParseDefValue(env, argv[1], context) == OK);
259         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
260         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
261                                                                                              "setting value."));
262     };
263     auto exec = [context]() -> int {
264         auto instance = context->instance_.lock();
265         if (instance == nullptr) {
266             return E_INNER_ERROR;
267         }
268         return instance->Put(context->key, context->defValue);
269     };
270     auto output = [context](napi_env env, napi_value &result) {
271         napi_status status = napi_get_undefined(env, &result);
272         PRE_CHECK_RETURN_VOID_SET(status == napi_ok, std::make_shared<InnerError>("Failed to get undefined when "
273                                                                                   "setting value."));
274         LOG_DEBUG("SetValue end.");
275     };
276     context->SetAction(env, info, input, exec, output);
277 
278     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
279     return AsyncCall::Call(env, context, "SendablePreferencesSetValue");
280 }
281 
Delete(napi_env env,napi_callback_info info)282 napi_value PreferencesProxy::Delete(napi_env env, napi_callback_info info)
283 {
284     LOG_DEBUG("Delete start");
285     auto context = std::make_shared<PreferencesAysncContext>();
286     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
287         PRE_CHECK_RETURN_VOID_SET(argc == 1, std::make_shared<ParamNumError>("1 or 2"));
288         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
289         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
290         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
291                                                                                              "deleting value."));
292     };
293     auto exec = [context]() -> int {
294         auto instance = context->instance_.lock();
295         if (instance == nullptr) {
296             return E_INNER_ERROR;
297         }
298         return instance->Delete(context->key);
299     };
300     auto output = [context](napi_env env, napi_value &result) {
301         napi_status status = napi_get_undefined(env, &result);
302         PRE_CHECK_RETURN_VOID_SET(status == napi_ok, std::make_shared<InnerError>("Failed to get undefined when "
303                                                                                   "deleting value."));
304         LOG_DEBUG("Delete end.");
305     };
306     context->SetAction(env, info, input, exec, output);
307 
308     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
309     return AsyncCall::Call(env, context, "SendablePreferencesDelete");
310 }
311 
HasKey(napi_env env,napi_callback_info info)312 napi_value PreferencesProxy::HasKey(napi_env env, napi_callback_info info)
313 {
314     LOG_DEBUG("HasKey start");
315     auto context = std::make_shared<PreferencesAysncContext>();
316     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
317         PRE_CHECK_RETURN_VOID_SET(argc == 1, std::make_shared<ParamNumError>("1 or 2"));
318         PRE_CHECK_RETURN_VOID(ParseKey(env, argv[0], context) == OK);
319         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
320         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
321                                                                                              "having key."));
322     };
323     auto exec = [context]() -> int {
324         auto instance = context->instance_.lock();
325         if (instance == nullptr) {
326             return E_INNER_ERROR;
327         }
328         context->hasKey = instance->HasKey(context->key);
329         return OK;
330     };
331     auto output = [context](napi_env env, napi_value &result) {
332         napi_status status = napi_get_boolean(env, context->hasKey, &result);
333         PRE_CHECK_RETURN_VOID_SET(status == napi_ok, std::make_shared<InnerError>("Failed to get boolean when having "
334                                                                                   "key."));
335         LOG_DEBUG("HasKey end.");
336     };
337     context->SetAction(env, info, input, exec, output);
338 
339     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
340     return AsyncCall::Call(env, context, "SendablePreferencesHasKey");
341 }
342 
Flush(napi_env env,napi_callback_info info)343 napi_value PreferencesProxy::Flush(napi_env env, napi_callback_info info)
344 {
345     LOG_DEBUG("Flush start");
346     auto context = std::make_shared<PreferencesAysncContext>();
347     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
348         PRE_CHECK_RETURN_VOID_SET(argc == 0, std::make_shared<ParamNumError>("0 or 1"));
349         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
350         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap when "
351                                                                                              "flushing."));
352     };
353     auto exec = [context]() -> int {
354         auto instance = context->instance_.lock();
355         if (instance == nullptr) {
356             return E_INNER_ERROR;
357         }
358         return instance->FlushSync();
359     };
360     auto output = [context](napi_env env, napi_value &result) {
361         napi_status status = napi_get_undefined(env, &result);
362         PRE_CHECK_RETURN_VOID_SET(status == napi_ok, std::make_shared<InnerError>("Failed to get undefined when "
363                                                                                   "flushing."));
364         LOG_DEBUG("Flush end.");
365     };
366     context->SetAction(env, info, input, exec, output);
367 
368     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
369     return AsyncCall::Call(env, context, "SendablePreferencesFlush");
370 }
371 
Clear(napi_env env,napi_callback_info info)372 napi_value PreferencesProxy::Clear(napi_env env, napi_callback_info info)
373 {
374     LOG_DEBUG("Clear start");
375     auto context = std::make_shared<PreferencesAysncContext>();
376     auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
377         PRE_CHECK_RETURN_VOID_SET(argc == 0, std::make_shared<ParamNumError>("0 or 1"));
378         std::tie(context->boundObj, context->instance_) = GetSelfInstance(env, self);
379         PRE_CHECK_RETURN_VOID_SET(context->boundObj != nullptr, std::make_shared<InnerError>("Failed to unwrap unwrap "
380                                                                                              "when clearing."));
381     };
382     auto exec = [context]() -> int {
383         auto instance = context->instance_.lock();
384         if (instance == nullptr) {
385             return E_INNER_ERROR;
386         }
387         return instance->Clear();
388     };
389     auto output = [context](napi_env env, napi_value &result) {
390         napi_status status = napi_get_undefined(env, &result);
391         PRE_CHECK_RETURN_VOID_SET(status == napi_ok, std::make_shared<InnerError>("Failed to get undefined when "
392                                                                                   "clearing."));
393         LOG_DEBUG("Clear end.");
394     };
395     context->SetAction(env, info, input, exec, output);
396 
397     PRE_CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
398     return AsyncCall::Call(env, context, "SendablePreferencesClear");
399 }
400 
RegisterObserver(napi_env env,napi_callback_info info)401 napi_value PreferencesProxy::RegisterObserver(napi_env env, napi_callback_info info)
402 {
403     napi_value thiz = nullptr;
404     size_t argc = 3;            // 3 is specifies the length of the provided argc array
405     napi_value args[3] = { 0 }; // 3 is the max args length
406 
407     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
408     // This interface must have 2 or 3 parameters.
409     PRE_NAPI_ASSERT(env, argc == 2 || argc == 3, std::make_shared<ParamNumError>("2 or 3"));
410     napi_valuetype type;
411     NAPI_CALL(env, napi_typeof(env, args[0], &type));
412     PRE_NAPI_ASSERT(env, type == napi_string, std::make_shared<ParamTypeError>("The registerMode must be string."));
413     std::string registerMode;
414     Utils::ConvertFromSendable(env, args[0], registerMode);
415     PRE_NAPI_ASSERT(env,
416         registerMode == STR_CHANGE || registerMode == STR_MULTI_PRECESS_CHANGE || registerMode == STR_DATA_CHANGE,
417         std::make_shared<ParamTypeError>("The registerMode must be 'change' or 'multiProcessChange' or "
418                                          "'dataChange'."));
419 
420     size_t funIndex = 1;
421     std::vector<std::string> keys;
422     auto mode = ConvertToRegisterMode(registerMode);
423     if (mode == Observer::DATA_CHANGE) {
424         int errCode = Utils::ConvertFromSendable(env, args[funIndex], keys);
425         PRE_NAPI_ASSERT(env, errCode == napi_ok && !keys.empty(),
426             std::make_shared<ParamTypeError>("The keys must be Array<string>."));
427         funIndex++;
428     }
429 
430     PRE_NAPI_ASSERT(env, argc == funIndex + 1, std::make_shared<ParamNumError>("2 or 3"));
431     NAPI_CALL(env, napi_typeof(env, args[funIndex], &type));
432     PRE_NAPI_ASSERT(env, type == napi_function, std::make_shared<ParamTypeError>("The callback must be function."));
433 
434     auto [obj, instance] = GetSelfInstance(env, thiz);
435     PRE_NAPI_ASSERT(env, obj != nullptr && obj->GetInstance() != nullptr,
436         std::make_shared<InnerError>("Failed to unwrap when register callback"));
437     int errCode = obj->RegisteredObserver(env, args[funIndex], mode, keys);
438     LOG_DEBUG("The observer subscribe %{public}d.", errCode);
439     PRE_NAPI_ASSERT(env, errCode == OK, std::make_shared<InnerError>(errCode));
440     return nullptr;
441 }
442 
UnregisterObserver(napi_env env,napi_callback_info info)443 napi_value PreferencesProxy::UnregisterObserver(napi_env env, napi_callback_info info)
444 {
445     napi_value thiz = nullptr;
446     size_t argc = 3;            // 3 is specifies the length of the provided argc array
447     napi_value args[3] = { 0 }; // 3 is the max args length
448 
449     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
450     PRE_NAPI_ASSERT(env, argc > 0, std::make_shared<ParamNumError>("more than 1"));
451 
452     napi_valuetype type;
453     NAPI_CALL(env, napi_typeof(env, args[0], &type));
454     PRE_NAPI_ASSERT(env, type == napi_string, std::make_shared<ParamTypeError>("The registerMode must be string."));
455 
456     std::string registerMode;
457     Utils::ConvertFromSendable(env, args[0], registerMode);
458     PRE_NAPI_ASSERT(env,
459         registerMode == STR_CHANGE || registerMode == STR_MULTI_PRECESS_CHANGE || registerMode == STR_DATA_CHANGE,
460         std::make_shared<ParamTypeError>("The unRegisterMode must be 'change' or 'multiProcessChange' or "
461                                          "'dataChange'."));
462 
463     size_t funIndex = 1;
464     napi_value callback = nullptr;
465     std::vector<std::string> keys;
466     auto mode = ConvertToRegisterMode(registerMode);
467     if (mode == Observer::DATA_CHANGE) {
468         int errCode = Utils::ConvertFromSendable(env, args[funIndex], keys);
469         PRE_NAPI_ASSERT(env, errCode == napi_ok && !keys.empty(),
470             std::make_shared<ParamTypeError>("The keys must be Array<string>."));
471         funIndex++;
472     }
473 
474     PRE_NAPI_ASSERT(env, argc <= funIndex + 1, std::make_shared<ParamNumError>("1 or 2 or 3"));
475     if (argc == funIndex + 1) {
476         NAPI_CALL(env, napi_typeof(env, args[funIndex], &type));
477         PRE_NAPI_ASSERT(env, type == napi_function || type == napi_undefined || type == napi_null,
478             std::make_shared<ParamTypeError>("The callback must be function."));
479         callback = args[funIndex];
480     }
481 
482     auto [obj, instance] = GetSelfInstance(env, thiz);
483     PRE_NAPI_ASSERT(env, obj != nullptr, std::make_shared<InnerError>("Failed to unwrap when unregister callback"));
484 
485     int errCode = obj->UnregisteredObserver(env, callback, mode, keys);
486     LOG_DEBUG("The observer unsubscribe 0x%{public}x.", errCode);
487     PRE_NAPI_ASSERT(env, errCode == OK, std::make_shared<InnerError>(errCode));
488     return nullptr;
489 }
490 
ConvertToRegisterMode(const std::string & mode)491 RegisterMode PreferencesProxy::ConvertToRegisterMode(const std::string &mode)
492 {
493     if (mode == STR_CHANGE) {
494         return RegisterMode::LOCAL_CHANGE;
495     } else if (mode == STR_MULTI_PRECESS_CHANGE) {
496         return RegisterMode::MULTI_PRECESS_CHANGE;
497     } else {
498         return RegisterMode::DATA_CHANGE;
499     }
500 }
501 
RegisteredObserver(napi_env env,napi_value callback,RegisterMode mode,const std::vector<std::string> & keys)502 int PreferencesProxy::RegisteredObserver(
503     napi_env env, napi_value callback, RegisterMode mode, const std::vector<std::string> &keys)
504 {
505     std::lock_guard<decltype(mutex_)> lockGuard(mutex_);
506     auto it = observers_.find(env);
507     if (it == observers_.end()) {
508         it = observers_.emplace(
509             std::piecewise_construct, std::forward_as_tuple(env), std::forward_as_tuple(env, this)).first;
510     }
511     if (it == observers_.end()) {
512         return E_INNER_ERROR;
513     }
514     return it->second.Subscribe(callback, mode, keys);
515 }
516 
UnregisteredObserver(napi_env env,napi_value callback,RegisterMode mode,const std::vector<std::string> & keys)517 int PreferencesProxy::UnregisteredObserver(
518     napi_env env, napi_value callback, RegisterMode mode, const std::vector<std::string> &keys)
519 {
520     std::lock_guard<decltype(mutex_)> lockGuard(mutex_);
521     auto it = observers_.find(env);
522     if (it == observers_.end()) {
523         return E_OK;
524     }
525     return it->second.Unsubscribe(callback, mode, keys);
526 }
527 
~JSObservers()528 PreferencesProxy::JSObservers::~JSObservers()
529 {
530     for (int mode = 0; mode < RegisterMode::CHANGE_BUTT; ++mode) {
531         Unsubscribe(nullptr, RegisterMode(mode), {});
532     }
533     napi_remove_env_cleanup_hook(env_, &CleanEnv, this);
534     uvQueue_ = nullptr;
535     env_ = nullptr;
536     proxy_ = nullptr;
537 }
538 
JSObservers(napi_env env,PreferencesProxy * proxy)539 PreferencesProxy::JSObservers::JSObservers(napi_env env, PreferencesProxy *proxy) : env_(env), proxy_(proxy)
540 {
541     uvQueue_ = std::make_shared<UvQueue>(env);
542     napi_add_env_cleanup_hook(env_, &CleanEnv, this);
543 }
544 
CleanEnv(void * obj)545 void PreferencesProxy::JSObservers::CleanEnv(void *obj)
546 {
547     auto *realObj = reinterpret_cast<JSObservers *>(obj);
548     if (realObj == nullptr) {
549         return;
550     }
551     auto proxy = realObj->proxy_;
552     auto env = realObj->env_;
553     if (proxy == nullptr || env == nullptr) {
554         return;
555     }
556 
557     std::lock_guard<decltype(proxy->mutex_)> lockGuard(proxy->mutex_);
558     proxy->observers_.erase(env);
559 }
560 
Subscribe(napi_value callback,RegisterMode mode,const std::vector<std::string> & keys)561 int PreferencesProxy::JSObservers::Subscribe(
562     napi_value callback, RegisterMode mode, const std::vector<std::string> &keys)
563 {
564     auto &observers = observers_[mode];
565     auto observerIt = std::find_if(
566         observers.begin(), observers.end(), [env = env_, callback](std::shared_ptr<JSObserverImpl> observer) {
567             if (observer == nullptr) {
568                 return false;
569             }
570             return JSUtils::Equals(env, callback, observer->GetCallback());
571         });
572     if (observerIt != observers.end()) {
573         return E_OK;
574     }
575 
576     auto jsObserver = std::make_shared<JSObserverImpl>(uvQueue_, callback);
577     auto instance = proxy_->GetInstance();
578     if (instance == nullptr) {
579         return E_INNER_ERROR;
580     }
581     auto errCode = instance->Subscribe(jsObserver, mode, keys);
582     if (errCode != E_OK) {
583         return errCode;
584     }
585     observers.push_back(jsObserver);
586     return errCode;
587 }
588 
Unsubscribe(napi_value callback,RegisterMode mode,const std::vector<std::string> & keys)589 int PreferencesProxy::JSObservers::Unsubscribe(
590     napi_value callback, RegisterMode mode, const std::vector<std::string> &keys)
591 {
592     auto instance = proxy_->GetInstance();
593     if (instance == nullptr) {
594         return E_INNER_ERROR;
595     }
596     int errCode = E_OK;
597     auto &observers = observers_[mode];
598     for (auto observer = observers.begin(); observer != observers.end();) {
599         if (callback == nullptr || JSUtils::Equals(env_, callback, (*observer)->GetCallback())) {
600             int status = instance->Unsubscribe(*observer, mode, keys);
601             if (status == E_OK) {
602                 (*observer)->ClearCallback();
603                 observer = observers.erase(observer);
604                 continue;
605             }
606             errCode = status;
607         }
608         ++observer;
609     }
610 
611     return errCode;
612 }
613 } // namespace OHOS::Sendable::JSPreferences
614