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