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 "webview_javascript_execute_callback.h"
17 
18 #include "business_error.h"
19 #include "napi_parse_utils.h"
20 #include "nweb_log.h"
21 #include "web_errors.h"
22 
23 namespace OHOS::NWeb {
24 using namespace NWebError;
25 const std::string JS_EXCUTE_MSG_ENUM_NAME = "JsMessageType";
26 const std::string JS_EXT_MSG_CLASS_NAME = "JsMessageExt";
27 thread_local napi_ref g_jsMsgExtClassRef;
28 // static
InitJSExcute(napi_env env,napi_value exports)29 void WebviewJavaScriptExecuteCallback::InitJSExcute(napi_env env, napi_value exports)
30 {
31     napi_value jsMsgTypeEnum = nullptr;
32     napi_property_descriptor jsMsgTypeProperties[] = {
33         DECLARE_NAPI_STATIC_PROPERTY("NOT_SUPPORT", NapiParseUtils::ToInt32Value(env,
34             static_cast<int32_t>(JsMessageType::NOTSUPPORT))),
35         DECLARE_NAPI_STATIC_PROPERTY("STRING", NapiParseUtils::ToInt32Value(env,
36             static_cast<int32_t>(JsMessageType::STRING))),
37         DECLARE_NAPI_STATIC_PROPERTY("NUMBER", NapiParseUtils::ToInt32Value(env,
38             static_cast<int32_t>(JsMessageType::NUMBER))),
39         DECLARE_NAPI_STATIC_PROPERTY("BOOLEAN", NapiParseUtils::ToInt32Value(env,
40             static_cast<int32_t>(JsMessageType::BOOLEAN))),
41         DECLARE_NAPI_STATIC_PROPERTY("ARRAY_BUFFER", NapiParseUtils::ToInt32Value(env,
42             static_cast<int32_t>(JsMessageType::ARRAYBUFFER))),
43         DECLARE_NAPI_STATIC_PROPERTY("ARRAY", NapiParseUtils::ToInt32Value(env,
44             static_cast<int32_t>(JsMessageType::ARRAY)))
45     };
46     napi_define_class(env, JS_EXCUTE_MSG_ENUM_NAME.c_str(), JS_EXCUTE_MSG_ENUM_NAME.length(),
47         NapiParseUtils::CreateEnumConstructor, nullptr, sizeof(jsMsgTypeProperties) /
48         sizeof(jsMsgTypeProperties[0]), jsMsgTypeProperties, &jsMsgTypeEnum);
49     napi_set_named_property(env, exports, JS_EXCUTE_MSG_ENUM_NAME.c_str(), jsMsgTypeEnum);
50 
51     napi_value jsMsgExtClass = nullptr;
52     napi_property_descriptor jsMsgExtClsProperties[] = {
53         DECLARE_NAPI_FUNCTION("getType", NapiJsMessageExt::GetType),
54         DECLARE_NAPI_FUNCTION("getString", NapiJsMessageExt::GetString),
55         DECLARE_NAPI_FUNCTION("getNumber", NapiJsMessageExt::GetNumber),
56         DECLARE_NAPI_FUNCTION("getBoolean", NapiJsMessageExt::GetBoolean),
57         DECLARE_NAPI_FUNCTION("getArrayBuffer", NapiJsMessageExt::GetArrayBuffer),
58         DECLARE_NAPI_FUNCTION("getArray", NapiJsMessageExt::GetArray)
59     };
60     napi_define_class(env, JS_EXT_MSG_CLASS_NAME.c_str(), JS_EXT_MSG_CLASS_NAME.length(),
61         NapiJsMessageExt::JsConstructor, nullptr, sizeof(jsMsgExtClsProperties) / sizeof(jsMsgExtClsProperties[0]),
62         jsMsgExtClsProperties, &jsMsgExtClass);
63     napi_create_reference(env, jsMsgExtClass, 1, &g_jsMsgExtClassRef);
64     napi_set_named_property(env, exports, JS_EXT_MSG_CLASS_NAME.c_str(), jsMsgExtClass);
65 }
66 
OnReceiveValue(std::shared_ptr<NWebMessage> result)67 void WebviewJavaScriptExecuteCallback::OnReceiveValue(std::shared_ptr<NWebMessage> result)
68 {
69     WVLOG_D("WebviewJavaScriptExecuteCallback::OnReceiveValue start");
70     uv_loop_s *loop = nullptr;
71     uv_work_t *work = nullptr;
72 
73     napi_get_uv_event_loop(env_, &loop);
74     if (loop == nullptr) {
75         return;
76     }
77     work = new (std::nothrow) uv_work_t;
78     if (work == nullptr) {
79         return;
80     }
81 
82     JavaScriptExecuteParam *param = new (std::nothrow) JavaScriptExecuteParam();
83     if (param == nullptr) {
84         delete work;
85         return;
86     }
87     param->env_ = env_;
88     param->callbackRef_ = callbackRef_;
89     param->deferred_ = deferred_;
90     param->result_ = result;
91     param->extention_ = extention_;
92 
93     work->data = reinterpret_cast<void*>(param);
94 
95     int ret = uv_queue_work_with_qos(loop, work, [](uv_work_t *work) {}, UvAfterWorkCb, uv_qos_user_initiated);
96     if (ret != 0) {
97         if (param != nullptr) {
98             delete param;
99             param = nullptr;
100         }
101         if (work != nullptr) {
102             delete work;
103             work = nullptr;
104         }
105     }
106 }
107 
UvAfterWorkCb(uv_work_t * work,int status)108 void WebviewJavaScriptExecuteCallback::UvAfterWorkCb(uv_work_t* work, int status)
109 {
110     WVLOG_D("WebviewJavaScriptExecuteCallback::UvAfterWorkCb");
111     (void)status;
112     if (!work) {
113         return;
114     }
115     JavaScriptExecuteParam *param = reinterpret_cast<JavaScriptExecuteParam*>(work->data);
116     if (!param) {
117         delete work;
118         work = nullptr;
119         return;
120     }
121     napi_handle_scope scope = nullptr;
122     napi_open_handle_scope(param->env_, &scope);
123     if (scope == nullptr) {
124         return;
125     }
126 
127     if (param->callbackRef_) {
128         UvAfterWorkCbAsync(param->env_, param->callbackRef_, param->result_, param->extention_);
129     } else if (param->deferred_) {
130         UvAfterWorkCbPromise(param->env_, param->deferred_, param->result_, param->extention_);
131     }
132 
133     napi_close_handle_scope(param->env_, scope);
134     delete param;
135     param = nullptr;
136     delete work;
137     work = nullptr;
138 }
139 
UvAfterWorkCbAsync(napi_env env,napi_ref callbackRef,std::shared_ptr<NWebMessage> result,bool extention)140 void WebviewJavaScriptExecuteCallback::UvAfterWorkCbAsync(napi_env env, napi_ref callbackRef,
141     std::shared_ptr<NWebMessage> result, bool extention)
142 {
143     WVLOG_D("WebviewJavaScriptExecuteCallback::UvAfterWorkCbAsync");
144     napi_value setResult[INTEGER_TWO] = {0};
145     if (result->GetType() == NWebValue::Type::STRING && result->GetString().empty()) {
146         setResult[INTEGER_ZERO] = BusinessError::CreateError(env, NWebError::INVALID_RESOURCE);
147         napi_get_null(env, &setResult[INTEGER_ONE]);
148     } else {
149         napi_get_undefined(env, &setResult[INTEGER_ZERO]);
150         if (!extention) {
151             OHOS::NWeb::NapiParseUtils::ConvertNWebToNapiValue(env, result, setResult[INTEGER_ONE]);
152         } else {
153             napi_value jsMsgExt = nullptr;
154             NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, g_jsMsgExtClassRef, &jsMsgExt));
155             NAPI_CALL_RETURN_VOID(env, napi_new_instance(env, jsMsgExt, 0, NULL, &setResult[INTEGER_ONE]));
156 
157             WebJsMessageExt *webJsMessageExt = new (std::nothrow) WebJsMessageExt(result);
158             if (webJsMessageExt == nullptr) {
159                 WVLOG_E("new WebJsMessageExt failed.");
160                 return;
161             }
162 
163             napi_status status = napi_wrap(env, setResult[INTEGER_ONE], webJsMessageExt,
164                 [](napi_env env, void *data, void *hint) {
165                     WebJsMessageExt *webJsMessageExt = static_cast<WebJsMessageExt *>(data);
166                     delete webJsMessageExt;
167                     webJsMessageExt = nullptr;
168                 },
169                 nullptr, nullptr);
170             if (status != napi_status::napi_ok) {
171                 WVLOG_E("napi_wrap failed");
172                 delete webJsMessageExt;
173                 webJsMessageExt = nullptr;
174                 return;
175             }
176         }
177     }
178     napi_value args[INTEGER_TWO] = {setResult[INTEGER_ZERO], setResult[INTEGER_ONE]};
179     napi_value callback = nullptr;
180     napi_value callbackResult = nullptr;
181 
182     napi_get_reference_value(env, callbackRef, &callback);
183     napi_call_function(env, nullptr, callback, INTEGER_TWO, args, &callbackResult);
184     napi_delete_reference(env, callbackRef);
185 }
186 
UvAfterWorkCbPromise(napi_env env,napi_deferred deferred,std::shared_ptr<NWebMessage> result,bool extention)187 void WebviewJavaScriptExecuteCallback::UvAfterWorkCbPromise(napi_env env, napi_deferred deferred,
188     std::shared_ptr<NWebMessage> result, bool extention)
189 {
190     WVLOG_D("WebviewJavaScriptExecuteCallback::UvAfterWorkCbPromise");
191     napi_value setResult[INTEGER_TWO] = {0};
192     setResult[INTEGER_ZERO] = NWebError::BusinessError::CreateError(env, NWebError::INVALID_RESOURCE);
193     if (!extention) {
194         OHOS::NWeb::NapiParseUtils::ConvertNWebToNapiValue(env, result, setResult[INTEGER_ONE]);
195     } else {
196         napi_value jsMsgExt = nullptr;
197         napi_status status = napi_get_reference_value(env, g_jsMsgExtClassRef, &jsMsgExt);
198         if (status != napi_status::napi_ok) {
199             WVLOG_E("napi_get_reference_value failed.");
200             return;
201         }
202         status = napi_new_instance(env, jsMsgExt, 0, NULL, &setResult[INTEGER_ONE]);
203         if (status != napi_status::napi_ok) {
204             WVLOG_E("napi_new_instance failed.");
205             return;
206         }
207         WebJsMessageExt *webJsMessageExt = new (std::nothrow) WebJsMessageExt(result);
208         if (webJsMessageExt == nullptr) {
209             WVLOG_E("new WebJsMessageExt failed.");
210             return;
211         }
212 
213         status = napi_wrap(env, setResult[INTEGER_ONE], webJsMessageExt,
214             [](napi_env env, void *data, void *hint) {
215                 WebJsMessageExt *webJsMessageExt = static_cast<WebJsMessageExt *>(data);
216                 delete webJsMessageExt;
217                 webJsMessageExt = nullptr;
218             },
219             nullptr, nullptr);
220         if (status != napi_status::napi_ok) {
221             WVLOG_E("napi_wrap failed.");
222             return;
223         }
224     }
225 
226     napi_value args[INTEGER_TWO] = {setResult[INTEGER_ZERO], setResult[INTEGER_ONE]};
227     if (result->GetType() == NWebValue::Type::STRING && result->GetString().empty()) {
228         napi_reject_deferred(env, deferred, args[INTEGER_ZERO]);
229     } else {
230         napi_resolve_deferred(env, deferred, args[INTEGER_ONE]);
231     }
232 }
233 
JsConstructor(napi_env env,napi_callback_info info)234 napi_value NapiJsMessageExt::JsConstructor(napi_env env, napi_callback_info info)
235 {
236     napi_value thisVar = nullptr;
237     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
238     return thisVar;
239 }
240 
ConvertToJsType(NWebValue::Type type)241 int32_t WebJsMessageExt::ConvertToJsType(NWebValue::Type type)
242 {
243     JsMessageType jsMessageType = JsMessageType::NOTSUPPORT;
244     switch (type) {
245         case NWebValue::Type::STRING:
246             jsMessageType = JsMessageType::STRING;
247             break;
248         case NWebValue::Type::INTEGER:
249         case NWebValue::Type::DOUBLE:
250             jsMessageType = JsMessageType::NUMBER;
251             break;
252         case NWebValue::Type::BOOLEAN:
253             jsMessageType = JsMessageType::BOOLEAN;
254             break;
255         case NWebValue::Type::BINARY:
256             jsMessageType = JsMessageType::ARRAYBUFFER;
257             break;
258         case NWebValue::Type::STRINGARRAY:
259         case NWebValue::Type::BOOLEANARRAY:
260         case NWebValue::Type::DOUBLEARRAY:
261         case NWebValue::Type::INT64ARRAY:
262             jsMessageType = JsMessageType::ARRAY;
263             break;
264         default:
265             jsMessageType = JsMessageType::NOTSUPPORT;
266             break;
267     }
268     return static_cast<int32_t>(jsMessageType);
269 }
270 
GetType()271 int32_t WebJsMessageExt::GetType()
272 {
273     if (value_) {
274         return ConvertToJsType(value_->GetType());
275     }
276     return static_cast<int32_t>(JsMessageType::NOTSUPPORT);
277 }
278 
GetString()279 std::string WebJsMessageExt::GetString()
280 {
281     if (value_) {
282         return value_->GetString();
283     }
284     return "";
285 }
286 
GetNumber()287 double WebJsMessageExt::GetNumber()
288 {
289     if (value_) {
290         return value_->GetDouble();
291     }
292     return 0;
293 }
294 
GetBoolean()295 bool WebJsMessageExt::GetBoolean()
296 {
297     if (value_) {
298         return value_->GetBoolean();
299     }
300     return false;
301 }
302 
GetType(napi_env env,napi_callback_info info)303 napi_value NapiJsMessageExt::GetType(napi_env env, napi_callback_info info)
304 {
305     WVLOG_D("GetType webJsMessageExt start");
306     napi_value thisVar = nullptr;
307     napi_value result = nullptr;
308     size_t argc = INTEGER_ONE;
309     napi_value argv[INTEGER_ONE] = { 0 };
310 
311     WebJsMessageExt *webJsMessageExt = nullptr;
312     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
313     if (status != napi_status::napi_ok) {
314         WVLOG_E("napi_get_cb_info failed.");
315         return result;
316     }
317     status = napi_unwrap(env, thisVar, (void **)&webJsMessageExt);
318     if (status != napi_status::napi_ok) {
319         WVLOG_E("napi_unwrap failed.");
320         return result;
321     }
322     if (webJsMessageExt == nullptr) {
323         WVLOG_E("unwrap webJsMessageExt failed.");
324         return result;
325     }
326 
327     int32_t type = webJsMessageExt->GetType();
328     status = napi_create_int32(env, type, &result);
329     if (status != napi_status::napi_ok) {
330         WVLOG_E("napi_create_int32 failed.");
331         return result;
332     }
333     return result;
334 }
335 
GetString(napi_env env,napi_callback_info info)336 napi_value NapiJsMessageExt::GetString(napi_env env, napi_callback_info info)
337 {
338     WVLOG_D("GetString webJsMessageExt");
339     napi_value thisVar = nullptr;
340     napi_value result = nullptr;
341     size_t argc = INTEGER_ONE;
342     napi_value argv[INTEGER_ONE] = { 0 };
343 
344     WebJsMessageExt *webJsMessageExt = nullptr;
345     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
346     NAPI_CALL(env, napi_unwrap(env, thisVar, (void **)&webJsMessageExt));
347     if (webJsMessageExt == nullptr) {
348         WVLOG_E("unwrap webJsMessageExt failed.");
349         return result;
350     }
351 
352     if (webJsMessageExt->GetType() != static_cast<int32_t>(JsMessageType::STRING)) {
353         BusinessError::ThrowErrorByErrcode(env, TYPE_NOT_MATCH_WITCH_VALUE);
354         return nullptr;
355     }
356 
357     NapiParseUtils::ConvertNWebToNapiValue(env, webJsMessageExt->GetJsMsgResult(), result);
358     return result;
359 }
360 
GetNumber(napi_env env,napi_callback_info info)361 napi_value NapiJsMessageExt::GetNumber(napi_env env, napi_callback_info info)
362 {
363     WVLOG_D("GetNumber webJsMessageExt");
364     napi_value thisVar = nullptr;
365     napi_value result = nullptr;
366     size_t argc = INTEGER_ONE;
367     napi_value argv[INTEGER_ONE] = { 0 };
368 
369     WebJsMessageExt *webJsMessageExt = nullptr;
370     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
371     NAPI_CALL(env, napi_unwrap(env, thisVar, (void **)&webJsMessageExt));
372     if (webJsMessageExt == nullptr) {
373         WVLOG_E("unwrap webJsMessageExt failed.");
374         return result;
375     }
376 
377     if (webJsMessageExt->GetType() != static_cast<int32_t>(JsMessageType::NUMBER)) {
378         BusinessError::ThrowErrorByErrcode(env, TYPE_NOT_MATCH_WITCH_VALUE);
379         WVLOG_E("GetNumber webJsMessageExt failed not match");
380         return nullptr;
381     }
382 
383     NapiParseUtils::ConvertNWebToNapiValue(env, webJsMessageExt->GetJsMsgResult(), result);
384     return result;
385 }
386 
GetBoolean(napi_env env,napi_callback_info info)387 napi_value NapiJsMessageExt::GetBoolean(napi_env env, napi_callback_info info)
388 {
389     napi_value thisVar = nullptr;
390     napi_value result = nullptr;
391     size_t argc = INTEGER_ONE;
392     napi_value argv[INTEGER_ONE] = { 0 };
393 
394     WebJsMessageExt *webJsMessageExt = nullptr;
395     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
396     NAPI_CALL(env, napi_unwrap(env, thisVar, (void **)&webJsMessageExt));
397     if (webJsMessageExt == nullptr) {
398         WVLOG_E("unwrap webJsMessageExt failed.");
399         return result;
400     }
401 
402     if (webJsMessageExt->GetType() != static_cast<int32_t>(JsMessageType::BOOLEAN)) {
403         BusinessError::ThrowErrorByErrcode(env, TYPE_NOT_MATCH_WITCH_VALUE);
404         return nullptr;
405     }
406 
407     NapiParseUtils::ConvertNWebToNapiValue(env, webJsMessageExt->GetJsMsgResult(), result);
408     return result;
409 }
410 
GetArrayBuffer(napi_env env,napi_callback_info info)411 napi_value NapiJsMessageExt::GetArrayBuffer(napi_env env, napi_callback_info info)
412 {
413     napi_value thisVar = nullptr;
414     napi_value result = nullptr;
415     size_t argc = INTEGER_ONE;
416     napi_value argv[INTEGER_ONE] = { 0 };
417 
418     WebJsMessageExt *webJsMessageExt = nullptr;
419     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
420     NAPI_CALL(env, napi_unwrap(env, thisVar, (void **)&webJsMessageExt));
421     if (webJsMessageExt == nullptr) {
422         WVLOG_E("unwrap webJsMessageExt failed.");
423         return result;
424     }
425 
426     if (webJsMessageExt->GetType() != static_cast<int32_t>(JsMessageType::ARRAYBUFFER)) {
427         BusinessError::ThrowErrorByErrcode(env, TYPE_NOT_MATCH_WITCH_VALUE);
428         return nullptr;
429     }
430     NapiParseUtils::ConvertNWebToNapiValue(env, webJsMessageExt->GetJsMsgResult(), result);
431     return result;
432 }
433 
GetArray(napi_env env,napi_callback_info info)434 napi_value NapiJsMessageExt::GetArray(napi_env env, napi_callback_info info)
435 {
436     napi_value thisVar = nullptr;
437     napi_value result = nullptr;
438     size_t argc = INTEGER_ONE;
439     napi_value argv[INTEGER_ONE] = { 0 };
440 
441     WebJsMessageExt *webJsMessageExt = nullptr;
442     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
443     NAPI_CALL(env, napi_unwrap(env, thisVar, (void **)&webJsMessageExt));
444     if (webJsMessageExt == nullptr) {
445         WVLOG_E("unwrap webJsMessageExt failed.");
446         return result;
447     }
448 
449     if (webJsMessageExt->GetType() != static_cast<int32_t>(JsMessageType::ARRAY)) {
450         BusinessError::ThrowErrorByErrcode(env, TYPE_NOT_MATCH_WITCH_VALUE);
451         return nullptr;
452     }
453 
454     NapiParseUtils::ConvertNWebToNapiValue(env, webJsMessageExt->GetJsMsgResult(), result);
455     return result;
456 }
457 
458 } // namespace OHOS::NWeb
459