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 <linux/limits.h>
19
20 #include <cerrno>
21 #include <cmath>
22 #include <limits>
23
24 #include "js_logger.h"
25 #include "napi_async_proxy.h"
26 #include "preferences.h"
27 #include "preferences_errno.h"
28 #include "preferences_value.h"
29 #include "securec.h"
30
31 using namespace OHOS::NativePreferences;
32 using namespace OHOS::AppDataMgrJsKit;
33
34 namespace OHOS {
35 namespace PreferencesJsKit {
36 #define MAX_KEY_LENGTH Preferences::MAX_KEY_LENGTH
37 #define MAX_VALUE_LENGTH Preferences::MAX_VALUE_LENGTH
38
39 struct PreferencesAysncContext : NapiAsyncProxy<PreferencesAysncContext>::AysncContext {
40 std::string key;
41 PreferencesValue defValue = PreferencesValue((int)0);
42 bool hasKey;
43 };
44
45 static __thread napi_ref constructor_;
46
PreferencesProxy(std::shared_ptr<OHOS::NativePreferences::Preferences> & value)47 PreferencesProxy::PreferencesProxy(std::shared_ptr<OHOS::NativePreferences::Preferences> &value)
48 : value_(value), env_(nullptr), wrapper_(nullptr)
49 {
50 }
51
~PreferencesProxy()52 PreferencesProxy::~PreferencesProxy()
53 {
54 napi_delete_reference(env_, wrapper_);
55 }
56
Destructor(napi_env env,void * nativeObject,void * finalize_hint)57 void PreferencesProxy::Destructor(napi_env env, void *nativeObject, void *finalize_hint)
58 {
59 PreferencesProxy *obj = static_cast<PreferencesProxy *>(nativeObject);
60 delete obj;
61 }
62
Init(napi_env env,napi_value exports)63 void PreferencesProxy::Init(napi_env env, napi_value exports)
64 {
65 napi_property_descriptor descriptors[] = {
66 DECLARE_NAPI_FUNCTION("put", SetValue),
67 DECLARE_NAPI_FUNCTION("get", GetValue),
68 DECLARE_NAPI_FUNCTION("delete", Delete),
69 DECLARE_NAPI_FUNCTION("clear", Clear),
70 DECLARE_NAPI_FUNCTION("has", HasKey),
71 DECLARE_NAPI_FUNCTION("flush", Flush),
72 DECLARE_NAPI_FUNCTION("on", RegisterObserver),
73 DECLARE_NAPI_FUNCTION("off", UnRegisterObserver),
74 };
75 napi_value cons = nullptr;
76 napi_define_class(env, "Storage", NAPI_AUTO_LENGTH, New, nullptr,
77 sizeof(descriptors) / sizeof(napi_property_descriptor), descriptors, &cons);
78
79 napi_create_reference(env, cons, 1, &constructor_);
80 }
81
NewInstance(napi_env env,napi_value arg,napi_value * instance)82 napi_status PreferencesProxy::NewInstance(napi_env env, napi_value arg, napi_value *instance)
83 {
84 napi_status status;
85
86 const int argc = 1;
87 napi_value argv[argc] = { arg };
88
89 napi_value cons;
90 status = napi_get_reference_value(env, constructor_, &cons);
91 if (status != napi_ok) {
92 return status;
93 }
94
95 status = napi_new_instance(env, cons, argc, argv, instance);
96 if (status != napi_ok) {
97 return status;
98 }
99
100 return napi_ok;
101 }
102
New(napi_env env,napi_callback_info info)103 napi_value PreferencesProxy::New(napi_env env, napi_callback_info info)
104 {
105 size_t argc = 1;
106 napi_value args[1] = { 0 };
107 napi_value thiz = nullptr;
108 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
109 if (thiz == nullptr) {
110 LOG_WARN("get this failed");
111 return nullptr;
112 }
113
114 napi_valuetype valueType = napi_undefined;
115 NAPI_CALL(env, napi_typeof(env, args[0], &valueType));
116 NAPI_ASSERT(env, valueType == napi_string, "input type not string");
117 char *path = new char[PATH_MAX];
118 size_t pathLen = 0;
119 napi_status status = napi_get_value_string_utf8(env, args[0], path, PATH_MAX, &pathLen);
120 if (status != napi_ok) {
121 LOG_ERROR("get path failed. ");
122 delete[] path;
123 return nullptr;
124 }
125 // get native object
126 int errCode = 0;
127 std::shared_ptr<OHOS::NativePreferences::Preferences> preference =
128 OHOS::NativePreferences::PreferencesHelper::GetPreferences(path, errCode);
129 delete[] path;
130 NAPI_ASSERT(env, preference != nullptr, "failed to call native");
131 PreferencesProxy *obj = new PreferencesProxy(preference);
132 obj->env_ = env;
133 NAPI_CALL(env, napi_wrap(env, thiz, obj, PreferencesProxy::Destructor,
134 nullptr, // finalize_hint
135 &obj->wrapper_));
136 return thiz;
137 }
138
CheckNumberType(double input)139 template<typename T> bool CheckNumberType(double input)
140 {
141 if (input > (std::numeric_limits<T>::max)() || input < (std::numeric_limits<T>::min)()) {
142 return false;
143 }
144 return true;
145 }
146
IsFloat(double input)147 bool IsFloat(double input)
148 {
149 return abs(input - floor(input)) >= 0; // DBL_EPSILON;
150 }
151
ParseKey(const napi_env & env,const napi_value & arg,PreferencesAysncContext * asyncContext)152 void ParseKey(const napi_env &env, const napi_value &arg, PreferencesAysncContext *asyncContext)
153 {
154 // get input key
155 char key[MAX_KEY_LENGTH] = { 0 };
156 size_t keySize = 0;
157 napi_get_value_string_utf8(env, arg, key, MAX_KEY_LENGTH, &keySize);
158 asyncContext->key = key;
159 }
160
ParseDefValue(const napi_env & env,const napi_value & arg,PreferencesAysncContext * asyncContext)161 void ParseDefValue(const napi_env &env, const napi_value &arg, PreferencesAysncContext *asyncContext)
162 {
163 napi_valuetype valueType = napi_undefined;
164 napi_typeof(env, arg, &valueType);
165 if (valueType == napi_number) {
166 double number = 0.0;
167 napi_get_value_double(env, arg, &number);
168 PreferencesValue value((double)number);
169 asyncContext->defValue = value;
170 } else if (valueType == napi_string) {
171 char *str = new char[MAX_VALUE_LENGTH];
172 size_t valueSize = 0;
173 napi_get_value_string_utf8(env, arg, str, MAX_VALUE_LENGTH, &valueSize);
174 PreferencesValue value((std::string)(str));
175 asyncContext->defValue = value;
176 delete[] str;
177 } else if (valueType == napi_boolean) {
178 bool bValue = false;
179 napi_get_value_bool(env, arg, &bValue);
180 PreferencesValue value((bool)(bValue));
181 asyncContext->defValue = value;
182 } else {
183 LOG_ERROR("Wrong second parameter type");
184 }
185 }
186
GetValue(napi_env env,napi_callback_info info)187 napi_value PreferencesProxy::GetValue(napi_env env, napi_callback_info info)
188 {
189 NapiAsyncProxy<PreferencesAysncContext> proxy;
190 proxy.Init(env, info);
191 std::vector<NapiAsyncProxy<PreferencesAysncContext>::InputParser> parsers;
192 parsers.push_back(ParseKey);
193 parsers.push_back(ParseDefValue);
194 proxy.ParseInputs(parsers);
195
196 return proxy.DoAsyncWork(
197 "GetValue",
198 [](PreferencesAysncContext *asyncContext) {
199 int errCode = OK;
200 PreferencesProxy *obj = reinterpret_cast<PreferencesProxy *>(asyncContext->boundObj);
201 if (asyncContext->defValue.IsBool()) {
202 bool tmpValue = (bool)obj->value_->GetBool(asyncContext->key, (bool)asyncContext->defValue);
203 asyncContext->defValue = PreferencesValue((bool)tmpValue);
204 } else if (asyncContext->defValue.IsString()) {
205 std::string tmpValue = obj->value_->GetString(asyncContext->key, (std::string)asyncContext->defValue);
206 asyncContext->defValue = PreferencesValue((std::string)tmpValue);
207 } else if (asyncContext->defValue.IsDouble()) {
208 double tmpValue = obj->value_->GetDouble(asyncContext->key, (double)asyncContext->defValue);
209 asyncContext->defValue = PreferencesValue((double)tmpValue);
210 } else {
211 errCode = ERR;
212 }
213
214 return errCode;
215 },
216 [](PreferencesAysncContext *asyncContext, napi_value &output) {
217 int errCode = OK;
218 if (asyncContext->defValue.IsBool()) {
219 napi_get_boolean(asyncContext->env, (bool)asyncContext->defValue, &output);
220 } else if (asyncContext->defValue.IsString()) {
221 std::string tempStr = (std::string)asyncContext->defValue;
222 napi_create_string_utf8(asyncContext->env, tempStr.c_str(), tempStr.size(), &output);
223 } else if (asyncContext->defValue.IsDouble()) {
224 napi_create_double(asyncContext->env, (double)asyncContext->defValue, &output);
225 } else {
226 errCode = ERR;
227 }
228
229 return errCode;
230 });
231 }
232
SetValue(napi_env env,napi_callback_info info)233 napi_value PreferencesProxy::SetValue(napi_env env, napi_callback_info info)
234 {
235 NapiAsyncProxy<PreferencesAysncContext> proxy;
236 proxy.Init(env, info);
237 std::vector<NapiAsyncProxy<PreferencesAysncContext>::InputParser> parsers;
238 parsers.push_back(ParseKey);
239 parsers.push_back(ParseDefValue);
240 proxy.ParseInputs(parsers);
241
242 return proxy.DoAsyncWork(
243 "SetValue",
244 [](PreferencesAysncContext *asyncContext) {
245 int errCode = OK;
246 PreferencesProxy *obj = reinterpret_cast<PreferencesProxy *>(asyncContext->boundObj);
247 if (asyncContext->defValue.IsBool()) {
248 errCode = obj->value_->PutBool(asyncContext->key, (bool)asyncContext->defValue);
249 } else if (asyncContext->defValue.IsString()) {
250 errCode = obj->value_->PutString(asyncContext->key, (std::string)asyncContext->defValue);
251 } else if (asyncContext->defValue.IsDouble()) {
252 errCode = obj->value_->PutDouble(asyncContext->key, (double)asyncContext->defValue);
253 } else {
254 errCode = ERR;
255 }
256
257 return errCode;
258 },
259 [](PreferencesAysncContext *asyncContext, napi_value &output) {
260 napi_status status = napi_get_undefined(asyncContext->env, &output);
261 return (status == napi_ok) ? OK : ERR;
262 });
263 }
264
Delete(napi_env env,napi_callback_info info)265 napi_value PreferencesProxy::Delete(napi_env env, napi_callback_info info)
266 {
267 NapiAsyncProxy<PreferencesAysncContext> proxy;
268 proxy.Init(env, info);
269 std::vector<NapiAsyncProxy<PreferencesAysncContext>::InputParser> parsers;
270 parsers.push_back(ParseKey);
271 proxy.ParseInputs(parsers);
272
273 return proxy.DoAsyncWork(
274 "Delete",
275 [](PreferencesAysncContext *asyncContext) {
276 PreferencesProxy *obj = reinterpret_cast<PreferencesProxy *>(asyncContext->boundObj);
277 int errCode = obj->value_->Delete(asyncContext->key);
278
279 return errCode;
280 },
281 [](PreferencesAysncContext *asyncContext, napi_value &output) {
282 napi_status status = napi_get_undefined(asyncContext->env, &output);
283 return (status == napi_ok) ? OK : ERR;
284 });
285 }
286
HasKey(napi_env env,napi_callback_info info)287 napi_value PreferencesProxy::HasKey(napi_env env, napi_callback_info info)
288 {
289 NapiAsyncProxy<PreferencesAysncContext> proxy;
290 proxy.Init(env, info);
291 std::vector<NapiAsyncProxy<PreferencesAysncContext>::InputParser> parsers;
292 parsers.push_back(ParseKey);
293 proxy.ParseInputs(parsers);
294
295 return proxy.DoAsyncWork(
296 "HasKey",
297 [](PreferencesAysncContext *asyncContext) {
298 PreferencesProxy *obj = reinterpret_cast<PreferencesProxy *>(asyncContext->boundObj);
299 asyncContext->hasKey = obj->value_->HasKey(asyncContext->key);
300
301 return OK;
302 },
303 [](PreferencesAysncContext *asyncContext, napi_value &output) {
304 napi_status status = napi_get_boolean(asyncContext->env, asyncContext->hasKey, &output);
305 return (status == napi_ok) ? OK : ERR;
306 });
307 }
308
Flush(napi_env env,napi_callback_info info)309 napi_value PreferencesProxy::Flush(napi_env env, napi_callback_info info)
310 {
311 NapiAsyncProxy<PreferencesAysncContext> proxy;
312 proxy.Init(env, info);
313 std::vector<NapiAsyncProxy<PreferencesAysncContext>::InputParser> parsers;
314 proxy.ParseInputs(parsers);
315
316 return proxy.DoAsyncWork(
317 "Flush",
318 [](PreferencesAysncContext *asyncContext) {
319 PreferencesProxy *obj = reinterpret_cast<PreferencesProxy *>(asyncContext->boundObj);
320 return obj->value_->FlushSync();
321 },
322 [](PreferencesAysncContext *asyncContext, napi_value &output) {
323 napi_status status = napi_get_undefined(asyncContext->env, &output);
324 return (status == napi_ok) ? OK : ERR;
325 });
326 }
327
Clear(napi_env env,napi_callback_info info)328 napi_value PreferencesProxy::Clear(napi_env env, napi_callback_info info)
329 {
330 NapiAsyncProxy<PreferencesAysncContext> proxy;
331 proxy.Init(env, info);
332 std::vector<NapiAsyncProxy<PreferencesAysncContext>::InputParser> parsers;
333 proxy.ParseInputs(parsers);
334
335 return proxy.DoAsyncWork(
336 "Clear",
337 [](PreferencesAysncContext *asyncContext) {
338 PreferencesProxy *obj = reinterpret_cast<PreferencesProxy *>(asyncContext->boundObj);
339 return obj->value_->Clear();
340 },
341 [](PreferencesAysncContext *asyncContext, napi_value &output) {
342 napi_status status = napi_get_undefined(asyncContext->env, &output);
343 return (status == napi_ok) ? OK : ERR;
344 });
345 }
346
RegisterObserver(napi_env env,napi_callback_info info)347 napi_value PreferencesProxy::RegisterObserver(napi_env env, napi_callback_info info)
348 {
349 napi_value thiz = nullptr;
350 size_t argc = 2;
351 napi_value args[2] = { 0 };
352
353 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
354 napi_valuetype type;
355 NAPI_CALL(env, napi_typeof(env, args[0], &type));
356 NAPI_ASSERT(env, type == napi_string, "key not string type");
357
358 NAPI_CALL(env, napi_typeof(env, args[1], &type));
359 NAPI_ASSERT(env, type == napi_function, "observer not function type");
360
361 PreferencesProxy *obj = nullptr;
362 NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
363
364 // reference save
365 obj->observer_ = std::make_shared<PreferencesObserverImpl>(env, args[1]);
366 obj->value_->RegisterObserver(obj->observer_);
367 LOG_DEBUG("RegisterObserver end");
368
369 return nullptr;
370 }
371
UnRegisterObserver(napi_env env,napi_callback_info info)372 napi_value PreferencesProxy::UnRegisterObserver(napi_env env, napi_callback_info info)
373 {
374 napi_value thiz = nullptr;
375 size_t argc = 2;
376 napi_value args[2] = { 0 };
377
378 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thiz, nullptr));
379 napi_valuetype type;
380 NAPI_CALL(env, napi_typeof(env, args[0], &type));
381 NAPI_ASSERT(env, type == napi_string, "key not string type");
382
383 NAPI_CALL(env, napi_typeof(env, args[1], &type));
384 NAPI_ASSERT(env, type == napi_function, "observer not function type");
385
386 PreferencesProxy *obj = nullptr;
387 NAPI_CALL(env, napi_unwrap(env, thiz, reinterpret_cast<void **>(&obj)));
388 obj->value_->UnRegisterObserver(obj->observer_);
389 obj->observer_.reset();
390 obj->observer_ = nullptr;
391 LOG_DEBUG("UnRegisterObserver end");
392 return nullptr;
393 }
394
PreferencesObserverImpl(napi_env env,napi_value callback)395 PreferencesObserverImpl::PreferencesObserverImpl(napi_env env, napi_value callback) : observerRef(nullptr)
396 {
397 this->env_ = env;
398 napi_create_reference(env_, callback, 1, &observerRef);
399 }
400
~PreferencesObserverImpl()401 PreferencesObserverImpl::~PreferencesObserverImpl()
402 {
403 napi_delete_reference(env_, observerRef);
404 }
405
OnChange(Preferences & preferences,const std::string & key)406 void PreferencesObserverImpl::OnChange(Preferences &preferences, const std::string &key)
407 {
408 LOG_DEBUG("OnChange key:%{public}s", key.c_str());
409 napi_value callback = nullptr;
410 napi_value global = nullptr;
411 napi_value result = nullptr;
412 napi_value args[1] = { 0 };
413
414 napi_create_string_utf8(env_, key.c_str(), key.size(), &args[0]);
415 napi_get_reference_value(env_, observerRef, &callback);
416 napi_get_global(env_, &global);
417
418 napi_call_function(env_, global, callback, 1, args, &result);
419 LOG_DEBUG("OnChange key end");
420 }
421 } // namespace PreferencesJsKit
422 } // namespace OHOS
423