• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, [&param] { 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, [&param] { 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, [&param] { 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, [&param] { 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