diff --git a/frameworks/bridge/declarative_frontend/jsview/js_web.cpp b/frameworks/bridge/declarative_frontend/jsview/js_web.cpp index 6c89810db3d036ab41f613554257ed5e9074a5b9..e4b7ddaf05600cf22ee181c7c4f6eeac86eda826 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_web.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_web.cpp @@ -2046,13 +2046,11 @@ public: void OnConnect(const JSCallbackInfo &args) { - int connectid; - if ((args.Length() <= 0) || !(args[0]->IsNumber())) { TAG_LOGW(AceLogTag::ACE_WEB, "JSWebNativeMessageCallback OnConnect type error"); return; } - connectid = args[0]->ToNumber(); + int connectid = args[0]->ToNumber(); TAG_LOGI(AceLogTag::ACE_WEB, "JSWebNativeMessageCallback OnConnect, connectid=%{public}d", connectid); if (callback_) { @@ -2062,13 +2060,11 @@ public: void OnDisconnect(const JSCallbackInfo &args) { - int connectid; - if ((args.Length() <= 0) || !(args[0]->IsNumber())) { TAG_LOGW(AceLogTag::ACE_WEB, "JSWebNativeMessageCallback OnDisconnect type error"); return; } - connectid = args[0]->ToNumber(); + int connectid = args[0]->ToNumber(); if (callback_) { callback_->OnDisconnect(connectid); } @@ -2076,13 +2072,11 @@ public: void OnFailed(const JSCallbackInfo &args) { - int failedCode; - if ((args.Length() <= 0) || !(args[0]->IsNumber())) { TAG_LOGW(AceLogTag::ACE_WEB, "JSWebNativeMessageCallback failedCode type error"); return; } - failedCode = args[0]->ToNumber(); + int failedCode = args[0]->ToNumber(); TAG_LOGW(AceLogTag::ACE_WEB, "JSWebNativeMessageCallback OnFailed, failedCode is %{public}d", failedCode); if (callback_) { diff --git a/frameworks/core/components/web/resource/web_delegate.cpp b/frameworks/core/components/web/resource/web_delegate.cpp index 5e2d8c8350ed2d61a6141e6ec739bde740a1c445..46e77a4b5293bca409c1d55c99da7ab7a1c9d2a6 100644 --- a/frameworks/core/components/web/resource/web_delegate.cpp +++ b/frameworks/core/components/web/resource/web_delegate.cpp @@ -9191,57 +9191,108 @@ void WebDelegate::OnExtensionDisconnect(int32_t connectId) "ArkUIWebExtensionDisconnect"); } -std::string WebDelegate::OnWebNativeMessage(std::shared_ptr info, - std::shared_ptr callback) +bool WebDelegate::ValidateInputs( + std::shared_ptr callback, const RefPtr &context) { if (!callback) { - return std::string("error:-1"); + TAG_LOGE(AceLogTag::ACE_WEB, "web native message callback is nullptr"); + return false; } - auto context = context_.Upgrade(); - CHECK_NULL_RETURN(context, std::string("error:-1")); - auto pipelineContext = DynamicCast(context); - CHECK_NULL_RETURN(pipelineContext, std::string("error:-1")); + + if (!context) { + TAG_LOGE(AceLogTag::ACE_WEB, "context is nullptr"); + return false; + } + auto pipeline = NG::PipelineContext::GetCurrentContextSafely(); - std::mutex mtx; - std::condition_variable cv; - bool isCallbackDone = false; - std::string callbackResult; - bool isTimeout = false; - constexpr auto timeout = std::chrono::seconds(TIMEOUT_SECONDS); - auto jsTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::JS); - jsTaskExecutor.PostSyncTask( - [weak = WeakClaim(this), info, callback, &mtx, &cv, &isCallbackDone, &callbackResult, &isTimeout]() { - auto setResultAndNotify = [&](const std::string& result = "") { - std::unique_lock lock(mtx); - if (!isTimeout) { - callbackResult = result; - isCallbackDone = true; - } - cv.notify_one(); - }; - auto delegate = weak.Upgrade(); - if (!delegate) return setResultAndNotify(); - auto webPattern = delegate->webPattern_.Upgrade(); - if (!webPattern) return setResultAndNotify(); - auto webNativeMessageCallback = webPattern->GetWebNativeMessageConnectCallback(); - if (!webNativeMessageCallback) return setResultAndNotify(); - auto wrappedCallback = AceType::MakeRefPtr(callback); - wrappedCallback->SetCompletionHandler([&](const std::string &result) { setResultAndNotify(result); }); - webNativeMessageCallback(std::make_shared(info->GetBundleName(), - info->GetExtensionOrigin(), - info->GetMessageReadPipe(), - info->GetMessageWritePipe(), - wrappedCallback)); - }, "ArkUIWebNativeMessage"); - { - std::unique_lock lock(mtx); - if (!cv.wait_for(lock, timeout, [&]() { return isCallbackDone; })) { - isTimeout = true; - callbackResult = "error:4201"; - return callbackResult; + if (pipeline && pipeline->CheckThreadSafe()) { + TAG_LOGE(AceLogTag::ACE_WEB, "OnWebNativeMessage doesn't run on UI thread"); + return false; + } + + return true; +} + +void WebDelegate::ExecuteNativeMessageTask(WeakPtr weak, + std::shared_ptr info, + std::shared_ptr callback, std::shared_ptr sharedState) +{ + std::lock_guard lock(sharedState->mtx); + if (sharedState->isTimeout) + return; + + auto delegate = weak.Upgrade(); + auto webPattern = delegate ? delegate->webPattern_.Upgrade() : nullptr; + auto webNativeMessageCallback = webPattern ? webPattern->GetWebNativeMessageConnectCallback() : nullptr; + + if (!webNativeMessageCallback) + return; + + auto wrappedCallback = AceType::MakeRefPtr(callback); + wrappedCallback->SetCompletionHandler([sharedState](const std::string &result) { + bool expected = false; + if (!sharedState->hasBeenCalled.compare_exchange_strong(expected, true)) { + TAG_LOGW(AceLogTag::ACE_WEB, "OnWebNativeMessage callback called multiple times, ignoring"); + return; + } + + std::unique_lock lock(sharedState->mtx); + if (sharedState->isTimeout) { + TAG_LOGW(AceLogTag::ACE_WEB, "OnWebNativeMessage callback after timeout"); + return; + } + + sharedState->callbackResult = result; + sharedState->isCallbackDone = true; + sharedState->cv.notify_one(); + }); + + webNativeMessageCallback(std::make_shared(info->GetBundleName(), + info->GetExtensionOrigin(), + info->GetMessageReadPipe(), + info->GetMessageWritePipe(), + wrappedCallback)); +} + +std::string WebDelegate::WaitForResult(std::shared_ptr sharedState) +{ + TAG_LOGI(AceLogTag::ACE_WEB, "OnWebNativeMessage waiting start"); + std::unique_lock lock(sharedState->mtx); + if (!sharedState->cv.wait_for( + lock, std::chrono::seconds(TIMEOUT_SECONDS), + [sharedState]() {State->isCallbackDone; })) { + bool expected = false; + if (sharedState->hasBeenCalled.compare_exchange_strong(expected, true)) { + sharedState->isTimeout = true; + sharedState->callbackResult = "error:4204"; + TAG_LOGW(AceLogTag::ACE_WEB, "OnWebNativeMessage return by timeout"); + } else { + sharedState->cv.wait(lock, [sharedState]() { return sharedState->isCallbackDone; }); } } - return callbackResult; + + TAG_LOGI(AceLogTag::ACE_WEB, "OnWebNativeMessage return result"); + return sharedState->callbackResult; +} + +std::string WebDelegate::OnWebNativeMessage(std::shared_ptr info, + std::shared_ptr callback) +{ + auto context = context_.Upgrade(); + if (!ValidateInputs(callback, context)) { + return "error:4201"; + } + + auto sharedState = std::make_shared(); + auto jsTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::JS); + + jsTaskExecutor.PostSyncTask( + [this, info, callback, sharedState]() { + ExecuteNativeMessageTask(WeakClaim(this), info, callback, sharedState); + }, + "ArkUIWebNativeMessage"); + + return WaitForResult(sharedState); } void WebDelegate::SetImeShow(bool visible) diff --git a/frameworks/core/components/web/resource/web_delegate.h b/frameworks/core/components/web/resource/web_delegate.h index f241e29876be38d455a68ce7f8bfc8738af5ccaa..a6a6b42057458706fc64194e57dc536e3facb1ec 100644 --- a/frameworks/core/components/web/resource/web_delegate.h +++ b/frameworks/core/components/web/resource/web_delegate.h @@ -1434,7 +1434,19 @@ public: void OnLoadFinished(const std::string& param); void SetIsFileSelectorShow(bool isFileSelectorShow) { isFileSelectorShow_ = isFileSelectorShow; } bool IsFileSelectorShow() { return isFileSelectorShow_; } - + struct SharedState { + std::mutex mtx; + std::condition_variable cv; + bool isCallbackDone = false; + bool isTimeout = false; + std::string callbackResult; + std::atomic hasBeenCalled { false }; + }; + bool ValidateInputs( + std::shared_ptr callback, const RefPtr& context); + void ExecuteNativeMessageTask(WeakPtr weak, std::shared_ptr info, + std::shared_ptr callback, std::shared_ptr sharedState); + std::string WaitForResult(std::shared_ptr sharedState); void OnExtensionDisconnect(int32_t connectId); std::string OnWebNativeMessage(std::shared_ptr info, std::shared_ptr callback);