summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/renderer/chrome_content_renderer_client.cc8
-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.cc483
-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
-rw-r--r--content/browser/renderer_host/render_view_host_impl.cc13
-rw-r--r--content/browser/renderer_host/render_widget_host_impl.cc11
-rw-r--r--content/browser/renderer_host/render_widget_host_impl.h7
-rw-r--r--content/browser/web_contents/web_contents_impl.cc74
-rw-r--r--content/browser/web_contents/web_contents_impl.h30
-rw-r--r--content/common/browser_plugin_messages.h12
-rw-r--r--content/content_browser.gypi9
-rw-r--r--content/content_tests.gypi5
-rw-r--r--content/renderer/browser_plugin/browser_plugin.cc69
-rw-r--r--content/renderer/browser_plugin/browser_plugin.h10
-rw-r--r--content/renderer/browser_plugin/browser_plugin_browsertest.cc20
-rw-r--r--content/renderer/browser_plugin/browser_plugin_manager_impl.cc2
-rw-r--r--content/renderer/renderer_main.cc2
-rw-r--r--content/test/data/browser_plugin_embedder.html18
-rw-r--r--content/test/data/browser_plugin_embedder_crash.html17
-rw-r--r--content/test/data/browser_plugin_focus.html21
-rw-r--r--content/test/data/browser_plugin_focus_child.html6
-rw-r--r--content/test/data/browser_plugin_title_change.html12
33 files changed, 2360 insertions, 36 deletions
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 40c9c1b..c692fa2 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -316,10 +316,12 @@ bool ChromeContentRendererClient::OverrideCreatePlugin(
std::string actual_mime_type;
std::string orig_mime_type = params.mimeType.utf8();
- if (orig_mime_type == content::kBrowserPluginMimeType &&
- extensions::ExtensionHelper::Get(render_view)->view_type() ==
- VIEW_TYPE_APP_SHELL)
+ if (orig_mime_type == content::kBrowserPluginNewMimeType ||
+ ((orig_mime_type == content::kBrowserPluginMimeType) &&
+ extensions::ExtensionHelper::Get(render_view)->view_type() ==
+ VIEW_TYPE_APP_SHELL)) {
return false;
+ }
render_view->Send(new ChromeViewHostMsg_GetPluginInfo(
render_view->GetRoutingID(), GURL(params.url),
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..d64c134
--- /dev/null
+++ b/content/browser/browser_plugin/browser_plugin_host_browsertest.cc
@@ -0,0 +1,483 @@
+// 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.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, 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_
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 6984a0d..7b99267 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -29,6 +29,7 @@
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/common/accessibility_messages.h"
+#include "content/common/browser_plugin_messages.h"
#include "content/common/content_constants_internal.h"
#include "content/common/desktop_notification_messages.h"
#include "content/common/drag_messages.h"
@@ -833,7 +834,17 @@ bool RenderViewHostImpl::SuddenTerminationAllowed() const {
// RenderViewHostImpl, IPC message handlers:
bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) {
- if (!BrowserMessageFilter::CheckCanDispatchOnUI(msg, this))
+ // Allow BrowserPluginHostMsg_* sync messages to run on the UI thread.
+ // Platform apps will not support windowed plugins so the deadlock cycle
+ // browser -> plugin -> renderer -> browser referred in
+ // BrowserMessageFilter::CheckCanDispatchOnUI() is not supposed to happen. If
+ // we want to support windowed plugins, sync messages in BrowserPlugin might
+ // need to be changed to async messages.
+ // TODO(fsamuel): Disallow BrowserPluginHostMsg_* sync messages to run on UI
+ // thread and make these messages async: http://crbug.com/149063.
+ if (msg.type() != BrowserPluginHostMsg_HandleInputEvent::ID &&
+ msg.type() != BrowserPluginHostMsg_ResizeGuest::ID &&
+ !BrowserMessageFilter::CheckCanDispatchOnUI(msg, this))
return true;
// Filter out most IPC messages if this renderer is swapped out.
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index d4e2e53..e5aaac8 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -235,6 +235,12 @@ void RenderWidgetHostImpl::CompositingSurfaceUpdated() {
process_->SurfaceUpdated(surface_id_);
}
+void RenderWidgetHostImpl::ResetSizeAndRepaintPendingFlags() {
+ resize_ack_pending_ = false;
+ repaint_ack_pending_ = false;
+ in_flight_size_.SetSize(0, 0);
+}
+
void RenderWidgetHostImpl::Init() {
DCHECK(process_->HasConnection());
@@ -1111,10 +1117,7 @@ void RenderWidgetHostImpl::RendererExited(base::TerminationStatus status,
suppress_next_char_events_ = false;
// Reset some fields in preparation for recovering from a crash.
- resize_ack_pending_ = false;
- repaint_ack_pending_ = false;
-
- in_flight_size_.SetSize(0, 0);
+ ResetSizeAndRepaintPendingFlags();
current_size_.SetSize(0, 0);
is_hidden_ = false;
is_accelerated_compositing_active_ = false;
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 4f25ec7..4eeaa44 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -412,6 +412,13 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
virtual void RemoveLayer(WebKit::WebLayer* layer) {}
#endif
+ // Resets state variables related to tracking pending size and painting.
+ //
+ // We need to reset these flags when we want to repaint the contents of
+ // browser plugin in this RWH. Resetting these flags will ensure we ignore
+ // any previous pending acks that are not relevant upon repaint.
+ void ResetSizeAndRepaintPendingFlags();
+
protected:
virtual RenderWidgetHostImpl* AsRenderWidgetHostImpl() OVERRIDE;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 8256d03..fe9c088 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -15,6 +15,8 @@
#include "base/sys_info.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
+#include "content/browser/browser_plugin/browser_plugin_embedder.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
#include "content/browser/browser_plugin/old/old_browser_plugin_host.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/debugger/devtools_manager_impl.h"
@@ -35,6 +37,7 @@
#include "content/browser/web_contents/interstitial_page_impl.h"
#include "content/browser/web_contents/navigation_entry_impl.h"
#include "content/browser/webui/web_ui_impl.h"
+#include "content/common/browser_plugin_messages.h"
#include "content/common/intents_messages.h"
#include "content/common/ssl_status_serialization.h"
#include "content/common/view_messages.h"
@@ -423,6 +426,34 @@ WebContentsImpl* WebContentsImpl::CreateWithOpener(
return new_contents;
}
+WebContentsImpl* WebContentsImpl::CreateGuest(BrowserContext* browser_context,
+ const std::string& host_url,
+ int guest_instance_id) {
+ // The SiteInstance of a given guest is based on the fact that it's a guest
+ // in addition to which platform application the guest belongs to, rather
+ // than the URL that the guest is being navigated to.
+ GURL guest_site(
+ base::StringPrintf("%s://%s", chrome::kGuestScheme, host_url.c_str()));
+ SiteInstance* guest_site_instance =
+ SiteInstance::CreateForURL(browser_context, guest_site);
+ WebContentsImpl* new_contents = WebContentsImpl::Create(
+ browser_context,
+ guest_site_instance,
+ MSG_ROUTING_NONE,
+ NULL); // base WebContents
+ WebContentsImpl* new_contents_impl =
+ static_cast<WebContentsImpl*>(new_contents);
+
+ // This makes |new_contents| act as a guest.
+ // For more info, see comment above class BrowserPluginGuest.
+ new_contents_impl->browser_plugin_guest_.reset(
+ content::BrowserPluginGuest::Create(
+ guest_instance_id,
+ new_contents_impl,
+ new_contents_impl->GetRenderViewHost()));
+ return new_contents;
+}
+
WebPreferences WebContentsImpl::GetWebkitPrefs(RenderViewHost* rvh,
const GURL& url) {
WebPreferences prefs;
@@ -699,6 +730,8 @@ bool WebContentsImpl::OnMessageReceived(RenderViewHost* render_view_host,
IPC_MESSAGE_HANDLER(ViewHostMsg_WebUISend, OnWebUISend)
IPC_MESSAGE_HANDLER(ViewHostMsg_RequestPpapiBrokerPermission,
OnRequestPpapiBrokerPermission)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_NavigateGuest,
+ OnBrowserPluginNavigateGuest)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP_EX()
message_source_ = NULL;
@@ -2310,6 +2343,29 @@ void WebContentsImpl::OnPpapiBrokerPermissionResult(int request_id,
result));
}
+void WebContentsImpl::OnBrowserPluginNavigateGuest(int instance_id,
+ int64 frame_id,
+ const std::string& src,
+ const gfx::Size& size) {
+ // This is the first 'navigate' to a browser plugin, before WebContents has/is
+ // an 'Embedder'; subsequent navigate messages for this WebContents will
+ // be handled by the BrowserPluginEmbedderHelper of the embedder itself (this
+ // also means any message from browser plugin renderer prior to NavigateGuest
+ // which is not NavigateGuest will be ignored). Therefore
+ // |browser_plugin_embedder_| should not be set.
+ // For more info, see comment above classes BrowserPluginEmbedder and
+ // BrowserPluginGuest.
+ CHECK(!browser_plugin_embedder_.get());
+
+ browser_plugin_embedder_.reset(
+ content::BrowserPluginEmbedder::Create(this, GetRenderViewHost()));
+ browser_plugin_embedder_->NavigateGuest(GetRenderViewHost(),
+ instance_id,
+ frame_id,
+ src,
+ size);
+}
+
// Notifies the RenderWidgetHost instance about the fact that the page is
// loading, or done loading and calls the base implementation.
void WebContentsImpl::SetIsLoading(bool is_loading,
@@ -2466,6 +2522,11 @@ void WebContentsImpl::NotifySwapped() {
content::NOTIFICATION_WEB_CONTENTS_SWAPPED,
content::Source<WebContents>(this),
content::NotificationService::NoDetails());
+
+ // Ensure that the associated embedder gets cleared after a RenderViewHost
+ // gets swapped, so we don't reuse the same embedder next time a
+ // RenderViewHost is attached to this WebContents.
+ RemoveBrowserPluginEmbedder();
}
void WebContentsImpl::NotifyConnected() {
@@ -2511,6 +2572,11 @@ gfx::Rect WebContentsImpl::GetRootWindowResizerRect() const {
return gfx::Rect();
}
+void WebContentsImpl::RemoveBrowserPluginEmbedder() {
+ if (browser_plugin_embedder_.get())
+ browser_plugin_embedder_.reset();
+}
+
void WebContentsImpl::RenderViewCreated(RenderViewHost* render_view_host) {
// Don't send notifications if we are just creating a swapped-out RVH for
// the opener chain. These won't be used for view-source or WebUI, so it's
@@ -3299,3 +3365,11 @@ void WebContentsImpl::GetBrowserPluginEmbedderInfo(
embedder_process_id);
}
}
+
+content::BrowserPluginGuest* WebContentsImpl::GetBrowserPluginGuest() {
+ return browser_plugin_guest_.get();
+}
+
+content::BrowserPluginEmbedder* WebContentsImpl::GetBrowserPluginEmbedder() {
+ return browser_plugin_embedder_.get();
+}
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 2fda9db..2dcbfd2 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -39,6 +39,8 @@ class WebContentsImpl;
struct ViewMsg_PostMessage_Params;
namespace content {
+class BrowserPluginEmbedder;
+class BrowserPluginGuest;
class ColorChooser;
class DownloadItem;
class JavaScriptDialogCreator;
@@ -89,6 +91,11 @@ class CONTENT_EXPORT WebContentsImpl
const WebContentsImpl* base_web_contents,
WebContentsImpl* opener);
+ // Creates a WebContents to be used as a browser plugin guest.
+ static WebContentsImpl* CreateGuest(content::BrowserContext* browser_context,
+ const std::string& host,
+ int guest_instance_id);
+
// Returns the content specific prefs for the given RVH.
static webkit_glue::WebPreferences GetWebkitPrefs(
content::RenderViewHost* rvh, const GURL& url);
@@ -162,6 +169,13 @@ class CONTENT_EXPORT WebContentsImpl
// Expose the render manager for testing.
RenderViewHostManager* GetRenderManagerForTesting();
+ // Returns guest browser plugin object, or NULL if this WebContents is not a
+ // guest.
+ content::BrowserPluginGuest* GetBrowserPluginGuest();
+ // Returns embedder browser plugin object, or NULL if this WebContents is not
+ // an embedder.
+ content::BrowserPluginEmbedder* GetBrowserPluginEmbedder();
+
// content::WebContents ------------------------------------------------------
virtual content::WebContentsDelegate* GetDelegate() OVERRIDE;
virtual void SetDelegate(content::WebContentsDelegate* delegate) OVERRIDE;
@@ -552,6 +566,10 @@ class CONTENT_EXPORT WebContentsImpl
void OnRequestPpapiBrokerPermission(int request_id,
const GURL& url,
const FilePath& plugin_path);
+ void OnBrowserPluginNavigateGuest(int instance_id,
+ int64 frame_id,
+ const std::string& src,
+ const gfx::Size& size);
// Changes the IsLoading state and notifies delegate as needed
// |details| is used to provide details on the load that just finished
@@ -657,6 +675,9 @@ class CONTENT_EXPORT WebContentsImpl
std::string* embedder_channel_name,
int* embedder_container_id);
+ // Removes browser plugin embedder if there is one.
+ void RemoveBrowserPluginEmbedder();
+
// Data for core operation ---------------------------------------------------
// Delegate for notifying our owner about stuff. Not owned by us.
@@ -704,7 +725,7 @@ class CONTENT_EXPORT WebContentsImpl
java_bridge_dispatcher_host_manager_;
// TODO(fsamuel): Remove this once upstreaming of the new browser plugin
- // implmentation is complete.
+ // implementation is complete.
// Manages the browser plugin instances hosted by this WebContents.
scoped_ptr<content::old::BrowserPluginHost> old_browser_plugin_host_;
@@ -823,6 +844,13 @@ class CONTENT_EXPORT WebContentsImpl
// Color chooser that was opened by this tab.
content::ColorChooser* color_chooser_;
+ // Manages the embedder state for browser plugins, if this WebContents is an
+ // embedder; NULL otherwise.
+ scoped_ptr<content::BrowserPluginEmbedder> browser_plugin_embedder_;
+ // Manages the guest state for browser plugin, if this WebContents is a guest;
+ // NULL otherwise.
+ scoped_ptr<content::BrowserPluginGuest> browser_plugin_guest_;
+
// This must be at the end, or else we might get notifications and use other
// member variables that are gone.
content::NotificationRegistrar registrar_;
diff --git a/content/common/browser_plugin_messages.h b/content/common/browser_plugin_messages.h
index af68c71..17ddfff 100644
--- a/content/common/browser_plugin_messages.h
+++ b/content/common/browser_plugin_messages.h
@@ -54,10 +54,11 @@ IPC_MESSAGE_ROUTED3(BrowserPluginHostMsg_UpdateRect_ACK,
// that WebContents. If not, it will create the WebContents, associate it with
// the BrowserPlugin's browser-side BrowserPluginHost as a guest, and navigate
// it to the requested URL.
-IPC_MESSAGE_ROUTED3(BrowserPluginHostMsg_NavigateOrCreateGuest,
+IPC_MESSAGE_ROUTED4(BrowserPluginHostMsg_NavigateGuest,
int /* instance_id*/,
- long long /* frame_id */,
- std::string /* src */)
+ int64 /* frame_id */,
+ std::string /* src */,
+ gfx::Size /* size */)
// When a BrowserPlugin has been removed from the embedder's DOM, it informs
// the browser process to cleanup the guest.
@@ -71,6 +72,11 @@ IPC_STRUCT_BEGIN(BrowserPluginHostMsg_ResizeGuest_Params)
// A handle to the new buffer to use to transport damage to the
// embedder renderer process.
IPC_STRUCT_MEMBER(TransportDIB::Id, damage_buffer_id)
+#if defined(OS_WIN)
+ // The size of the damage buffer because this information is not available
+ // on Windows.
+ IPC_STRUCT_MEMBER(int, damage_buffer_size)
+#endif
// The new width of the plugin container.
IPC_STRUCT_MEMBER(int, width)
// The new height of the plugin container.
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 0c87839..42f26fa 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -236,6 +236,15 @@
'browser/browser_main_loop.cc',
'browser/browser_main_loop.h',
'browser/browser_main_runner.cc',
+ 'browser/browser_plugin/browser_plugin_embedder.cc',
+ 'browser/browser_plugin/browser_plugin_embedder.h',
+ 'browser/browser_plugin/browser_plugin_embedder_helper.cc',
+ 'browser/browser_plugin/browser_plugin_embedder_helper.h',
+ 'browser/browser_plugin/browser_plugin_guest.cc',
+ 'browser/browser_plugin/browser_plugin_guest.h',
+ 'browser/browser_plugin/browser_plugin_guest_helper.cc',
+ 'browser/browser_plugin/browser_plugin_guest_helper.h',
+ 'browser/browser_plugin/browser_plugin_host_factory.h',
'browser/browser_plugin/old/old_browser_plugin_host.cc',
'browser/browser_plugin/old/old_browser_plugin_host.h',
'browser/browser_plugin/old/old_browser_plugin_host_helper.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 8ef434f..c531e76 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -637,6 +637,11 @@
'browser/appcache/appcache_browsertest.cc',
'browser/audio_browsertest.cc',
'browser/bookmarklet_browsertest.cc',
+ 'browser/browser_plugin/browser_plugin_host_browsertest.cc',
+ 'browser/browser_plugin/test_browser_plugin_embedder.cc',
+ 'browser/browser_plugin/test_browser_plugin_embedder.h',
+ 'browser/browser_plugin/test_browser_plugin_guest.cc',
+ 'browser/browser_plugin/test_browser_plugin_guest.h',
'browser/child_process_security_policy_browsertest.cc',
'browser/database_browsertest.cc',
'browser/device_orientation/device_orientation_browsertest.cc',
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 8ba5421..fe3ff38 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -6,6 +6,9 @@
#include "base/message_loop.h"
#include "base/string_util.h"
+#if defined (OS_WIN)
+#include "base/sys_info.h"
+#endif
#include "content/common/browser_plugin_messages.h"
#include "content/public/common/content_client.h"
#include "content/public/renderer/content_renderer_client.h"
@@ -54,6 +57,7 @@ BrowserPlugin::BrowserPlugin(
sad_guest_(NULL),
guest_crashed_(false),
resize_pending_(false),
+ navigate_src_sent_(false),
parent_frame_(frame->identifier()) {
BrowserPluginManager::Get()->AddBrowserPlugin(instance_id, this);
bindings_.reset(new BrowserPluginBindings(this));
@@ -90,13 +94,21 @@ std::string BrowserPlugin::GetSrcAttribute() const {
void BrowserPlugin::SetSrcAttribute(const std::string& src) {
if (src == src_ && !guest_crashed_)
return;
- if (!src.empty()) {
+ if (!src.empty() || navigate_src_sent_) {
BrowserPluginManager::Get()->Send(
- new BrowserPluginHostMsg_NavigateOrCreateGuest(
+ new BrowserPluginHostMsg_NavigateGuest(
render_view_->GetRoutingID(),
instance_id_,
parent_frame_,
- src));
+ src,
+ gfx::Size(width(), height())));
+ // Record that we sent a NavigateGuest message to embedder. Once we send
+ // such a message, subsequent SetSrcAttribute() calls must always send
+ // NavigateGuest messages to the embedder (even if |src| is empty), so
+ // resize works correctly for all cases (e.g. The embedder can reset the
+ // guest's |src| to empty value, resize and then set the |src| to a
+ // non-empty value).
+ navigate_src_sent_ = true;
}
src_ = src;
guest_crashed_ = false;
@@ -310,8 +322,9 @@ void BrowserPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
paint.setStyle(SkPaint::kFill_Style);
paint.setColor(SK_ColorWHITE);
canvas->drawRect(image_data_rect, paint);
- // Stay at white if we have no src set, or we don't yet have a backing store.
- if (!backing_store_.get() || src_.empty())
+ // Stay at white if we have never set a non-empty src, or we don't yet have a
+ // backing store.
+ if (!backing_store_.get() || !navigate_src_sent_)
return;
float inverse_scale_factor = 1.0f / backing_store_->GetScaleFactor();
canvas->scale(inverse_scale_factor, inverse_scale_factor);
@@ -327,23 +340,51 @@ void BrowserPlugin::updateGeometry(
int old_height = height();
plugin_rect_ = window_rect;
if (old_width == window_rect.width &&
- old_height == window_rect.height)
+ old_height == window_rect.height) {
+ return;
+ }
+ // Until an actual navigation occurs, there is no browser side embedder
+ // present to notify about geometry updates. In this case, after we've updated
+ // the BrowserPlugin's state we are done and can return immediately.
+ if (!navigate_src_sent_)
return;
const size_t stride = skia::PlatformCanvas::StrideForWidth(window_rect.width);
- const size_t size = window_rect.height *
- stride *
- GetDeviceScaleFactor() *
- GetDeviceScaleFactor();
+ // Make sure the size of the damage buffer is at least four bytes so that we
+ // can fit in a magic word to verify that the memory is shared correctly.
+ size_t size =
+ std::max(sizeof(unsigned int),
+ static_cast<size_t>(window_rect.height *
+ stride *
+ GetDeviceScaleFactor() *
+ GetDeviceScaleFactor()));
// Don't drop the old damage buffer until after we've made sure that the
// browser process has dropped it.
- TransportDIB* new_damage_buffer =
- RenderProcess::current()->CreateTransportDIB(size);
- DCHECK(new_damage_buffer);
+ TransportDIB* new_damage_buffer = NULL;
+#if defined(OS_WIN)
+ size_t allocation_granularity = base::SysInfo::VMAllocationGranularity();
+ size_t shared_mem_size = size / allocation_granularity + 1;
+ shared_mem_size = shared_mem_size * allocation_granularity;
+
+ base::SharedMemory shared_mem;
+ if (!shared_mem.CreateAnonymous(shared_mem_size))
+ NOTREACHED() << "Unable to create shared memory of size:" << size;
+ new_damage_buffer = TransportDIB::Map(shared_mem.handle());
+#else
+ new_damage_buffer = RenderProcess::current()->CreateTransportDIB(size);
+#endif
+ if (!new_damage_buffer)
+ NOTREACHED() << "Unable to create damage buffer";
+ DCHECK(new_damage_buffer->memory());
+ // Insert the magic word.
+ *static_cast<unsigned int*>(new_damage_buffer->memory()) = 0xdeadbeef;
BrowserPluginHostMsg_ResizeGuest_Params params;
params.damage_buffer_id = new_damage_buffer->id();
+#if defined(OS_WIN)
+ params.damage_buffer_size = size;
+#endif
params.width = window_rect.width;
params.height = window_rect.height;
params.resize_pending = resize_pending_;
@@ -377,7 +418,7 @@ bool BrowserPlugin::acceptsInputEvents() {
bool BrowserPlugin::handleInputEvent(const WebKit::WebInputEvent& event,
WebKit::WebCursorInfo& cursor_info) {
- if (guest_crashed_ || src_.empty())
+ if (guest_crashed_ || !navigate_src_sent_)
return false;
bool handled = false;
WebCursor cursor;
diff --git a/content/renderer/browser_plugin/browser_plugin.h b/content/renderer/browser_plugin/browser_plugin.h
index 210774b..29f9b6d 100644
--- a/content/renderer/browser_plugin/browser_plugin.h
+++ b/content/renderer/browser_plugin/browser_plugin.h
@@ -9,6 +9,9 @@
#include "base/memory/scoped_ptr.h"
#include "base/sequenced_task_runner_helpers.h"
+#if defined(OS_WIN)
+#include "base/shared_memory.h"
+#endif
#include "content/renderer/browser_plugin/browser_plugin_backing_store.h"
#include "content/renderer/browser_plugin/browser_plugin_bindings.h"
#include "content/renderer/render_view_impl.h"
@@ -136,11 +139,16 @@ class CONTENT_EXPORT BrowserPlugin :
SkBitmap* sad_guest_;
bool guest_crashed_;
bool resize_pending_;
- long long parent_frame_;
+ // True if we have ever sent a NavigateGuest message to the embedder.
+ bool navigate_src_sent_;
+ int64 parent_frame_;
std::string src_;
typedef std::vector<v8::Persistent<v8::Function> > EventListeners;
typedef std::map<std::string, EventListeners> EventListenerMap;
EventListenerMap event_listener_map_;
+#if defined(OS_WIN)
+ base::SharedMemory shared_memory_;
+#endif
DISALLOW_COPY_AND_ASSIGN(BrowserPlugin);
};
diff --git a/content/renderer/browser_plugin/browser_plugin_browsertest.cc b/content/renderer/browser_plugin/browser_plugin_browsertest.cc
index 7afdd9a..6d23e0c 100644
--- a/content/renderer/browser_plugin/browser_plugin_browsertest.cc
+++ b/content/renderer/browser_plugin/browser_plugin_browsertest.cc
@@ -30,7 +30,7 @@ std::string GetHTMLForBrowserPluginObject() {
content::kBrowserPluginNewMimeType);
}
-}
+} // namespace
namespace content {
@@ -114,39 +114,43 @@ TEST_F(BrowserPluginTest, SrcAttribute) {
{
const IPC::Message* msg =
browser_plugin_manager()->sink().GetUniqueMessageMatching(
- BrowserPluginHostMsg_NavigateOrCreateGuest::ID);
+ BrowserPluginHostMsg_NavigateGuest::ID);
ASSERT_TRUE(msg);
int instance_id;
long long frame_id;
std::string src;
- BrowserPluginHostMsg_NavigateOrCreateGuest::Read(
+ gfx::Size size;
+ BrowserPluginHostMsg_NavigateGuest::Read(
msg,
&instance_id,
&frame_id,
- &src);
+ &src,
+ &size);
EXPECT_EQ("foo", src);
}
browser_plugin_manager()->sink().ClearMessages();
// Navigate to bar and observe the associated
- // BrowserPluginHostMsg_NavigateOrCreateGuest message.
+ // BrowserPluginHostMsg_NavigateGuest message.
// Verify that the src attribute is updated as well.
ExecuteJavaScript("document.getElementById('browserplugin').src = 'bar'");
{
const IPC::Message* msg =
browser_plugin_manager()->sink().GetUniqueMessageMatching(
- BrowserPluginHostMsg_NavigateOrCreateGuest::ID);
+ BrowserPluginHostMsg_NavigateGuest::ID);
ASSERT_TRUE(msg);
int instance_id;
long long frame_id;
std::string src;
- BrowserPluginHostMsg_NavigateOrCreateGuest::Read(
+ gfx::Size size;
+ BrowserPluginHostMsg_NavigateGuest::Read(
msg,
&instance_id,
&frame_id,
- &src);
+ &src,
+ &size);
EXPECT_EQ("bar", src);
std::string src_value =
ExecuteScriptAndReturnString(
diff --git a/content/renderer/browser_plugin/browser_plugin_manager_impl.cc b/content/renderer/browser_plugin/browser_plugin_manager_impl.cc
index 32d3573..4c256b0 100644
--- a/content/renderer/browser_plugin/browser_plugin_manager_impl.cc
+++ b/content/renderer/browser_plugin/browser_plugin_manager_impl.cc
@@ -36,7 +36,7 @@ bool BrowserPluginManagerImpl::OnControlMessageReceived(
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(BrowserPluginManagerImpl, message)
IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdateRect, OnUpdateRect)
- IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestCrashed,OnGuestCrashed)
+ IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestCrashed, OnGuestCrashed)
IPC_MESSAGE_HANDLER(BrowserPluginMsg_DidNavigate, OnDidNavigate)
IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus, OnAdvanceFocus)
IPC_MESSAGE_UNHANDLED(handled = false)
diff --git a/content/renderer/renderer_main.cc b/content/renderer/renderer_main.cc
index f6dc886..4a7deaa 100644
--- a/content/renderer/renderer_main.cc
+++ b/content/renderer/renderer_main.cc
@@ -24,6 +24,7 @@
#include "content/public/common/content_switches.h"
#include "content/public/common/main_function_params.h"
#include "content/public/renderer/content_renderer_client.h"
+#include "content/renderer/browser_plugin/browser_plugin_manager_impl.h"
#include "content/renderer/render_process_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/renderer_main_platform_delegate.h"
@@ -223,6 +224,7 @@ int RendererMain(const content::MainFunctionParams& parameters) {
RenderProcessImpl render_process;
new RenderThreadImpl();
#endif
+ new content::BrowserPluginManagerImpl();
platform.RunSandboxTests();
diff --git a/content/test/data/browser_plugin_embedder.html b/content/test/data/browser_plugin_embedder.html
new file mode 100644
index 0000000..31a7846
--- /dev/null
+++ b/content/test/data/browser_plugin_embedder.html
@@ -0,0 +1,18 @@
+<script type="text/javascript">
+function SetSrc(src) {
+ plugin = document.getElementById('plugin');
+ plugin.src = src;
+}
+function SetSize(w, h) {
+ plugin = document.getElementById('plugin');
+ plugin.width = w;
+ plugin.height = h;
+}
+</script>
+
+<object id="plugin"
+ tabindex="0"
+ type="application/new-browser-plugin"
+ width="640"
+ height="480"
+ border="0px"></object>
diff --git a/content/test/data/browser_plugin_embedder_crash.html b/content/test/data/browser_plugin_embedder_crash.html
new file mode 100644
index 0000000..fd066e5
--- /dev/null
+++ b/content/test/data/browser_plugin_embedder_crash.html
@@ -0,0 +1,17 @@
+<script type="text/javascript">
+function SetSrc(src) {
+ plugin = document.getElementById('plugin');
+ plugin.src = src;
+}
+function SetSize(w, h) {
+ plugin = document.getElementById('plugin');
+ plugin.width = w;
+ plugin.height = h;
+}
+</script>
+
+<!-- The plugin size is same as the browser's size -->
+<object id="plugin"
+ tabindex="0"
+ type="application/new-browser-plugin"
+ style="height: 100%; width: 100%; border: 0px"></object>
diff --git a/content/test/data/browser_plugin_focus.html b/content/test/data/browser_plugin_focus.html
new file mode 100644
index 0000000..280de3c
--- /dev/null
+++ b/content/test/data/browser_plugin_focus.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+<script type="text/javascript">
+ function SetSrc(src) {
+ plugin = document.getElementById('plugin');
+ plugin.src = src;
+ }
+</script>
+</head>
+
+<body>
+ <button id="before" tabindex="0">Before</button>
+ <object id="plugin"
+ tabindex="0"
+ type="application/new-browser-plugin"
+ width="640"
+ height="480"
+ border="0px"></object>
+ <button id="after" tabindex="0">After</button>
+</body>
+</html>
diff --git a/content/test/data/browser_plugin_focus_child.html b/content/test/data/browser_plugin_focus_child.html
new file mode 100644
index 0000000..133fc0d
--- /dev/null
+++ b/content/test/data/browser_plugin_focus_child.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ <button id="before" tabindex="0">Before</button>
+ <button id="after" tabindex="0">After</button>
+ </body>
+</html>
diff --git a/content/test/data/browser_plugin_title_change.html b/content/test/data/browser_plugin_title_change.html
new file mode 100644
index 0000000..0ad78e8a
--- /dev/null
+++ b/content/test/data/browser_plugin_title_change.html
@@ -0,0 +1,12 @@
+<html>
+ <head>
+ <script type="text/javascript">
+ function SetTitle(title) {
+ document.title = title;
+ }
+ </script>
+ </head>
+ <body>
+ <script>SetTitle('done');</script>
+ </body>
+</html>