Initialize BrowserPluginGuest to attach to the View hierarchy
This CL makes sure that a BrowserPluginGuest is attached to its owner's View hierarchy on creation.
This allows the guest WebContents to navigate without needing to be attached to a container, as if it was a worker with a DOM.
This CL will also ACK the last CompositorFrameSwapped message from the guest on attachment if it hasn't been ACK already so that the guest continues to produce frames once it's attached to a new container.
BUG=434226
Review URL: https://codereview.chromium.org/769363002
Cr-Commit-Position: refs/heads/master@{#306983}
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index dba54f42..ed103b0a 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -72,7 +72,7 @@
WebContentsImpl* web_contents,
BrowserPluginGuestDelegate* delegate)
: WebContentsObserver(web_contents),
- owner_web_contents_(NULL),
+ owner_web_contents_(nullptr),
attached_(false),
browser_plugin_instance_id_(browser_plugin::kInstanceIDNone),
guest_device_scale_factor_(1.0f),
@@ -84,6 +84,7 @@
is_full_page_plugin_(false),
has_render_view_(has_render_view),
is_in_destruction_(false),
+ initialized_(false),
last_text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
last_input_mode_(ui::TEXT_INPUT_MODE_DEFAULT),
last_input_flags_(0),
@@ -99,6 +100,22 @@
base::Bind(&BrowserPluginGuest::WillDestroy, AsWeakPtr()));
}
+void BrowserPluginGuest::Init() {
+ if (initialized_)
+ return;
+ initialized_ = true;
+
+ // TODO(fsamuel): Initiailization prior to attachment should be behind a
+ // command line flag once we introduce experimental guest types that rely on
+ // this functionality.
+ if (!delegate_->CanRunInDetachedState())
+ return;
+
+ WebContentsImpl* owner_web_contents = static_cast<WebContentsImpl*>(
+ delegate_->GetOwnerWebContents());
+ InitInternal(BrowserPluginHostMsg_Attach_Params(), owner_web_contents);
+}
+
void BrowserPluginGuest::WillDestroy() {
is_in_destruction_ = true;
owner_web_contents_ = NULL;
@@ -193,26 +210,30 @@
return handled;
}
-void BrowserPluginGuest::Initialize(
- int browser_plugin_instance_id,
+void BrowserPluginGuest::InitInternal(
const BrowserPluginHostMsg_Attach_Params& params,
- WebContentsImpl* embedder_web_contents) {
- browser_plugin_instance_id_ = browser_plugin_instance_id;
+ WebContentsImpl* owner_web_contents) {
focused_ = params.focused;
+ OnSetFocus(browser_plugin::kInstanceIDNone, focused_);
+
guest_visible_ = params.visible;
+ UpdateVisibility();
+
is_full_page_plugin_ = params.is_full_page_plugin;
guest_window_rect_ = gfx::Rect(params.origin,
params.resize_guest_params.view_size);
- WebContentsViewGuest* new_view =
- static_cast<WebContentsViewGuest*>(GetWebContents()->GetView());
- if (attached())
- new_view->OnGuestDetached(owner_web_contents_->GetView());
+ if (owner_web_contents_ != owner_web_contents) {
+ WebContentsViewGuest* new_view =
+ static_cast<WebContentsViewGuest*>(GetWebContents()->GetView());
+ if (owner_web_contents_)
+ new_view->OnGuestDetached(owner_web_contents_->GetView());
- // Once a BrowserPluginGuest has an embedder WebContents, it's considered to
- // be attached.
- owner_web_contents_ = embedder_web_contents;
- new_view->OnGuestAttached(owner_web_contents_->GetView());
+ // Once a BrowserPluginGuest has an embedder WebContents, it's considered to
+ // be attached.
+ owner_web_contents_ = owner_web_contents;
+ new_view->OnGuestAttached(owner_web_contents_->GetView());
+ }
RendererPreferences* renderer_prefs =
GetWebContents()->GetMutableRendererPrefs();
@@ -238,7 +259,9 @@
embedder_visibility_observer_.reset(new EmbedderVisibilityObserver(this));
- OnResizeGuest(browser_plugin_instance_id_, params.resize_guest_params);
+ // The instance ID does not matter here. This instance ID is used by the IPC
+ // for routing, but here we're calling OnResizeGuest for initialization.
+ OnResizeGuest(browser_plugin::kInstanceIDNone, params.resize_guest_params);
// TODO(chrishtr): this code is wrong. The navigate_on_drag_drop field will
// be reset again the next time preferences are updated.
@@ -320,14 +343,15 @@
last_seen_view_size_ = view_size;
}
- FrameMsg_CompositorFrameSwapped_Params guest_params;
- frame->AssignTo(&guest_params.frame);
- guest_params.output_surface_id = output_surface_id;
- guest_params.producing_route_id = host_routing_id;
- guest_params.producing_host_id = host_process_id;
+ last_pending_frame_.reset(new FrameMsg_CompositorFrameSwapped_Params());
+ frame->AssignTo(&last_pending_frame_->frame);
+ last_pending_frame_->output_surface_id = output_surface_id;
+ last_pending_frame_->producing_route_id = host_routing_id;
+ last_pending_frame_->producing_host_id = host_process_id;
+
SendMessageToEmbedder(
new BrowserPluginMsg_CompositorFrameSwapped(
- browser_plugin_instance_id(), guest_params));
+ browser_plugin_instance_id(), *last_pending_frame_));
}
void BrowserPluginGuest::SetContentsOpaque(bool opaque) {
@@ -513,6 +537,21 @@
int browser_plugin_instance_id,
WebContentsImpl* embedder_web_contents,
const BrowserPluginHostMsg_Attach_Params& params) {
+ browser_plugin_instance_id_ = browser_plugin_instance_id;
+ // If a guest is detaching from one container and attaching to another
+ // container, then late arriving ACKs may be lost if the mapping from
+ // |browser_plugin_instance_id| to |guest_instance_id| changes. Thus we
+ // ensure that we always get new frames on attachment by ACKing the pending
+ // frame if it's still waiting on the ACK.
+ if (last_pending_frame_) {
+ cc::CompositorFrameAck ack;
+ RenderWidgetHostImpl::SendSwapCompositorFrameAck(
+ last_pending_frame_->producing_route_id,
+ last_pending_frame_->output_surface_id,
+ last_pending_frame_->producing_host_id,
+ ack);
+ last_pending_frame_.reset();
+ }
delegate_->WillAttach(embedder_web_contents, browser_plugin_instance_id,
params.is_full_page_plugin);
@@ -531,7 +570,7 @@
}
}
- Initialize(browser_plugin_instance_id, params, embedder_web_contents);
+ InitInternal(params, embedder_web_contents);
attached_ = true;
SendQueuedMessages();
@@ -561,6 +600,7 @@
params.output_surface_id,
params.producing_host_id,
params.ack);
+ last_pending_frame_.reset();
}
void BrowserPluginGuest::OnDetach(int browser_plugin_instance_id) {
diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h
index d3cd739..6e6c2761 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.h
+++ b/content/browser/browser_plugin/browser_plugin_guest.h
@@ -43,6 +43,8 @@
struct BrowserPluginHostMsg_ResizeGuest_Params;
struct FrameHostMsg_CompositorFrameSwappedACK_Params;
struct FrameHostMsg_ReclaimCompositorResources_Params;
+struct FrameMsg_CompositorFrameSwapped_Params;
+
#if defined(OS_MACOSX)
struct FrameHostMsg_ShowPopup_Params;
#endif
@@ -98,6 +100,14 @@
// Returns whether the given RenderviewHost is a BrowserPlugin guest.
static bool IsGuest(RenderViewHostImpl* render_view_host);
+ // BrowserPluginGuest::Init is called after the associated guest WebContents
+ // initializes. If this guest cannot navigate without being attached to a
+ // container, then this call is a no-op. For guest types that can be
+ // navigated, this call adds the associated RenderWdigetHostViewGuest to the
+ // view hierachy and sets up the appropriate RendererPreferences so that this
+ // guest can navigate and resize offscreen.
+ void Init();
+
// Returns a WeakPtr to this BrowserPluginGuest.
base::WeakPtr<BrowserPluginGuest> AsWeakPtr();
@@ -216,9 +226,8 @@
void WillDestroy();
- void Initialize(int browser_plugin_instance_id,
- const BrowserPluginHostMsg_Attach_Params& params,
- WebContentsImpl* embedder_web_contents);
+ void InitInternal(const BrowserPluginHostMsg_Attach_Params& params,
+ WebContentsImpl* owner_web_contents);
bool InAutoSizeBounds(const gfx::Size& size) const;
@@ -252,7 +261,8 @@
void OnLockMouseAck(int instance_id, bool succeeded);
// Resizes the guest's web contents.
void OnResizeGuest(
- int instance_id, const BrowserPluginHostMsg_ResizeGuest_Params& params);
+ int browser_plugin_instance_id,
+ const BrowserPluginHostMsg_ResizeGuest_Params& params);
void OnSetFocus(int instance_id, bool focused);
// Sets the name of the guest so that other guests in the same partition can
// access it.
@@ -358,6 +368,10 @@
bool is_in_destruction_;
+ // BrowserPluginGuest::Init can only be called once. This flag allows it to
+ // exit early if it's already been called.
+ bool initialized_;
+
// Text input type states.
ui::TextInputType last_text_input_type_;
ui::TextInputMode last_input_mode_;
@@ -368,6 +382,15 @@
// WebContents in the embedder's process.
int guest_proxy_routing_id_;
+ // Guests generate frames and send a CompositorFrameSwapped (CFS) message
+ // indicating the next frame is ready to be positioned and composited.
+ // Subsequent frames are not generated until the IPC is ACKed. We would like
+ // to ensure that the guest generates frames on attachment so we directly ACK
+ // an unACKed CFS. ACKs could get lost between the time a guest is detached
+ // from a container and the time it is attached elsewhere. This mitigates this
+ // race by ensuring the guest is ACKed on attachment.
+ scoped_ptr<FrameMsg_CompositorFrameSwapped_Params> last_pending_frame_;
+
// This is a queue of messages that are destined to be sent to the embedder
// once the guest is attached to a particular embedder.
std::deque<linked_ptr<IPC::Message> > pending_messages_;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 64679cd..9614814 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1196,6 +1196,11 @@
#if defined(OS_ANDROID)
date_time_chooser_.reset(new DateTimeChooserAndroid());
#endif
+
+ // BrowserPluginGuest::Init needs to be called after this WebContents has
+ // a RenderWidgetHostViewGuest. That is, |view_->CreateView| above.
+ if (browser_plugin_guest_)
+ browser_plugin_guest_->Init();
}
void WebContentsImpl::OnWebContentsDestroyed(WebContentsImpl* web_contents) {