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