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 (!isMethodsSetup_) { 247 SetUpMethods(); 248 } 249 250 if (HasMethod(methodName)) { 251 bool hasFunc = false; 252 napi_value result = nullptr; 253 napi_valuetype valueType = napi_undefined; 254 napi_value obj = GetValue(); 255 if (!obj) { 256 WVLOG_E("JavaScriptOb FindMethod obj null"); 257 return nullptr; 258 } 259 napi_status s = napi_has_named_property(env_, obj, methodName.c_str(), &hasFunc); 260 if (s != napi_ok) { 261 WVLOG_E("JavaScriptOb FindMethod fail"); 262 return nullptr; 263 } 264 if (!hasFunc) { 265 WVLOG_E("JavaScriptOb FindMethod fail"); 266 return nullptr; 267 } 268 s = napi_get_named_property(env_, obj, methodName.c_str(), &result); 269 if (s != napi_ok) { 270 WVLOG_E("JavaScriptOb FindMethod fail"); 271 return nullptr; 272 } 273 napi_typeof(env_, result, &valueType); 274 if (valueType != napi_function) { 275 WVLOG_E("JavaScriptOb FindMethod not function"); 276 return nullptr; 277 } 278 return result; 279 } 280 281 return nullptr; 282 } 283 SetUpMethods()284 void SetUpMethods() 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 std::unique_lock<std::mutex> lock(mutex_); 321 methods_.push_back(methodName); 322 } 323 } 324 std::unique_lock<std::mutex> lock(mutex_); 325 isMethodsSetup_ = true; 326 } 327 SetMethods(std::vector<std::string> methods_name)328 void SetMethods(std::vector<std::string> methods_name) 329 { 330 std::unique_lock<std::mutex> lock(mutex_); 331 methods_ = methods_name; 332 isMethodsSetup_ = true; 333 } 334 SetAsyncMethods(std::vector<std::string> async_methods_name)335 void SetAsyncMethods(std::vector<std::string> async_methods_name) 336 { 337 std::unique_lock<std::mutex> lock(mutex_); 338 asyncMethods_ = async_methods_name; 339 } 340 SetPermission(std::string permission)341 void SetPermission(std::string permission) 342 { 343 std::unique_lock<std::mutex> lock(mutex_); 344 permission_ = permission; 345 } 346 347 private: CreateNewWeakRef(napi_env env,napi_ref ref,napi_ref * new_ref)348 static napi_status CreateNewWeakRef(napi_env env, napi_ref ref, napi_ref* new_ref) 349 { 350 napi_value val = nullptr; 351 napi_status sts = napi_get_reference_value(env, ref, &val); 352 if (sts != napi_ok) 353 return sts; 354 return napi_create_reference(env, val, 0, new_ref); 355 } 356 Delete()357 void Delete() 358 { 359 if (objRef_ && Release() == 0) { 360 WVLOG_D("JavaScriptOb delete called"); 361 napi_delete_reference(env_, objRef_); 362 objRef_ = nullptr; 363 } 364 } 365 Release()366 uint32_t Release() 367 { 368 if (!objRef_ || !isStrongRef_) { 369 return 0; 370 } 371 uint32_t refCount = 0; 372 napi_status s = napi_reference_unref(env_, objRef_, &refCount); 373 if (s != napi_ok) { 374 WVLOG_E("JavaScriptOb Release fail"); 375 } 376 return refCount; 377 } 378 379 napi_env env_ = nullptr; 380 int32_t containerScopeId_ = -1; 381 382 napi_ref objRef_ = nullptr; 383 bool isStrongRef_ = true; 384 385 // methods_ contains sync methods and async methods. 386 std::vector<std::string> methods_; 387 std::vector<std::string> asyncMethods_; 388 389 // allow list 390 std::string permission_; 391 392 // An object must be kept in retainedObjectSet_ either if it has 393 // names or if it has a non-empty holders set. 394 int namesCount_; 395 std::set<int32_t> holders_; 396 bool isMethodsSetup_ = false; 397 std::mutex mutex_; 398 }; 399 400 class WebviewJavaScriptResultCallBack : public NWebJavaScriptResultCallBack { 401 public: 402 typedef std::unordered_map<std::string, JavaScriptOb::ObjectID> NamedObjectMap; 403 typedef std::unordered_map<JavaScriptOb::ObjectID, std::shared_ptr<JavaScriptOb>> ObjectMap; 404 typedef int32_t ObjectID; 405 struct H5Bundle { 406 int32_t nwebId; 407 int32_t frameRoutingId; 408 int32_t h5Id; 409 std::string funcName; 410 }; 411 412 enum class NapiJsCallBackParmFlag : int32_t { ISOBJECT = 1, END = 2 }; 413 414 struct NapiJsCallBackInParm { 415 WebviewJavaScriptResultCallBack* webJsResCb = nullptr; 416 int32_t nwebId = -1; 417 int32_t frameRoutingId = -1; 418 int32_t objId = -1; 419 std::string objName; 420 std::string methodName; 421 void* data = nullptr; 422 }; 423 424 struct NapiJsCallBackOutParm { 425 napi_status status; 426 int32_t errCode = -1; 427 NapiJsCallBackParmFlag flag; 428 void* ret = nullptr; 429 }; 430 431 struct NapiJsCallBackParm { 432 // environment 433 napi_env env = nullptr; 434 int32_t containerScopedId = -1; 435 436 std::mutex mutex; 437 // sync 438 std::condition_variable condition; 439 bool ready = false; 440 // async 441 napi_async_work asyncWork = nullptr; 442 napi_deferred deferred = nullptr; 443 napi_ref callbackRef = nullptr; 444 // input 445 void* input = nullptr; 446 // out 447 void* out = nullptr; 448 }; 449 WebviewJavaScriptResultCallBack()450 WebviewJavaScriptResultCallBack() {} 451 452 explicit WebviewJavaScriptResultCallBack(int32_t nwebId); 453 454 ~WebviewJavaScriptResultCallBack() override; 455 456 std::shared_ptr<NWebValue> GetJavaScriptResult(std::vector<std::shared_ptr<NWebValue>> args, 457 const std::string& method, const std::string& objName, int32_t routingId, int32_t objectId) override; 458 459 std::shared_ptr<NWebValue> GetJavaScriptResultFlowbuf(std::vector<std::shared_ptr<NWebValue>> args, 460 const std::string& method, const std::string& objName, int fd, int32_t routingId, int32_t objectId) override; 461 462 bool HasJavaScriptObjectMethods(int32_t objectId, const std::string& methodName) override; 463 464 std::shared_ptr<NWebValue> GetJavaScriptObjectMethods(int32_t objectId) override; 465 466 std::shared_ptr<JavaScriptOb> FindObject(JavaScriptOb::ObjectID objectId); 467 468 void RemoveJavaScriptObjectHolder(int32_t holder, JavaScriptOb::ObjectID objectId) override; 469 470 void RemoveJavaScriptObjectHolderInJsTd(int32_t holder, JavaScriptOb::ObjectID objectId); 471 472 void RemoveTransientJavaScriptObject() override; 473 474 bool FindObjectIdInJsTd(napi_env env, napi_value object, JavaScriptOb::ObjectID* objectId); 475 476 std::unordered_map<std::string, std::shared_ptr<JavaScriptOb>> GetNamedObjects(); 477 478 ObjectMap GetObjectMap(); 479 480 JavaScriptOb::ObjectID AddObject(napi_env env, const napi_value& object, bool methodName, int32_t holder); 481 482 void SetUpAnnotateMethods(JavaScriptOb::ObjectID objId, std::vector<std::string>& methodNameList); 483 484 JavaScriptOb::ObjectID RegisterJavaScriptProxy(RegisterJavaScriptProxyParam& param); 485 486 bool DeleteJavaScriptRegister(const std::string& objName); 487 488 void CallH5FunctionInternal(napi_env env, H5Bundle& bundle, std::vector<std::shared_ptr<NWebValue>> nwebArgs); 489 GetNWebId()490 int32_t GetNWebId() 491 { 492 return nwebId_; 493 } 494 495 void UpdateInstanceId(int32_t newId); 496 private: 497 bool RemoveNamedObject(const std::string& name); 498 499 JavaScriptOb::ObjectID AddNamedObject(napi_env env, napi_value& obj, const std::string& objName); 500 501 std::shared_ptr<NWebValue> PostGetJavaScriptResultToJsThread(std::vector<std::shared_ptr<NWebValue>> args, 502 const std::string& method, const std::string& objName, int32_t routingId, int32_t objectId); 503 504 bool PostHasJavaScriptObjectMethodsToJsThread(int32_t objectId, const std::string& methodName); 505 std::shared_ptr<NWebValue> PostGetJavaScriptObjectMethodsToJsThread(int32_t objectId); 506 507 void PostRemoveJavaScriptObjectHolderToJsThread(int32_t holder, JavaScriptOb::ObjectID objectId); 508 509 std::shared_ptr<NWebValue> GetJavaScriptResultSelf(std::vector<std::shared_ptr<NWebValue>> args, 510 const std::string& method, const std::string& objName, int32_t routingId, int32_t objectId); 511 512 bool ConstructArgv(void* ashmem, std::vector<std::shared_ptr<NWebValue>> args, 513 std::vector<napi_value>& argv, std::shared_ptr<JavaScriptOb> jsObj, int32_t routingId); 514 515 std::shared_ptr<NWebValue> GetJavaScriptResultSelfHelper(std::shared_ptr<JavaScriptOb> jsObj, 516 const std::string& method, int32_t routingId, napi_handle_scope scope, std::vector<napi_value> argv); 517 518 char* FlowbufStrAtIndex(void* mem, int flowbufIndex, int* argIndex, int* strLen); 519 520 std::shared_ptr<NWebValue> GetJavaScriptResultSelfFlowbuf(std::vector<std::shared_ptr<NWebValue>> args, 521 const std::string& method, const std::string& objName, int fd, int32_t routingId, int32_t objectId); 522 523 int32_t nwebId_ = -1; 524 525 JavaScriptOb::ObjectID nextObjectId_ = 1; 526 NamedObjectMap namedObjects_; 527 ObjectMap objects_; 528 std::unordered_set<std::shared_ptr<JavaScriptOb>> retainedObjectSet_; 529 }; 530 } 531 #endif 532