summaryrefslogtreecommitdiffstats
path: root/content/browser/browser_plugin
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/browser_plugin')
-rw-r--r--content/browser/browser_plugin/browser_plugin_embedder.cc231
-rw-r--r--content/browser/browser_plugin/browser_plugin_embedder.h144
-rw-r--r--content/browser/browser_plugin/browser_plugin_embedder_helper.cc151
-rw-r--r--content/browser/browser_plugin/browser_plugin_embedder_helper.h72
-rw-r--r--content/browser/browser_plugin/browser_plugin_guest.cc280
-rw-r--r--content/browser/browser_plugin/browser_plugin_guest.h174
-rw-r--r--content/browser/browser_plugin/browser_plugin_guest_helper.cc62
-rw-r--r--content/browser/browser_plugin/browser_plugin_guest_helper.h61
-rw-r--r--content/browser/browser_plugin/browser_plugin_host_browsertest.cc490
-rw-r--r--content/browser/browser_plugin/browser_plugin_host_factory.h40
-rw-r--r--content/browser/browser_plugin/test_browser_plugin_embedder.cc42
-rw-r--r--content/browser/browser_plugin/test_browser_plugin_embedder.h49
-rw-r--r--content/browser/browser_plugin/test_browser_plugin_guest.cc171
-rw-r--r--content/browser/browser_plugin/test_browser_plugin_guest.h90
14 files changed, 2057 insertions, 0 deletions
diff --git a/content/browser/browser_plugin/browser_plugin_embedder.cc b/content/browser/browser_plugin/browser_plugin_embedder.cc
new file mode 100644
index 0000000..fef57e6
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_embedder.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/browser_plugin/browser_plugin_embedder.h"
+
+#include <set>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/time.h"
+#include "content/browser/browser_plugin/browser_plugin_embedder_helper.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/web_contents_view.h"
+#include "content/public/common/url_constants.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
+#include "ui/gfx/size.h"
+#include "ui/surface/transport_dib.h"
+
+namespace content {
+
+// static
+BrowserPluginHostFactory* BrowserPluginEmbedder::factory_ = NULL;
+
+BrowserPluginEmbedder::BrowserPluginEmbedder(
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host)
+ : WebContentsObserver(web_contents),
+ render_view_host_(render_view_host) {
+ // Listen to visibility changes so that an embedder hides its guests
+ // as well.
+ registrar_.Add(this,
+ NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED,
+ Source<WebContents>(web_contents));
+
+ // |render_view_host| manages the ownership of this BrowserPluginGuestHelper.
+ new BrowserPluginEmbedderHelper(this, render_view_host);
+}
+
+BrowserPluginEmbedder::~BrowserPluginEmbedder() {
+ // Destroy guests that are managed by the current embedder.
+ DestroyGuests();
+}
+
+// static
+BrowserPluginEmbedder* BrowserPluginEmbedder::Create(
+ WebContentsImpl* web_contents,
+ content::RenderViewHost* render_view_host) {
+ if (factory_) {
+ return factory_->CreateBrowserPluginEmbedder(web_contents,
+ render_view_host);
+ }
+ return new BrowserPluginEmbedder(web_contents, render_view_host);
+}
+
+BrowserPluginGuest* BrowserPluginEmbedder::GetGuestByInstanceID(
+ int instance_id) const {
+ ContainerInstanceMap::const_iterator it =
+ guest_web_contents_by_instance_id_.find(instance_id);
+ if (it != guest_web_contents_by_instance_id_.end())
+ return static_cast<WebContentsImpl*>(it->second)->GetBrowserPluginGuest();
+ return NULL;
+}
+
+void BrowserPluginEmbedder::AddGuest(int instance_id,
+ WebContents* guest_web_contents,
+ int64 frame_id) {
+ DCHECK(guest_web_contents_by_instance_id_.find(instance_id) ==
+ guest_web_contents_by_instance_id_.end());
+ guest_web_contents_by_instance_id_[instance_id] = guest_web_contents;
+}
+
+void BrowserPluginEmbedder::NavigateGuest(RenderViewHost* render_view_host,
+ int instance_id,
+ int64 frame_id,
+ const std::string& src,
+ const gfx::Size& size) {
+ BrowserPluginGuest* guest = GetGuestByInstanceID(instance_id);
+ WebContentsImpl* guest_web_contents = NULL;
+ GURL url(src);
+ if (!guest) {
+ const std::string& host =
+ render_view_host->GetSiteInstance()->GetSite().host();
+ guest_web_contents = WebContentsImpl::CreateGuest(
+ web_contents()->GetBrowserContext(),
+ host,
+ instance_id);
+
+ guest = guest_web_contents->GetBrowserPluginGuest();
+ guest->set_embedder_render_process_host(
+ render_view_host->GetProcess());
+
+ guest_web_contents->GetMutableRendererPrefs()->
+ throttle_input_events = false;
+ AddGuest(instance_id, guest_web_contents, frame_id);
+ guest_web_contents->SetDelegate(guest);
+ } else {
+ guest_web_contents = static_cast<WebContentsImpl*>(guest->web_contents());
+ }
+
+ // We ignore loading empty urls in web_contents.
+ // If a guest sets empty src attribute after it has navigated to some
+ // non-empty page, the action is considered no-op.
+ // TODO(lazyboy): The js shim for browser-plugin might need to reflect empty
+ // src ignoring in the shadow DOM element: http://crbug.com/149001.
+ if (!src.empty()) {
+ guest_web_contents->GetController().LoadURL(url,
+ Referrer(),
+ PAGE_TRANSITION_AUTO_SUBFRAME,
+ std::string());
+ }
+
+ if (!size.IsEmpty())
+ guest_web_contents->GetView()->SizeContents(size);
+}
+
+void BrowserPluginEmbedder::UpdateRectACK(int instance_id,
+ int message_id,
+ const gfx::Size& size) {
+ BrowserPluginGuest* guest = GetGuestByInstanceID(instance_id);
+ if (guest)
+ guest->UpdateRectACK(message_id, size);
+}
+
+void BrowserPluginEmbedder::ResizeGuest(int instance_id,
+ TransportDIB* damage_buffer,
+#if defined(OS_WIN)
+ int damage_buffer_size,
+#endif
+ int width,
+ int height,
+ bool resize_pending,
+ float scale_factor) {
+ BrowserPluginGuest* guest = GetGuestByInstanceID(instance_id);
+ if (!guest)
+ return;
+ WebContentsImpl* guest_web_contents =
+ static_cast<WebContentsImpl*>(guest->web_contents());
+ guest->SetDamageBuffer(damage_buffer,
+#if defined(OS_WIN)
+ damage_buffer_size,
+#endif
+ gfx::Size(width, height),
+ scale_factor);
+ if (!resize_pending)
+ guest_web_contents->GetView()->SizeContents(gfx::Size(width, height));
+}
+
+void BrowserPluginEmbedder::SetFocus(int instance_id,
+ bool focused) {
+ BrowserPluginGuest* guest = GetGuestByInstanceID(instance_id);
+ if (guest)
+ guest->SetFocus(focused);
+}
+
+void BrowserPluginEmbedder::DestroyGuests() {
+ STLDeleteContainerPairSecondPointers(
+ guest_web_contents_by_instance_id_.begin(),
+ guest_web_contents_by_instance_id_.end());
+ guest_web_contents_by_instance_id_.clear();
+}
+
+void BrowserPluginEmbedder::HandleInputEvent(int instance_id,
+ RenderViewHost* render_view_host,
+ const gfx::Rect& guest_rect,
+ const WebKit::WebInputEvent& event,
+ IPC::Message* reply_message) {
+ BrowserPluginGuest* guest = GetGuestByInstanceID(instance_id);
+ if (guest)
+ guest->HandleInputEvent(render_view_host, guest_rect, event, reply_message);
+}
+
+void BrowserPluginEmbedder::DestroyGuestByInstanceID(int instance_id) {
+ BrowserPluginGuest* guest = GetGuestByInstanceID(instance_id);
+ if (guest) {
+ WebContents* guest_web_contents = guest->web_contents();
+
+ // Destroy the guest's web_contents.
+ delete guest_web_contents;
+ guest_web_contents_by_instance_id_.erase(instance_id);
+ }
+}
+
+void BrowserPluginEmbedder::RenderViewDeleted(
+ RenderViewHost* render_view_host) {
+ DestroyGuests();
+}
+
+void BrowserPluginEmbedder::RenderViewGone(base::TerminationStatus status) {
+ DestroyGuests();
+}
+
+void BrowserPluginEmbedder::WebContentsVisibilityChanged(bool visible) {
+ // If the embedder is hidden we need to hide the guests as well.
+ for (ContainerInstanceMap::const_iterator it =
+ guest_web_contents_by_instance_id_.begin();
+ it != guest_web_contents_by_instance_id_.end(); ++it) {
+ WebContents* web_contents = it->second;
+ if (visible)
+ web_contents->WasShown();
+ else
+ web_contents->WasHidden();
+ }
+}
+
+void BrowserPluginEmbedder::PluginDestroyed(int instance_id) {
+ DestroyGuestByInstanceID(instance_id);
+}
+
+void BrowserPluginEmbedder::Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED: {
+ bool visible = *Details<bool>(details).ptr();
+ WebContentsVisibilityChanged(visible);
+ break;
+ }
+ default:
+ NOTREACHED() << "Unexpected notification type: " << type;
+ }
+}
+
+} // namespace content
diff --git a/content/browser/browser_plugin/browser_plugin_embedder.h b/content/browser/browser_plugin/browser_plugin_embedder.h
new file mode 100644
index 0000000..2a92f94
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_embedder.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A BrowserPluginEmbedder has a list of guests it manages.
+// In the beginning when a renderer sees one or more guests (BrowserPlugin
+// instance(s)) and there is a request to navigate to them, the WebContents for
+// that renderer creates a BrowserPluginEmbedder for itself. The
+// BrowserPluginEmbedder, in turn, manages a set of BrowserPluginGuests -- one
+// BrowserPluginGuest for each guest in the embedding WebContents. Note that
+// each of these BrowserPluginGuest objects has its own WebContents.
+// BrowserPluginEmbedder routes any messages directed to a guest from the
+// renderer (BrowserPlugin) to the appropriate guest (identified by the guest's
+// |instance_id|).
+//
+// BrowserPluginEmbedder is responsible for cleaning up the guests when the
+// embedder frame navigates away to a different page or deletes the guests from
+// the existing page.
+
+#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_H_
+
+#include <map>
+
+#include "base/compiler_specific.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents_observer.h"
+
+class TransportDIB;
+class WebContentsImpl;
+
+namespace WebKit {
+class WebInputEvent;
+}
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+namespace content {
+
+class BrowserPluginGuest;
+class BrowserPluginHostFactory;
+
+// A browser plugin embedder provides functionality for WebContents to operate
+// in the 'embedder' role. It manages list of guests inside the embedder.
+//
+// The embedder's WebContents manages the lifetime of the embedder. They are
+// created when a renderer asks WebContents to navigate (for the first time) to
+// some guest. It gets destroyed when either the WebContents goes away or there
+// is a RenderViewHost swap in WebContents.
+class CONTENT_EXPORT BrowserPluginEmbedder : public WebContentsObserver,
+ public NotificationObserver {
+ public:
+ typedef std::map<int, WebContents*> ContainerInstanceMap;
+
+ virtual ~BrowserPluginEmbedder();
+
+ static BrowserPluginEmbedder* Create(WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host);
+
+ // Navigates in a guest (new or existing).
+ void NavigateGuest(RenderViewHost* render_view_host,
+ int instance_id,
+ int64 frame_id,
+ const std::string& src,
+ const gfx::Size& size);
+
+ // WebContentsObserver implementation.
+ virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE;
+ virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE;
+
+ // NotificationObserver method override.
+ virtual void Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) OVERRIDE;
+
+ // Overrides factory for testing. Default (NULL) value indicates regular
+ // (non-test) environment.
+ static void set_factory_for_testing(BrowserPluginHostFactory* factory) {
+ factory_ = factory;
+ }
+
+ private:
+ friend class BrowserPluginEmbedderHelper;
+ friend class TestBrowserPluginEmbedder;
+
+ BrowserPluginEmbedder(WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host);
+
+ // Returns a guest browser plugin delegate by its container ID specified
+ // in BrowserPlugin.
+ BrowserPluginGuest* GetGuestByInstanceID(int instance_id) const;
+ // Adds a new guest web_contents to the embedder (overridable in test).
+ virtual void AddGuest(int instance_id,
+ WebContents* guest_web_contents,
+ int64 frame_id);
+ void DestroyGuestByInstanceID(int instance_id);
+ void DestroyGuests();
+
+ // Message handlers (direct/indirect via BrowserPluginEmbedderHelper).
+ // Routes update rect ack message to the appropriate guest.
+ void UpdateRectACK(int instance_id, int message_id, const gfx::Size& size);
+ void SetFocus(int instance_id, bool focused);
+ void ResizeGuest(int instance_id,
+ TransportDIB* damage_buffer,
+#if defined(OS_WIN)
+ int damage_buffer_size,
+#endif
+ int width,
+ int height,
+ bool resize_pending,
+ float scale_factor);
+ // Handles input events sent from the BrowserPlugin (embedder's renderer
+ // process) by passing them to appropriate guest's input handler.
+ void HandleInputEvent(int instance_id,
+ RenderViewHost* render_view_host,
+ const gfx::Rect& guest_rect,
+ const WebKit::WebInputEvent& event,
+ IPC::Message* reply_message);
+ void PluginDestroyed(int instance_id);
+
+ // Called when visiblity of web_contents changes, so the embedder will
+ // show/hide its guest.
+ void WebContentsVisibilityChanged(bool visible);
+
+ // Static factory instance (always NULL for non-test).
+ static BrowserPluginHostFactory* factory_;
+
+ // A scoped container for notification registries.
+ NotificationRegistrar registrar_;
+
+ // Contains guests' WebContents, mapping from their instance ids.
+ ContainerInstanceMap guest_web_contents_by_instance_id_;
+ RenderViewHost* render_view_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginEmbedder);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_H_
diff --git a/content/browser/browser_plugin/browser_plugin_embedder_helper.cc b/content/browser/browser_plugin/browser_plugin_embedder_helper.cc
new file mode 100644
index 0000000..2bc9cdf
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_embedder_helper.cc
@@ -0,0 +1,151 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/browser_plugin/browser_plugin_embedder_helper.h"
+
+#include "content/browser/browser_plugin/browser_plugin_embedder.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/common/browser_plugin_messages.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "ui/gfx/size.h"
+
+namespace content {
+
+BrowserPluginEmbedderHelper::BrowserPluginEmbedderHelper(
+ BrowserPluginEmbedder* embedder,
+ RenderViewHost* render_view_host)
+ : RenderViewHostObserver(render_view_host),
+ embedder_(embedder) {
+}
+
+BrowserPluginEmbedderHelper::~BrowserPluginEmbedderHelper() {
+}
+
+bool BrowserPluginEmbedderHelper::Send(IPC::Message* message) {
+ return RenderViewHostObserver::Send(message);
+}
+
+bool BrowserPluginEmbedderHelper::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(BrowserPluginEmbedderHelper, message)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_NavigateGuest,
+ OnNavigateGuest);
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ResizeGuest, OnResizeGuest)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UpdateRect_ACK, OnUpdateRectACK);
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetFocus, OnSetFocus);
+ IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginHostMsg_HandleInputEvent,
+ OnHandleInputEvent(*static_cast<const IPC::SyncMessage*>(&message),
+ &handled))
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginDestroyed,
+ OnPluginDestroyed);
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void BrowserPluginEmbedderHelper::OnResizeGuest(
+ int instance_id,
+ const BrowserPluginHostMsg_ResizeGuest_Params& params) {
+ TransportDIB* damage_buffer = NULL;
+#if defined(OS_WIN)
+ // On Windows we need to duplicate the handle from the remote process.
+ HANDLE section;
+ DuplicateHandle(render_view_host()->GetProcess()->GetHandle(),
+ params.damage_buffer_id.handle,
+ GetCurrentProcess(),
+ &section,
+ STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ | FILE_MAP_WRITE,
+ FALSE, 0);
+ damage_buffer = TransportDIB::Map(section);
+#elif defined(OS_MACOSX)
+ // On OSX, the browser allocates all DIBs and keeps a file descriptor around
+ // for each.
+ damage_buffer = render_view_host()->GetProcess()->
+ GetTransportDIB(params.damage_buffer_id);
+#elif defined(OS_ANDROID)
+ damage_buffer = TransportDIB::Map(params.damage_buffer_id);
+#elif defined(OS_POSIX)
+ damage_buffer = TransportDIB::Map(params.damage_buffer_id.shmkey);
+#endif // defined(OS_POSIX)
+ DCHECK(damage_buffer);
+ // TODO(fsamuel): Schedule this later so that we don't stall the embedder for
+ // too long.
+ embedder_->ResizeGuest(instance_id,
+ damage_buffer,
+#if defined(OS_WIN)
+ params.damage_buffer_size,
+#endif
+ params.width,
+ params.height,
+ params.resize_pending,
+ params.scale_factor);
+}
+
+void BrowserPluginEmbedderHelper::OnHandleInputEvent(
+ const IPC::SyncMessage& message,
+ bool* handled) {
+ *handled = true;
+ PickleIterator iter(message);
+
+ // TODO(fsamuel): This appears to be a monotonically increasing value.
+ int instance_id = -1;
+ const char* guest_rect_data = NULL;
+ int guest_rect_data_length = -1;
+ const char* input_event_data = NULL;
+ int input_event_data_length = -1;
+ if (!iter.SkipBytes(4) ||
+ !message.ReadInt(&iter, &instance_id) ||
+ !message.ReadData(&iter, &guest_rect_data, &guest_rect_data_length) ||
+ !message.ReadData(&iter, &input_event_data, &input_event_data_length)) {
+ *handled = false;
+ return;
+ }
+ const gfx::Rect* guest_rect =
+ reinterpret_cast<const gfx::Rect*>(guest_rect_data);
+ const WebKit::WebInputEvent* input_event =
+ reinterpret_cast<const WebKit::WebInputEvent*>(input_event_data);
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ render_view_host());
+
+ // Convert the window coordinates into screen coordinates.
+ gfx::Rect guest_screen_rect(*guest_rect);
+ if (rvh->GetView())
+ guest_screen_rect.Offset(rvh->GetView()->GetViewBounds().origin());
+
+ IPC::Message* reply_message =
+ IPC::SyncMessage::GenerateReply(&message);
+ embedder_->HandleInputEvent(instance_id,
+ rvh,
+ guest_screen_rect,
+ *input_event,
+ reply_message);
+}
+
+void BrowserPluginEmbedderHelper::OnNavigateGuest(int instance_id,
+ int64 frame_id,
+ const std::string& src,
+ const gfx::Size& size) {
+ embedder_->NavigateGuest(render_view_host(), instance_id, frame_id, src,
+ size);
+}
+
+void BrowserPluginEmbedderHelper::OnUpdateRectACK(int instance_id,
+ int message_id,
+ const gfx::Size& size) {
+ embedder_->UpdateRectACK(instance_id, message_id, size);
+}
+
+void BrowserPluginEmbedderHelper::OnSetFocus(int instance_id, bool focused) {
+ embedder_->SetFocus(instance_id, focused);
+}
+
+void BrowserPluginEmbedderHelper::OnPluginDestroyed(int instance_id) {
+ embedder_->PluginDestroyed(instance_id);
+}
+
+} // namespace content
diff --git a/content/browser/browser_plugin/browser_plugin_embedder_helper.h b/content/browser/browser_plugin/browser_plugin_embedder_helper.h
new file mode 100644
index 0000000..50e25e31
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_embedder_helper.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_HELPER_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_HELPER_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "content/public/browser/render_view_host_observer.h"
+
+namespace IPC {
+class Message;
+class SyncMessage;
+}
+
+namespace gfx {
+class Size;
+}
+
+struct BrowserPluginHostMsg_ResizeGuest_Params;
+
+namespace content {
+
+class BrowserPluginEmbedder;
+class RenderViewHost;
+
+// Helper for browser plugin embedder.
+//
+// A lot of messages coming from guests need to know the guest's RenderViewHost.
+// BrowserPluginEmbedderHelper handles BrowserPluginHostMsg messages and
+// relays them with their associated RenderViewHosts to its delegate where they
+// will be handled.
+//
+// A BrowserPluginEmbedderHelper is created whenever a BrowserPluginEmbedder is
+// created. BrowserPluginEmbedder's lifetime is managed by the associated
+// RenderViewHost. Functions in this class is assumed to be run on UI thread.
+class BrowserPluginEmbedderHelper : public RenderViewHostObserver {
+ public:
+ BrowserPluginEmbedderHelper(BrowserPluginEmbedder* embedder,
+ RenderViewHost* render_view_host);
+ virtual ~BrowserPluginEmbedderHelper();
+
+ // Make it public for sync IPCs.
+ virtual bool Send(IPC::Message* message) OVERRIDE;
+
+ protected:
+ // RenderViewHostObserver implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ private:
+ // Message handlers.
+ void OnNavigateGuest(int instance_id,
+ int64 frame_id,
+ const std::string& src,
+ const gfx::Size& size);
+ void OnResizeGuest(int instance_id,
+ const BrowserPluginHostMsg_ResizeGuest_Params& params);
+ void OnUpdateRectACK(int instance_id, int message_id, const gfx::Size& size);
+ void OnHandleInputEvent(const IPC::SyncMessage& message, bool* handled);
+ void OnSetFocus(int instance_id, bool focused);
+ void OnPluginDestroyed(int instance_id);
+
+ BrowserPluginEmbedder* embedder_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginEmbedderHelper);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_HELPER_H_
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
new file mode 100644
index 0000000..374deec
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -0,0 +1,280 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+
+#include <algorithm>
+
+#include "content/browser/browser_plugin/browser_plugin_guest_helper.h"
+#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/browser_plugin_messages.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/common/result_codes.h"
+#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
+#include "ui/surface/transport_dib.h"
+
+namespace content {
+
+// static
+BrowserPluginHostFactory* BrowserPluginGuest::factory_ = NULL;
+
+namespace {
+const int kGuestHangTimeoutMs = 5000;
+}
+
+BrowserPluginGuest::BrowserPluginGuest(int instance_id,
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host)
+ : WebContentsObserver(web_contents),
+ embedder_render_process_host_(NULL),
+ instance_id_(instance_id),
+#if defined(OS_WIN)
+ damage_buffer_size_(0),
+#endif
+ pending_update_counter_(0),
+ guest_hang_timeout_(
+ base::TimeDelta::FromMilliseconds(kGuestHangTimeoutMs)) {
+ DCHECK(web_contents);
+ // |render_view_host| manages the ownership of this BrowserPluginGuestHelper.
+ new BrowserPluginGuestHelper(this, render_view_host);
+}
+
+BrowserPluginGuest::~BrowserPluginGuest() {
+}
+
+// static
+BrowserPluginGuest* BrowserPluginGuest::Create(
+ int instance_id,
+ WebContentsImpl* web_contents,
+ content::RenderViewHost* render_view_host) {
+ if (factory_) {
+ return factory_->CreateBrowserPluginGuest(instance_id,
+ web_contents,
+ render_view_host);
+ }
+ return new BrowserPluginGuest(instance_id, web_contents, render_view_host);
+}
+
+bool BrowserPluginGuest::ViewTakeFocus(bool reverse) {
+ SendMessageToEmbedder(
+ new BrowserPluginMsg_AdvanceFocus(instance_id(), reverse));
+ return true;
+}
+
+void BrowserPluginGuest::RendererUnresponsive(WebContents* source) {
+ base::ProcessHandle process_handle =
+ web_contents()->GetRenderProcessHost()->GetHandle();
+ base::KillProcess(process_handle, RESULT_CODE_HUNG, false);
+}
+
+void BrowserPluginGuest::SetDamageBuffer(
+ TransportDIB* damage_buffer,
+#if defined(OS_WIN)
+ int damage_buffer_size,
+#endif
+ const gfx::Size& damage_view_size,
+ float scale_factor) {
+ // Sanity check: Verify that we've correctly shared the damage buffer memory
+ // between the embedder and browser processes.
+ DCHECK(*static_cast<unsigned int*>(damage_buffer->memory()) == 0xdeadbeef);
+ damage_buffer_.reset(damage_buffer);
+#if defined(OS_WIN)
+ damage_buffer_size_ = damage_buffer_size;
+#endif
+ damage_view_size_ = damage_view_size;
+ damage_buffer_scale_factor_ = scale_factor;
+}
+
+void BrowserPluginGuest::UpdateRect(
+ RenderViewHost* render_view_host,
+ const ViewHostMsg_UpdateRect_Params& params) {
+ RenderWidgetHostImpl* render_widget_host =
+ RenderWidgetHostImpl::From(render_view_host);
+ render_widget_host->ResetSizeAndRepaintPendingFlags();
+ // This handler is only of interest to us for the 2D software rendering path.
+ // needs_ack should always be true for the 2D path.
+ // TODO(fsamuel): Do we need to do something different in the 3D case?
+ if (!params.needs_ack)
+ return;
+
+ // Only copy damage if the guest's view size is equal to the damage buffer's
+ // size and the guest's scale factor is equal to the damage buffer's scale
+ // factor.
+ // The scaling change can happen due to asynchronous updates of the DPI on a
+ // resolution change.
+ if (params.view_size.width() == damage_view_size().width() &&
+ params.view_size.height() == damage_view_size().height() &&
+ params.scale_factor == damage_buffer_scale_factor()) {
+ TransportDIB* dib = render_view_host->GetProcess()->
+ GetTransportDIB(params.bitmap);
+ if (dib) {
+#if defined(OS_WIN)
+ size_t guest_damage_buffer_size = params.bitmap_rect.width() *
+ params.bitmap_rect.height() * 4;
+ size_t embedder_damage_buffer_size = damage_buffer_size_;
+#else
+ size_t guest_damage_buffer_size = dib->size();
+ size_t embedder_damage_buffer_size = damage_buffer_->size();
+#endif
+ void* guest_memory = dib->memory();
+ void* embedder_memory = damage_buffer_->memory();
+ size_t size = std::min(guest_damage_buffer_size,
+ embedder_damage_buffer_size);
+ memcpy(embedder_memory, guest_memory, size);
+ }
+ }
+ DCHECK(embedder_render_process_host());
+ BrowserPluginMsg_UpdateRect_Params relay_params;
+ relay_params.bitmap_rect = params.bitmap_rect;
+ relay_params.dx = params.dx;
+ relay_params.dy = params.dy;
+ relay_params.scroll_rect = params.scroll_rect;
+ relay_params.copy_rects = params.copy_rects;
+ relay_params.view_size = params.view_size;
+ relay_params.scale_factor = params.scale_factor;
+ relay_params.is_resize_ack = ViewHostMsg_UpdateRect_Flags::is_resize_ack(
+ params.flags);
+
+ // We need to send the ACK to the same render_view_host that issued
+ // the UpdateRect. We keep track of this correspondence via a message_id.
+ int message_id = pending_update_counter_++;
+ pending_updates_.AddWithID(render_view_host, message_id);
+
+ gfx::Size param_size = gfx::Size(params.view_size.width(),
+ params.view_size.height());
+
+ SendMessageToEmbedder(new BrowserPluginMsg_UpdateRect(instance_id(),
+ message_id,
+ relay_params));
+}
+
+void BrowserPluginGuest::UpdateRectACK(int message_id, const gfx::Size& size) {
+ RenderViewHost* render_view_host = pending_updates_.Lookup(message_id);
+ // If the guest has crashed since it sent the initial ViewHostMsg_UpdateRect
+ // then the pending_updates_ map will have been cleared.
+ if (!render_view_host)
+ return;
+ pending_updates_.Remove(message_id);
+ render_view_host->Send(
+ new ViewMsg_UpdateRect_ACK(render_view_host->GetRoutingID()));
+ if (!size.IsEmpty())
+ render_view_host->GetView()->SetSize(size);
+}
+
+void BrowserPluginGuest::HandleInputEvent(RenderViewHost* render_view_host,
+ const gfx::Rect& guest_rect,
+ const WebKit::WebInputEvent& event,
+ IPC::Message* reply_message) {
+ DCHECK(!pending_input_event_reply_.get());
+ guest_rect_ = guest_rect;
+ RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
+ web_contents()->GetRenderViewHost());
+ IPC::Message* message = new ViewMsg_HandleInputEvent(
+ guest_rvh->GetRoutingID());
+
+ // Copy the WebInputEvent and modify the event type. The guest expects
+ // WebInputEvent::RawKeyDowns and not KeyDowns.
+ scoped_array<char> input_buffer(new char[event.size]);
+ memcpy(input_buffer.get(), &event, event.size);
+ WebKit::WebInputEvent* input_event =
+ reinterpret_cast<WebKit::WebInputEvent*>(input_buffer.get());
+ if (event.type == WebKit::WebInputEvent::KeyDown)
+ input_event->type = WebKit::WebInputEvent::RawKeyDown;
+
+ message->WriteData(input_buffer.get(), event.size);
+ // TODO(fsamuel): What do we need to do here? This is for keyboard shortcuts.
+ if (input_event->type == WebKit::WebInputEvent::RawKeyDown)
+ message->WriteBool(false);
+ bool sent = guest_rvh->Send(message);
+ if (!sent) {
+ // If the embedder is waiting for a previous input ack, a new input message
+ // won't get sent to the guest. Reply immediately with handled = false so
+ // embedder doesn't hang.
+ BrowserPluginHostMsg_HandleInputEvent::WriteReplyParams(
+ reply_message, false /* handled */, cursor_);
+ embedder_render_process_host()->Send(reply_message);
+ return;
+ }
+
+ pending_input_event_reply_.reset(reply_message);
+ // Input events are handled synchronously, meaning it blocks the embedder. We
+ // set a hang monitor here that will kill the guest process (5s timeout) if we
+ // don't receive an ack. This will kill all the guests that are running in the
+ // same process (undesired behavior).
+ // TODO(fsamuel,lazyboy): Find a way to get rid of guest process kill
+ // behavior. http://crbug.com/147272.
+ guest_rvh->StartHangMonitorTimeout(guest_hang_timeout_);
+}
+
+void BrowserPluginGuest::HandleInputEventAck(RenderViewHost* render_view_host,
+ bool handled) {
+ RenderViewHostImpl* guest_rvh =
+ static_cast<RenderViewHostImpl*>(render_view_host);
+ guest_rvh->StopHangMonitorTimeout();
+ DCHECK(pending_input_event_reply_.get());
+ IPC::Message* reply_message = pending_input_event_reply_.release();
+ BrowserPluginHostMsg_HandleInputEvent::WriteReplyParams(reply_message,
+ handled,
+ cursor_);
+ SendMessageToEmbedder(reply_message);
+}
+
+void BrowserPluginGuest::SetFocus(bool focused) {
+ RenderViewHost* render_view_host = web_contents()->GetRenderViewHost();
+ render_view_host->Send(
+ new ViewMsg_SetFocus(render_view_host->GetRoutingID(), focused));
+}
+
+void BrowserPluginGuest::ShowWidget(RenderViewHost* render_view_host,
+ int route_id,
+ const gfx::Rect& initial_pos) {
+ gfx::Rect screen_pos(initial_pos);
+ screen_pos.Offset(guest_rect_.origin());
+ static_cast<WebContentsImpl*>(web_contents())->ShowCreatedWidget(route_id,
+ screen_pos);
+}
+
+void BrowserPluginGuest::SetCursor(const WebCursor& cursor) {
+ cursor_ = cursor;
+}
+
+void BrowserPluginGuest::DidCommitProvisionalLoadForFrame(
+ int64 frame_id,
+ bool is_main_frame,
+ const GURL& url,
+ PageTransition transition_type,
+ RenderViewHost* render_view_host) {
+ // Inform its embedder of the updated URL.
+ DCHECK(embedder_render_process_host());
+ if (is_main_frame)
+ SendMessageToEmbedder(new BrowserPluginMsg_DidNavigate(instance_id(), url));
+}
+
+void BrowserPluginGuest::RenderViewGone(base::TerminationStatus status) {
+ DCHECK(embedder_render_process_host());
+ if (pending_input_event_reply_.get()) {
+ IPC::Message* reply_message = pending_input_event_reply_.release();
+ BrowserPluginHostMsg_HandleInputEvent::WriteReplyParams(reply_message,
+ false,
+ cursor_);
+ SendMessageToEmbedder(reply_message);
+ }
+ SendMessageToEmbedder(new BrowserPluginMsg_GuestCrashed(instance_id()));
+ IDMap<RenderViewHost>::const_iterator iter(&pending_updates_);
+ while (!iter.IsAtEnd()) {
+ pending_updates_.Remove(iter.GetCurrentKey());
+ iter.Advance();
+ }
+}
+
+void BrowserPluginGuest::SendMessageToEmbedder(IPC::Message* msg) {
+ embedder_render_process_host()->Send(msg);
+}
+
+} // namespace content
diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h
new file mode 100644
index 0000000..27d438b
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_guest.h
@@ -0,0 +1,174 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A BrowserPluginGuest represents the browser side of browser <--> renderer
+// communication. A BrowserPlugin (a WebPlugin) is on the renderer side of
+// browser <--> guest renderer communication. The 'guest' renderer is a
+// <browser> tag.
+//
+// BrowserPluginGuest lives on the UI thread of the browser process. It has a
+// helper, BrowserPluginGuestHelper, which is a RenderViewHostObserver. The
+// helper object receives messages (ViewHostMsg_*) directed at the browser
+// plugin and redirects them to this class. Any messages the embedder might be
+// interested in knowing or modifying about the guest should be listened for
+// here.
+//
+// Since BrowserPlugin is a WebPlugin, we need to provide overridden behaviors
+// for messages like handleInputEvent, updateGeometry. Such messages get
+// routed into BrowserPluginGuest via its embedder (BrowserPluginEmbedder).
+// These are BrowserPluginHost_* messages sent from the BrowserPlugin.
+//
+// BrowserPluginGuest knows about its embedder process. Communication to
+// renderer happens through the embedder process.
+//
+// A BrowserPluginGuest is also associated directly with the WebContents related
+// to the BrowserPlugin. BrowserPluginGuest is a WebContentsDelegate and
+// WebContentsObserver for the WebContents.
+
+#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_H_
+
+#include <map>
+
+#include "base/compiler_specific.h"
+#include "base/id_map.h"
+#include "base/time.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "ui/gfx/rect.h"
+#include "webkit/glue/webcursor.h"
+
+class TransportDIB;
+struct ViewHostMsg_UpdateRect_Params;
+
+namespace WebKit {
+class WebInputEvent;
+}
+
+namespace content {
+
+class BrowserPluginHostFactory;
+class BrowserPluginEmbedder;
+class RenderProcessHost;
+
+// A browser plugin guest provides functionality for WebContents to operate in
+// the guest role and implements guest specific overrides for ViewHostMsg_*
+// messages.
+//
+// BrowserPluginEmbedder is responsible for creating and destroying a guest.
+class CONTENT_EXPORT BrowserPluginGuest : public WebContentsDelegate,
+ public WebContentsObserver {
+ public:
+ virtual ~BrowserPluginGuest();
+
+ static BrowserPluginGuest* Create(int instance_id,
+ WebContentsImpl* web_contents,
+ content::RenderViewHost* render_view_host);
+
+ // Overrides factory for testing. Default (NULL) value indicates regular
+ // (non-test) environment.
+ static void set_factory_for_testing(BrowserPluginHostFactory* factory) {
+ content::BrowserPluginGuest::factory_ = factory;
+ }
+
+ void set_guest_hang_timeout_for_testing(const base::TimeDelta& timeout) {
+ guest_hang_timeout_ = timeout;
+ }
+
+ // WebContentsObserver implementation.
+ virtual void DidCommitProvisionalLoadForFrame(
+ int64 frame_id,
+ bool is_main_frame,
+ const GURL& url,
+ PageTransition transition_type,
+ RenderViewHost* render_view_host) OVERRIDE;
+ virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE;
+
+ // WebContentsDelegate implementation.
+ virtual void RendererUnresponsive(WebContents* source) OVERRIDE;
+
+ private:
+ friend class BrowserPluginEmbedder;
+ friend class BrowserPluginGuestHelper;
+ friend class TestBrowserPluginGuest;
+
+ BrowserPluginGuest(int instance_id,
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host);
+
+ void set_embedder_render_process_host(
+ RenderProcessHost* render_process_host) {
+ embedder_render_process_host_ = render_process_host;
+ }
+ RenderProcessHost* embedder_render_process_host() {
+ return embedder_render_process_host_;
+ }
+ // Returns the identifier that uniquely identifies a browser plugin guest
+ // within an embedder.
+ int instance_id() const { return instance_id_; }
+ TransportDIB* damage_buffer() const { return damage_buffer_.get(); }
+ const gfx::Size& damage_view_size() const { return damage_view_size_; }
+ float damage_buffer_scale_factor() const {
+ return damage_buffer_scale_factor_;
+ }
+ void SetDamageBuffer(TransportDIB* damage_buffer,
+#if defined(OS_WIN)
+ int damage_buffer_size,
+#endif
+ const gfx::Size& damage_view_size,
+ float scale_factor);
+
+ void UpdateRect(RenderViewHost* render_view_host,
+ const ViewHostMsg_UpdateRect_Params& params);
+ void UpdateRectACK(int message_id, const gfx::Size& size);
+ // Handles input event routed through the embedder (which is initiated in the
+ // browser plugin (renderer side of the embedder)).
+ void HandleInputEvent(RenderViewHost* render_view_host,
+ const gfx::Rect& guest_rect,
+ const WebKit::WebInputEvent& event,
+ IPC::Message* reply_message);
+ // Overrides default ShowWidget message so we show them on the correct
+ // coordinates.
+ void ShowWidget(RenderViewHost* render_view_host,
+ int route_id,
+ const gfx::Rect& initial_pos);
+ void SetCursor(const WebCursor& cursor);
+ // Handles input event acks so they are sent to browser plugin host (via
+ // embedder) instead of default view/widget host.
+ void HandleInputEventAck(RenderViewHost* render_view_host, bool handled);
+
+ // Helper to send messages to embedder. Overridden in test implementation
+ // since we want to intercept certain messages for testing.
+ virtual void SendMessageToEmbedder(IPC::Message* msg);
+ // Overridden in tests.
+ virtual void SetFocus(bool focused);
+ // Overridden in tests.
+ virtual bool ViewTakeFocus(bool reverse);
+
+ // Static factory instance (always NULL for non-test).
+ static content::BrowserPluginHostFactory* factory_;
+
+ RenderProcessHost* embedder_render_process_host_;
+ // An identifier that uniquely identifies a browser plugin guest within an
+ // embedder.
+ int instance_id_;
+ scoped_ptr<TransportDIB> damage_buffer_;
+#if defined(OS_WIN)
+ size_t damage_buffer_size_;
+#endif
+ gfx::Size damage_view_size_;
+ float damage_buffer_scale_factor_;
+ scoped_ptr<IPC::Message> pending_input_event_reply_;
+ gfx::Rect guest_rect_;
+ WebCursor cursor_;
+ IDMap<RenderViewHost> pending_updates_;
+ int pending_update_counter_;
+ base::TimeDelta guest_hang_timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuest);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_H_
diff --git a/content/browser/browser_plugin/browser_plugin_guest_helper.cc b/content/browser/browser_plugin/browser_plugin_guest_helper.cc
new file mode 100644
index 0000000..9ca3c10
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_guest_helper.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/browser_plugin/browser_plugin_guest_helper.h"
+
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/render_view_host.h"
+
+namespace content {
+
+BrowserPluginGuestHelper::BrowserPluginGuestHelper(
+ BrowserPluginGuest* guest,
+ RenderViewHost* render_view_host)
+ : RenderViewHostObserver(render_view_host),
+ guest_(guest) {
+}
+
+BrowserPluginGuestHelper::~BrowserPluginGuestHelper() {
+}
+
+bool BrowserPluginGuestHelper::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuestHelper, message)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnUpdateRect)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_HandleInputEvent_ACK, OnHandleInputEventAck)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_TakeFocus, OnTakeFocus)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ShowWidget, OnShowWidget)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnSetCursor)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void BrowserPluginGuestHelper::OnUpdateRect(
+ const ViewHostMsg_UpdateRect_Params& params) {
+ guest_->UpdateRect(render_view_host(), params);
+}
+
+void BrowserPluginGuestHelper::OnHandleInputEventAck(
+ WebKit::WebInputEvent::Type event_type,
+ bool processed) {
+ guest_->HandleInputEventAck(render_view_host(), processed);
+}
+
+void BrowserPluginGuestHelper::OnTakeFocus(bool reverse) {
+ guest_->ViewTakeFocus(reverse);
+}
+
+void BrowserPluginGuestHelper::OnShowWidget(int route_id,
+ const gfx::Rect& initial_pos) {
+ guest_->ShowWidget(render_view_host(), route_id, initial_pos);
+}
+
+void BrowserPluginGuestHelper::OnSetCursor(const WebCursor& cursor) {
+ guest_->SetCursor(cursor);
+}
+
+} // namespace content
diff --git a/content/browser/browser_plugin/browser_plugin_guest_helper.h b/content/browser/browser_plugin/browser_plugin_guest_helper.h
new file mode 100644
index 0000000..19b3c7c
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_guest_helper.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_HELPER_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_HELPER_H_
+
+#include "content/public/browser/render_view_host_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
+
+class WebCursor;
+struct ViewHostMsg_UpdateRect_Params;
+
+namespace gfx {
+class Size;
+}
+
+namespace content {
+class BrowserPluginGuest;
+class RenderViewHost;
+
+// Helper for browser plugin guest.
+//
+// It overrides different WebContents messages that require special treatment
+// for a WebContents to act as a guest. All functionality is handled by its
+// delegate. This class exists so we have separation of messages requiring
+// special handling, which can be moved to a message filter (IPC thread) for
+// future optimization.
+//
+// The lifetime of this class is managed by the associated RenderViewHost. A
+// BrowserPluginGuestHelper is created whenever a BrowserPluginGuest is created.
+class BrowserPluginGuestHelper : public RenderViewHostObserver {
+ public:
+ BrowserPluginGuestHelper(BrowserPluginGuest* guest,
+ RenderViewHost* render_view_host);
+ virtual ~BrowserPluginGuestHelper();
+
+ protected:
+ // RenderViewHostObserver implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ private:
+ // Message handlers
+ void OnUpdateRect(const ViewHostMsg_UpdateRect_Params& params);
+ void OnHandleInputEventAck(WebKit::WebInputEvent::Type event_type,
+ bool processed);
+ void OnTakeFocus(bool reverse);
+ void OnShowWidget(int route_id, const gfx::Rect& initial_pos);
+ void OnSetCursor(const WebCursor& cursor);
+
+ BrowserPluginGuest* guest_;
+ // A scoped container for notification registries.
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuestHelper);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_HELPER_H_
diff --git a/content/browser/browser_plugin/browser_plugin_host_browsertest.cc b/content/browser/browser_plugin/browser_plugin_host_browsertest.cc
new file mode 100644
index 0000000..28686b4
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_host_browsertest.cc
@@ -0,0 +1,490 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/singleton.h"
+#include "base/run_loop.h"
+#include "base/test/test_timeouts.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
+#include "content/browser/browser_plugin/test_browser_plugin_embedder.h"
+#include "content/browser/browser_plugin/test_browser_plugin_guest.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/shell.h"
+#include "content/test/content_browser_test_utils.h"
+#include "content/test/content_browser_test.h"
+#include "net/base/net_util.h"
+#include "net/test/test_server.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
+
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using content::BrowserPluginEmbedder;
+using content::BrowserPluginGuest;
+using content::BrowserPluginHostFactory;
+
+namespace content {
+
+const char* kHTMLForGuest =
+ "data:text/html,<html><body>hello world</body></html>";
+const char* kHTMLForGuestInfiniteLoop =
+ "data:text/html,<html><head><script type=\"text/javascript\">"
+ "function StartInfiniteLoop() {"
+ " setTimeout(function () {while (true) {} }, 0);"
+ "}"
+ "</script></head><body></body></html>";
+
+// Test factory for creating test instances of BrowserPluginEmbedder and
+// BrowserPluginGuest.
+class TestBrowserPluginHostFactory : public BrowserPluginHostFactory {
+ public:
+ virtual BrowserPluginGuest* CreateBrowserPluginGuest(
+ int instance_id,
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host) OVERRIDE {
+ return new TestBrowserPluginGuest(instance_id,
+ web_contents,
+ render_view_host);
+ }
+
+ // Also keeps track of number of instances created.
+ virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host) OVERRIDE {
+ embedder_instance_count_++;
+ if (message_loop_runner_)
+ message_loop_runner_->Quit();
+
+ return new TestBrowserPluginEmbedder(web_contents, render_view_host);
+ }
+
+ // Singleton getter.
+ static TestBrowserPluginHostFactory* GetInstance() {
+ return Singleton<TestBrowserPluginHostFactory>::get();
+ }
+
+ // Waits for at least one embedder to be created in the test. Returns true if
+ // we have a guest, false if waiting times out.
+ void WaitForEmbedderCreation() {
+ // Check if already have created instance.
+ if (embedder_instance_count_ > 0)
+ return;
+ // Wait otherwise.
+ message_loop_runner_ = new MessageLoopRunner();
+ message_loop_runner_->Run();
+ }
+
+ protected:
+ TestBrowserPluginHostFactory() : embedder_instance_count_(0) {}
+ virtual ~TestBrowserPluginHostFactory() {}
+
+ private:
+ // For Singleton.
+ friend struct DefaultSingletonTraits<TestBrowserPluginHostFactory>;
+
+ scoped_refptr<MessageLoopRunner> message_loop_runner_;
+ int embedder_instance_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginHostFactory);
+};
+
+// Test factory class for browser plugin that creates guests with short hang
+// timeout.
+class TestShortHangTimeoutGuestFactory : public TestBrowserPluginHostFactory {
+ public:
+ virtual BrowserPluginGuest* CreateBrowserPluginGuest(
+ int instance_id,
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host) OVERRIDE {
+ BrowserPluginGuest* guest = new TestBrowserPluginGuest(instance_id,
+ web_contents,
+ render_view_host);
+ guest->set_guest_hang_timeout_for_testing(TestTimeouts::tiny_timeout());
+ return guest;
+ }
+
+ // Singleton getter.
+ static TestShortHangTimeoutGuestFactory* GetInstance() {
+ return Singleton<TestShortHangTimeoutGuestFactory>::get();
+ }
+
+ protected:
+ TestShortHangTimeoutGuestFactory() {}
+ virtual ~TestShortHangTimeoutGuestFactory() {}
+
+ private:
+ // For Singleton.
+ friend struct DefaultSingletonTraits<TestShortHangTimeoutGuestFactory>;
+
+ DISALLOW_COPY_AND_ASSIGN(TestShortHangTimeoutGuestFactory);
+};
+
+class BrowserPluginHostTest : public ContentBrowserTest {
+ public:
+ BrowserPluginHostTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ // Override factory to create tests instances of BrowserPlugin*.
+ content::BrowserPluginEmbedder::set_factory_for_testing(
+ TestBrowserPluginHostFactory::GetInstance());
+ content::BrowserPluginGuest::set_factory_for_testing(
+ TestBrowserPluginHostFactory::GetInstance());
+
+ ContentBrowserTest::SetUp();
+ }
+ virtual void TearDown() OVERRIDE {
+ content::BrowserPluginEmbedder::set_factory_for_testing(NULL);
+ content::BrowserPluginGuest::set_factory_for_testing(NULL);
+
+ ContentBrowserTest::TearDown();
+ }
+
+ static void SimulateTabKeyPress(WebContents* web_contents) {
+ SimulateKeyPress(web_contents,
+ ui::VKEY_TAB,
+ false, // control.
+ false, // shift.
+ false, // alt.
+ false); // command.
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginHostTest);
+};
+
+// This test loads a guest that has infinite loop, therefore it hangs the guest
+// and eventually gets killed.
+// TODO(lazyboy): This test is flaky on Windows, since this relies on
+// RenderViewGone to be called and times out. http://crbug.com/151190.
+#if defined(OS_WIN)
+#define MAYBE_NavigateGuest DISABLED_NavigateGuest
+#else
+#define MAYBE_NavigateGuest NavigateGuest
+#endif
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_NavigateGuest) {
+ // Override the hang timeout for guest to be very small.
+ content::BrowserPluginGuest::set_factory_for_testing(
+ TestShortHangTimeoutGuestFactory::GetInstance());
+ ASSERT_TRUE(test_server()->Start());
+ GURL test_url(test_server()->GetURL(
+ "files/browser_plugin_embedder_crash.html"));
+ NavigateToURL(shell(), test_url);
+
+ WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
+ shell()->web_contents());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ embedder_web_contents->GetRenderViewHost());
+
+ rvh->ExecuteJavascriptAndGetValue(string16(), ASCIIToUTF16(
+ StringPrintf("SetSrc('%s');", kHTMLForGuestInfiniteLoop)));
+
+ // Wait to make sure embedder is created/attached to WebContents.
+ TestBrowserPluginHostFactory::GetInstance()->WaitForEmbedderCreation();
+
+ TestBrowserPluginEmbedder* test_embedder =
+ static_cast<TestBrowserPluginEmbedder*>(
+ embedder_web_contents->GetBrowserPluginEmbedder());
+ ASSERT_TRUE(test_embedder);
+ test_embedder->WaitForGuestAdded();
+
+ // Verify that we have exactly one guest.
+ const BrowserPluginEmbedder::ContainerInstanceMap& instance_map =
+ test_embedder->guest_web_contents_for_testing();
+ EXPECT_EQ(1u, instance_map.size());
+
+ WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
+ instance_map.begin()->second);
+ TestBrowserPluginGuest* test_guest = static_cast<TestBrowserPluginGuest*>(
+ test_guest_web_contents->GetBrowserPluginGuest());
+
+ // Wait for the guest to send an UpdateRectMsg, meaning it is ready.
+ test_guest->WaitForUpdateRectMsg();
+
+ test_guest_web_contents->GetRenderViewHost()->ExecuteJavascriptAndGetValue(
+ string16(), ASCIIToUTF16("StartInfiniteLoop();"));
+
+ // Send a mouse event to the guest.
+ SimulateMouseClick(embedder_web_contents);
+
+ // Expect the guest to crash.
+ test_guest->WaitForCrashed();
+}
+
+// This test ensures that if guest isn't there and we resize the guest (from
+// js), it remembers the size correctly.
+//
+// Initially we load an embedder with a guest without a src attribute (which has
+// dimension 640x480), resize it to 100x200, and then we set the source to a
+// sample guest. In the end we verify that the correct size has been set.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, NavigateAfterResize) {
+ ASSERT_TRUE(test_server()->Start());
+ GURL test_url(test_server()->GetURL(
+ "files/browser_plugin_embedder.html"));
+ NavigateToURL(shell(), test_url);
+
+ WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
+ shell()->web_contents());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ embedder_web_contents->GetRenderViewHost());
+
+ int nxt_width = 100;
+ int nxt_height = 200;
+ rvh->ExecuteJavascriptAndGetValue(string16(), ASCIIToUTF16(
+ StringPrintf("SetSize(%d, %d);", nxt_width, nxt_height)));
+
+ rvh->ExecuteJavascriptAndGetValue(string16(), ASCIIToUTF16(
+ StringPrintf("SetSrc('%s');", kHTMLForGuest)));
+
+ // Wait to make sure embedder is created/attached to WebContents.
+ TestBrowserPluginHostFactory::GetInstance()->WaitForEmbedderCreation();
+
+ TestBrowserPluginEmbedder* test_embedder =
+ static_cast<TestBrowserPluginEmbedder*>(
+ embedder_web_contents->GetBrowserPluginEmbedder());
+ ASSERT_TRUE(test_embedder);
+ test_embedder->WaitForGuestAdded();
+
+ // Verify that we have exactly one guest.
+ const BrowserPluginEmbedder::ContainerInstanceMap& instance_map =
+ test_embedder->guest_web_contents_for_testing();
+ EXPECT_EQ(1u, instance_map.size());
+
+ WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
+ instance_map.begin()->second);
+ TestBrowserPluginGuest* test_guest = static_cast<TestBrowserPluginGuest*>(
+ test_guest_web_contents->GetBrowserPluginGuest());
+
+
+ // Wait for the guest to send an UpdateRectMsg, the dimensions should be
+ // 100 x 200.
+ test_guest->WaitForUpdateRectMsgWithSize(nxt_width, nxt_height);
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AdvanceFocus) {
+ ASSERT_TRUE(test_server()->Start());
+ GURL test_url(test_server()->GetURL(
+ "files/browser_plugin_focus.html"));
+ NavigateToURL(shell(), test_url);
+
+ WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
+ shell()->web_contents());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ embedder_web_contents->GetRenderViewHost());
+
+ test_url = test_server()->GetURL(
+ "files/browser_plugin_focus_child.html");
+ rvh->ExecuteJavascriptAndGetValue(string16(), ASCIIToUTF16(
+ StringPrintf("SetSrc('%s');", test_url.spec().c_str())));
+
+ // Wait to make sure embedder is created/attached to WebContents.
+ TestBrowserPluginHostFactory::GetInstance()->WaitForEmbedderCreation();
+
+ TestBrowserPluginEmbedder* test_embedder =
+ static_cast<TestBrowserPluginEmbedder*>(
+ embedder_web_contents->GetBrowserPluginEmbedder());
+ ASSERT_TRUE(test_embedder);
+ test_embedder->WaitForGuestAdded();
+
+ // Verify that we have exactly one guest.
+ const BrowserPluginEmbedder::ContainerInstanceMap& instance_map =
+ test_embedder->guest_web_contents_for_testing();
+ EXPECT_EQ(1u, instance_map.size());
+
+ WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
+ instance_map.begin()->second);
+ TestBrowserPluginGuest* test_guest = static_cast<TestBrowserPluginGuest*>(
+ test_guest_web_contents->GetBrowserPluginGuest());
+ test_guest->WaitForUpdateRectMsg();
+
+ SimulateMouseClick(embedder_web_contents);
+ BrowserPluginHostTest::SimulateTabKeyPress(embedder_web_contents);
+ // Wait until we focus into the guest.
+ test_guest->WaitForFocus();
+
+ // TODO(fsamuel): A third Tab key press should not be necessary.
+ // The browser plugin will take keyboard focus but it will not
+ // focus an initial element. The initial element is dependent
+ // upon tab direction which WebKit does not propagate to the plugin.
+ // See http://crbug.com/147644.
+ BrowserPluginHostTest::SimulateTabKeyPress(embedder_web_contents);
+ BrowserPluginHostTest::SimulateTabKeyPress(embedder_web_contents);
+ BrowserPluginHostTest::SimulateTabKeyPress(embedder_web_contents);
+ test_guest->WaitForAdvanceFocus();
+}
+
+// This test opens a page in http and then opens another page in https, forcing
+// a RenderViewHost swap in the web_contents. We verify that the embedder in the
+// web_contents gets cleared properly.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderChangedAfterSwap) {
+ ASSERT_TRUE(test_server()->Start());
+ net::TestServer https_server(
+ net::TestServer::TYPE_HTTPS,
+ net::TestServer::kLocalhost,
+ FilePath(FILE_PATH_LITERAL("content/test/data")));
+ ASSERT_TRUE(https_server.Start());
+
+ // 1. Load an embedder page with one guest in it.
+ GURL test_url(test_server()->GetURL("files/browser_plugin_embedder.html"));
+ NavigateToURL(shell(), test_url);
+
+ WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
+ shell()->web_contents());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ embedder_web_contents->GetRenderViewHost());
+ rvh->ExecuteJavascriptAndGetValue(string16(), ASCIIToUTF16(
+ StringPrintf("SetSrc('%s');", kHTMLForGuest)));
+
+ // Wait to make sure embedder is created/attached to WebContents.
+ TestBrowserPluginHostFactory::GetInstance()->WaitForEmbedderCreation();
+
+ TestBrowserPluginEmbedder* test_embedder_before_swap =
+ static_cast<TestBrowserPluginEmbedder*>(
+ embedder_web_contents->GetBrowserPluginEmbedder());
+ ASSERT_TRUE(test_embedder_before_swap);
+ test_embedder_before_swap->WaitForGuestAdded();
+
+ // Verify that we have exactly one guest.
+ const BrowserPluginEmbedder::ContainerInstanceMap& instance_map =
+ test_embedder_before_swap->guest_web_contents_for_testing();
+ EXPECT_EQ(1u, instance_map.size());
+
+ WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
+ instance_map.begin()->second);
+ TestBrowserPluginGuest* test_guest = static_cast<TestBrowserPluginGuest*>(
+ test_guest_web_contents->GetBrowserPluginGuest());
+
+ // Wait for the guest to send an UpdateRectMsg, which means the guest is
+ // ready.
+ test_guest->WaitForUpdateRectMsg();
+
+ // 2. Navigate to a URL in https, so we trigger a RenderViewHost swap.
+ GURL test_https_url(https_server.GetURL(
+ "files/browser_plugin_title_change.html"));
+ content::WindowedNotificationObserver swap_observer(
+ content::NOTIFICATION_WEB_CONTENTS_SWAPPED,
+ content::Source<WebContents>(embedder_web_contents));
+ NavigateToURL(shell(), test_https_url);
+ swap_observer.Wait();
+
+ TestBrowserPluginEmbedder* test_embedder_after_swap =
+ static_cast<TestBrowserPluginEmbedder*>(
+ static_cast<WebContentsImpl*>(shell()->web_contents())->
+ GetBrowserPluginEmbedder());
+ // Verify we have a no embedder in web_contents (since the new page doesn't
+ // have any browser plugin).
+ ASSERT_TRUE(!test_embedder_after_swap);
+ ASSERT_NE(test_embedder_before_swap, test_embedder_after_swap);
+}
+
+// This test opens two pages in http and there is no RenderViewHost swap,
+// therefore the embedder created on first page navigation stays the same in
+// web_contents.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderSameAfterNav) {
+ ASSERT_TRUE(test_server()->Start());
+
+ GURL test_url(test_server()->GetURL("files/browser_plugin_embedder.html"));
+ NavigateToURL(shell(), test_url);
+
+ WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
+ shell()->web_contents());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ embedder_web_contents->GetRenderViewHost());
+
+ rvh->ExecuteJavascriptAndGetValue(string16(), ASCIIToUTF16(
+ StringPrintf("SetSrc('%s');", kHTMLForGuest)));
+
+ // Wait to make sure embedder is created/attached to WebContents.
+ TestBrowserPluginHostFactory::GetInstance()->WaitForEmbedderCreation();
+
+ TestBrowserPluginEmbedder* test_embedder =
+ static_cast<TestBrowserPluginEmbedder*>(
+ embedder_web_contents->GetBrowserPluginEmbedder());
+ ASSERT_TRUE(test_embedder);
+ test_embedder->WaitForGuestAdded();
+
+ // Verify that we have exactly one guest.
+ const BrowserPluginEmbedder::ContainerInstanceMap& instance_map =
+ test_embedder->guest_web_contents_for_testing();
+ EXPECT_EQ(1u, instance_map.size());
+
+ WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
+ instance_map.begin()->second);
+ TestBrowserPluginGuest* test_guest = static_cast<TestBrowserPluginGuest*>(
+ test_guest_web_contents->GetBrowserPluginGuest());
+
+ // Wait for the guest to send an UpdateRectMsg, which means the guest is
+ // ready.
+ test_guest->WaitForUpdateRectMsg();
+
+ // Navigate to another page in same host and port, so RenderViewHost swap
+ // does not happen and existing embedder doesn't change in web_contents.
+ GURL test_url_new(test_server()->GetURL(
+ "files/browser_plugin_title_change.html"));
+ const string16 expected_title = ASCIIToUTF16("done");
+ content::TitleWatcher title_watcher(shell()->web_contents(), expected_title);
+ NavigateToURL(shell(), test_url_new);
+ LOG(INFO) << "Start waiting for title";
+ string16 actual_title = title_watcher.WaitAndGetTitle();
+ EXPECT_EQ(expected_title, actual_title);
+ LOG(INFO) << "Done navigating to second page";
+
+ TestBrowserPluginEmbedder* test_embedder_after_nav =
+ static_cast<TestBrowserPluginEmbedder*>(
+ embedder_web_contents->GetBrowserPluginEmbedder());
+ // Embedder must not change in web_contents.
+ ASSERT_EQ(test_embedder_after_nav, test_embedder);
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VisibilityChanged) {
+ ASSERT_TRUE(test_server()->Start());
+ GURL test_url(test_server()->GetURL(
+ "files/browser_plugin_focus.html"));
+ NavigateToURL(shell(), test_url);
+
+ WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
+ shell()->web_contents());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ embedder_web_contents->GetRenderViewHost());
+
+ test_url = test_server()->GetURL(
+ "files/browser_plugin_focus_child.html");
+ rvh->ExecuteJavascriptAndGetValue(string16(), ASCIIToUTF16(
+ StringPrintf("SetSrc('%s');", test_url.spec().c_str())));
+
+ // Wait to make sure embedder is created/attached to WebContents.
+ TestBrowserPluginHostFactory::GetInstance()->WaitForEmbedderCreation();
+
+ TestBrowserPluginEmbedder* test_embedder =
+ static_cast<TestBrowserPluginEmbedder*>(
+ embedder_web_contents->GetBrowserPluginEmbedder());
+ ASSERT_TRUE(test_embedder);
+ test_embedder->WaitForGuestAdded();
+
+ // Verify that we have exactly one guest.
+ const BrowserPluginEmbedder::ContainerInstanceMap& instance_map =
+ test_embedder->guest_web_contents_for_testing();
+ EXPECT_EQ(1u, instance_map.size());
+
+ WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
+ instance_map.begin()->second);
+ TestBrowserPluginGuest* test_guest = static_cast<TestBrowserPluginGuest*>(
+ test_guest_web_contents->GetBrowserPluginGuest());
+
+ // Wait for the guest to send an UpdateRectMsg, meaning it is ready.
+ test_guest->WaitForUpdateRectMsg();
+
+ // Hide the embedder.
+ embedder_web_contents->WasHidden();
+
+ // Make sure that hiding the embedder also hides the guest.
+ test_guest->WaitUntilHidden();
+}
+
+} // namespace content
diff --git a/content/browser/browser_plugin/browser_plugin_host_factory.h b/content/browser/browser_plugin/browser_plugin_host_factory.h
new file mode 100644
index 0000000..b90b9e0
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_host_factory.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_HOST_FACTORY_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_HOST_FACTORY_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/string16.h"
+#include "content/common/content_export.h"
+
+class WebContentsImpl;
+
+namespace content {
+
+class BrowserPluginEmbedder;
+class BrowserPluginGuest;
+class RenderViewHost;
+
+// Factory to create BrowserPlugin embedder and guest.
+class CONTENT_EXPORT BrowserPluginHostFactory {
+ public:
+ virtual BrowserPluginGuest* CreateBrowserPluginGuest(
+ int instance_id,
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host) = 0;
+
+ virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host) = 0;
+
+ protected:
+ virtual ~BrowserPluginHostFactory() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_HOST_FACTORY_H_
diff --git a/content/browser/browser_plugin/test_browser_plugin_embedder.cc b/content/browser/browser_plugin/test_browser_plugin_embedder.cc
new file mode 100644
index 0000000..8d0ae8f
--- /dev/null
+++ b/content/browser/browser_plugin/test_browser_plugin_embedder.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/browser_plugin/test_browser_plugin_embedder.h"
+
+#include "base/time.h"
+#include "content/browser/browser_plugin/browser_plugin_embedder.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/test/test_utils.h"
+
+namespace content {
+
+TestBrowserPluginEmbedder::TestBrowserPluginEmbedder(
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host)
+ : BrowserPluginEmbedder(web_contents, render_view_host) {
+}
+
+TestBrowserPluginEmbedder::~TestBrowserPluginEmbedder() {
+}
+
+void TestBrowserPluginEmbedder::AddGuest(int instance_id,
+ WebContents* guest_web_contents,
+ int64 frame_id) {
+ BrowserPluginEmbedder::AddGuest(instance_id, guest_web_contents, frame_id);
+ if (message_loop_runner_)
+ message_loop_runner_->Quit();
+}
+
+void TestBrowserPluginEmbedder::WaitForGuestAdded() {
+ // Check if guests were already created.
+ if (guest_web_contents_by_instance_id_.size() > 0)
+ return;
+ // Wait otherwise.
+ message_loop_runner_ = new MessageLoopRunner();
+ message_loop_runner_->Run();
+}
+
+} // namespace content
diff --git a/content/browser/browser_plugin/test_browser_plugin_embedder.h b/content/browser/browser_plugin/test_browser_plugin_embedder.h
new file mode 100644
index 0000000..6c62bdb
--- /dev/null
+++ b/content/browser/browser_plugin/test_browser_plugin_embedder.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_EMBEDDER_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_EMBEDDER_H_
+
+#include "base/compiler_specific.h"
+#include "content/browser/browser_plugin/browser_plugin_embedder.h"
+#include "content/public/test/test_utils.h"
+
+class WebContentsImpl;
+
+namespace content {
+
+class BrowserPluginGuest;
+class RenderViewHost;
+
+// Test class for BrowserPluginEmbedder.
+//
+// Provides utilities to wait for certain state/messages in
+// BrowserPluginEmbedder to be used in tests.
+class TestBrowserPluginEmbedder : public BrowserPluginEmbedder {
+ public:
+ TestBrowserPluginEmbedder(WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host);
+ virtual ~TestBrowserPluginEmbedder();
+
+ const ContainerInstanceMap& guest_web_contents_for_testing() const {
+ return guest_web_contents_by_instance_id_;
+ }
+
+ // Waits until at least one guest is added to this embedder.
+ void WaitForGuestAdded();
+
+ private:
+ // Overridden to intercept in test.
+ virtual void AddGuest(int instance_id,
+ WebContents* guest_web_contents,
+ int64 frame_id) OVERRIDE;
+
+ scoped_refptr<MessageLoopRunner> message_loop_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginEmbedder);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_EMBEDDER_H_
diff --git a/content/browser/browser_plugin/test_browser_plugin_guest.cc b/content/browser/browser_plugin/test_browser_plugin_guest.cc
new file mode 100644
index 0000000..35f119e
--- /dev/null
+++ b/content/browser/browser_plugin/test_browser_plugin_guest.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/browser_plugin/test_browser_plugin_guest.h"
+
+#include "base/test/test_timeouts.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/browser_plugin_messages.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/test/test_utils.h"
+#include "ui/gfx/size.h"
+
+namespace content {
+
+class BrowserPluginGuest;
+
+TestBrowserPluginGuest::TestBrowserPluginGuest(
+ int instance_id,
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host)
+ : BrowserPluginGuest(instance_id, web_contents, render_view_host),
+ update_rect_count_(0),
+ crash_observed_(false),
+ focus_observed_(false),
+ advance_focus_observed_(false),
+ was_hidden_observed_(false),
+ waiting_for_update_rect_msg_with_size_(false),
+ last_update_rect_width_(-1),
+ last_update_rect_height_(-1) {
+ // Listen to visibility changes so that a test can wait for these changes.
+ registrar_.Add(this,
+ NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED,
+ Source<WebContents>(web_contents));
+}
+
+TestBrowserPluginGuest::~TestBrowserPluginGuest() {
+}
+
+void TestBrowserPluginGuest::Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED: {
+ bool visible = *Details<bool>(details).ptr();
+ if (!visible) {
+ was_hidden_observed_ = true;
+ if (was_hidden_message_loop_runner_)
+ was_hidden_message_loop_runner_->Quit();
+ }
+ break;
+ }
+ default:
+ NOTREACHED() << "Unexpected notification type: " << type;
+ }
+}
+
+void TestBrowserPluginGuest::SendMessageToEmbedder(IPC::Message* msg) {
+ if (msg->type() == BrowserPluginMsg_UpdateRect::ID) {
+ PickleIterator iter(*msg);
+
+ int instance_id;
+ int message_id;
+ BrowserPluginMsg_UpdateRect_Params update_rect_params;
+
+ if (!IPC::ReadParam(msg, &iter, &instance_id) ||
+ !IPC::ReadParam(msg, &iter, &message_id) ||
+ !IPC::ReadParam(msg, &iter, &update_rect_params)) {
+ NOTREACHED() <<
+ "Cannot read BrowserPluginMsg_UpdateRect params from ipc message";
+ }
+ last_update_rect_width_ = update_rect_params.view_size.width();
+ last_update_rect_height_ = update_rect_params.view_size.height();
+ update_rect_count_++;
+ if (waiting_for_update_rect_msg_with_size_ &&
+ expected_width_ == last_update_rect_width_ &&
+ expected_height_ == last_update_rect_height_) {
+ waiting_for_update_rect_msg_with_size_ = false;
+ if (send_message_loop_runner_)
+ send_message_loop_runner_->Quit();
+ } else if (!waiting_for_update_rect_msg_with_size_) {
+ if (send_message_loop_runner_)
+ send_message_loop_runner_->Quit();
+ }
+ }
+ BrowserPluginGuest::SendMessageToEmbedder(msg);
+}
+
+void TestBrowserPluginGuest::WaitForUpdateRectMsg() {
+ // Check if we already got any UpdateRect message.
+ if (update_rect_count_ > 0)
+ return;
+ send_message_loop_runner_ = new MessageLoopRunner();
+ send_message_loop_runner_->Run();
+}
+
+void TestBrowserPluginGuest::WaitForUpdateRectMsgWithSize(int width,
+ int height) {
+ if (update_rect_count_ > 0 &&
+ last_update_rect_width_ == width &&
+ last_update_rect_height_ == height) {
+ // We already saw this message.
+ return;
+ }
+ waiting_for_update_rect_msg_with_size_ = true;
+ expected_width_ = width;
+ expected_height_ = height;
+
+ send_message_loop_runner_ = new MessageLoopRunner();
+ send_message_loop_runner_->Run();
+}
+
+void TestBrowserPluginGuest::RenderViewGone(base::TerminationStatus status) {
+ crash_observed_ = true;
+ LOG(INFO) << "Guest crashed";
+ if (crash_message_loop_runner_)
+ crash_message_loop_runner_->Quit();
+ BrowserPluginGuest::RenderViewGone(status);
+}
+
+void TestBrowserPluginGuest::WaitForCrashed() {
+ // Check if we already observed a guest crash, return immediately if so.
+ if (crash_observed_)
+ return;
+
+ crash_message_loop_runner_ = new MessageLoopRunner();
+ crash_message_loop_runner_->Run();
+}
+
+void TestBrowserPluginGuest::WaitForFocus() {
+ if (focus_observed_)
+ return;
+ focus_message_loop_runner_ = new MessageLoopRunner();
+ focus_message_loop_runner_->Run();
+}
+
+void TestBrowserPluginGuest::WaitForAdvanceFocus() {
+ if (advance_focus_observed_)
+ return;
+ advance_focus_message_loop_runner_ = new MessageLoopRunner();
+ advance_focus_message_loop_runner_->Run();
+}
+
+void TestBrowserPluginGuest::WaitUntilHidden() {
+ if (was_hidden_observed_) {
+ was_hidden_observed_ = false;
+ return;
+ }
+ was_hidden_message_loop_runner_ = new MessageLoopRunner();
+ was_hidden_message_loop_runner_->Run();
+ was_hidden_observed_ = false;
+}
+
+void TestBrowserPluginGuest::SetFocus(bool focused) {
+ focus_observed_ = true;
+ if (focus_message_loop_runner_)
+ focus_message_loop_runner_->Quit();
+ BrowserPluginGuest::SetFocus(focused);
+}
+
+bool TestBrowserPluginGuest::ViewTakeFocus(bool reverse) {
+ advance_focus_observed_ = true;
+ if (advance_focus_message_loop_runner_)
+ advance_focus_message_loop_runner_->Quit();
+ return BrowserPluginGuest::ViewTakeFocus(reverse);
+}
+
+} // namespace content
diff --git a/content/browser/browser_plugin/test_browser_plugin_guest.h b/content/browser/browser_plugin/test_browser_plugin_guest.h
new file mode 100644
index 0000000..7460505
--- /dev/null
+++ b/content/browser/browser_plugin/test_browser_plugin_guest.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_H_
+
+#include "base/compiler_specific.h"
+#include "base/process_util.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/test/test_utils.h"
+
+class WebContentsImpl;
+
+namespace content {
+
+class RenderProcessHost;
+class RenderViewHost;
+
+// Test class for BrowserPluginGuest.
+//
+// Provides utilities to wait for certain state/messages in BrowserPluginGuest
+// to be used in tests.
+class TestBrowserPluginGuest : public BrowserPluginGuest,
+ public NotificationObserver {
+ public:
+ TestBrowserPluginGuest(int instance_id,
+ WebContentsImpl* web_contents,
+ RenderViewHost* render_view_host);
+ virtual ~TestBrowserPluginGuest();
+
+ // NotificationObserver method override.
+ virtual void Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) OVERRIDE;
+
+ // Overridden methods from BrowserPluginGuest to intercept in test objects.
+ virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE;
+ virtual void SetFocus(bool focused) OVERRIDE;
+ virtual bool ViewTakeFocus(bool reverse) OVERRIDE;
+
+ // Test utilities to wait for a event we are interested in.
+ // Waits until UpdateRect message is sent from the guest, meaning it is
+ // ready/rendered.
+ void WaitForUpdateRectMsg();
+ // Waits until UpdateRect message with a specific size is sent from the guest.
+ void WaitForUpdateRectMsgWithSize(int width, int height);
+ // Waits for focus to reach this guest.
+ void WaitForFocus();
+ // Wait for focus to move out of this guest.
+ void WaitForAdvanceFocus();
+ // Wait until the guest is hidden.
+ void WaitUntilHidden();
+ // Waits until guest crashes.
+ void WaitForCrashed();
+
+ private:
+ // Overridden methods from BrowserPluginGuest to intercept in test objects.
+ virtual void SendMessageToEmbedder(IPC::Message* msg) OVERRIDE;
+
+ int update_rect_count_;
+ bool crash_observed_;
+ bool focus_observed_;
+ bool advance_focus_observed_;
+ bool was_hidden_observed_;
+
+ // For WaitForUpdateRectMsgWithSize().
+ bool waiting_for_update_rect_msg_with_size_;
+ int expected_width_;
+ int expected_height_;
+
+ int last_update_rect_width_;
+ int last_update_rect_height_;
+
+ scoped_refptr<MessageLoopRunner> send_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> crash_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> focus_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> advance_focus_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> was_hidden_message_loop_runner_;
+
+ // A scoped container for notification registries.
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginGuest);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_H_