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