Support reconnect of the mojo frame channel (fixes issue #3260)

The mojo channel used for frame communication may disconnect for a variety of
reasons including frame navigation, frame destruction, or insertion into the
BackForwardCache (when the browser-side frame representation is destroyed and
closes the connection). When disconnect occurs we now evaluate the situation
and reconnect if appropriate.

Connections are now initiated solely from the renderer process and the
RenderFrame is passed as an argument to FrameAttached() instead of being
retrieved independently. Messages are queued while the frame is disconnected
and sent only after FrameAttachedAck() is received from the browser process.
The renderer process will be crashed intentionally with a "connection retry
failure" message if the reconnect fails 3 times in a row.
This commit is contained in:
Marshall Greenblatt 2022-02-10 16:52:36 -05:00
parent 0e8702df26
commit 9782362fea
12 changed files with 340 additions and 131 deletions

View File

@ -35,18 +35,20 @@ void CefBrowserFrame::RegisterBrowserInterfaceBindersForFrame(
void CefBrowserFrame::SendMessage(const std::string& name,
base::Value arguments) {
// Always associate with the newly created RFH, which may be speculative when
// Always send to the newly created RFH, which may be speculative when
// navigating cross-origin.
if (auto host = GetFrameHost(/*prefer_speculative=*/true)) {
host->SendMessage(name, std::move(arguments));
}
}
void CefBrowserFrame::FrameAttached() {
void CefBrowserFrame::FrameAttached(
mojo::PendingRemote<cef::mojom::RenderFrame> render_frame,
bool reattached) {
// Always send to the newly created RFH, which may be speculative when
// navigating cross-origin.
if (auto host = GetFrameHost(/*prefer_speculative=*/true)) {
host->FrameAttached();
host->FrameAttached(std::move(render_frame), reattached);
}
}

View File

@ -36,7 +36,8 @@ class CefBrowserFrame
private:
// cef::mojom::BrowserFrame methods:
void SendMessage(const std::string& name, base::Value arguments) override;
void FrameAttached() override;
void FrameAttached(mojo::PendingRemote<cef::mojom::RenderFrame> render_frame,
bool reattached) override;
void DidFinishFrameLoad(const GURL& validated_url,
int32_t http_status_code) override;
void UpdateDraggableRegions(

View File

@ -168,10 +168,13 @@ void CefBrowserInfo::FrameHostStateChanged(
content::RenderFrameHost::LifecycleState::kInBackForwardCache) &&
new_state == content::RenderFrameHost::LifecycleState::kActive) {
if (auto frame = GetFrameForHost(host)) {
// Should only occur for the main frame.
CHECK(frame->IsMain());
// Update the associated RFH, which may have changed.
frame->MaybeReAttach(this, host);
if (frame->IsMain()) {
{
// Update the main frame object.
NotificationStateLock lock_scope(this);
SetMainFrame(browser_, frame);

View File

@ -449,13 +449,12 @@ bool CefFrameHostImpl::Detach() {
}
// In case we never attached, clean up.
while (!queued_actions_.empty()) {
queued_actions_.pop();
while (!queued_renderer_actions_.empty()) {
queued_renderer_actions_.pop();
}
render_frame_.reset();
render_frame_host_ = nullptr;
is_attached_ = false;
return first_detach;
}
@ -464,14 +463,14 @@ void CefFrameHostImpl::MaybeReAttach(
scoped_refptr<CefBrowserInfo> browser_info,
content::RenderFrameHost* render_frame_host) {
CEF_REQUIRE_UIT();
if (is_attached_ && render_frame_host_ == render_frame_host) {
if (render_frame_.is_bound() && render_frame_host_ == render_frame_host) {
// Nothing to do here.
return;
}
// We expect that Detach() was called previously.
CHECK(!is_temporary());
CHECK(!is_attached_);
CHECK(!render_frame_.is_bound());
CHECK(!render_frame_host_);
// The RFH may change but the GlobalId should remain the same.
@ -486,8 +485,7 @@ void CefFrameHostImpl::MaybeReAttach(
render_frame_host_ = render_frame_host;
RefreshAttributes();
// Restore the RenderFrame connection.
FrameAttachedInternal(/*reattached=*/true);
// We expect a reconnect to be triggered via FrameAttached().
}
// kMainFrameId must be -1 to align with renderer expectations.
@ -517,20 +515,6 @@ CefRefPtr<CefBrowserHostBase> CefFrameHostImpl::GetBrowserHostBase() const {
return nullptr;
}
const mojo::Remote<cef::mojom::RenderFrame>&
CefFrameHostImpl::GetRenderFrame() {
CEF_REQUIRE_UIT();
DCHECK(is_attached_);
if (!render_frame_.is_bound() && render_frame_host_ &&
render_frame_host_->GetRemoteInterfaces()) {
// Connects to a CefFrameImpl that already exists in the renderer process.
render_frame_host_->GetRemoteInterfaces()->GetInterface(
render_frame_.BindNewPipeAndPassReceiver());
}
return render_frame_;
}
void CefFrameHostImpl::SendToRenderFrame(const std::string& function_name,
RenderFrameAction action) {
if (!CEF_CURRENTLY_ON_UIT()) {
@ -553,18 +537,22 @@ void CefFrameHostImpl::SendToRenderFrame(const std::string& function_name,
return;
}
if (!is_attached_) {
if (!render_frame_.is_bound()) {
// Queue actions until we're notified by the renderer that it's ready to
// handle them.
queued_actions_.push(std::make_pair(function_name, std::move(action)));
queued_renderer_actions_.push(
std::make_pair(function_name, std::move(action)));
return;
}
auto& render_frame = GetRenderFrame();
if (!render_frame)
return;
std::move(action).Run(render_frame_);
}
std::move(action).Run(render_frame);
void CefFrameHostImpl::OnRenderFrameDisconnect() {
CEF_REQUIRE_UIT();
// Reconnect, if any, will be triggered via FrameAttached().
render_frame_.reset();
}
void CefFrameHostImpl::SendMessage(const std::string& name,
@ -581,12 +569,11 @@ void CefFrameHostImpl::SendMessage(const std::string& name,
}
}
void CefFrameHostImpl::FrameAttached() {
FrameAttachedInternal(/*reattached=*/false);
}
void CefFrameHostImpl::FrameAttachedInternal(bool reattached) {
void CefFrameHostImpl::FrameAttached(
mojo::PendingRemote<cef::mojom::RenderFrame> render_frame_remote,
bool reattached) {
CEF_REQUIRE_UIT();
CHECK(render_frame_remote);
auto browser_info = GetBrowserInfo();
if (!browser_info) {
@ -594,27 +581,32 @@ void CefFrameHostImpl::FrameAttachedInternal(bool reattached) {
return;
}
DCHECK(!is_attached_);
if (!is_attached_) {
is_attached_ = true;
auto& render_frame = GetRenderFrame();
while (!queued_actions_.empty()) {
if (render_frame) {
std::move(queued_actions_.front().second).Run(render_frame);
}
queued_actions_.pop();
}
browser_info->MaybeExecuteFrameNotification(base::BindOnce(
[](CefRefPtr<CefFrameHostImpl> self, bool reattached,
CefRefPtr<CefFrameHandler> handler) {
if (auto browser = self->GetBrowserHostBase()) {
handler->OnFrameAttached(browser, self, reattached);
}
},
CefRefPtr<CefFrameHostImpl>(this), reattached));
if (reattached) {
LOG(INFO) << (is_main_frame_ ? "main" : "sub") << "frame "
<< frame_util::GetFrameDebugString(frame_id_)
<< " has reconnected";
}
render_frame_.Bind(std::move(render_frame_remote));
render_frame_.set_disconnect_handler(
base::BindOnce(&CefFrameHostImpl::OnRenderFrameDisconnect, this));
// Notify the renderer process that it can start sending messages.
render_frame_->FrameAttachedAck();
while (!queued_renderer_actions_.empty()) {
std::move(queued_renderer_actions_.front().second).Run(render_frame_);
queued_renderer_actions_.pop();
}
browser_info->MaybeExecuteFrameNotification(base::BindOnce(
[](CefRefPtr<CefFrameHostImpl> self, bool reattached,
CefRefPtr<CefFrameHandler> handler) {
if (auto browser = self->GetBrowserHostBase()) {
handler->OnFrameAttached(browser, self, reattached);
}
},
CefRefPtr<CefFrameHostImpl>(this), reattached));
}
void CefFrameHostImpl::DidFinishFrameLoad(const GURL& validated_url,

View File

@ -130,7 +130,8 @@ class CefFrameHostImpl : public CefFrame, public cef::mojom::BrowserFrame {
// cef::mojom::BrowserFrame methods forwarded from CefBrowserFrame.
void SendMessage(const std::string& name, base::Value arguments) override;
void FrameAttached() override;
void FrameAttached(mojo::PendingRemote<cef::mojom::RenderFrame> render_frame,
bool reattached) override;
void DidFinishFrameLoad(const GURL& validated_url,
int32_t http_status_code) override;
void UpdateDraggableRegions(
@ -152,17 +153,14 @@ class CefFrameHostImpl : public CefFrame, public cef::mojom::BrowserFrame {
scoped_refptr<CefBrowserInfo> GetBrowserInfo() const;
CefRefPtr<CefBrowserHostBase> GetBrowserHostBase() const;
// Returns the remote RenderFrame object.
using RenderFrameType = mojo::Remote<cef::mojom::RenderFrame>;
const RenderFrameType& GetRenderFrame();
// Send an action to the remote RenderFrame. This will queue the action if the
// remote frame is not yet attached.
using RenderFrameType = mojo::Remote<cef::mojom::RenderFrame>;
using RenderFrameAction = base::OnceCallback<void(const RenderFrameType&)>;
void SendToRenderFrame(const std::string& function_name,
RenderFrameAction action);
void FrameAttachedInternal(bool reattached);
void OnRenderFrameDisconnect();
const bool is_main_frame_;
@ -179,9 +177,8 @@ class CefFrameHostImpl : public CefFrame, public cef::mojom::BrowserFrame {
// The following members are only accessed on the UI thread.
content::RenderFrameHost* render_frame_host_ = nullptr;
bool is_attached_ = false;
std::queue<std::pair<std::string, RenderFrameAction>> queued_actions_;
std::queue<std::pair<std::string, RenderFrameAction>>
queued_renderer_actions_;
mojo::Remote<cef::mojom::RenderFrame> render_frame_;

View File

@ -19,21 +19,6 @@ struct DraggableRegionEntry {
bool draggable;
};
// Interface for communicating with a frame in the browser process.
interface BrowserFrame {
// Send a message to the browser process.
SendMessage(string name, mojo_base.mojom.ListValue arguments);
// The render frame is ready to begin handling actions.
FrameAttached();
// The render frame has finished loading.
DidFinishFrameLoad(url.mojom.Url validated_url, int32 http_status_code);
// Draggable regions have updated.
UpdateDraggableRegions(array<DraggableRegionEntry>? regions);
};
// Structure passed to LoadRequest().
struct RequestParams {
// Request method.
@ -64,6 +49,9 @@ struct RequestParams {
// Interface for communicating with a frame in the renderer process.
interface RenderFrame {
// Browser process has received the FrameAttached() message.
FrameAttachedAck();
// Send a message to the render process.
SendMessage(string name, mojo_base.mojom.ListValue arguments);
@ -91,6 +79,22 @@ interface RenderFrame {
MoveOrResizeStarted();
};
// Interface for communicating with a frame in the browser process.
interface BrowserFrame {
// Send a message to the browser process.
SendMessage(string name, mojo_base.mojom.ListValue arguments);
// The render frame is ready to begin handling actions.
FrameAttached(pending_remote<RenderFrame> render_frame,
bool reattached);
// The render frame has finished loading.
DidFinishFrameLoad(url.mojom.Url validated_url, int32 http_status_code);
// Draggable regions have updated.
UpdateDraggableRegions(array<DraggableRegionEntry>? regions);
};
struct CrossOriginWhiteListEntry {
string source_origin;
string target_protocol;

View File

@ -29,12 +29,14 @@
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/loader/frame_load_request.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/script/classic_script.h"
#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
#include "third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h"
#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
#undef LOG
@ -86,6 +88,14 @@ void GoForward(blink::WebView* view) {
}
}
bool IsInBackForwardCache(blink::WebLocalFrame* frame) {
blink::Frame* core_frame = blink::WebFrame::ToCoreFrame(*frame);
return blink::To<blink::LocalFrame>(core_frame)
->GetPage()
->GetPageScheduler()
->IsInBackForwardCache();
}
blink::WebString DumpDocumentText(blink::WebLocalFrame* frame) {
// We use the document element's text instead of the body text here because
// not all documents have a body, such as XML documents.

View File

@ -39,6 +39,8 @@ BLINK_EXPORT bool CanGoForward(blink::WebView* view);
BLINK_EXPORT void GoBack(blink::WebView* view);
BLINK_EXPORT void GoForward(blink::WebView* view);
BLINK_EXPORT bool IsInBackForwardCache(blink::WebLocalFrame* frame);
// Returns the text of the document element.
BLINK_EXPORT blink::WebString DumpDocumentText(blink::WebLocalFrame* frame);
// Returns the markup of the document element.

View File

@ -48,6 +48,19 @@
#include "third_party/blink/public/web/web_script_source.h"
#include "third_party/blink/public/web/web_view.h"
namespace {
// Maximum number of times to retry the browser connection.
constexpr size_t kConnectionRetryMaxCt = 3U;
// Length of time to wait before initiating a browser connection retry.
constexpr auto kConnectionRetryDelay = base::Seconds(1);
// Length of time to wait for the browser connection ACK before timing out.
constexpr auto kConnectionTimeout = base::Seconds(4);
} // namespace
CefFrameImpl::CefFrameImpl(CefBrowserImpl* browser,
blink::WebLocalFrame* frame,
int64_t frame_id)
@ -254,15 +267,16 @@ void CefFrameImpl::SendProcessMessage(CefProcessId target_process,
if (!message || !message->IsValid())
return;
if (!frame_)
return;
auto& browser_frame = GetBrowserFrame();
if (!browser_frame)
return;
auto impl = static_cast<CefProcessMessageImpl*>(message.get());
browser_frame->SendMessage(impl->GetName(), impl->TakeArgumentList());
SendToBrowserFrame(
__FUNCTION__,
base::BindOnce(
[](CefRefPtr<CefProcessMessage> message,
const BrowserFrameType& browser_frame) {
auto impl = static_cast<CefProcessMessageImpl*>(message.get());
browser_frame->SendMessage(impl->GetName(),
impl->TakeArgumentList());
},
message));
}
std::unique_ptr<blink::WebURLLoader> CefFrameImpl::CreateURLLoader() {
@ -298,14 +312,15 @@ CefFrameImpl::CreateResourceLoadInfoNotifierWrapper() {
return nullptr;
}
void CefFrameImpl::OnAttached(service_manager::BinderRegistry* registry) {
void CefFrameImpl::OnAttached() {
// Called indirectly from RenderFrameCreated.
registry->AddInterface(base::BindRepeating(
&CefFrameImpl::BindRenderFrameReceiver, weak_ptr_factory_.GetWeakPtr()));
ConnectBrowserFrame();
}
auto& browser_frame = GetBrowserFrame();
if (browser_frame) {
browser_frame->FrameAttached();
void CefFrameImpl::OnWasShown() {
if (browser_connection_state_ == ConnectionState::DISCONNECTED) {
// Reconnect a frame that has exited the bfcache.
ConnectBrowserFrame();
}
}
@ -317,10 +332,15 @@ void CefFrameImpl::OnDidFinishLoad() {
blink::WebDocumentLoader* dl = frame_->GetDocumentLoader();
const int http_status_code = dl->GetResponse().HttpStatusCode();
auto& browser_frame = GetBrowserFrame();
if (browser_frame) {
browser_frame->DidFinishFrameLoad(dl->GetUrl(), http_status_code);
}
SendToBrowserFrame(__FUNCTION__,
base::BindOnce(
[](const GURL& url, int http_status_code,
const BrowserFrameType& browser_frame) {
browser_frame->DidFinishFrameLoad(url,
http_status_code);
},
dl->GetUrl(), http_status_code));
CefRefPtr<CefApp> app = CefAppManager::Get()->GetApplication();
if (app) {
@ -356,22 +376,28 @@ void CefFrameImpl::OnDraggableRegionsChanged() {
}
}
auto& browser_frame = GetBrowserFrame();
if (browser_frame) {
browser_frame->UpdateDraggableRegions(
regions.empty() ? absl::nullopt
: absl::make_optional(std::move(regions)));
}
using RegionsArg =
absl::optional<std::vector<cef::mojom::DraggableRegionEntryPtr>>;
RegionsArg regions_arg =
regions.empty() ? absl::nullopt : absl::make_optional(std::move(regions));
SendToBrowserFrame(
__FUNCTION__,
base::BindOnce(
[](RegionsArg regions_arg, const BrowserFrameType& browser_frame) {
browser_frame->UpdateDraggableRegions(std::move(regions_arg));
},
std::move(regions_arg)));
}
void CefFrameImpl::OnContextCreated() {
context_created_ = true;
CHECK(frame_);
while (!queued_actions_.empty()) {
auto& action = queued_actions_.front();
while (!queued_context_actions_.empty()) {
auto& action = queued_context_actions_.front();
std::move(action.second).Run(frame_);
queued_actions_.pop();
queued_context_actions_.pop();
}
}
@ -382,21 +408,31 @@ void CefFrameImpl::OnDetached() {
// keep |this| alive until after this method returns.
CefRefPtr<CefFrameImpl> self = this;
frame_ = nullptr;
browser_->FrameDetached(frame_id_);
receivers_.Clear();
browser_frame_.reset();
OnBrowserFrameDisconnect();
browser_ = nullptr;
frame_ = nullptr;
url_loader_factory_.reset();
// In case we're destroyed without the context being created.
while (!queued_actions_.empty()) {
auto& action = queued_actions_.front();
// In case we never attached.
while (!queued_browser_actions_.empty()) {
auto& action = queued_browser_actions_.front();
LOG(WARNING) << action.first << " sent to detached frame "
<< frame_util::GetFrameDebugString(frame_id_)
<< " will be ignored";
queued_actions_.pop();
queued_browser_actions_.pop();
}
// In case we're destroyed without the context being created.
while (!queued_context_actions_.empty()) {
auto& action = queued_context_actions_.front();
LOG(WARNING) << action.first << " sent to detached frame "
<< frame_util::GetFrameDebugString(frame_id_)
<< " will be ignored";
queued_context_actions_.pop();
}
}
@ -405,7 +441,8 @@ void CefFrameImpl::ExecuteOnLocalFrame(const std::string& function_name,
CEF_REQUIRE_RT_RETURN_VOID();
if (!context_created_) {
queued_actions_.push(std::make_pair(function_name, std::move(action)));
queued_context_actions_.push(
std::make_pair(function_name, std::move(action)));
return;
}
@ -418,21 +455,140 @@ void CefFrameImpl::ExecuteOnLocalFrame(const std::string& function_name,
}
}
const mojo::Remote<cef::mojom::BrowserFrame>& CefFrameImpl::GetBrowserFrame() {
void CefFrameImpl::ConnectBrowserFrame() {
DCHECK(browser_connection_state_ == ConnectionState::DISCONNECTED ||
browser_connection_state_ == ConnectionState::RECONNECT_PENDING);
// Don't attempt to connect an invalid or bfcache'd frame. If a bfcache'd
// frame returns to active status a reconnect will be triggered via
// OnWasShown().
if (!frame_ || blink_glue::IsInBackForwardCache(frame_)) {
browser_connection_state_ = ConnectionState::DISCONNECTED;
browser_connect_timer_.Stop();
LOG(INFO) << "Connection retry canceled for frame "
<< frame_util::GetFrameDebugString(frame_id_);
return;
}
if (browser_connect_retry_ct_ > 0) {
LOG(INFO) << "Connection retry " << browser_connect_retry_ct_ << "/"
<< kConnectionRetryMaxCt << " for frame "
<< frame_util::GetFrameDebugString(frame_id_);
}
browser_connection_state_ = ConnectionState::CONNECTION_PENDING;
browser_connect_timer_.Start(FROM_HERE, kConnectionTimeout, this,
&CefFrameImpl::OnBrowserFrameTimeout);
auto& browser_frame = GetBrowserFrame(/*expect_acked=*/false);
CHECK(browser_frame);
// If the channel is working we should get a call to FrameAttachedAck().
// Otherwise, OnBrowserFrameDisconnect() should be called to retry the
// connection.
browser_frame->FrameAttached(receiver_.BindNewPipeAndPassRemote(),
browser_connect_retry_ct_ > 0);
receiver_.set_disconnect_handler(
base::BindOnce(&CefFrameImpl::OnBrowserFrameDisconnect, this));
}
const mojo::Remote<cef::mojom::BrowserFrame>& CefFrameImpl::GetBrowserFrame(
bool expect_acked) {
DCHECK_EQ(expect_acked,
browser_connection_state_ == ConnectionState::CONNECTION_ACKED);
if (!browser_frame_.is_bound()) {
auto render_frame = content::RenderFrameImpl::FromWebFrame(frame_);
if (render_frame) {
// Triggers creation of a CefBrowserFrame in the browser process.
render_frame->GetBrowserInterfaceBroker()->GetInterface(
browser_frame_.BindNewPipeAndPassReceiver());
browser_frame_.set_disconnect_handler(
base::BindOnce(&CefFrameImpl::OnBrowserFrameDisconnect, this));
}
}
return browser_frame_;
}
void CefFrameImpl::BindRenderFrameReceiver(
mojo::PendingReceiver<cef::mojom::RenderFrame> receiver) {
receivers_.Add(this, std::move(receiver));
void CefFrameImpl::OnBrowserFrameTimeout() {
LOG(ERROR) << "Connection timeout for frame "
<< frame_util::GetFrameDebugString(frame_id_);
OnBrowserFrameDisconnect();
}
void CefFrameImpl::OnBrowserFrameDisconnect() {
// Ignore multiple calls in close proximity (which may occur if both
// |browser_frame_| and |receiver_| disconnect). |frame_| will be nullptr
// when called from/after OnDetached().
if (frame_ &&
browser_connection_state_ == ConnectionState::RECONNECT_PENDING) {
return;
}
browser_frame_.reset();
receiver_.reset();
browser_connection_state_ = ConnectionState::DISCONNECTED;
browser_connect_timer_.Stop();
// Only retry if the frame is still valid.
if (frame_) {
if (browser_connect_retry_ct_++ < kConnectionRetryMaxCt) {
// Retry after a delay in case the frame is currently navigating, being
// destroyed, or entering the bfcache. In the navigation case the retry
// will likely succeed. In the destruction case the retry will be
// ignored/canceled due to OnDetached(). In the bfcache case the status
// may not be updated immediately, so we allow the reconnect timer to
// trigger and check the status in ConnectBrowserFrame() instead.
browser_connection_state_ = ConnectionState::RECONNECT_PENDING;
browser_connect_timer_.Start(FROM_HERE, kConnectionRetryDelay, this,
&CefFrameImpl::ConnectBrowserFrame);
} else {
// Trigger a crash in official builds.
LOG(FATAL) << "Connection retry failure for frame "
<< frame_util::GetFrameDebugString(frame_id_);
}
}
}
void CefFrameImpl::SendToBrowserFrame(const std::string& function_name,
BrowserFrameAction action) {
if (!frame_) {
// We've been detached.
LOG(WARNING) << function_name << " sent to detached frame "
<< frame_util::GetFrameDebugString(frame_id_)
<< " will be ignored";
return;
}
if (browser_connection_state_ != ConnectionState::CONNECTION_ACKED) {
// Queue actions until we're notified by the browser that it's ready to
// handle them.
queued_browser_actions_.push(
std::make_pair(function_name, std::move(action)));
return;
}
auto& browser_frame = GetBrowserFrame();
CHECK(browser_frame);
std::move(action).Run(browser_frame);
}
void CefFrameImpl::FrameAttachedAck() {
// Sent from the browser process in response to ConnectBrowserFrame() sending
// FrameAttached().
CHECK_EQ(ConnectionState::CONNECTION_PENDING, browser_connection_state_);
browser_connection_state_ = ConnectionState::CONNECTION_ACKED;
browser_connect_retry_ct_ = 0;
browser_connect_timer_.Stop();
auto& browser_frame = GetBrowserFrame();
CHECK(browser_frame);
while (!queued_browser_actions_.empty()) {
std::move(queued_browser_actions_.front().second).Run(browser_frame);
queued_browser_actions_.pop();
}
}
void CefFrameImpl::SendMessage(const std::string& name, base::Value arguments) {

View File

@ -6,16 +6,18 @@
#define CEF_LIBCEF_RENDERER_FRAME_IMPL_H_
#pragma once
#include <queue>
#include <string>
#include "include/cef_frame.h"
#include "include/cef_v8.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "cef/libcef/common/mojom/cef.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/service_manager/public/cpp/binder_registry.h"
namespace base {
class ListValue;
@ -84,7 +86,8 @@ class CefFrameImpl : public CefFrame, public cef::mojom::RenderFrame {
CreateResourceLoadInfoNotifierWrapper();
// Forwarded from CefRenderFrameObserver.
void OnAttached(service_manager::BinderRegistry* registry);
void OnAttached();
void OnWasShown();
void OnDidFinishLoad();
void OnDraggableRegionsChanged();
void OnContextCreated();
@ -100,13 +103,29 @@ class CefFrameImpl : public CefFrame, public cef::mojom::RenderFrame {
void ExecuteOnLocalFrame(const std::string& function_name,
LocalFrameAction action);
// Returns the remote BrowserFrame object.
const mojo::Remote<cef::mojom::BrowserFrame>& GetBrowserFrame();
// Initiate the connection to the BrowserFrame channel.
void ConnectBrowserFrame();
void BindRenderFrameReceiver(
mojo::PendingReceiver<cef::mojom::RenderFrame> receiver);
// Returns the remote BrowserFrame object.
using BrowserFrameType = mojo::Remote<cef::mojom::BrowserFrame>;
const BrowserFrameType& GetBrowserFrame(bool expect_acked = true);
// Called if the BrowserFrame connection attempt times out.
void OnBrowserFrameTimeout();
// Called if/when the BrowserFrame channel is disconnected. This may occur due
// to frame navigation, destruction, or insertion into the bfcache (when the
// browser-side frame representation is destroyed and closes the connection).
void OnBrowserFrameDisconnect();
// Send an action to the remote BrowserFrame. This will queue the action if
// the remote frame is not yet attached.
using BrowserFrameAction = base::OnceCallback<void(const BrowserFrameType&)>;
void SendToBrowserFrame(const std::string& function_name,
BrowserFrameAction action);
// cef::mojom::RenderFrame methods:
void FrameAttachedAck() override;
void SendMessage(const std::string& name, base::Value arguments) override;
void SendCommand(const std::string& command) override;
void SendCommandWithResponse(
@ -125,11 +144,27 @@ class CefFrameImpl : public CefFrame, public cef::mojom::RenderFrame {
const int64 frame_id_;
bool context_created_ = false;
std::queue<std::pair<std::string, LocalFrameAction>> queued_actions_;
std::queue<std::pair<std::string, LocalFrameAction>> queued_context_actions_;
// Number of times that browser reconnect has been attempted.
size_t browser_connect_retry_ct_ = 0;
// Current browser connection state.
enum class ConnectionState {
DISCONNECTED,
CONNECTION_PENDING,
CONNECTION_ACKED,
RECONNECT_PENDING,
} browser_connection_state_ = ConnectionState::DISCONNECTED;
base::OneShotTimer browser_connect_timer_;
std::queue<std::pair<std::string, BrowserFrameAction>>
queued_browser_actions_;
std::unique_ptr<blink::WebURLLoaderFactory> url_loader_factory_;
mojo::ReceiverSet<cef::mojom::RenderFrame> receivers_;
mojo::Receiver<cef::mojom::RenderFrame> receiver_{this};
mojo::Remote<cef::mojom::BrowserFrame> browser_frame_;

View File

@ -50,6 +50,12 @@ void CefRenderFrameObserver::DidCommitProvisionalLoad(
OnLoadStart();
}
void CefRenderFrameObserver::WasShown() {
if (frame_) {
frame_->OnWasShown();
}
}
void CefRenderFrameObserver::DidFailProvisionalLoad() {
if (frame_) {
OnLoadError();
@ -199,7 +205,7 @@ void CefRenderFrameObserver::AttachFrame(CefFrameImpl* frame) {
DCHECK(frame);
DCHECK(!frame_);
frame_ = frame;
frame_->OnAttached(&registry_);
frame_->OnAttached();
}
void CefRenderFrameObserver::OnLoadStart() {

View File

@ -27,6 +27,7 @@ class CefRenderFrameObserver : public content::RenderFrameObserver {
~CefRenderFrameObserver() override;
// RenderFrameObserver methods:
void WasShown() override;
void DidCommitProvisionalLoad(ui::PageTransition transition) override;
void DidFailProvisionalLoad() override;
void DidFinishLoad() override;