1 /*
2 * Copyright (c) 2022 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_web_async_controller.h"
17 #include "nweb.h"
18 #include "nweb_helper.h"
19 #include "nweb_store_web_archive_callback.h"
20 #include <memory>
21
22 #undef LOG_TAG
23 #define LOG_TAG "NapiWebAsyncController"
24
25 namespace {
26 // Max length of string.
27 constexpr size_t MAX_WEB_STRING_LENGTH = 40960;
28 } // namespace
29
30 namespace OHOS {
31 namespace NWeb {
Init(napi_env env,napi_value exports)32 napi_value NapiWebAsyncController::Init(napi_env env, napi_value exports)
33 {
34 const std::string WEB_ASYNC_CLIENT_CLASS_NAME = "WebAsyncController";
35 napi_property_descriptor properties[] = {
36 {"storeWebArchive", nullptr, JS_StoreWebArchive, nullptr, nullptr, nullptr,
37 napi_default, nullptr},
38 };
39 napi_value constructor = nullptr;
40 napi_define_class(env, WEB_ASYNC_CLIENT_CLASS_NAME.c_str(), WEB_ASYNC_CLIENT_CLASS_NAME.length(),
41 JS_NapiWebAsyncController, nullptr, sizeof(properties) / sizeof(properties[0]), properties, &constructor);
42 NAPI_ASSERT(env, constructor != nullptr, "define js class WebAsyncController failed");
43 napi_status status = napi_set_named_property(env, exports, "WebAsyncController", constructor);
44 NAPI_ASSERT(env, status == napi_ok, "set property WebAsyncController failed");
45 return exports;
46 }
47
StoreWebArchiveInternal(napi_env env,napi_callback_info cbinfo,const std::string & baseName,bool autoName)48 napi_value NapiWebAsyncController::StoreWebArchiveInternal(napi_env env, napi_callback_info cbinfo,
49 const std::string &baseName, bool autoName)
50 {
51 napi_value thisVar = nullptr;
52 size_t argc = 1;
53 size_t argcPromise = 2;
54 size_t argcCallback = 3;
55 napi_value argv[3] = {0};
56
57 napi_value result = nullptr;
58 napi_get_undefined(env, &result);
59
60 napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
61
62 // Get the native object of NapiWebAsyncController.
63 NapiWebAsyncController *webAsyncController = nullptr;
64 napi_unwrap(env, thisVar, (void **)&webAsyncController);
65
66 if (!webAsyncController) {
67 return result;
68 }
69
70 // If there are three parameters, it is considered to be called by callback,
71 // if it is one, it is considered to be called by returning a promise.
72 if (argc == argcCallback) {
73 napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
74 napi_ref jsCallback = nullptr;
75 napi_create_reference(env, argv[argcCallback - 1], 1, &jsCallback);
76
77 if (jsCallback) {
78 webAsyncController->StoreWebArchiveCallback(baseName, autoName, env, std::move(jsCallback));
79 }
80 return result;
81 } else if (argc == argcPromise) {
82 napi_deferred deferred = nullptr;
83 napi_value promise = nullptr;
84 napi_create_promise(env, &deferred, &promise);
85 if (promise && deferred) {
86 webAsyncController->StoreWebArchivePromise(baseName, autoName, env, deferred);
87 }
88 return promise;
89 }
90
91 return result;
92 }
93
JS_StoreWebArchive(napi_env env,napi_callback_info cbinfo)94 napi_value NapiWebAsyncController::JS_StoreWebArchive(napi_env env, napi_callback_info cbinfo)
95 {
96 napi_value thisVar = nullptr;
97
98 size_t argc = 1;
99 size_t argcPromise = 2;
100 size_t argcCallback = 3;
101 napi_value argv[3] = {0};
102
103 napi_value result = nullptr;
104 napi_get_undefined(env, &result);
105
106 napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
107 NAPI_ASSERT(env, argc == argcPromise || argc == argcCallback, "requires 2 or 3 parameter");
108
109 // Check first param is string.
110 napi_valuetype valueType = napi_null;
111 napi_typeof(env, argv[0], &valueType);
112 NAPI_ASSERT(env, valueType == napi_string, "type mismatch for parameter 1");
113
114 // Get the first param as string.
115 size_t bufferSize = 0;
116 napi_get_value_string_utf8(env, argv[0], nullptr, 0, &bufferSize);
117 NAPI_ASSERT_BASE(env, bufferSize > 0, "buffer len = 0", result);
118
119 bufferSize = std::min(bufferSize, MAX_WEB_STRING_LENGTH);
120 char stringValue[bufferSize + 1];
121 size_t jsStringLength = 0;
122 napi_get_value_string_utf8(env, argv[0], stringValue, bufferSize + 1, &jsStringLength);
123 NAPI_ASSERT_BASE(env, jsStringLength == bufferSize, "string length wrong", result);
124 std::string baseName(stringValue);
125
126 // Check second param is boolean.
127 napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
128 napi_typeof(env, argv[1], &valueType);
129 NAPI_ASSERT(env, valueType == napi_boolean, "type mismatch for parameter 2");
130
131 // Get the second param as bool.
132 bool autoName = false;
133 NAPI_CALL(env, napi_get_value_bool(env, argv[1], &autoName));
134
135 // Check third param is a function.
136 if (argc == argcCallback) {
137 napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
138 napi_typeof(env, argv[argcCallback - 1], &valueType);
139 NAPI_ASSERT(env, valueType == napi_function, "type mismatch for parameter 3");
140 }
141
142 return StoreWebArchiveInternal(env, cbinfo, baseName, autoName);
143 }
144
JS_NapiWebAsyncController(napi_env env,napi_callback_info info)145 napi_value NapiWebAsyncController::JS_NapiWebAsyncController(napi_env env, napi_callback_info info)
146 {
147 size_t argc = 1;
148 napi_value argv[1] = {0};
149 napi_value thisVar = nullptr;
150 void *data = nullptr;
151 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
152
153 napi_valuetype valueType;
154 NAPI_CALL(env, napi_typeof(env, argv[0], &valueType));
155
156 NAPI_ASSERT(env, valueType == napi_object, "Wrong type of arguments. Expects an object as first argument.");
157
158 napi_value obj = argv[0];
159 napi_value getNWebIdFunc;
160
161 napi_status status = napi_get_named_property(env, obj, "getWebId", &getNWebIdFunc);
162 if (status != napi_status::napi_ok) {
163 return nullptr;
164 }
165
166 napi_value result;
167 status = napi_call_function(env, obj, getNWebIdFunc, 0, nullptr, &result);
168 if (status != napi_status::napi_ok) {
169 return nullptr;
170 }
171
172 int32_t nwebId = -1;
173 napi_get_value_int32(env, result, &nwebId);
174
175 NapiWebAsyncController *webAsyncController = new (std::nothrow) NapiWebAsyncController(env, thisVar, nwebId);
176 if (webAsyncController == nullptr) {
177 HILOG_ERROR(LOG_APP, "new webAsyncController failed");
178 return nullptr;
179 }
180
181 napi_wrap(
182 env, thisVar, webAsyncController,
183 [](napi_env env, void *data, void *hint) {
184 NapiWebAsyncController *webAsyncController = static_cast<NapiWebAsyncController *>(data);
185 delete webAsyncController;
186 },
187 nullptr, nullptr);
188
189 return thisVar;
190 }
191
NapiWebAsyncController(napi_env env,napi_value thisVar,int32_t nwebId)192 NapiWebAsyncController::NapiWebAsyncController(napi_env env, napi_value thisVar, int32_t nwebId) : nwebId_(nwebId) {}
193
StoreWebArchiveCallback(const std::string & baseName,bool autoName,napi_env env,napi_ref jsCallback)194 void NapiWebAsyncController::StoreWebArchiveCallback(const std::string &baseName, bool autoName, napi_env env,
195 napi_ref jsCallback)
196 {
197 std::weak_ptr<NWeb> nweb_weak = OHOS::NWeb::NWebHelper::Instance().GetNWeb(nwebId_);
198 auto nweb = nweb_weak.lock();
199 if (!nweb) {
200 HILOG_ERROR(LOG_APP, "not found a valid nweb");
201 napi_value callback = nullptr;
202 napi_value jsResult = nullptr;
203 napi_value callbackResult = nullptr;
204
205 napi_get_reference_value(env, jsCallback, &callback);
206 napi_get_null(env, &jsResult);
207 napi_call_function(env, nullptr, callback, 1, &jsResult, &callbackResult);
208 napi_delete_reference(env, jsCallback);
209 return;
210 }
211
212 if (jsCallback == nullptr) {
213 return;
214 }
215
216 auto callbackImpl = std::make_shared<OHOS::NWeb::NWebStoreWebArchiveCallback>();
217 callbackImpl->SetCallBack([env, jCallback = std::move(jsCallback)](std::string result) {
218 if (!env) {
219 return;
220 }
221 napi_handle_scope scope = nullptr;
222 napi_open_handle_scope(env, &scope);
223 if (scope == nullptr) {
224 return;
225 }
226
227 napi_value callback = nullptr;
228 napi_get_reference_value(env, jCallback, &callback);
229
230 napi_value jsResult = nullptr;
231 napi_value callbackResult = nullptr;
232 if (result.empty()) {
233 napi_get_null(env, &jsResult);
234 } else {
235 napi_create_string_utf8(env, result.c_str(), NAPI_AUTO_LENGTH, &jsResult);
236 }
237 napi_call_function(env, nullptr, callback, 1, &jsResult, &callbackResult);
238
239 napi_delete_reference(env, jCallback);
240 napi_close_handle_scope(env, scope);
241 });
242 nweb->StoreWebArchive(baseName, autoName, callbackImpl);
243
244 return;
245 }
246
StoreWebArchivePromise(const std::string & baseName,bool autoName,napi_env env,napi_deferred deferred)247 void NapiWebAsyncController::StoreWebArchivePromise(const std::string &baseName, bool autoName, napi_env env,
248 napi_deferred deferred)
249 {
250 std::weak_ptr<NWeb> nweb_weak = OHOS::NWeb::NWebHelper::Instance().GetNWeb(nwebId_);
251 auto nweb = nweb_weak.lock();
252 if (!nweb) {
253 HILOG_ERROR(LOG_APP, "not found a valid nweb");
254 napi_value jsResult = nullptr;
255 napi_get_null(env, &jsResult);
256 napi_reject_deferred(env, deferred, jsResult);
257 return;
258 }
259
260 if (deferred == nullptr) {
261 return;
262 }
263
264 auto callbackImpl = std::make_shared<OHOS::NWeb::NWebStoreWebArchiveCallback>();
265 callbackImpl->SetCallBack([env, deferred](std::string result) {
266 if (!env) {
267 return;
268 }
269 napi_handle_scope scope = nullptr;
270 napi_open_handle_scope(env, &scope);
271 if (scope == nullptr) {
272 return;
273 }
274
275 napi_value jsResult = nullptr;
276 if (!result.empty()) {
277 napi_create_string_utf8(env, result.c_str(), NAPI_AUTO_LENGTH, &jsResult);
278 napi_resolve_deferred(env, deferred, jsResult);
279 } else {
280 napi_get_null(env, &jsResult);
281 napi_reject_deferred(env, deferred, jsResult);
282 }
283 napi_close_handle_scope(env, scope);
284 });
285 nweb->StoreWebArchive(baseName, autoName, callbackImpl);
286 return;
287 }
288 } // namespace NWeb
289 } // namespace OHOS
290