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