• 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 #ifndef NWEB_WEBVIEW_JAVASCRIPT_RESULT_CALLBACK_IMPL_H
17 #define NWEB_WEBVIEW_JAVASCRIPT_RESULT_CALLBACK_IMPL_H
18 
19 #include <condition_variable>
20 #include <mutex>
21 #include <string>
22 #include <unordered_map>
23 #include <unordered_set>
24 #include <vector>
25 
26 #include "napi/native_api.h"
27 #include "napi/native_common.h"
28 #include "napi/native_node_api.h"
29 #include "napi_parse_utils.h"
30 #include "nweb.h"
31 #include "nweb_javascript_result_callback.h"
32 #include "nweb_log.h"
33 #include "nweb_value.h"
34 #include "uv.h"
35 
36 namespace OHOS::NWeb {
37 typedef struct RegisterJavaScriptProxyParam {
38     napi_env env;
39     napi_value obj;
40     std::string objName;
41     std::vector<std::string> syncMethodList;
42     std::vector<std::string> asyncMethodList;
43     std::string permission;
44 } RegisterJavaScriptProxyParam;
45 
46 class JavaScriptOb {
47 public:
48     // to be compatible with older webcotroller, be sure to the same as ace and core
49     enum class JavaScriptObjIdErrorCode : int32_t { WEBCONTROLLERERROR = -2, WEBVIEWCONTROLLERERROR = -1, END = 0 };
50 
51     typedef int32_t ObjectID;
52 
53     static std::shared_ptr<JavaScriptOb> CreateNamed(
54         napi_env env, int32_t containerScopeId, napi_value value, size_t refCount = 1);
55     static std::shared_ptr<JavaScriptOb> CreateTransient(
56         napi_env env, int32_t containerScopeId, napi_value value, int32_t holder, size_t refCount = 1);
57 
58     JavaScriptOb(napi_env env, int32_t containerScopeId, napi_value value, size_t refCount = 1);
59 
60     JavaScriptOb(
61         napi_env env, int32_t containerScopeId, napi_value value, std::set<int32_t> holders, size_t refCount = 1);
62 
JavaScriptOb(const JavaScriptOb & job)63     JavaScriptOb(const JavaScriptOb& job)
64     {
65         *this = job;
66     }
67 
JavaScriptOb(JavaScriptOb && job)68     JavaScriptOb(JavaScriptOb&& job)
69     {
70         *this = std::move(job);
71     }
72 
73     JavaScriptOb& operator=(const JavaScriptOb& job)
74     {
75         if (this != &job) {
76             Delete();
77             env_ = job.env_;
78             isStrongRef_ = job.isStrongRef_;
79             if (isStrongRef_) {
80                 objRef_ = job.objRef_;
81                 napi_status s = napi_reference_ref(env_, objRef_, nullptr);
82                 if (s != napi_ok) {
83                     WVLOG_E("JavaScriptOb copy assign fail");
84                 }
85             } else {
86                 napi_status s = CreateNewWeakRef(env_, job.objRef_, &objRef_);
87                 if (s != napi_ok) {
88                     WVLOG_E("JavaScriptOb copy assign fail");
89                 }
90             }
91         }
92         return *this;
93     }
94 
95     JavaScriptOb& operator=(JavaScriptOb&& job)
96     {
97         if (this != &job) {
98             Delete();
99             env_ = job.env_;
100             objRef_ = job.objRef_;
101             isStrongRef_ = job.isStrongRef_;
102             job.env_ = nullptr;
103             job.objRef_ = nullptr;
104         }
105         return *this;
106     }
107 
~JavaScriptOb()108     ~JavaScriptOb()
109     {
110         Delete();
111     }
112 
GetEnv()113     napi_env GetEnv() const
114     {
115         return env_;
116     }
117 
SetContainerScopeId(int32_t newId)118     void SetContainerScopeId(int32_t newId)
119     {
120         containerScopeId_ = newId;
121     }
122 
GetContainerScopeId()123     int32_t GetContainerScopeId() const
124     {
125         return containerScopeId_;
126     }
127 
IsEmpty()128     bool IsEmpty() const
129     {
130         return !objRef_;
131     }
132 
IsStrongRef()133     bool IsStrongRef()
134     {
135         return isStrongRef_;
136     }
137 
GetValue()138     napi_value GetValue() const
139     {
140         napi_value result = nullptr;
141         napi_get_reference_value(env_, objRef_, &result);
142         return result;
143     }
144 
ToWeakRef()145     void ToWeakRef()
146     {
147         if (!isStrongRef_ || !objRef_) {
148             return;
149         }
150 
151         if (Release() == 0) {
152             isStrongRef_ = false;
153             return;
154         }
155 
156         isStrongRef_ = false;
157         napi_status s = CreateNewWeakRef(env_, objRef_, &objRef_);
158         if (s != napi_ok) {
159             WVLOG_E("JavaScriptOb ToWeakRef fail");
160         }
161     }
162 
IsNamed()163     bool IsNamed() const
164     {
165         return namesCount_ > 0;
166     }
AddName()167     void AddName()
168     {
169         ++namesCount_;
170     }
RemoveName()171     void RemoveName()
172     {
173         --namesCount_;
174     }
175 
HasHolders()176     bool HasHolders()
177     {
178         return !holders_.empty();
179     }
AddHolder(int32_t holder)180     void AddHolder(int32_t holder)
181     {
182         holders_.insert(holder);
183     }
RemoveHolder(int32_t holder)184     void RemoveHolder(int32_t holder)
185     {
186         holders_.erase(holder);
187     }
188 
GetMethodNames()189     std::vector<std::string> GetMethodNames()
190     {
191         if (!isMethodsSetup_) {
192             SetUpMethods();
193         }
194         return methods_;
195     }
196 
GetSyncMethodNames()197     std::vector<std::string> GetSyncMethodNames()
198     {
199         if (!isMethodsSetup_) {
200             SetUpMethods();
201         }
202         if (asyncMethods_.empty()) {
203             return methods_;
204         }
205 
206         std::vector<std::string> syncMethodNames;
207         for (const auto& method : methods_) {
208             auto it = std::find(asyncMethods_.begin(), asyncMethods_.end(), method);
209             if (it == asyncMethods_.end()) {
210                 syncMethodNames.emplace_back(method);
211             }
212         }
213         return syncMethodNames;
214     }
215 
GetAsyncMethodNames()216     std::vector<std::string> GetAsyncMethodNames()
217     {
218         return asyncMethods_;
219     }
220 
GetPermission()221     std::string GetPermission()
222     {
223         return permission_;
224     }
225 
HasMethod(const std::string & methodName)226     bool HasMethod(const std::string& methodName)
227     {
228         if (methodName.empty()) {
229             WVLOG_E("HasMethod methodName null");
230             return false;
231         }
232 
233         if (!isMethodsSetup_) {
234             SetUpMethods();
235         }
236         for (std::vector<std::string>::iterator iter = methods_.begin(); iter != methods_.end(); ++iter) {
237             if (*iter == methodName) {
238                 return true;
239             }
240         }
241         return false;
242     }
243 
FindMethod(const std::string & methodName)244     napi_value FindMethod(const std::string& methodName)
245     {
246         if (HasMethod(methodName)) {
247             bool hasFunc = false;
248             napi_value result = nullptr;
249             napi_valuetype valueType = napi_undefined;
250             napi_value obj = GetValue();
251             if (!obj) {
252                 WVLOG_E("JavaScriptOb FindMethod obj null");
253                 return nullptr;
254             }
255             napi_status s = napi_has_named_property(env_, obj, methodName.c_str(), &hasFunc);
256             if (s != napi_ok) {
257                 WVLOG_E("JavaScriptOb FindMethod fail");
258                 return nullptr;
259             }
260             if (!hasFunc) {
261                 WVLOG_E("JavaScriptOb FindMethod fail");
262                 return nullptr;
263             }
264             s = napi_get_named_property(env_, obj, methodName.c_str(), &result);
265             if (s != napi_ok) {
266                 WVLOG_E("JavaScriptOb FindMethod fail");
267                 return nullptr;
268             }
269             napi_typeof(env_, result, &valueType);
270             if (valueType != napi_function) {
271                 WVLOG_E("JavaScriptOb FindMethod not function");
272                 return nullptr;
273             }
274             return result;
275         }
276 
277         return nullptr;
278     }
279 
SetUpMethods()280     void SetUpMethods()
281     {
282         std::unique_lock<std::mutex> lock(mutex_);
283         if (isMethodsSetup_) {
284             return;
285         }
286         napi_value propertyNames;
287         napi_value obj = GetValue();
288         napi_status s = napi_get_all_property_names(env_, obj, napi_key_include_prototypes, napi_key_all_properties,
289             napi_key_numbers_to_strings, &propertyNames);
290         if (s != napi_ok) {
291             WVLOG_E("JavaScriptOb SetUpMethods fail");
292             return;
293         }
294         uint32_t size;
295         s = napi_get_array_length(env_, propertyNames, &size);
296         if (s != napi_ok) {
297             WVLOG_E("JavaScriptOb SetUpMethods fail");
298             return;
299         }
300         for (uint32_t i = 0; i < size; i++) {
301             napi_value napiKeyTmp;
302             s = napi_get_element(env_, propertyNames, i, &napiKeyTmp);
303             if (s != napi_ok) {
304                 WVLOG_E("JavaScriptOb SetUpMethods fail");
305                 return;
306             }
307             napi_valuetype valueType = napi_undefined;
308             napi_value napiValueTmp;
309             s = napi_get_property(env_, obj, napiKeyTmp, &napiValueTmp);
310             if (s != napi_ok) {
311                 WVLOG_E("JavaScriptOb SetUpMethods fail");
312                 return;
313             }
314             napi_typeof(env_, napiValueTmp, &valueType);
315             if (valueType != napi_function) {
316                 continue;
317             }
318             std::string methodName;
319             if (NapiParseUtils::ParseString(env_, napiKeyTmp, methodName)) {
320                 methods_.push_back(methodName);
321             }
322         }
323         isMethodsSetup_ = true;
324     }
325 
SetMethods(std::vector<std::string> methods_name)326     void SetMethods(std::vector<std::string> methods_name)
327     {
328         std::unique_lock<std::mutex> lock(mutex_);
329         methods_ = methods_name;
330         isMethodsSetup_ = true;
331     }
332 
SetAsyncMethods(std::vector<std::string> async_methods_name)333     void SetAsyncMethods(std::vector<std::string> async_methods_name)
334     {
335         std::unique_lock<std::mutex> lock(mutex_);
336         asyncMethods_ = async_methods_name;
337     }
338 
SetPermission(std::string permission)339     void SetPermission(std::string permission)
340     {
341         std::unique_lock<std::mutex> lock(mutex_);
342         permission_ = permission;
343     }
344 
345 private:
CreateNewWeakRef(napi_env env,napi_ref ref,napi_ref * new_ref)346     static napi_status CreateNewWeakRef(napi_env env, napi_ref ref, napi_ref* new_ref)
347     {
348         napi_value val = nullptr;
349         napi_status sts = napi_get_reference_value(env, ref, &val);
350         if (sts != napi_ok)
351             return sts;
352         return napi_create_reference(env, val, 0, new_ref);
353     }
354 
Delete()355     void Delete()
356     {
357         if (objRef_ && Release() == 0) {
358             WVLOG_D("JavaScriptOb delete called");
359             napi_delete_reference(env_, objRef_);
360             objRef_ = nullptr;
361         }
362     }
363 
Release()364     uint32_t Release()
365     {
366         if (!objRef_ || !isStrongRef_) {
367             return 0;
368         }
369         uint32_t refCount = 0;
370         napi_status s = napi_reference_unref(env_, objRef_, &refCount);
371         if (s != napi_ok) {
372             WVLOG_E("JavaScriptOb Release fail");
373         }
374         return refCount;
375     }
376 
377     napi_env env_ = nullptr;
378     int32_t containerScopeId_ = -1;
379 
380     napi_ref objRef_ = nullptr;
381     bool isStrongRef_ = true;
382 
383     // methods_ contains sync methods and async methods.
384     std::vector<std::string> methods_;
385     std::vector<std::string> asyncMethods_;
386 
387     // allow list
388     std::string permission_;
389 
390     // An object must be kept in retainedObjectSet_ either if it has
391     // names or if it has a non-empty holders set.
392     int namesCount_;
393     std::set<int32_t> holders_;
394     bool isMethodsSetup_ = false;
395     std::mutex mutex_;
396 };
397 
398 class WebviewJavaScriptResultCallBack : public NWebJavaScriptResultCallBack {
399 public:
400     typedef std::unordered_map<std::string, JavaScriptOb::ObjectID> NamedObjectMap;
401     typedef std::unordered_map<JavaScriptOb::ObjectID, std::shared_ptr<JavaScriptOb>> ObjectMap;
402     typedef int32_t ObjectID;
403     struct H5Bundle {
404         int32_t nwebId;
405         int32_t frameRoutingId;
406         int32_t h5Id;
407         std::string funcName;
408     };
409 
410     enum class NapiJsCallBackParmFlag : int32_t { ISOBJECT = 1, END = 2 };
411 
412     struct NapiJsCallBackInParm {
413         WebviewJavaScriptResultCallBack* webJsResCb = nullptr;
414         int32_t nwebId = -1;
415         int32_t frameRoutingId = -1;
416         int32_t objId = -1;
417         int32_t containerScopeId = -1;
418         std::string objName;
419         std::string methodName;
420         void* data = nullptr;
421     };
422 
423     struct NapiJsCallBackOutParm {
424         napi_status status;
425         int32_t errCode = -1;
426         NapiJsCallBackParmFlag flag;
427         void* ret = nullptr;
428     };
429 
430     struct NapiJsCallBackParm {
431         // environment
432         napi_env env = nullptr;
433         int32_t containerScopedId = -1;
434 
435         std::mutex mutex;
436         //  sync
437         std::condition_variable condition;
438         bool ready = false;
439         // async
440         napi_async_work asyncWork = nullptr;
441         napi_deferred deferred = nullptr;
442         napi_ref callbackRef = nullptr;
443         // input
444         void* input = nullptr;
445         // out
446         void* out = nullptr;
447     };
448 
WebviewJavaScriptResultCallBack()449     WebviewJavaScriptResultCallBack() {}
450 
451     explicit WebviewJavaScriptResultCallBack(int32_t nwebId);
452 
453     ~WebviewJavaScriptResultCallBack() override;
454 
455     std::shared_ptr<NWebValue> GetJavaScriptResult(std::vector<std::shared_ptr<NWebValue>> args,
456         const std::string& method, const std::string& objName, int32_t routingId, int32_t objectId) override;
457 
458     std::shared_ptr<NWebValue> GetJavaScriptResultFlowbuf(std::vector<std::shared_ptr<NWebValue>> args,
459         const std::string& method, const std::string& objName, int fd, int32_t routingId, int32_t objectId) override;
460 
461     bool HasJavaScriptObjectMethods(int32_t objectId, const std::string& methodName) override;
462 
463     std::shared_ptr<NWebValue> GetJavaScriptObjectMethods(int32_t objectId) override;
464 
465     std::shared_ptr<JavaScriptOb> FindObject(JavaScriptOb::ObjectID objectId);
466 
467     void RemoveJavaScriptObjectHolder(int32_t holder, JavaScriptOb::ObjectID objectId) override;
468 
469     void RemoveJavaScriptObjectHolderInJsTd(int32_t holder, JavaScriptOb::ObjectID objectId);
470 
471     void RemoveTransientJavaScriptObject() override;
472 
473     void RemoveTransientJavaScriptObjectInJsTd();
474 
475     void GetJavaScriptResultV2(const std::vector<std::shared_ptr<NWebHapValue>>& args, const std::string& method,
476         const std::string& objectName, int32_t routingId, int32_t objectId,
477         std::shared_ptr<NWebHapValue> result) override;
478 
479     void GetJavaScriptResultFlowbufV2(const std::vector<std::shared_ptr<NWebHapValue>>& args, const std::string& method,
480         const std::string& objectName, int fd, int32_t routingId, int32_t objectId,
481         std::shared_ptr<NWebHapValue> result) override;
482 
483     void GetJavaScriptObjectMethodsV2(int32_t objectId, std::shared_ptr<NWebHapValue> result) override;
484 
485     bool FindObjectIdInJsTd(napi_env env, napi_value object, JavaScriptOb::ObjectID* objectId);
486 
487     std::unordered_map<std::string, std::shared_ptr<JavaScriptOb>> GetNamedObjects();
488 
489     ObjectMap GetObjectMap();
490 
491     JavaScriptOb::ObjectID AddObject(napi_env env, const napi_value& object, bool methodName, int32_t holder);
492 
493     void SetUpAnnotateMethods(JavaScriptOb::ObjectID objId, std::vector<std::string>& methodNameList);
494 
495     JavaScriptOb::ObjectID RegisterJavaScriptProxy(RegisterJavaScriptProxyParam& param);
496 
497     bool DeleteJavaScriptRegister(const std::string& objName);
498 
499     void CallH5FunctionInternal(
500         napi_env env, H5Bundle& bundle, const std::vector<std::shared_ptr<NWebRomValue>>& romArgs,
501         const std::vector<std::shared_ptr<NWebValue>>& nwebArgs);
502 
GetNWebId()503     int32_t GetNWebId()
504     {
505         return nwebId_;
506     }
507 
508     void UpdateInstanceId(int32_t newId);
509 private:
510     bool RemoveNamedObject(const std::string& name);
511 
512     JavaScriptOb::ObjectID AddNamedObject(napi_env env, napi_value& obj, const std::string& objName);
513 
514     std::shared_ptr<NWebValue> PostGetJavaScriptResultToJsThread(std::vector<std::shared_ptr<NWebValue>> args,
515         const std::string& method, const std::string& objName, int32_t routingId, int32_t objectId);
516 
517     bool PostHasJavaScriptObjectMethodsToJsThread(int32_t objectId, const std::string& methodName);
518     std::shared_ptr<NWebValue> PostGetJavaScriptObjectMethodsToJsThread(int32_t objectId);
519 
520     void PostRemoveJavaScriptObjectHolderToJsThread(int32_t holder, JavaScriptOb::ObjectID objectId);
521 
522     std::shared_ptr<NWebValue> GetJavaScriptResultSelf(std::vector<std::shared_ptr<NWebValue>> args,
523         const std::string& method, const std::string& objName, int32_t routingId, int32_t objectId);
524 
525     bool ConstructArgv(void* ashmem, std::vector<std::shared_ptr<NWebValue>> args,
526     	std::vector<napi_value>& argv, std::shared_ptr<JavaScriptOb> jsObj, int32_t routingId);
527 
528     std::shared_ptr<NWebValue> GetJavaScriptResultSelfHelper(std::shared_ptr<JavaScriptOb> jsObj,
529         const std::string& method, int32_t routingId, std::vector<napi_value> argv);
530 
531     char* FlowbufStrAtIndex(void* mem, int flowbufIndex, int* argIndex, int* strLen);
532 
533     std::shared_ptr<NWebValue> GetJavaScriptResultSelfFlowbuf(std::vector<std::shared_ptr<NWebValue>> args,
534         const std::string& method, const std::string& objName, int fd, int32_t routingId, int32_t objectId);
535 
536     void PostRemoveTransientJavaScriptObjectToJsThread(std::shared_ptr<JavaScriptOb> jsObj);
537 
538     bool ConstructArgvV2(void* ashmem, const std::vector<std::shared_ptr<NWebHapValue>>& args,
539         std::vector<napi_value>& argv, std::shared_ptr<JavaScriptOb> jsObj, int32_t routingId);
540 
541     void GetJavaScriptResultSelfV2(const std::vector<std::shared_ptr<NWebHapValue>>& args, const std::string& method,
542         int32_t routingId, int32_t objectId, std::shared_ptr<NWebHapValue> result);
543 
544     void GetJavaScriptResultSelfHelperV2(std::shared_ptr<JavaScriptOb> jsObj, const std::string& method,
545         int32_t routingId, const std::vector<napi_value>& argv, std::shared_ptr<NWebHapValue> result);
546 
547     void GetJavaScriptResultSelfFlowbufV2(const std::vector<std::shared_ptr<NWebHapValue>>& args,
548         const std::string& method, int fd, int32_t routingId, int32_t objectId, std::shared_ptr<NWebHapValue> result);
549 
550     void PostGetJavaScriptResultToJsThreadV2(std::vector<napi_value>& args, const std::string& method,
551         int32_t routingId, int32_t objectId, std::shared_ptr<NWebHapValue> result);
552 
553     void PostGetJavaScriptObjectMethodsToJsThreadV2(int32_t objectId, std::shared_ptr<NWebHapValue> result);
554 
555     int32_t nwebId_ = -1;
556 
557     JavaScriptOb::ObjectID nextObjectId_ = 1;
558     NamedObjectMap namedObjects_;
559     ObjectMap objects_;
560     std::unordered_set<std::shared_ptr<JavaScriptOb>> retainedObjectSet_;
561 };
562 }
563 #endif
564