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_result_callback.h"
17
18 #include "core/common/container_scope.h"
19 #include "napi_parse_utils.h"
20 #include "native_engine/native_engine.h"
21 #include "nweb_log.h"
22
23 namespace OHOS::NWeb {
24 namespace {
25 #define JS_BRIDGE_BINARY_ARGS_COUNT 2
26 // For the sake of the storage API, make this quite large.
27 const uint32_t MAX_RECURSION_DEPTH = 11;
28 const uint32_t MAX_DATA_LENGTH = 10000;
29
30 std::unordered_map<int32_t, WebviewJavaScriptResultCallBack*> g_webviewJsResultCallbackMap;
31 std::mutex g_objectMtx;
32
33 std::vector<std::string> ParseNapiValue2NwebValue(napi_env env, napi_value& value,
34 std::shared_ptr<NWebValue> nwebValue, bool* isObject);
35 std::vector<std::string> ParseNapiValue2NwebValue(napi_env env, napi_value* value,
36 std::shared_ptr<NWebValue> nwebValue, bool* isObject);
37
38 class ValueConvertState {
39 public:
40 // Level scope which updates the current depth of some ValueConvertState.
41 class Level {
42 public:
Level(ValueConvertState * state)43 explicit Level(ValueConvertState* state) : state_(state)
44 {
45 state_->maxRecursionDepth_--;
46 }
~Level()47 ~Level()
48 {
49 state_->maxRecursionDepth_++;
50 }
51
52 private:
53 ValueConvertState* state_;
54 };
55
ValueConvertState()56 explicit ValueConvertState() : maxRecursionDepth_(MAX_RECURSION_DEPTH)
57 {
58 }
59
60 ValueConvertState(const ValueConvertState&) = delete;
61 ValueConvertState& operator=(const ValueConvertState&) = delete;
62
HasReachedMaxRecursionDepth()63 bool HasReachedMaxRecursionDepth()
64 {
65 return maxRecursionDepth_ == 0;
66 }
67
68 private:
69 uint32_t maxRecursionDepth_;
70 };
71
FromNwebID(int32_t nwebId)72 WebviewJavaScriptResultCallBack* FromNwebID(int32_t nwebId)
73 {
74 std::unique_lock<std::mutex> lk(g_objectMtx);
75 if (auto it = g_webviewJsResultCallbackMap.find(nwebId); it != g_webviewJsResultCallbackMap.end()) {
76 auto js_result_callback = it->second;
77 return js_result_callback;
78 }
79 return nullptr;
80 }
81
StringSplit(std::string str,const char split,std::vector<std::string> & result)82 void StringSplit(std::string str, const char split, std::vector<std::string>& result)
83 {
84 std::istringstream iss(str);
85 std::string token;
86 while (getline(iss, token, split)) {
87 result.push_back(token);
88 }
89 }
90
CallH5Function(napi_env env,napi_value * napiArg,std::shared_ptr<NWebValue> nwebValue,WebviewJavaScriptResultCallBack::H5Bundle bundle)91 void CallH5Function(napi_env env, napi_value* napiArg, std::shared_ptr<NWebValue> nwebValue,
92 WebviewJavaScriptResultCallBack::H5Bundle bundle)
93 {
94 WVLOG_D("CallH5Function called");
95 bool isObject = false;
96 std::vector<std::string> methodNameList;
97 methodNameList = ParseNapiValue2NwebValue(env, napiArg, nwebValue, &isObject);
98 if (isObject && FromNwebID(bundle.nwebId)) {
99 JavaScriptOb::ObjectID returnedObjectId;
100 if (FromNwebID(bundle.nwebId)->FindObjectIdInJsTd(env, *napiArg, &returnedObjectId)) {
101 FromNwebID(bundle.nwebId)->FindObject(returnedObjectId)->AddHolder(bundle.frameRoutingId);
102 } else {
103 returnedObjectId = FromNwebID(bundle.nwebId)->AddObject(env, *napiArg, false, bundle.frameRoutingId);
104 }
105
106 FromNwebID(bundle.nwebId)->SetUpAnnotateMethods(returnedObjectId, methodNameList);
107
108 napi_valuetype valueType = napi_undefined;
109 napi_typeof(env, *napiArg, &valueType);
110 if (valueType == napi_function) {
111 WVLOG_D("CallH5Function function");
112 nwebValue = std::make_shared<NWebValue>();
113 } else {
114 WVLOG_D("CallH5Function object");
115 std::string bin = std::string("TYPE_OBJECT_ID") + std::string(";") + std::to_string(returnedObjectId);
116 nwebValue = std::make_shared<NWebValue>(bin.c_str(), bin.size());
117 }
118 }
119 }
120
CallbackFunctionForH5(napi_env env,napi_callback_info info)121 napi_value CallbackFunctionForH5(napi_env env, napi_callback_info info)
122 {
123 WVLOG_D("CallbackFunctionForH5 called");
124 napi_escapable_handle_scope scope = nullptr;
125 napi_open_escapable_handle_scope(env, &scope);
126 napi_value h5CallBackResult = nullptr;
127 napi_value thisVar = nullptr;
128 size_t argc = 0;
129 void* data = nullptr;
130 napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
131 napi_value* napiArgs = nullptr;
132 if (argc > 0) {
133 WVLOG_D("CallbackFunctionForH5 argc not zero");
134 napiArgs = new napi_value[argc];
135 if (!napiArgs) {
136 WVLOG_D("CallbackFunctionForH5 argc malloc fail");
137 napi_get_undefined(env, &h5CallBackResult);
138 napi_close_escapable_handle_scope(env, scope);
139 return h5CallBackResult;
140 }
141 }
142
143 napi_get_cb_info(env, info, &argc, napiArgs, &thisVar, &data);
144 WebviewJavaScriptResultCallBack::H5Bundle bundle =
145 *reinterpret_cast<WebviewJavaScriptResultCallBack::H5Bundle*>(data);
146
147 std::vector<std::shared_ptr<NWebValue>> nwebArgs;
148 for (size_t i = 0; i < argc; i++) {
149 std::shared_ptr<NWebValue> nwebArg = std::make_shared<NWebValue>(NWebValue::Type::NONE);
150 napi_value napiArg = napiArgs[i];
151 napi_escape_handle(env, scope, napiArg, &napiArg);
152 CallH5Function(env, &napiArg, nwebArg, bundle);
153 nwebArgs.push_back(nwebArg);
154 }
155
156 if (FromNwebID(bundle.nwebId)) {
157 FromNwebID(bundle.nwebId)->CallH5FunctionInternal(env, bundle, nwebArgs);
158 }
159
160 if (napiArgs) {
161 delete[] napiArgs;
162 }
163
164 napi_get_undefined(env, &h5CallBackResult);
165 napi_close_escapable_handle_scope(env, scope);
166 return h5CallBackResult;
167 }
168
CreateFunctionForH5(napi_env env,int32_t nwebId,int32_t frameRoutingId,int32_t h5ObjectId,std::string funcName)169 napi_value CreateFunctionForH5(
170 napi_env env, int32_t nwebId, int32_t frameRoutingId, int32_t h5ObjectId, std::string funcName)
171 {
172 // Create a bundle.
173 auto bundle = std::make_unique<OHOS::NWeb::WebviewJavaScriptResultCallBack::H5Bundle>();
174 bundle->nwebId = nwebId;
175 bundle->frameRoutingId = frameRoutingId;
176 bundle->h5Id = h5ObjectId;
177 bundle->funcName = funcName;
178
179 napi_value func = nullptr;
180 napi_status s = napi_ok;
181 s = napi_create_function(env, funcName.c_str(), funcName.size(), CallbackFunctionForH5, bundle.release(), &func);
182 if (s != napi_ok) {
183 WVLOG_E("CreateFunctionForH5 napi api call fail");
184 return nullptr;
185 }
186 return func;
187 }
188
AddFunctionToObjectForH5(napi_env env,OHOS::NWeb::WebviewJavaScriptResultCallBack::H5Bundle & bundle,napi_value obj)189 void AddFunctionToObjectForH5(
190 napi_env env, OHOS::NWeb::WebviewJavaScriptResultCallBack::H5Bundle& bundle, napi_value obj)
191 {
192 if (!obj || bundle.frameRoutingId < 0) {
193 WVLOG_D("AddFunctionToObjectForH5 obj or frame id error");
194 return;
195 }
196 napi_value func = CreateFunctionForH5(env, bundle.nwebId, bundle.frameRoutingId, bundle.h5Id, bundle.funcName);
197 if (!func) {
198 WVLOG_D("AddFunctionToObjectForH5 create func fail");
199 return;
200 }
201 napi_status s = napi_ok;
202 s = napi_set_named_property(env, obj, bundle.funcName.c_str(), func);
203 if (s != napi_ok) {
204 WVLOG_E("AddFunctionToObjectForH5 napi api call fail");
205 }
206 }
207
CreateProxyForH5Object(napi_env env,napi_value * result)208 void CreateProxyForH5Object(napi_env env, napi_value* result)
209 {
210 napi_status s = napi_ok;
211 s = napi_create_object(env, result);
212 if (s != napi_ok) {
213 WVLOG_E("CreateProxyFOrH5Object napi api call fail");
214 }
215 }
216
OpenScope(napi_env env)217 napi_handle_scope OpenScope(napi_env env)
218 {
219 napi_handle_scope scope = nullptr;
220 NAPI_CALL(env, napi_open_handle_scope(env, &scope));
221 return scope;
222 }
223
CloseScope(napi_env env,napi_handle_scope scope)224 void CloseScope(napi_env env, napi_handle_scope scope)
225 {
226 (void)napi_close_handle_scope(env, scope);
227 }
228
CreateUvQueueWorkEnhanced(napi_env env,WebviewJavaScriptResultCallBack::NapiJsCallBackParm * data,void (* handler)(napi_env env,napi_status status,WebviewJavaScriptResultCallBack::NapiJsCallBackParm * data))229 void CreateUvQueueWorkEnhanced(napi_env env, WebviewJavaScriptResultCallBack::NapiJsCallBackParm* data,
230 void (*handler)(napi_env env, napi_status status, WebviewJavaScriptResultCallBack::NapiJsCallBackParm* data))
231 {
232 uv_loop_s* loop = nullptr;
233 NAPI_CALL_RETURN_VOID(env, napi_get_uv_event_loop(env, &loop));
234 class WorkData {
235 public:
236 WorkData() = delete;
237
238 WorkData(napi_env env, WebviewJavaScriptResultCallBack::NapiJsCallBackParm* data,
239 void (*handler)(
240 napi_env env, napi_status status, WebviewJavaScriptResultCallBack::NapiJsCallBackParm* data))
241 : env_(env), data_(data), handler_(handler)
242 {}
243
244 napi_env env_;
245 WebviewJavaScriptResultCallBack::NapiJsCallBackParm* data_;
246 void (*handler_)(napi_env env, napi_status status, WebviewJavaScriptResultCallBack::NapiJsCallBackParm* data);
247 };
248
249 auto workData = new WorkData(env, data, handler);
250 auto work = new uv_work_t;
251 work->data = reinterpret_cast<void*>(workData);
252
253 auto callback = [](uv_work_t* work, int status) {
254 auto workData = static_cast<WorkData*>(work->data);
255 if (!workData) {
256 delete work;
257 return;
258 }
259
260 if (!workData->env_ || !workData->data_ || !workData->handler_) {
261 delete workData;
262 delete work;
263 return;
264 }
265
266 napi_env env = workData->env_;
267 auto closeScope = [env](napi_handle_scope scope) { CloseScope(env, scope); };
268 std::unique_ptr<napi_handle_scope__, decltype(closeScope)> scope(OpenScope(env), closeScope);
269
270 workData->handler_(workData->env_, static_cast<napi_status>(status), workData->data_);
271
272 delete workData;
273 delete work;
274 };
275 int ret = uv_queue_work_with_qos(
276 loop, work, [](uv_work_t* work) {}, callback, uv_qos_user_initiated);
277 if (ret != 0) {
278 if (workData) {
279 delete workData;
280 workData = nullptr;
281 }
282 if (work) {
283 delete work;
284 work = nullptr;
285 }
286 return;
287 }
288 }
289
CreateNapiJsCallBackParm(WebviewJavaScriptResultCallBack::NapiJsCallBackInParm * & inParam,WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm * & outParam,WebviewJavaScriptResultCallBack::NapiJsCallBackParm * & param)290 bool CreateNapiJsCallBackParm(WebviewJavaScriptResultCallBack::NapiJsCallBackInParm*& inParam,
291 WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm*& outParam,
292 WebviewJavaScriptResultCallBack::NapiJsCallBackParm*& param)
293 {
294 inParam = new (std::nothrow) WebviewJavaScriptResultCallBack::NapiJsCallBackInParm();
295 if (inParam == nullptr) {
296 WVLOG_D("CreateNapiJsCallBackParm argc malloc fail");
297 return false;
298 }
299 outParam = new (std::nothrow) WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm();
300 if (outParam == nullptr) {
301 WVLOG_D("CreateNapiJsCallBackParm argc malloc fail");
302 delete inParam;
303 return false;
304 }
305 param = new (std::nothrow) WebviewJavaScriptResultCallBack::NapiJsCallBackParm();
306 if (param == nullptr) {
307 WVLOG_D("CreateNapiJsCallBackParm argc malloc fail");
308 delete inParam;
309 delete outParam;
310 return false;
311 }
312 return true;
313 }
314
DeleteNapiJsCallBackParm(WebviewJavaScriptResultCallBack::NapiJsCallBackInParm * inParam,WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm * outParam,WebviewJavaScriptResultCallBack::NapiJsCallBackParm * param)315 void DeleteNapiJsCallBackParm(WebviewJavaScriptResultCallBack::NapiJsCallBackInParm* inParam,
316 WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm* outParam,
317 WebviewJavaScriptResultCallBack::NapiJsCallBackParm* param)
318 {
319 if (inParam != nullptr) {
320 delete inParam;
321 inParam = nullptr;
322 }
323
324 if (outParam != nullptr) {
325 delete outParam;
326 outParam = nullptr;
327 }
328
329 if (param != nullptr) {
330 delete param;
331 param = nullptr;
332 }
333 }
334
335 napi_value ParseArrayNwebValue2NapiValue(napi_env env, const std::shared_ptr<NWebValue>& value,
336 WebviewJavaScriptResultCallBack::ObjectMap& objectsMap, int32_t nwebId, int32_t frameId);
337 napi_value ParseDictionaryNwebValue2NapiValue(napi_env env, const std::shared_ptr<NWebValue>& value,
338 WebviewJavaScriptResultCallBack::ObjectMap& objectsMap, int32_t nwebId, int32_t frameId);
339 napi_value ParseBinNwebValue2NapiValue(napi_env env, const std::shared_ptr<NWebValue>& value,
340 WebviewJavaScriptResultCallBack::ObjectMap& objectsMap, int32_t nwebId, int32_t frameId);
341
342 void ParseDictionaryNapiValue2NwebValue(
343 napi_env env, ValueConvertState* state, napi_value& value, std::shared_ptr<NWebValue>& nwebValue, bool* isOject);
344
ParseBasicTypeNwebValue2NapiValue(napi_env env,const std::shared_ptr<NWebValue> & value,napi_value & napiValue)345 bool ParseBasicTypeNwebValue2NapiValue(napi_env env, const std::shared_ptr<NWebValue>& value, napi_value& napiValue)
346 {
347 napi_status s = napi_ok;
348 switch (value->GetType()) {
349 case NWebValue::Type::INTEGER:
350 WVLOG_D("ParseBasicTypeNwebValue2NapiValue INTEGER type");
351 s = napi_create_int32(env, value->GetInt(), &napiValue);
352 if (s != napi_ok) {
353 WVLOG_E("ParseBasicTypeNwebValue2NapiValue napi api call fail");
354 }
355 break;
356 case NWebValue::Type::DOUBLE:
357 WVLOG_D("ParseBasicTypeNwebValue2NapiValue DOUBLE type");
358 s = napi_create_double(env, value->GetDouble(), &napiValue);
359 if (s != napi_ok) {
360 WVLOG_E("ParseBasicTypeNwebValue2NapiValue napi api call fail");
361 }
362 break;
363 case NWebValue::Type::BOOLEAN:
364 WVLOG_D("ParseBasicTypeNwebValue2NapiValue BOOLEAN type");
365 s = napi_get_boolean(env, value->GetBoolean(), &napiValue);
366 if (s != napi_ok) {
367 WVLOG_E("ParseBasicTypeNwebValue2NapiValue napi api call fail");
368 }
369 break;
370 case NWebValue::Type::STRING:
371 WVLOG_D("ParseBasicTypeNwebValue2NapiValue STRING type");
372 s = napi_create_string_utf8(env, value->GetString().c_str(), NAPI_AUTO_LENGTH, &napiValue);
373 if (s != napi_ok) {
374 WVLOG_E("ParseBasicTypeNwebValue2NapiValue napi api call fail");
375 }
376 break;
377 default:
378 return false;
379 }
380 return true;
381 }
382
ParseNwebValue2NapiValueHelper(napi_env env,std::shared_ptr<NWebValue> value,WebviewJavaScriptResultCallBack::ObjectMap & objectsMap,int32_t nwebId,int32_t frameId)383 napi_value ParseNwebValue2NapiValueHelper(napi_env env, std::shared_ptr<NWebValue> value,
384 WebviewJavaScriptResultCallBack::ObjectMap& objectsMap, int32_t nwebId, int32_t frameId)
385 {
386 napi_value napiValue = nullptr;
387 if (!value) {
388 napi_get_undefined(env, &napiValue);
389 return napiValue;
390 }
391 if (ParseBasicTypeNwebValue2NapiValue(env, value, napiValue)) {
392 return napiValue;
393 }
394 switch (value->GetType()) {
395 case NWebValue::Type::LIST: {
396 WVLOG_D("ParseBasicTypeNwebValue2NapiValue LIST type");
397 napiValue = ParseArrayNwebValue2NapiValue(env, value, objectsMap, nwebId, frameId);
398 return napiValue;
399 }
400 case NWebValue::Type::DICTIONARY: {
401 WVLOG_D("ParseBasicTypeNwebValue2NapiValue DICTIONARY type");
402 napiValue = ParseDictionaryNwebValue2NapiValue(env, value, objectsMap, nwebId, frameId);
403 return napiValue;
404 }
405 case NWebValue::Type::BINARY: {
406 WVLOG_D("ParseBasicTypeNwebValue2NapiValue BINARY type");
407 napiValue = ParseBinNwebValue2NapiValue(env, value, objectsMap, nwebId, frameId);
408 return napiValue;
409 }
410 case NWebValue::Type::NONE: {
411 WVLOG_D("ParseBasicTypeNwebValue2NapiValue NONE type");
412 break;
413 }
414 default:
415 WVLOG_E("ParseNwebValue2NapiValueHelper invalid type");
416 break;
417 }
418 napi_get_undefined(env, &napiValue);
419 return napiValue;
420 }
421
ParseArrayNwebValue2NapiValue(napi_env env,const std::shared_ptr<NWebValue> & value,WebviewJavaScriptResultCallBack::ObjectMap & objectsMap,int32_t nwebId,int32_t frameId)422 napi_value ParseArrayNwebValue2NapiValue(napi_env env, const std::shared_ptr<NWebValue>& value,
423 WebviewJavaScriptResultCallBack::ObjectMap& objectsMap, int32_t nwebId, int32_t frameId)
424 {
425 napi_value napiValue = nullptr;
426 napi_status s = napi_ok;
427 size_t length = value->GetListValueSize();
428 s = napi_create_array_with_length(env, length, &napiValue);
429 if (s != napi_ok) {
430 WVLOG_E("ParseArrayNwebValue2NapiValue napi api call fail");
431 return napiValue;
432 }
433 for (size_t i = 0; i < length; ++i) {
434 auto nPtr = std::make_shared<NWebValue>(value->GetListValue(i));
435 s = napi_set_element(env, napiValue, i, ParseNwebValue2NapiValueHelper(env, nPtr, objectsMap, nwebId, frameId));
436 if (s != napi_ok) {
437 WVLOG_E("ParseArrayNwebValue2NapiValue napi api call fail");
438 }
439 }
440 return napiValue;
441 }
442
ParseDictionaryNwebValue2NapiValue(napi_env env,const std::shared_ptr<NWebValue> & value,WebviewJavaScriptResultCallBack::ObjectMap & objectsMap,int32_t nwebId,int32_t frameId)443 napi_value ParseDictionaryNwebValue2NapiValue(napi_env env, const std::shared_ptr<NWebValue>& value,
444 WebviewJavaScriptResultCallBack::ObjectMap& objectsMap, int32_t nwebId, int32_t frameId)
445 {
446 napi_value napiValue = nullptr;
447 napi_status s = napi_ok;
448 s = napi_create_object(env, &napiValue);
449 auto dict = value->GetDictionaryValue();
450 for (auto& item : dict) {
451 auto nValuePtr = std::make_shared<NWebValue>(item.second);
452 auto nKeyPtr = std::make_shared<NWebValue>(item.first);
453 s = napi_set_property(env, napiValue, ParseNwebValue2NapiValueHelper(env, nKeyPtr, objectsMap, nwebId, frameId),
454 ParseNwebValue2NapiValueHelper(env, nValuePtr, objectsMap, nwebId, frameId));
455 if (s != napi_ok) {
456 WVLOG_E("ParseDictionaryNwebValue2NapiValue napi api call fail");
457 }
458 }
459 return napiValue;
460 }
461
ParseBinNwebValue2NapiValue(napi_env env,const std::shared_ptr<NWebValue> & value,WebviewJavaScriptResultCallBack::ObjectMap & objectsMap,int32_t nwebId,int32_t frameId)462 napi_value ParseBinNwebValue2NapiValue(napi_env env, const std::shared_ptr<NWebValue>& value,
463 WebviewJavaScriptResultCallBack::ObjectMap& objectsMap, int32_t nwebId, int32_t frameId)
464 {
465 napi_value napiValue = nullptr;
466 napi_get_undefined(env, &napiValue);
467 auto buff = value->GetBinaryValue();
468 JavaScriptOb::ObjectID objId;
469 std::string str(buff);
470 std::vector<std::string> strList;
471 StringSplit(str, ';', strList);
472 if (strList.size() < JS_BRIDGE_BINARY_ARGS_COUNT) {
473 napi_get_undefined(env, &napiValue);
474 return napiValue;
475 }
476 std::istringstream ss(strList[1]);
477 ss >> objId;
478 if (strList[0] == "TYPE_OBJECT_ID") {
479 WVLOG_D("ParseNwebValue2NapiValueHelper: TYPE_OBJECT_ID");
480 auto iter = objectsMap.find(objId);
481 if (iter != objectsMap.end() && iter->second) {
482 WVLOG_I("ParseNwebValue2NapiValueHelper: type is "
483 "binary, object is found and object_id == %{public}d",
484 objId);
485 napiValue = iter->second->GetValue();
486 }
487 return napiValue;
488 } else if (strList[0] == "TYPE_H5_OBJECT_ID") {
489 CreateProxyForH5Object(env, &napiValue);
490 std::vector<std::string> methodNames;
491 methodNames.assign(strList.begin() + JS_BRIDGE_BINARY_ARGS_COUNT, strList.end()); // skip id and type
492 WVLOG_D("ParseNwebValue2NapiValueHelper: TYPE_H5_OBJECT_ID");
493 for (auto name : methodNames) {
494 OHOS::NWeb::WebviewJavaScriptResultCallBack::H5Bundle bundle;
495 bundle.nwebId = nwebId;
496 bundle.frameRoutingId = frameId;
497 bundle.h5Id = objId;
498 bundle.funcName = name;
499 AddFunctionToObjectForH5(env, bundle, napiValue);
500 }
501 return napiValue;
502 } else if (strList[0] == "TYPE_H5_FUNCTION_ID") {
503 WVLOG_D("ParseNwebValue2NapiValueHelper: TYPE_H5_FUNCTION_ID");
504 napiValue = CreateFunctionForH5(env, nwebId, frameId, objId, "");
505 return napiValue;
506 }
507 return napiValue;
508 }
509
ParseNwebValue2NapiValue(napi_env env,std::shared_ptr<NWebValue> value,WebviewJavaScriptResultCallBack::ObjectMap objectsMap,int32_t nwebId,int32_t frameId)510 napi_value ParseNwebValue2NapiValue(napi_env env, std::shared_ptr<NWebValue> value,
511 WebviewJavaScriptResultCallBack::ObjectMap objectsMap, int32_t nwebId, int32_t frameId)
512 {
513 return ParseNwebValue2NapiValueHelper(env, value, objectsMap, nwebId, frameId);
514 }
515
ParseBasicTypeNapiValue2NwebValue(napi_env env,napi_value & value,std::shared_ptr<NWebValue> & nwebValue,bool * isObject)516 bool ParseBasicTypeNapiValue2NwebValue(napi_env env, napi_value& value,
517 std::shared_ptr<NWebValue>& nwebValue, bool* isObject)
518 {
519 napi_valuetype valueType = napi_undefined;
520 napi_typeof(env, value, &valueType);
521 napi_status s = napi_ok;
522 switch (valueType) {
523 case napi_undefined: // fallthrough
524 case napi_null:
525 WVLOG_D("ParseBasicTypeNapiValue2NwebValue null or undefined type");
526 nwebValue->SetType(NWebValue::Type::NONE);
527 break;
528 case napi_number: {
529 WVLOG_D("ParseBasicTypeNapiValue2NwebValue number type");
530 double douVal = 0.0;
531 s = napi_get_value_double(env, value, &douVal);
532 nwebValue->SetType(NWebValue::Type::DOUBLE);
533 nwebValue->SetDouble(douVal);
534 break;
535 }
536 case napi_boolean: {
537 WVLOG_D("ParseBasicTypeNapiValue2NwebValue boolean type");
538 bool boolVal;
539 s = napi_get_value_bool(env, value, &boolVal);
540 nwebValue->SetType(NWebValue::Type::BOOLEAN);
541 nwebValue->SetBoolean(boolVal);
542 break;
543 }
544 case napi_symbol: // fallthrough
545 case napi_string: {
546 WVLOG_D("ParseBasicTypeNapiValue2NwebValue string type");
547 std::string strVal;
548 if (!NapiParseUtils::ParseString(env, value, strVal)) {
549 WVLOG_E("ParseBasicTypeNapiValue2NwebValue NapiParseUtils::ParseString "
550 "failed");
551 }
552 if (strVal == "methodNameListForJsProxy") {
553 *isObject = true;
554 }
555 nwebValue->SetType(NWebValue::Type::STRING);
556 nwebValue->SetString(strVal);
557 break;
558 }
559 default:
560 WVLOG_E("ParseBasicTypeNapiValue2NwebValue invalid type");
561 return false;
562 }
563 return true;
564 }
565
ParseNapiValue2NwebValueHelper(napi_env env,ValueConvertState * state,napi_value & value,std::shared_ptr<NWebValue> nwebValue,bool * isOject)566 void ParseNapiValue2NwebValueHelper(
567 napi_env env, ValueConvertState* state, napi_value& value,
568 std::shared_ptr<NWebValue> nwebValue, bool* isOject)
569 {
570 ValueConvertState::Level stateLevel(state);
571 if (state->HasReachedMaxRecursionDepth()) {
572 return;
573 }
574 if (!nwebValue || ParseBasicTypeNapiValue2NwebValue(env, value, nwebValue, isOject)) {
575 return;
576 }
577 napi_valuetype valueType = napi_undefined;
578 napi_typeof(env, value, &valueType);
579 napi_status s = napi_ok;
580 switch (valueType) {
581 case napi_object: {
582 bool isArray;
583 s = napi_is_array(env, value, &isArray);
584 if (s != napi_ok) {
585 WVLOG_E("ParseNapiValue2NwebValueHelper napi api call fail");
586 }
587 if (!isArray) {
588 WVLOG_D("ParseNapiValue2NwebValueHelper napi isArray");
589 ParseDictionaryNapiValue2NwebValue(env, state, value, nwebValue, isOject);
590 break;
591 }
592 nwebValue->SetType(NWebValue::Type::LIST);
593 uint32_t size;
594 s = napi_get_array_length(env, value, &size);
595 size = std::min(size, MAX_DATA_LENGTH);
596 if (s != napi_ok) {
597 WVLOG_E("ParseNapiValue2NwebValueHelper napi api call fail");
598 }
599 for (uint32_t i = 0; i < size; i++) {
600 napi_value napiTmp;
601 s = napi_get_element(env, value, i, &napiTmp);
602 if (s != napi_ok) {
603 WVLOG_E("ParseNapiValue2NwebValueHelper napi api call fail");
604 }
605 auto nwebTmp = std::make_shared<NWebValue>();
606 ParseNapiValue2NwebValueHelper(env, state, napiTmp, nwebTmp, isOject);
607 nwebValue->AddListValue(*nwebTmp);
608 }
609 break;
610 }
611 default: {
612 WVLOG_E("ParseNapiValue2NwebValueHelper invalid type");
613 }
614 }
615 }
616
ParseDictionaryNapiValue2NwebValue(napi_env env,ValueConvertState * state,napi_value & value,std::shared_ptr<NWebValue> & nwebValue,bool * isOject)617 void ParseDictionaryNapiValue2NwebValue(
618 napi_env env, ValueConvertState* state, napi_value& value, std::shared_ptr<NWebValue>& nwebValue, bool* isOject)
619 {
620 napi_status s = napi_ok;
621 nwebValue->SetType(NWebValue::Type::DICTIONARY);
622 napi_value propertyNames;
623 s = napi_get_property_names(env, value, &propertyNames);
624 if (s != napi_ok) {
625 WVLOG_E("ParseDictionaryNapiValue2NwebValue napi api call fail");
626 }
627 uint32_t size;
628 s = napi_get_array_length(env, propertyNames, &size);
629 size = std::min(size, MAX_DATA_LENGTH);
630 if (s != napi_ok) {
631 WVLOG_E("ParseDictionaryNapiValue2NwebValue napi api call fail");
632 }
633
634 for (uint32_t i = 0; i < size; i++) {
635 napi_value napiKeyTmp;
636 s = napi_get_element(env, propertyNames, i, &napiKeyTmp);
637 if (s != napi_ok) {
638 WVLOG_E("ParseDictionaryNapiValue2NwebValue napi api call fail");
639 }
640 bool hasOwnProperty = false;
641 s = napi_has_own_property(env, value, napiKeyTmp, &hasOwnProperty);
642 if (s != napi_ok) {
643 WVLOG_E("ParseDictionaryNapiValue2NwebValue napi api call fail");
644 }
645 if (!hasOwnProperty) {
646 continue;
647 }
648 napi_value napiValueTmp;
649 s = napi_get_property(env, value, napiKeyTmp, &napiValueTmp);
650 if (s != napi_ok) {
651 WVLOG_E("ParseDictionaryNapiValue2NwebValue napi api call fail");
652 }
653 auto nwebValueTmp = std::make_shared<NWebValue>();
654 auto nwebKeyTmp = std::make_shared<NWebValue>();
655 ParseNapiValue2NwebValueHelper(env, state, napiKeyTmp, nwebKeyTmp, isOject);
656 ParseNapiValue2NwebValueHelper(env, state, napiValueTmp, nwebValueTmp, isOject);
657 nwebValue->AddDictionaryValue(nwebKeyTmp->GetString(), *nwebValueTmp);
658 }
659 }
660
IsCallableObject(napi_env env,napi_value & value,std::vector<std::string> * methodNameList)661 bool IsCallableObject(napi_env env, napi_value& value, std::vector<std::string>* methodNameList)
662 {
663 std::string annotation = "methodNameListForJsProxy";
664 napi_status s = napi_ok;
665 bool hasProperty = false;
666 s = napi_has_named_property(env, value, annotation.c_str(), &hasProperty);
667 if (s != napi_ok) {
668 WVLOG_W("IsCallableObject napi api call fail");
669 }
670 if (!hasProperty) {
671 WVLOG_D("IsCallableObject has not methodNameList property");
672 return false;
673 }
674 napi_value result;
675 s = napi_get_named_property(env, value, annotation.c_str(), &result);
676 if (s != napi_ok) {
677 WVLOG_E("IsCallableObject napi api call fail");
678 }
679 bool isArray = false;
680 s = napi_is_array(env, result, &isArray);
681 if (s != napi_ok) {
682 WVLOG_E("IsCallableObject napi api call fail");
683 }
684 if (!isArray) {
685 return false;
686 }
687 uint32_t size;
688 s = napi_get_array_length(env, result, &size);
689 if (s != napi_ok) {
690 WVLOG_E("IsCallableObject napi api call fail");
691 }
692 for (uint32_t i = 0; i < size; i++) {
693 napi_value napiTmp;
694 s = napi_get_element(env, result, i, &napiTmp);
695 if (s != napi_ok) {
696 WVLOG_E("IsCallableObject napi api call fail");
697 }
698 napi_valuetype valueType = napi_undefined;
699 napi_typeof(env, napiTmp, &valueType);
700 if (valueType != napi_string) {
701 continue;
702 }
703 std::string strVal;
704 if (!NapiParseUtils::ParseString(env, napiTmp, strVal)) {
705 WVLOG_E("IsCallableObject NapiParseUtils::ParseString failed");
706 }
707 methodNameList->push_back(strVal);
708 }
709 return true;
710 }
711
ParseNapiValue2NwebValue(napi_env env,napi_value & value,std::shared_ptr<NWebValue> nwebValue,bool * isObject)712 std::vector<std::string> ParseNapiValue2NwebValue(napi_env env, napi_value& value,
713 std::shared_ptr<NWebValue> nwebValue, bool* isObject)
714 {
715 napi_status s = napi_ok;
716 std::vector<std::string> methodNameList;
717
718 s = napi_is_promise(env, value, isObject);
719 if (s != napi_ok) {
720 WVLOG_E("ParseNapiValue2NwebValue napi api call fail");
721 }
722
723 if (*isObject) {
724 return std::vector<std::string>{"then", "catch", "finally"};
725 }
726
727 if (IsCallableObject(env, value, &methodNameList)) {
728 *isObject = true;
729 return methodNameList;
730 }
731
732 ValueConvertState state;
733 bool isObjectForRecursion = false; // FixMe: for Recursion case, now not use
734 ParseNapiValue2NwebValueHelper(env, &state, value, nwebValue, &isObjectForRecursion);
735 return methodNameList;
736 }
737
ParseNapiValue2NwebValue(napi_env env,napi_value * value,std::shared_ptr<NWebValue> nwebValue,bool * isObject)738 std::vector<std::string> ParseNapiValue2NwebValue(napi_env env, napi_value* value,
739 std::shared_ptr<NWebValue> nwebValue, bool* isObject)
740 {
741 napi_status s = napi_ok;
742 std::vector<std::string> methodNameList;
743
744 s = napi_is_promise(env, *value, isObject);
745 if (s != napi_ok) {
746 WVLOG_E("ParseNapiValue2NwebValue napi api call fail");
747 }
748
749 if (*isObject) {
750 return std::vector<std::string>{"then", "catch", "finally"};
751 }
752
753 if (IsCallableObject(env, *value, &methodNameList)) {
754 *isObject = true;
755 return methodNameList;
756 }
757
758 ValueConvertState state;
759 bool isObjectForRecursion = false; // FixMe: for Recursion case, now not use
760 ParseNapiValue2NwebValueHelper(env, &state, *value, nwebValue, &isObjectForRecursion);
761 return methodNameList;
762 }
763 } // namespace
764
WebviewJavaScriptResultCallBack(std::weak_ptr<OHOS::NWeb::NWeb> & nweb,int32_t nwebId)765 WebviewJavaScriptResultCallBack::WebviewJavaScriptResultCallBack(std::weak_ptr<OHOS::NWeb::NWeb>& nweb, int32_t nwebId)
766 : nweb_(nweb), nwebId_(nwebId)
767 {
768 std::unique_lock<std::mutex> lk(g_objectMtx);
769 g_webviewJsResultCallbackMap.emplace(nwebId, this);
770 }
771
~WebviewJavaScriptResultCallBack()772 WebviewJavaScriptResultCallBack::~WebviewJavaScriptResultCallBack()
773 {
774 std::unique_lock<std::mutex> lk(g_objectMtx);
775 g_webviewJsResultCallbackMap.erase(nwebId_);
776 }
777
FindObject(JavaScriptOb::ObjectID objectId)778 std::shared_ptr<JavaScriptOb> WebviewJavaScriptResultCallBack::FindObject(JavaScriptOb::ObjectID objectId)
779 {
780 auto iter = objects_.find(objectId);
781 if (iter != objects_.end()) {
782 return iter->second;
783 }
784 WVLOG_E("WebviewJavaScriptResultCallBack::FindObject Unknown object: objectId = "
785 "%{public}d",
786 objectId);
787 return nullptr;
788 }
789
ProcessObjectCaseInJsTd(napi_env env,WebviewJavaScriptResultCallBack::NapiJsCallBackParm * param,napi_value callResult,std::vector<std::string> & methodNameList)790 void ProcessObjectCaseInJsTd(
791 napi_env env, WebviewJavaScriptResultCallBack::NapiJsCallBackParm* param,
792 napi_value callResult, std::vector<std::string>& methodNameList)
793 {
794 if (!param) {
795 WVLOG_E("WebviewJavaScriptResultCallBack::ProcessObjectCaseInJsTd param null");
796 return;
797 }
798
799 auto* inParam = static_cast<WebviewJavaScriptResultCallBack::NapiJsCallBackInParm*>(param->input);
800 auto* outParam = static_cast<WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm*>(param->out);
801 JavaScriptOb::ObjectID returnedObjectId;
802
803 if (inParam->webJsResCb->FindObjectIdInJsTd(env, callResult, &returnedObjectId)) {
804 inParam->webJsResCb->FindObject(returnedObjectId)->AddHolder(inParam->frameRoutingId);
805 } else {
806 returnedObjectId = inParam->webJsResCb->AddObject(env, callResult, false, inParam->frameRoutingId);
807 }
808
809 inParam->webJsResCb->SetUpAnnotateMethods(returnedObjectId, methodNameList);
810
811 napi_valuetype valueType = napi_undefined;
812 napi_typeof(env, callResult, &valueType);
813 if (valueType == napi_function) {
814 WVLOG_D("WebviewJavaScriptResultCallBack::ProcessObjectCaseInJsTd type is function");
815 *(static_cast<std::shared_ptr<NWebValue>*>(outParam->ret)) = std::make_shared<NWebValue>();
816 } else {
817 std::string bin = std::string("TYPE_OBJECT_ID") + std::string(";") + std::to_string(returnedObjectId);
818 *(static_cast<std::shared_ptr<NWebValue>*>(outParam->ret)) =
819 std::make_shared<NWebValue>(bin.c_str(), bin.size());
820 }
821 }
822
ExecuteGetJavaScriptResult(napi_env env,napi_status status,WebviewJavaScriptResultCallBack::NapiJsCallBackParm * param)823 void ExecuteGetJavaScriptResult(
824 napi_env env, napi_status status, WebviewJavaScriptResultCallBack::NapiJsCallBackParm* param)
825 {
826 WVLOG_D("WebviewJavaScriptResultCallBack::ExecuteGetJavaScriptResult called");
827 auto* inParam = static_cast<WebviewJavaScriptResultCallBack::NapiJsCallBackInParm*>(param->input);
828 auto* outParam = static_cast<WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm*>(param->out);
829 std::shared_ptr<JavaScriptOb> jsObj = inParam->webJsResCb->FindObject(inParam->objId);
830 if (!jsObj) {
831 std::unique_lock<std::mutex> lock(param->mutex);
832 param->ready = true;
833 param->condition.notify_all();
834 return;
835 }
836 Ace::ContainerScope containerScope(jsObj->GetContainerScopeId());
837 napi_handle_scope scope = nullptr;
838 napi_open_handle_scope(env, &scope);
839 if (scope == nullptr) {
840 std::unique_lock<std::mutex> lock(param->mutex);
841 param->ready = true;
842 param->condition.notify_all();
843 return;
844 }
845 if (jsObj && (jsObj->HasMethod(inParam->methodName))) {
846 std::vector<napi_value> argv = {};
847 auto nwebArgs = *(static_cast<std::vector<std::shared_ptr<NWebValue>>*>(inParam->data));
848 for (std::shared_ptr<NWebValue> input : nwebArgs) {
849 argv.push_back(ParseNwebValue2NapiValue(
850 env, input, inParam->webJsResCb->GetObjectMap(), inParam->nwebId, inParam->frameRoutingId));
851 }
852 napi_value callback = jsObj->FindMethod(inParam->methodName);
853 if (!callback) {
854 WVLOG_E("WebviewJavaScriptResultCallBack::ExecuteGetJavaScriptResult callback null");
855 }
856 napi_value callResult = nullptr;
857 napi_call_function(env, jsObj->GetValue(), callback, argv.size(), &argv[0], &callResult);
858 bool isObject = false;
859 std::vector<std::string> methodNameList;
860 methodNameList = ParseNapiValue2NwebValue(
861 env, callResult, *(static_cast<std::shared_ptr<NWebValue>*>(outParam->ret)), &isObject);
862 if (isObject) {
863 ProcessObjectCaseInJsTd(env, param, callResult, methodNameList);
864 }
865 }
866
867 napi_close_handle_scope(env, scope);
868 std::unique_lock<std::mutex> lock(param->mutex);
869 param->ready = true;
870 param->condition.notify_all();
871 }
872
GetJavaScriptResultSelf(std::vector<std::shared_ptr<NWebValue>> args,const std::string & method,const std::string & objName,int32_t routingId,int32_t objectId)873 std::shared_ptr<NWebValue> WebviewJavaScriptResultCallBack::GetJavaScriptResultSelf(
874 std::vector<std::shared_ptr<NWebValue>> args, const std::string& method, const std::string& objName,
875 int32_t routingId, int32_t objectId)
876 {
877 std::shared_ptr<NWebValue> ret = std::make_shared<NWebValue>(NWebValue::Type::NONE);
878 std::shared_ptr<JavaScriptOb> jsObj = FindObject(objectId);
879 Ace::ContainerScope containerScope(jsObj->GetContainerScopeId());
880 napi_handle_scope scope = nullptr;
881 napi_open_handle_scope(jsObj->GetEnv(), &scope);
882 if (scope == nullptr) {
883 return ret;
884 }
885
886 WVLOG_D("get javaScript result already in js thread");
887 std::vector<napi_value> argv = {};
888 for (std::shared_ptr<NWebValue> input : args) {
889 argv.push_back(ParseNwebValue2NapiValue(jsObj->GetEnv(), input, GetObjectMap(), GetNWebId(), routingId));
890 }
891 napi_value callback = jsObj->FindMethod(method);
892 if (!callback) {
893 WVLOG_E("WebviewJavaScriptResultCallBack::ExecuteGetJavaScriptResult callback null");
894 }
895 napi_value callResult = nullptr;
896 napi_call_function(jsObj->GetEnv(), jsObj->GetValue(), callback, argv.size(), &argv[0], &callResult);
897 bool isObject = false;
898 std::vector<std::string> methodNameList;
899 methodNameList = ParseNapiValue2NwebValue(jsObj->GetEnv(), &callResult, ret, &isObject);
900 napi_valuetype valueType = napi_undefined;
901 napi_typeof(jsObj->GetEnv(), callResult, &valueType);
902 WVLOG_D("get javaScript result already in js thread end");
903 if (isObject) {
904 JavaScriptOb::ObjectID returnedObjectId;
905 if (FindObjectIdInJsTd(jsObj->GetEnv(), callResult, &returnedObjectId)) {
906 FindObject(returnedObjectId)->AddHolder(routingId);
907 } else {
908 returnedObjectId = AddObject(jsObj->GetEnv(), callResult, false, routingId);
909 }
910 SetUpAnnotateMethods(returnedObjectId, methodNameList);
911 if (valueType == napi_function) {
912 WVLOG_D("WebviewJavaScriptResultCallBack::GetJavaScriptResultSelf type is function");
913 ret = std::make_shared<NWebValue>();
914 } else {
915 WVLOG_D("WebviewJavaScriptResultCallBack::GetJavaScriptResultSelf type is object");
916 std::string bin = std::string("TYPE_OBJECT_ID") + std::string(";") + std::to_string(returnedObjectId);
917 ret = std::make_shared<NWebValue>(bin.c_str(), bin.size());
918 }
919 }
920 napi_close_handle_scope(jsObj->GetEnv(), scope);
921 return ret;
922 }
923
GetJavaScriptResult(std::vector<std::shared_ptr<NWebValue>> args,const std::string & method,const std::string & objName,int32_t routingId,int32_t objectId)924 std::shared_ptr<NWebValue> WebviewJavaScriptResultCallBack::GetJavaScriptResult(
925 std::vector<std::shared_ptr<NWebValue>> args, const std::string& method, const std::string& objName,
926 int32_t routingId, int32_t objectId)
927 {
928 (void)objName; // to be compatible with older webcotroller, classname may be empty
929 WVLOG_D("GetJavaScriptResult method = %{public}s", method.c_str());
930 std::shared_ptr<NWebValue> ret = std::make_shared<NWebValue>(NWebValue::Type::NONE);
931 std::shared_ptr<JavaScriptOb> jsObj = FindObject(objectId);
932 if (!jsObj || !jsObj->HasMethod(method)) {
933 return ret;
934 }
935
936 auto engine = reinterpret_cast<NativeEngine*>(jsObj->GetEnv());
937 if (engine == nullptr) {
938 return ret;
939 }
940
941 if (pthread_self() == engine->GetTid()) {
942 return GetJavaScriptResultSelf(args, method, objName, routingId, objectId);
943 } else {
944 WVLOG_D("get javaScript result, not in js thread, post task to js thread");
945 return PostGetJavaScriptResultToJsThread(args, method, objName, routingId, objectId);
946 }
947 }
948
PostGetJavaScriptResultToJsThread(std::vector<std::shared_ptr<NWebValue>> args,const std::string & method,const std::string & objName,int32_t routingId,int32_t objectId)949 std::shared_ptr<NWebValue> WebviewJavaScriptResultCallBack::PostGetJavaScriptResultToJsThread(
950 std::vector<std::shared_ptr<NWebValue>> args, const std::string& method, const std::string& objName,
951 int32_t routingId, int32_t objectId)
952 {
953 // to be compatible with older webcotroller, classname may be empty
954 (void)objName;
955 WVLOG_D("WebviewJavaScriptResultCallBack::PostGetJavaScriptResultToJsThread method = "
956 "%{public}s",
957 method.c_str());
958 std::shared_ptr<NWebValue> ret = std::make_shared<NWebValue>(NWebValue::Type::NONE);
959 std::shared_ptr<JavaScriptOb> jsObj = FindObject(objectId);
960 if (!jsObj) {
961 return ret;
962 }
963 napi_env env = jsObj->GetEnv();
964 WebviewJavaScriptResultCallBack::NapiJsCallBackInParm* inParam = nullptr;
965 WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm* outParam = nullptr;
966 WebviewJavaScriptResultCallBack::NapiJsCallBackParm* param = nullptr;
967 if (!CreateNapiJsCallBackParm(inParam, outParam, param)) {
968 WVLOG_E("WebviewJavaScriptResultCallBack::PostGetJavaScriptResultToJsThread malloc fail");
969 return ret;
970 }
971
972 inParam->webJsResCb = this;
973 inParam->nwebId = GetNWebId();
974 inParam->frameRoutingId = routingId;
975 inParam->objId = objectId;
976 inParam->methodName = method;
977 inParam->data = reinterpret_cast<void*>(&args);
978 outParam->ret = reinterpret_cast<void*>(&ret);
979 param->input = reinterpret_cast<void*>(inParam);
980 param->out = reinterpret_cast<void*>(outParam);
981 param->env = env;
982
983 CreateUvQueueWorkEnhanced(env, param, ExecuteGetJavaScriptResult);
984 {
985 std::unique_lock<std::mutex> lock(param->mutex);
986 param->condition.wait(lock, [¶m] { return param->ready; });
987 }
988 DeleteNapiJsCallBackParm(inParam, outParam, param);
989 return ret;
990 }
991
FindObjectIdInJsTd(napi_env env,napi_value object,JavaScriptOb::ObjectID * objectId)992 bool WebviewJavaScriptResultCallBack::FindObjectIdInJsTd(
993 napi_env env, napi_value object, JavaScriptOb::ObjectID* objectId)
994 {
995 *objectId = static_cast<JavaScriptOb::ObjectID>(JavaScriptOb::JavaScriptObjIdErrorCode::WEBVIEWCONTROLLERERROR);
996 for (const auto& pair : objects_) {
997 bool result = false;
998 napi_status s = napi_strict_equals(env, object, pair.second->GetValue(), &result);
999 if (s != napi_ok) {
1000 WVLOG_E("WebviewJavaScriptResultCallBack::FindObjectIdInJsTd fail");
1001 }
1002 if (result) {
1003 *objectId = pair.first;
1004 return true;
1005 }
1006 }
1007 return false;
1008 }
1009
ExecuteHasJavaScriptObjectMethods(napi_env env,napi_status status,WebviewJavaScriptResultCallBack::NapiJsCallBackParm * param)1010 void ExecuteHasJavaScriptObjectMethods(
1011 napi_env env, napi_status status, WebviewJavaScriptResultCallBack::NapiJsCallBackParm* param)
1012 {
1013 if (!param) {
1014 std::unique_lock<std::mutex> lock(param->mutex);
1015 param->ready = true;
1016 param->condition.notify_all();
1017 return;
1018 }
1019
1020 auto* inParam = static_cast<WebviewJavaScriptResultCallBack::NapiJsCallBackInParm*>(param->input);
1021 auto* outParam = static_cast<WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm*>(param->out);
1022 std::shared_ptr<JavaScriptOb> jsObj = inParam->webJsResCb->FindObject(inParam->objId);
1023 if (!jsObj) {
1024 std::unique_lock<std::mutex> lock(param->mutex);
1025 param->ready = true;
1026 param->condition.notify_all();
1027 return;
1028 }
1029 Ace::ContainerScope containerScope(jsObj->GetContainerScopeId());
1030
1031 napi_handle_scope scope = nullptr;
1032 napi_open_handle_scope(env, &scope);
1033 if (scope) {
1034 if (jsObj && jsObj->HasMethod(inParam->methodName)) {
1035 *(static_cast<bool*>(outParam->ret)) = true;
1036 } else {
1037 WVLOG_D("WebviewJavaScriptResultCallBack::HasJavaScriptObjectMethods cannot find "
1038 "object");
1039 }
1040 napi_close_handle_scope(env, scope);
1041 }
1042
1043 std::unique_lock<std::mutex> lock(param->mutex);
1044 param->ready = true;
1045 param->condition.notify_all();
1046 }
1047
PostHasJavaScriptObjectMethodsToJsThread(int32_t objectId,const std::string & methodName)1048 bool WebviewJavaScriptResultCallBack::PostHasJavaScriptObjectMethodsToJsThread(
1049 int32_t objectId, const std::string& methodName)
1050 {
1051 bool ret = false;
1052 std::shared_ptr<JavaScriptOb> jsObj = FindObject(objectId);
1053 if (!jsObj) {
1054 return false;
1055 }
1056 napi_env env = jsObj->GetEnv();
1057 WebviewJavaScriptResultCallBack::NapiJsCallBackInParm* inParam = nullptr;
1058 WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm* outParam = nullptr;
1059 WebviewJavaScriptResultCallBack::NapiJsCallBackParm* param = nullptr;
1060 if (!CreateNapiJsCallBackParm(inParam, outParam, param)) {
1061 return false;
1062 }
1063
1064 inParam->webJsResCb = this;
1065 inParam->objId = objectId;
1066 inParam->methodName = methodName;
1067 outParam->ret = reinterpret_cast<void*>(&ret);
1068 param->input = reinterpret_cast<void*>(inParam);
1069 param->out = reinterpret_cast<void*>(outParam);
1070 param->env = env;
1071
1072 CreateUvQueueWorkEnhanced(env, param, ExecuteHasJavaScriptObjectMethods);
1073
1074 {
1075 std::unique_lock<std::mutex> lock(param->mutex);
1076 param->condition.wait(lock, [¶m] { return param->ready; });
1077 }
1078 DeleteNapiJsCallBackParm(inParam, outParam, param);
1079 return ret;
1080 }
1081
HasJavaScriptObjectMethods(int32_t objectId,const std::string & methodName)1082 bool WebviewJavaScriptResultCallBack::HasJavaScriptObjectMethods(int32_t objectId, const std::string& methodName)
1083 {
1084 bool ret = false;
1085 std::shared_ptr<JavaScriptOb> jsObj = FindObject(objectId);
1086 if (!jsObj) {
1087 return false;
1088 }
1089 napi_env env = jsObj->GetEnv();
1090 auto engine = reinterpret_cast<NativeEngine*>(env);
1091 if (engine == nullptr) {
1092 return ret;
1093 }
1094 if (pthread_self() == engine->GetTid()) {
1095 WVLOG_D("has javaScript object methods already in js thread");
1096 napi_handle_scope scope = nullptr;
1097 napi_open_handle_scope(env, &scope);
1098 if (scope == nullptr) {
1099 return ret;
1100 }
1101
1102 if (jsObj && jsObj->HasMethod(methodName)) {
1103 ret = true;
1104 } else {
1105 WVLOG_D("WebviewJavaScriptResultCallBack::HasJavaScriptObjectMethods cannot find "
1106 "object");
1107 }
1108
1109 napi_close_handle_scope(env, scope);
1110 return ret;
1111 } else {
1112 WVLOG_D("has javaScript object methods, not in js thread, post task to js thread");
1113 return PostHasJavaScriptObjectMethodsToJsThread(objectId, methodName);
1114 }
1115 }
1116
ExecuteGetJavaScriptObjectMethods(napi_env env,napi_status status,WebviewJavaScriptResultCallBack::NapiJsCallBackParm * param)1117 void ExecuteGetJavaScriptObjectMethods(
1118 napi_env env, napi_status status, WebviewJavaScriptResultCallBack::NapiJsCallBackParm* param)
1119 {
1120 if (!param) {
1121 std::unique_lock<std::mutex> lock(param->mutex);
1122 param->ready = true;
1123 param->condition.notify_all();
1124 return;
1125 }
1126
1127 auto* inParam = static_cast<WebviewJavaScriptResultCallBack::NapiJsCallBackInParm*>(param->input);
1128 auto* outParam = static_cast<WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm*>(param->out);
1129
1130 std::shared_ptr<JavaScriptOb> jsObj = inParam->webJsResCb->FindObject(inParam->objId);
1131 if (!jsObj) {
1132 std::unique_lock<std::mutex> lock(param->mutex);
1133 param->ready = true;
1134 param->condition.notify_all();
1135 return;
1136 }
1137 Ace::ContainerScope containerScope(jsObj->GetContainerScopeId());
1138
1139 napi_handle_scope scope = nullptr;
1140 napi_open_handle_scope(env, &scope);
1141
1142 if (scope) {
1143 if (jsObj) {
1144 auto methods = jsObj->GetMethodNames();
1145 for (auto& method : methods) {
1146 (*(static_cast<std::shared_ptr<NWebValue>*>(outParam->ret)))->AddListValue(NWebValue(method));
1147 }
1148 }
1149 napi_close_handle_scope(env, scope);
1150 }
1151
1152 std::unique_lock<std::mutex> lock(param->mutex);
1153 param->ready = true;
1154 param->condition.notify_all();
1155 }
1156
PostGetJavaScriptObjectMethodsToJsThread(int32_t objectId)1157 std::shared_ptr<NWebValue> WebviewJavaScriptResultCallBack::PostGetJavaScriptObjectMethodsToJsThread(int32_t objectId)
1158 {
1159 auto ret = std::make_shared<NWebValue>(NWebValue::Type::LIST);
1160 std::shared_ptr<JavaScriptOb> jsObj = FindObject(objectId);
1161 if (!jsObj) {
1162 return ret;
1163 }
1164 napi_env env = jsObj->GetEnv();
1165 WebviewJavaScriptResultCallBack::NapiJsCallBackInParm* inParam = nullptr;
1166 WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm* outParam = nullptr;
1167 WebviewJavaScriptResultCallBack::NapiJsCallBackParm* param = nullptr;
1168 if (!CreateNapiJsCallBackParm(inParam, outParam, param)) {
1169 return ret;
1170 }
1171
1172 inParam->webJsResCb = this;
1173 inParam->objId = objectId;
1174 outParam->ret = reinterpret_cast<void*>(&ret);
1175 param->input = reinterpret_cast<void*>(inParam);
1176 param->out = reinterpret_cast<void*>(outParam);
1177 param->env = env;
1178
1179 CreateUvQueueWorkEnhanced(env, param, ExecuteGetJavaScriptObjectMethods);
1180
1181 {
1182 std::unique_lock<std::mutex> lock(param->mutex);
1183 param->condition.wait(lock, [¶m] { return param->ready; });
1184 }
1185 DeleteNapiJsCallBackParm(inParam, outParam, param);
1186 return ret;
1187 }
1188
GetJavaScriptObjectMethods(int32_t objectId)1189 std::shared_ptr<NWebValue> WebviewJavaScriptResultCallBack::GetJavaScriptObjectMethods(int32_t objectId)
1190 {
1191 auto ret = std::make_shared<NWebValue>(NWebValue::Type::LIST);
1192 std::shared_ptr<JavaScriptOb> jsObj = FindObject(objectId);
1193 if (!jsObj) {
1194 return ret;
1195 }
1196 napi_env env = jsObj->GetEnv();
1197 auto engine = reinterpret_cast<NativeEngine*>(env);
1198 if (engine == nullptr) {
1199 return ret;
1200 }
1201
1202 if (pthread_self() == engine->GetTid()) {
1203 WVLOG_D("get javaScript object methods already in js thread");
1204 napi_handle_scope scope = nullptr;
1205 napi_open_handle_scope(env, &scope);
1206 if (scope == nullptr) {
1207 return ret;
1208 }
1209
1210 if (jsObj) {
1211 auto methods = jsObj->GetMethodNames();
1212 for (auto& method : methods) {
1213 ret->AddListValue(NWebValue(method));
1214 }
1215 }
1216
1217 napi_close_handle_scope(env, scope);
1218 return ret;
1219 } else {
1220 WVLOG_D("get javaScript object methods, not in js thread, post task to js thread");
1221 return PostGetJavaScriptObjectMethodsToJsThread(objectId);
1222 }
1223 }
1224
RemoveJavaScriptObjectHolderInJsTd(int32_t holder,JavaScriptOb::ObjectID objectId)1225 void WebviewJavaScriptResultCallBack::RemoveJavaScriptObjectHolderInJsTd(
1226 int32_t holder, JavaScriptOb::ObjectID objectId)
1227 {
1228 std::shared_ptr<JavaScriptOb> jsObj = FindObject(objectId);
1229 if (jsObj && !(jsObj->IsNamed())) {
1230 jsObj->RemoveHolder(holder);
1231 if (!(jsObj->HasHolders())) {
1232 // reminder me: object->ToWeakRef(), object is erased so the destructor
1233 // called
1234 retainedObjectSet_.erase(jsObj);
1235 objects_.erase(objects_.find(objectId));
1236 }
1237 }
1238 }
1239
ExecuteRemoveJavaScriptObjectHolder(napi_env env,napi_status status,WebviewJavaScriptResultCallBack::NapiJsCallBackParm * param)1240 void ExecuteRemoveJavaScriptObjectHolder(
1241 napi_env env, napi_status status, WebviewJavaScriptResultCallBack::NapiJsCallBackParm* param)
1242 {
1243 if (!param) {
1244 std::unique_lock<std::mutex> lock(param->mutex);
1245 param->ready = true;
1246 param->condition.notify_all();
1247 return;
1248 }
1249
1250 auto* inParam = static_cast<WebviewJavaScriptResultCallBack::NapiJsCallBackInParm*>(param->input);
1251
1252 std::shared_ptr<JavaScriptOb> jsObj = inParam->webJsResCb->FindObject(inParam->objId);
1253 if (!jsObj) {
1254 std::unique_lock<std::mutex> lock(param->mutex);
1255 param->ready = true;
1256 param->condition.notify_all();
1257 return;
1258 }
1259 Ace::ContainerScope containerScope(jsObj->GetContainerScopeId());
1260
1261 napi_handle_scope scope = nullptr;
1262 napi_open_handle_scope(env, &scope);
1263
1264 if (scope) {
1265 inParam->webJsResCb->RemoveJavaScriptObjectHolderInJsTd(inParam->frameRoutingId, inParam->objId);
1266 napi_close_handle_scope(env, scope);
1267 }
1268
1269 std::unique_lock<std::mutex> lock(param->mutex);
1270 param->ready = true;
1271 param->condition.notify_all();
1272 }
1273
PostRemoveJavaScriptObjectHolderToJsThread(int32_t holder,JavaScriptOb::ObjectID objectId)1274 void WebviewJavaScriptResultCallBack::PostRemoveJavaScriptObjectHolderToJsThread(
1275 int32_t holder, JavaScriptOb::ObjectID objectId)
1276 {
1277 WVLOG_D("WebviewJavaScriptResultCallBack::RemoveJavaScriptObjectHolder called, "
1278 "objectId = %{public}d",
1279 objectId);
1280 std::shared_ptr<JavaScriptOb> jsObj = FindObject(objectId);
1281 if (!jsObj) {
1282 return;
1283 }
1284 napi_env env = jsObj->GetEnv();
1285 WebviewJavaScriptResultCallBack::NapiJsCallBackInParm* inParam = nullptr;
1286 WebviewJavaScriptResultCallBack::NapiJsCallBackOutParm* outParam = nullptr;
1287 WebviewJavaScriptResultCallBack::NapiJsCallBackParm* param = nullptr;
1288 if (!CreateNapiJsCallBackParm(inParam, outParam, param)) {
1289 return;
1290 }
1291
1292 inParam->webJsResCb = this;
1293 inParam->objId = objectId;
1294 inParam->frameRoutingId = holder;
1295 param->input = reinterpret_cast<void*>(inParam);
1296 param->out = reinterpret_cast<void*>(outParam);
1297 param->env = env;
1298
1299 CreateUvQueueWorkEnhanced(env, param, ExecuteRemoveJavaScriptObjectHolder);
1300
1301 {
1302 std::unique_lock<std::mutex> lock(param->mutex);
1303 param->condition.wait(lock, [¶m] { return param->ready; });
1304 }
1305 DeleteNapiJsCallBackParm(inParam, outParam, param);
1306 }
1307
RemoveJavaScriptObjectHolder(int32_t holder,JavaScriptOb::ObjectID objectId)1308 void WebviewJavaScriptResultCallBack::RemoveJavaScriptObjectHolder(int32_t holder, JavaScriptOb::ObjectID objectId)
1309 {
1310 WVLOG_D("WebviewJavaScriptResultCallBack::RemoveJavaScriptObjectHolder called, "
1311 "objectId = %{public}d",
1312 objectId);
1313 std::shared_ptr<JavaScriptOb> jsObj = FindObject(objectId);
1314 if (!jsObj) {
1315 return;
1316 }
1317 napi_env env = jsObj->GetEnv();
1318 auto engine = reinterpret_cast<NativeEngine*>(env);
1319 if (engine == nullptr) {
1320 return;
1321 }
1322 if (pthread_self() == engine->GetTid()) {
1323 WVLOG_D("remove javaScript object holder already in js thread");
1324 napi_handle_scope scope = nullptr;
1325 napi_open_handle_scope(env, &scope);
1326 if (scope == nullptr) {
1327 return;
1328 }
1329
1330 RemoveJavaScriptObjectHolderInJsTd(holder, objectId);
1331
1332 napi_close_handle_scope(env, scope);
1333 return;
1334 } else {
1335 WVLOG_D("remove javaScript object holder, not in js thread, post task to js thread");
1336 PostRemoveJavaScriptObjectHolderToJsThread(holder, objectId);
1337 }
1338 }
1339
RemoveTransientJavaScriptObject()1340 void WebviewJavaScriptResultCallBack::RemoveTransientJavaScriptObject()
1341 {
1342 // remove retainedObjectSet_ and objects_ CreateTransient object
1343 auto iter = objects_.begin();
1344 while (iter != objects_.end()) {
1345 if (!(iter->second->IsNamed())) {
1346 WVLOG_D("WebviewJavaScriptResultCallBack::RemoveTransientJavaScriptObject "
1347 "objectId = %{public}d is removed",
1348 (int32_t)iter->first);
1349 // reminder me: object->ToWeakRef(), object is erased so the destructor called
1350 retainedObjectSet_.erase(iter->second);
1351 objects_.erase(iter++);
1352 } else {
1353 ++iter;
1354 }
1355 }
1356
1357 // remove retainedObjectSet_ named object but not in objects_
1358 auto iter1 = retainedObjectSet_.begin();
1359 while (iter1 != retainedObjectSet_.end()) {
1360 auto iter2 = objects_.begin();
1361 bool isHasObj = false;
1362 while (iter2 != objects_.end()) {
1363 if (*iter1 == iter2->second) {
1364 isHasObj = true;
1365 break;
1366 }
1367 ++iter2;
1368 }
1369 if (!isHasObj) {
1370 WVLOG_D("WebviewJavaScriptResultCallBack::RemoveTransientJavaScriptObject "
1371 "isHasObj == false");
1372 retainedObjectSet_.erase(*iter1);
1373 }
1374 ++iter1;
1375 }
1376 }
1377
SetUpAnnotateMethods(JavaScriptOb::ObjectID objId,std::vector<std::string> & methodNameList)1378 void WebviewJavaScriptResultCallBack::SetUpAnnotateMethods(
1379 JavaScriptOb::ObjectID objId, std::vector<std::string>& methodNameList)
1380 {
1381 // set up annotate(methodNameListForJsProxy) object method
1382 if (objects_[objId]) {
1383 objects_[objId]->SetMethods(methodNameList);
1384 }
1385 }
1386
AddObject(napi_env env,const napi_value & object,bool methodName,int32_t holder)1387 JavaScriptOb::ObjectID WebviewJavaScriptResultCallBack::AddObject(
1388 napi_env env, const napi_value& object, bool methodName, int32_t holder)
1389 {
1390 JavaScriptOb::ObjectID objectId;
1391 {
1392 int32_t containerScopeId = Ace::ContainerScope::CurrentId();
1393 auto new_object = methodName ? JavaScriptOb::CreateNamed(env, containerScopeId, object)
1394 : JavaScriptOb::CreateTransient(env, containerScopeId, object, holder);
1395 objectId = nextObjectId_++;
1396 WVLOG_D("WebviewJavaScriptResultCallBack::AddObject objectId = "
1397 "%{public}d",
1398 static_cast<int32_t>(objectId));
1399 objects_[objectId] = new_object;
1400 retainedObjectSet_.insert(new_object);
1401 }
1402 return objectId;
1403 }
1404
AddNamedObject(napi_env env,napi_value & obj,const std::string & objName)1405 JavaScriptOb::ObjectID WebviewJavaScriptResultCallBack::AddNamedObject(
1406 napi_env env, napi_value& obj, const std::string& objName)
1407 {
1408 JavaScriptOb::ObjectID objectId;
1409 NamedObjectMap::iterator iter = namedObjects_.find(objName);
1410 bool methodName = FindObjectIdInJsTd(env, obj, &objectId);
1411 if (methodName && iter != namedObjects_.end() && iter->second == objectId) {
1412 // Nothing to do.
1413 return objectId;
1414 }
1415 if (iter != namedObjects_.end()) {
1416 RemoveNamedObject(iter->first);
1417 }
1418 if (methodName) {
1419 objects_[objectId]->AddName();
1420 } else {
1421 objectId = AddObject(env, obj, true, 0);
1422 }
1423 namedObjects_[objName] = objectId;
1424 return objectId;
1425 }
1426
GetNamedObjects()1427 std::unordered_map<std::string, std::shared_ptr<JavaScriptOb>> WebviewJavaScriptResultCallBack::GetNamedObjects()
1428 {
1429 // Get named objects
1430 std::unordered_map<std::string, std::shared_ptr<JavaScriptOb>> ret;
1431 for (auto iter = namedObjects_.begin(); iter != namedObjects_.end(); iter++) {
1432 if (objects_.find(iter->second) != objects_.end()) {
1433 ret.emplace(iter->first, objects_[iter->second]);
1434 }
1435 }
1436 return ret;
1437 }
1438
GetObjectMap()1439 WebviewJavaScriptResultCallBack::ObjectMap WebviewJavaScriptResultCallBack::GetObjectMap()
1440 {
1441 return objects_;
1442 }
1443
RegisterJavaScriptProxy(napi_env env,napi_value obj,const std::string & objName,const std::vector<std::string> & methodList)1444 JavaScriptOb::ObjectID WebviewJavaScriptResultCallBack::RegisterJavaScriptProxy(
1445 napi_env env, napi_value obj, const std::string& objName, const std::vector<std::string>& methodList)
1446 {
1447 JavaScriptOb::ObjectID objId = AddNamedObject(env, obj, objName);
1448 // set up named object method
1449 if (namedObjects_.find(objName) != namedObjects_.end() && objects_[namedObjects_[objName]]) {
1450 objects_[namedObjects_[objName]]->SetMethods(methodList);
1451 }
1452 WVLOG_D("WebviewJavaScriptResultCallBack::RegisterJavaScriptProxy called, "
1453 "objectId = %{public}d",
1454 static_cast<int32_t>(objId));
1455 return objId;
1456 }
1457
RemoveNamedObject(const std::string & name)1458 bool WebviewJavaScriptResultCallBack::RemoveNamedObject(const std::string& name)
1459 {
1460 WVLOG_D("WebviewJavaScriptResultCallBack::RemoveNamedObject called, "
1461 "name = %{public}s",
1462 name.c_str());
1463 NamedObjectMap::iterator iter = namedObjects_.find(name);
1464 if (iter == namedObjects_.end()) {
1465 return false;
1466 }
1467 if (objects_[iter->second]) {
1468 objects_[iter->second]->RemoveName();
1469 }
1470 namedObjects_.erase(iter);
1471 return true;
1472 }
1473
DeleteJavaScriptRegister(const std::string & objName)1474 bool WebviewJavaScriptResultCallBack::DeleteJavaScriptRegister(const std::string& objName)
1475 {
1476 return RemoveNamedObject(objName);
1477 }
1478
CallH5FunctionInternal(napi_env env,H5Bundle & bundle,std::vector<std::shared_ptr<NWebValue>> nwebArgs)1479 void WebviewJavaScriptResultCallBack::CallH5FunctionInternal(
1480 napi_env env, H5Bundle& bundle, std::vector<std::shared_ptr<NWebValue>> nwebArgs)
1481 {
1482 if (bundle.nwebId != GetNWebId()) {
1483 WVLOG_E("WebviewJavaScriptResultCallBack::CallH5FunctionInternal nweb id not equal");
1484 return;
1485 }
1486 auto nweb_ptr = nweb_.lock();
1487 if (!nweb_ptr) {
1488 WVLOG_E("WebviewJavaScriptResultCallBack::CallH5FunctionInternal nweb_ptr null");
1489 return;
1490 }
1491
1492 nweb_ptr->CallH5Function(bundle.frameRoutingId, bundle.h5Id, bundle.funcName, nwebArgs);
1493 WVLOG_D("WebviewJavaScriptResultCallBack::CallH5FunctionInternal end");
1494 }
1495
1496 } // namespace OHOS::NWeb
1497