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