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 std::string objName; 418 std::string methodName; 419 void* data = nullptr; 420 }; 421 422 struct NapiJsCallBackOutParm { 423 napi_status status; 424 int32_t errCode = -1; 425 NapiJsCallBackParmFlag flag; 426 void* ret = nullptr; 427 }; 428 429 struct NapiJsCallBackParm { 430 // environment 431 napi_env env = nullptr; 432 int32_t containerScopedId = -1; 433 434 std::mutex mutex; 435 // sync 436 std::condition_variable condition; 437 bool ready = false; 438 // async 439 napi_async_work asyncWork = nullptr; 440 napi_deferred deferred = nullptr; 441 napi_ref callbackRef = nullptr; 442 // input 443 void* input = nullptr; 444 // out 445 void* out = nullptr; 446 }; 447 WebviewJavaScriptResultCallBack()448 WebviewJavaScriptResultCallBack() {} 449 450 explicit WebviewJavaScriptResultCallBack(int32_t nwebId); 451 452 ~WebviewJavaScriptResultCallBack() override; 453 454 std::shared_ptr<NWebValue> GetJavaScriptResult(std::vector<std::shared_ptr<NWebValue>> args, 455 const std::string& method, const std::string& objName, int32_t routingId, int32_t objectId) override; 456 457 std::shared_ptr<NWebValue> GetJavaScriptResultFlowbuf(std::vector<std::shared_ptr<NWebValue>> args, 458 const std::string& method, const std::string& objName, int fd, int32_t routingId, int32_t objectId) override; 459 460 bool HasJavaScriptObjectMethods(int32_t objectId, const std::string& methodName) override; 461 462 std::shared_ptr<NWebValue> GetJavaScriptObjectMethods(int32_t objectId) override; 463 464 std::shared_ptr<JavaScriptOb> FindObject(JavaScriptOb::ObjectID objectId); 465 466 void RemoveJavaScriptObjectHolder(int32_t holder, JavaScriptOb::ObjectID objectId) override; 467 468 void RemoveJavaScriptObjectHolderInJsTd(int32_t holder, JavaScriptOb::ObjectID objectId); 469 470 void RemoveTransientJavaScriptObject() override; 471 472 bool FindObjectIdInJsTd(napi_env env, napi_value object, JavaScriptOb::ObjectID* objectId); 473 474 std::unordered_map<std::string, std::shared_ptr<JavaScriptOb>> GetNamedObjects(); 475 476 ObjectMap GetObjectMap(); 477 478 JavaScriptOb::ObjectID AddObject(napi_env env, const napi_value& object, bool methodName, int32_t holder); 479 480 void SetUpAnnotateMethods(JavaScriptOb::ObjectID objId, std::vector<std::string>& methodNameList); 481 482 JavaScriptOb::ObjectID RegisterJavaScriptProxy(RegisterJavaScriptProxyParam& param); 483 484 bool DeleteJavaScriptRegister(const std::string& objName); 485 486 void CallH5FunctionInternal(napi_env env, H5Bundle& bundle, std::vector<std::shared_ptr<NWebValue>> nwebArgs); 487 GetNWebId()488 int32_t GetNWebId() 489 { 490 return nwebId_; 491 } 492 493 void UpdateInstanceId(int32_t newId); 494 private: 495 bool RemoveNamedObject(const std::string& name); 496 497 JavaScriptOb::ObjectID AddNamedObject(napi_env env, napi_value& obj, const std::string& objName); 498 499 std::shared_ptr<NWebValue> PostGetJavaScriptResultToJsThread(std::vector<std::shared_ptr<NWebValue>> args, 500 const std::string& method, const std::string& objName, int32_t routingId, int32_t objectId); 501 502 bool PostHasJavaScriptObjectMethodsToJsThread(int32_t objectId, const std::string& methodName); 503 std::shared_ptr<NWebValue> PostGetJavaScriptObjectMethodsToJsThread(int32_t objectId); 504 505 void PostRemoveJavaScriptObjectHolderToJsThread(int32_t holder, JavaScriptOb::ObjectID objectId); 506 507 std::shared_ptr<NWebValue> GetJavaScriptResultSelf(std::vector<std::shared_ptr<NWebValue>> args, 508 const std::string& method, const std::string& objName, int32_t routingId, int32_t objectId); 509 510 bool ConstructArgv(void* ashmem, std::vector<std::shared_ptr<NWebValue>> args, 511 std::vector<napi_value>& argv, std::shared_ptr<JavaScriptOb> jsObj, int32_t routingId); 512 513 std::shared_ptr<NWebValue> GetJavaScriptResultSelfHelper(std::shared_ptr<JavaScriptOb> jsObj, 514 const std::string& method, int32_t routingId, napi_handle_scope scope, std::vector<napi_value> argv); 515 516 char* FlowbufStrAtIndex(void* mem, int flowbufIndex, int* argIndex, int* strLen); 517 518 std::shared_ptr<NWebValue> GetJavaScriptResultSelfFlowbuf(std::vector<std::shared_ptr<NWebValue>> args, 519 const std::string& method, const std::string& objName, int fd, int32_t routingId, int32_t objectId); 520 521 int32_t nwebId_ = -1; 522 523 JavaScriptOb::ObjectID nextObjectId_ = 1; 524 NamedObjectMap namedObjects_; 525 ObjectMap objects_; 526 std::unordered_set<std::shared_ptr<JavaScriptOb>> retainedObjectSet_; 527 }; 528 } 529 #endif 530