From d8bc3d837237bf050cb655a109a171cda1e7d3bc Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt <magreenblatt@gmail.com> Date: Tue, 19 Dec 2017 15:14:00 -0500 Subject: [PATCH] Fix assertion when calling SendProcessMessage from non-UI thread (issue #2325) --- libcef/browser/browser_host_impl.cc | 101 +++++++++------------ libcef/browser/browser_host_impl.h | 16 +--- tests/ceftests/process_message_unittest.cc | 43 +++++++-- 3 files changed, 85 insertions(+), 75 deletions(-) diff --git a/libcef/browser/browser_host_impl.cc b/libcef/browser/browser_host_impl.cc index 7d1789dc6..f581945b4 100644 --- a/libcef/browser/browser_host_impl.cc +++ b/libcef/browser/browser_host_impl.cc @@ -1461,17 +1461,23 @@ void CefBrowserHostImpl::GetFrameNames(std::vector<CefString>& names) { bool CefBrowserHostImpl::SendProcessMessage( CefProcessId target_process, CefRefPtr<CefProcessMessage> message) { + DCHECK_EQ(PID_RENDERER, target_process); DCHECK(message.get()); Cef_Request_Params params; CefProcessMessageImpl* impl = static_cast<CefProcessMessageImpl*>(message.get()); - if (impl->CopyTo(params)) { - return SendProcessMessage(target_process, params.name, ¶ms.arguments, - true); - } + if (!impl->CopyTo(params)) + return false; - return false; + DCHECK(!params.name.empty()); + + params.frame_id = -1; + params.user_initiated = true; + params.request_id = -1; + params.expect_response = false; + + return Send(new CefMsg_Request(MSG_ROUTING_NONE, params)); } // CefBrowserHostImpl public methods. @@ -1612,7 +1618,7 @@ void CefBrowserHostImpl::Navigate(const CefNavigateParams& params) { request.load_flags = params.load_flags; request.upload_data = params.upload_data; - Send(new CefMsg_LoadRequest(routing_id(), request)); + Send(new CefMsg_LoadRequest(MSG_ROUTING_NONE, request)); OnSetFocus(FOCUS_SOURCE_NAVIGATION); } @@ -1692,7 +1698,7 @@ void CefBrowserHostImpl::LoadString(int64 frame_id, params.arguments.AppendString(string); params.arguments.AppendString(url); - Send(new CefMsg_Request(routing_id(), params)); + Send(new CefMsg_Request(MSG_ROUTING_NONE, params)); } void CefBrowserHostImpl::SendCommand( @@ -1722,7 +1728,7 @@ void CefBrowserHostImpl::SendCommand( params.arguments.AppendString(command); - Send(new CefMsg_Request(routing_id(), params)); + Send(new CefMsg_Request(MSG_ROUTING_NONE, params)); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::SendCommand, this, frame_id, command, responseHandler)); @@ -1763,7 +1769,7 @@ void CefBrowserHostImpl::SendCode( params.arguments.AppendString(script_url); params.arguments.AppendInteger(script_start_line); - Send(new CefMsg_Request(routing_id(), params)); + Send(new CefMsg_Request(MSG_ROUTING_NONE, params)); } else { CEF_POST_TASK(CEF_UIT, base::Bind(&CefBrowserHostImpl::SendCode, this, frame_id, is_javascript, code, script_url, @@ -1800,25 +1806,6 @@ void CefBrowserHostImpl::ExecuteJavaScriptWithUserGestureForTests( rfh->ExecuteJavaScriptWithUserGestureForTests(javascript); } -bool CefBrowserHostImpl::SendProcessMessage(CefProcessId target_process, - const std::string& name, - base::ListValue* arguments, - bool user_initiated) { - DCHECK_EQ(PID_RENDERER, target_process); - DCHECK(!name.empty()); - - Cef_Request_Params params; - params.name = name; - if (arguments) - params.arguments.Swap(arguments); - params.frame_id = -1; - params.user_initiated = user_initiated; - params.request_id = -1; - params.expect_response = false; - - return Send(new CefMsg_Request(routing_id(), params)); -} - void CefBrowserHostImpl::ViewText(const std::string& text) { if (!CEF_CURRENTLY_ON_UIT()) { CEF_POST_TASK(CEF_UIT, @@ -2911,33 +2898,6 @@ void CefBrowserHostImpl::OnWebContentsFocused( } } -bool CefBrowserHostImpl::Send(IPC::Message* message) { - if (CEF_CURRENTLY_ON_UIT()) { - if (queue_messages_) { - queued_messages_.push(message); - return true; - } else if (web_contents() && web_contents()->GetRenderViewHost()) { - return web_contents()->GetRenderViewHost()->Send(message); - } else { - delete message; - return false; - } - } else { - CEF_POST_TASK( - CEF_UIT, base::Bind(base::IgnoreResult(&CefBrowserHostImpl::Send), this, - message)); - return true; - } -} - -int CefBrowserHostImpl::routing_id() const { - CEF_REQUIRE_UIT(); - if (!web_contents()) - return MSG_ROUTING_NONE; - - return web_contents()->GetRenderViewHost()->GetRoutingID(); -} - void CefBrowserHostImpl::AddObserver(Observer* observer) { CEF_REQUIRE_UIT(); observers_.AddObserver(observer); @@ -3032,6 +2992,8 @@ void CefBrowserHostImpl::OnUpdateDraggableRegions( } void CefBrowserHostImpl::OnRequest(const Cef_Request_Params& params) { + CEF_REQUIRE_UIT(); + bool success = false; std::string response; bool expect_response_ack = false; @@ -3059,14 +3021,16 @@ void CefBrowserHostImpl::OnRequest(const Cef_Request_Params& params) { response_params.success = success; response_params.response = response; response_params.expect_response_ack = expect_response_ack; - Send(new CefMsg_Response(routing_id(), response_params)); + Send(new CefMsg_Response(MSG_ROUTING_NONE, response_params)); } } void CefBrowserHostImpl::OnResponse(const Cef_Response_Params& params) { + CEF_REQUIRE_UIT(); + response_manager_->RunHandler(params); if (params.expect_response_ack) - Send(new CefMsg_ResponseAck(routing_id(), params.request_id)); + Send(new CefMsg_ResponseAck(MSG_ROUTING_NONE, params.request_id)); } void CefBrowserHostImpl::OnResponseAck(int request_id) { @@ -3513,3 +3477,26 @@ void CefBrowserHostImpl::EnsureFileDialogManager() { this, platform_delegate_->CreateFileDialogRunner())); } } + +bool CefBrowserHostImpl::Send(IPC::Message* message) { + if (!CEF_CURRENTLY_ON_UIT()) { + CEF_POST_TASK( + CEF_UIT, base::Bind(base::IgnoreResult(&CefBrowserHostImpl::Send), this, + message)); + return true; + } + + if (queue_messages_) { + queued_messages_.push(message); + return true; + } else if (web_contents()) { + content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); + if (rvh) { + message->set_routing_id(rvh->GetRoutingID()); + return rvh->Send(message); + } + } + + delete message; + return false; +} diff --git a/libcef/browser/browser_host_impl.h b/libcef/browser/browser_host_impl.h index 12bd2a9d3..bf15a93ff 100644 --- a/libcef/browser/browser_host_impl.h +++ b/libcef/browser/browser_host_impl.h @@ -339,11 +339,6 @@ class CefBrowserHostImpl : public CefBrowserHost, void ExecuteJavaScriptWithUserGestureForTests(int64 frame_id, const CefString& javascript); - bool SendProcessMessage(CefProcessId target_process, - const std::string& name, - base::ListValue* arguments, - bool user_initiated); - // Open the specified text in the default text editor. void ViewText(const std::string& text); @@ -517,12 +512,6 @@ class CefBrowserHostImpl : public CefBrowserHost, void OnWebContentsFocused( content::RenderWidgetHost* render_widget_host) override; - // Send a message to the RenderViewHost associated with this browser. - // TODO(cef): With the introduction of OOPIFs, WebContents can span multiple - // processes. Messages should be sent to specific RenderFrameHosts instead. - bool Send(IPC::Message* message); - int routing_id() const; - // Manage observer objects. The observer must either outlive this object or // remove itself before destruction. These methods can only be called on the // UI thread. @@ -634,6 +623,11 @@ class CefBrowserHostImpl : public CefBrowserHost, // Create the CefFileDialogManager if it doesn't already exist. void EnsureFileDialogManager(); + // Send a message to the RenderViewHost associated with this browser. + // TODO(cef): With the introduction of OOPIFs, WebContents can span multiple + // processes. Messages should be sent to specific RenderFrameHosts instead. + bool Send(IPC::Message* message); + CefBrowserSettings settings_; CefRefPtr<CefClient> client_; std::unique_ptr<content::WebContents> web_contents_; diff --git a/tests/ceftests/process_message_unittest.cc b/tests/ceftests/process_message_unittest.cc index 316e41141..8352f2039 100644 --- a/tests/ceftests/process_message_unittest.cc +++ b/tests/ceftests/process_message_unittest.cc @@ -2,8 +2,10 @@ // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE file. +#include "include/base/cef_bind.h" #include "include/cef_process_message.h" #include "include/cef_task.h" +#include "include/wrapper/cef_closure_task.h" #include "tests/ceftests/test_handler.h" #include "tests/ceftests/test_util.h" #include "tests/gtest/include/gtest/gtest.h" @@ -70,7 +72,8 @@ class SendRecvRendererTest : public ClientAppRenderer::Delegate { // Browser side. class SendRecvTestHandler : public TestHandler { public: - SendRecvTestHandler() {} + explicit SendRecvTestHandler(cef_thread_id_t send_thread) + : send_thread_(send_thread) {} void RunTest() override { message_ = CreateTestMessage(); @@ -85,13 +88,21 @@ class SendRecvTestHandler : public TestHandler { void OnLoadEnd(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + // Send the message to the renderer process. - EXPECT_TRUE(browser->SendProcessMessage(PID_RENDERER, message_)); + if (!CefCurrentlyOn(send_thread_)) { + CefPostTask(send_thread_, + base::Bind(&SendRecvTestHandler::SendMessage, this, browser)); + } else { + SendMessage(browser); + } } bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefProcessId source_process, CefRefPtr<CefProcessMessage> message) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); EXPECT_TRUE(browser.get()); EXPECT_EQ(PID_RENDERER, source_process); EXPECT_TRUE(message.get()); @@ -108,6 +119,20 @@ class SendRecvTestHandler : public TestHandler { return true; } + protected: + void DestroyTest() override { + EXPECT_TRUE(got_message_); + TestHandler::DestroyTest(); + } + + private: + void SendMessage(CefRefPtr<CefBrowser> browser) { + EXPECT_TRUE(CefCurrentlyOn(send_thread_)); + EXPECT_TRUE(browser->SendProcessMessage(PID_RENDERER, message_)); + } + + cef_thread_id_t send_thread_; + CefRefPtr<CefProcessMessage> message_; TrackCallback got_message_; @@ -116,13 +141,17 @@ class SendRecvTestHandler : public TestHandler { } // namespace -// Verify send and recieve. -TEST(ProcessMessageTest, SendRecv) { - CefRefPtr<SendRecvTestHandler> handler = new SendRecvTestHandler(); +// Verify send from the UI thread and recieve. +TEST(ProcessMessageTest, SendRecvUI) { + CefRefPtr<SendRecvTestHandler> handler = new SendRecvTestHandler(TID_UI); handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} - EXPECT_TRUE(handler->got_message_); - +// Verify send from the IO thread and recieve. +TEST(ProcessMessageTest, SendRecvIO) { + CefRefPtr<SendRecvTestHandler> handler = new SendRecvTestHandler(TID_IO); + handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); }