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