summaryrefslogtreecommitdiff
path: root/chromium/content/browser/browser_plugin
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/browser_plugin')
-rw-r--r--chromium/content/browser/browser_plugin/OWNERS1
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_embedder.cc232
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_embedder.h139
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc94
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h44
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_guest.cc1667
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_guest.h534
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_guest_helper.cc55
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_guest_helper.h59
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc274
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_guest_manager.h137
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc814
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_host_factory.h41
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc72
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_message_filter.h45
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h34
-rw-r--r--chromium/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.mm23
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_embedder.cc40
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_embedder.h47
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_guest.cc250
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_guest.h108
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.cc35
-rw-r--r--chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.h51
23 files changed, 4796 insertions, 0 deletions
diff --git a/chromium/content/browser/browser_plugin/OWNERS b/chromium/content/browser/browser_plugin/OWNERS
new file mode 100644
index 00000000000..c92691f05e4
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/OWNERS
@@ -0,0 +1 @@
+fsamuel@chromium.org
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc b/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc
new file mode 100644
index 00000000000..4a06aca32c9
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_embedder.cc
@@ -0,0 +1,232 @@
+// 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 "base/values.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
+#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/browser_plugin/browser_plugin_constants.h"
+#include "content/common/browser_plugin/browser_plugin_messages.h"
+#include "content/common/drag_messages.h"
+#include "content/common/gpu/gpu_messages.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/user_metrics.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/result_codes.h"
+#include "content/public/common/url_constants.h"
+#include "net/base/escape.h"
+
+namespace content {
+
+// static
+BrowserPluginHostFactory* BrowserPluginEmbedder::factory_ = NULL;
+
+BrowserPluginEmbedder::BrowserPluginEmbedder(WebContentsImpl* web_contents)
+ : WebContentsObserver(web_contents),
+ next_get_render_view_request_id_(0) {
+}
+
+BrowserPluginEmbedder::~BrowserPluginEmbedder() {
+ CleanUp();
+}
+
+// static
+BrowserPluginEmbedder* BrowserPluginEmbedder::Create(
+ WebContentsImpl* web_contents) {
+ if (factory_)
+ return factory_->CreateBrowserPluginEmbedder(web_contents);
+ return new BrowserPluginEmbedder(web_contents);
+}
+
+void BrowserPluginEmbedder::DragEnteredGuest(BrowserPluginGuest* guest) {
+ guest_dragging_over_ = guest->AsWeakPtr();
+}
+
+void BrowserPluginEmbedder::DragLeftGuest(BrowserPluginGuest* guest) {
+ // Avoid race conditions in switching between guests being hovered over by
+ // only un-setting if the caller is marked as the guest being dragged over.
+ if (guest_dragging_over_.get() == guest) {
+ guest_dragging_over_.reset();
+ }
+}
+
+void BrowserPluginEmbedder::StartDrag(BrowserPluginGuest* guest) {
+ guest_started_drag_ = guest->AsWeakPtr();
+}
+
+void BrowserPluginEmbedder::StopDrag(BrowserPluginGuest* guest) {
+ if (guest_started_drag_.get() == guest) {
+ guest_started_drag_.reset();
+ }
+}
+
+void BrowserPluginEmbedder::GetRenderViewHostAtPosition(
+ int x, int y, const WebContents::GetRenderViewHostCallback& callback) {
+ // Store the callback so we can call it later when we have the response.
+ pending_get_render_view_callbacks_.insert(
+ std::make_pair(next_get_render_view_request_id_, callback));
+ Send(new BrowserPluginMsg_PluginAtPositionRequest(
+ routing_id(),
+ next_get_render_view_request_id_,
+ gfx::Point(x, y)));
+ ++next_get_render_view_request_id_;
+}
+
+void BrowserPluginEmbedder::DidSendScreenRects() {
+ GetBrowserPluginGuestManager()->DidSendScreenRects(
+ static_cast<WebContentsImpl*>(web_contents()));
+}
+
+bool BrowserPluginEmbedder::HandleKeyboardEvent(
+ const NativeWebKeyboardEvent& event) {
+ return GetBrowserPluginGuestManager()->UnlockMouseIfNecessary(
+ static_cast<WebContentsImpl*>(web_contents()), event);
+}
+
+void BrowserPluginEmbedder::RenderProcessGone(base::TerminationStatus status) {
+ CleanUp();
+}
+
+bool BrowserPluginEmbedder::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(BrowserPluginEmbedder, message)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_AllocateInstanceID,
+ OnAllocateInstanceID)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Attach, OnAttach)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginAtPositionResponse,
+ OnPluginAtPositionResponse)
+ IPC_MESSAGE_HANDLER_GENERIC(DragHostMsg_UpdateDragCursor,
+ OnUpdateDragCursor(&handled));
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void BrowserPluginEmbedder::DragSourceEndedAt(int client_x, int client_y,
+ int screen_x, int screen_y, WebKit::WebDragOperation operation) {
+ if (guest_started_drag_.get()) {
+ gfx::Point guest_offset =
+ guest_started_drag_->GetScreenCoordinates(gfx::Point());
+ guest_started_drag_->DragSourceEndedAt(client_x - guest_offset.x(),
+ client_y - guest_offset.y(), screen_x, screen_y, operation);
+ }
+}
+
+void BrowserPluginEmbedder::DragSourceMovedTo(int client_x, int client_y,
+ int screen_x, int screen_y) {
+ if (guest_started_drag_.get()) {
+ gfx::Point guest_offset =
+ guest_started_drag_->GetScreenCoordinates(gfx::Point());
+ guest_started_drag_->DragSourceMovedTo(client_x - guest_offset.x(),
+ client_y - guest_offset.y(), screen_x, screen_y);
+ }
+}
+
+void BrowserPluginEmbedder::SystemDragEnded() {
+ if (guest_started_drag_.get() &&
+ (guest_started_drag_.get() != guest_dragging_over_.get()))
+ guest_started_drag_->EndSystemDrag();
+ guest_started_drag_.reset();
+ guest_dragging_over_.reset();
+}
+
+void BrowserPluginEmbedder::OnUpdateDragCursor(bool* handled) {
+ *handled = (guest_dragging_over_.get() != NULL);
+}
+
+void BrowserPluginEmbedder::CleanUp() {
+ // CleanUp gets called when BrowserPluginEmbedder's WebContents goes away
+ // or the associated RenderViewHost is destroyed or swapped out. Therefore we
+ // don't need to care about the pending callbacks anymore.
+ pending_get_render_view_callbacks_.clear();
+}
+
+BrowserPluginGuestManager*
+ BrowserPluginEmbedder::GetBrowserPluginGuestManager() {
+ BrowserPluginGuestManager* guest_manager = static_cast<WebContentsImpl*>(
+ web_contents())->GetBrowserPluginGuestManager();
+ if (!guest_manager) {
+ guest_manager = BrowserPluginGuestManager::Create();
+ web_contents()->GetBrowserContext()->SetUserData(
+ browser_plugin::kBrowserPluginGuestManagerKeyName, guest_manager);
+ }
+ return guest_manager;
+}
+
+void BrowserPluginEmbedder::OnAllocateInstanceID(int request_id) {
+ int instance_id = GetBrowserPluginGuestManager()->get_next_instance_id();
+ Send(new BrowserPluginMsg_AllocateInstanceID_ACK(
+ routing_id(), request_id, instance_id));
+}
+
+void BrowserPluginEmbedder::OnAttach(
+ int instance_id,
+ const BrowserPluginHostMsg_Attach_Params& params,
+ const base::DictionaryValue& extra_params) {
+ if (!GetBrowserPluginGuestManager()->CanEmbedderAccessInstanceIDMaybeKill(
+ web_contents()->GetRenderProcessHost()->GetID(), instance_id))
+ return;
+
+ BrowserPluginGuest* guest =
+ GetBrowserPluginGuestManager()->GetGuestByInstanceID(
+ instance_id, web_contents()->GetRenderProcessHost()->GetID());
+
+
+ if (guest) {
+ // There is an implicit order expectation here:
+ // 1. The content embedder is made aware of the attachment.
+ // 2. BrowserPluginGuest::Attach is called.
+ // 3. The content embedder issues queued events if any that happened
+ // prior to attachment.
+ GetContentClient()->browser()->GuestWebContentsAttached(
+ guest->GetWebContents(),
+ web_contents(),
+ extra_params);
+ guest->Attach(static_cast<WebContentsImpl*>(web_contents()), params);
+ return;
+ }
+
+ scoped_ptr<base::DictionaryValue> copy_extra_params(extra_params.DeepCopy());
+ guest = GetBrowserPluginGuestManager()->CreateGuest(
+ web_contents()->GetSiteInstance(),
+ instance_id, params,
+ copy_extra_params.Pass());
+ if (guest) {
+ GetContentClient()->browser()->GuestWebContentsAttached(
+ guest->GetWebContents(),
+ web_contents(),
+ extra_params);
+ guest->Initialize(static_cast<WebContentsImpl*>(web_contents()), params);
+ }
+}
+
+void BrowserPluginEmbedder::OnPluginAtPositionResponse(
+ int instance_id, int request_id, const gfx::Point& position) {
+ const std::map<int, WebContents::GetRenderViewHostCallback>::iterator
+ callback_iter = pending_get_render_view_callbacks_.find(request_id);
+ if (callback_iter == pending_get_render_view_callbacks_.end())
+ return;
+
+ RenderViewHost* render_view_host;
+ BrowserPluginGuest* guest = NULL;
+ if (instance_id != browser_plugin::kInstanceIDNone) {
+ guest = GetBrowserPluginGuestManager()->GetGuestByInstanceID(
+ instance_id, web_contents()->GetRenderProcessHost()->GetID());
+ }
+
+ if (guest)
+ render_view_host = guest->GetWebContents()->GetRenderViewHost();
+ else // No plugin, use embedder's RenderViewHost.
+ render_view_host = web_contents()->GetRenderViewHost();
+
+ callback_iter->second.Run(render_view_host, position.x(), position.y());
+ pending_get_render_view_callbacks_.erase(callback_iter);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_embedder.h b/chromium/content/browser/browser_plugin/browser_plugin_embedder.h
new file mode 100644
index 00000000000..52610632f59
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_embedder.h
@@ -0,0 +1,139 @@
+// 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 handles messages coming from a BrowserPlugin's
+// embedder that are not directed at any particular existing guest process.
+// In the beginning, when a BrowserPlugin instance in the embedder renderer
+// process requests an initial navigation, the WebContents for that renderer
+// renderer creates a BrowserPluginEmbedder for itself. The
+// BrowserPluginEmbedder, in turn, forwards the requests to a
+// BrowserPluginGuestManager, which creates and manages the lifetime of the new
+// guest.
+
+#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_H_
+
+#include <map>
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "third_party/WebKit/public/web/WebDragOperation.h"
+
+struct BrowserPluginHostMsg_Attach_Params;
+struct BrowserPluginHostMsg_ResizeGuest_Params;
+
+namespace gfx {
+class Point;
+}
+
+namespace content {
+
+class BrowserPluginGuest;
+class BrowserPluginGuestManager;
+class BrowserPluginHostFactory;
+class RenderWidgetHostImpl;
+class WebContentsImpl;
+struct NativeWebKeyboardEvent;
+
+class CONTENT_EXPORT BrowserPluginEmbedder : public WebContentsObserver {
+ public:
+ virtual ~BrowserPluginEmbedder();
+
+ static BrowserPluginEmbedder* Create(WebContentsImpl* web_contents);
+
+ // Returns the RenderViewHost at a point (|x|, |y|) asynchronously via
+ // |callback|. We need a roundtrip to renderer process to get this
+ // information.
+ void GetRenderViewHostAtPosition(
+ int x,
+ int y,
+ const WebContents::GetRenderViewHostCallback& callback);
+
+ // Called when embedder's |rwh| has sent screen rects to renderer.
+ void DidSendScreenRects();
+
+ // Called when embedder's WebContentsImpl has unhandled keyboard input.
+ // Returns whether the BrowserPlugin has handled the keyboard event.
+ // Currently we are only interested in checking for the escape key to
+ // unlock hte guest's pointer lock.
+ bool HandleKeyboardEvent(const NativeWebKeyboardEvent& event);
+
+ // Overrides factory for testing. Default (NULL) value indicates regular
+ // (non-test) environment.
+ static void set_factory_for_testing(BrowserPluginHostFactory* factory) {
+ factory_ = factory;
+ }
+
+ // WebContentsObserver implementation.
+ virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ void DragSourceEndedAt(int client_x, int client_y, int screen_x,
+ int screen_y, WebKit::WebDragOperation operation);
+
+ void DragSourceMovedTo(int client_x, int client_y,
+ int screen_x, int screen_y);
+
+ void OnUpdateDragCursor(bool* handled);
+
+ void DragEnteredGuest(BrowserPluginGuest* guest);
+
+ void DragLeftGuest(BrowserPluginGuest* guest);
+
+ void StartDrag(BrowserPluginGuest* guest);
+
+ void StopDrag(BrowserPluginGuest* guest);
+
+ void SystemDragEnded();
+
+ private:
+ friend class TestBrowserPluginEmbedder;
+
+ BrowserPluginEmbedder(WebContentsImpl* web_contents);
+
+ void CleanUp();
+
+ BrowserPluginGuestManager* GetBrowserPluginGuestManager();
+
+ // Message handlers.
+
+ void OnAllocateInstanceID(int request_id);
+ void OnAttach(int instance_id,
+ const BrowserPluginHostMsg_Attach_Params& params,
+ const base::DictionaryValue& extra_params);
+ void OnPluginAtPositionResponse(int instance_id,
+ int request_id,
+ const gfx::Point& position);
+
+ // Static factory instance (always NULL for non-test).
+ static BrowserPluginHostFactory* factory_;
+
+ // Map that contains outstanding queries to |GetRenderViewHostAtPosition|.
+ // We need a roundtrip to the renderer process to retrieve the answer,
+ // so we store these callbacks until we hear back from the renderer.
+ typedef std::map<int, WebContents::GetRenderViewHostCallback>
+ GetRenderViewHostCallbackMap;
+ GetRenderViewHostCallbackMap pending_get_render_view_callbacks_;
+ // Next request id for BrowserPluginMsg_PluginAtPositionRequest query.
+ int next_get_render_view_request_id_;
+
+ // Used to correctly update the cursor when dragging over a guest, and to
+ // handle a race condition when dropping onto the guest that started the drag
+ // (the race is that the dragend message arrives before the drop message so
+ // the drop never takes place).
+ // crbug.com/233571
+ base::WeakPtr<BrowserPluginGuest> guest_dragging_over_;
+
+ // Pointer to the guest that started the drag, used to forward necessary drag
+ // status messages to the correct guest.
+ base::WeakPtr<BrowserPluginGuest> guest_started_drag_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginEmbedder);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_H_
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc b/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc
new file mode 100644
index 00000000000..8a803c47120
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.cc
@@ -0,0 +1,94 @@
+// Copyright 2013 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_geolocation_permission_context.h"
+
+#include "base/bind.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+
+namespace content {
+
+BrowserPluginGeolocationPermissionContext::
+ BrowserPluginGeolocationPermissionContext() {
+}
+
+BrowserPluginGeolocationPermissionContext::
+ ~BrowserPluginGeolocationPermissionContext() {
+}
+
+void BrowserPluginGeolocationPermissionContext::RequestGeolocationPermission(
+ int render_process_id,
+ int render_view_id,
+ int bridge_id,
+ const GURL& requesting_frame,
+ base::Callback<void(bool)> callback) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &BrowserPluginGeolocationPermissionContext::
+ RequestGeolocationPermission,
+ this,
+ render_process_id,
+ render_view_id,
+ bridge_id,
+ requesting_frame,
+ callback));
+ return;
+ }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // Note that callback.Run(true) allows geolocation access, callback.Run(false)
+ // denies geolocation access.
+ // We need to go to the renderer to ask embedder's js if we are allowed to
+ // have geolocation access.
+ RenderViewHost* rvh = RenderViewHost::FromID(render_process_id,
+ render_view_id);
+ if (rvh) {
+ DCHECK(rvh->GetProcess()->IsGuest());
+ WebContentsImpl* guest_web_contents =
+ static_cast<WebContentsImpl*>(rvh->GetDelegate()->GetAsWebContents());
+ BrowserPluginGuest* guest = guest_web_contents->GetBrowserPluginGuest();
+ guest->AskEmbedderForGeolocationPermission(bridge_id,
+ requesting_frame,
+ callback);
+ }
+}
+
+void BrowserPluginGeolocationPermissionContext::
+ CancelGeolocationPermissionRequest(int render_process_id,
+ int render_view_id,
+ int bridge_id,
+ const GURL& requesting_frame) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &BrowserPluginGeolocationPermissionContext::
+ CancelGeolocationPermissionRequest,
+ this,
+ render_process_id,
+ render_view_id,
+ bridge_id,
+ requesting_frame));
+ return;
+ }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ RenderViewHost* rvh = RenderViewHost::FromID(render_process_id,
+ render_view_id);
+ if (rvh) {
+ DCHECK(rvh->GetProcess()->IsGuest());
+ WebContentsImpl* guest_web_contents =
+ static_cast<WebContentsImpl*>(rvh->GetDelegate()->GetAsWebContents());
+ BrowserPluginGuest* guest = guest_web_contents->GetBrowserPluginGuest();
+ if (guest)
+ guest->CancelGeolocationRequest(bridge_id);
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h b/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h
new file mode 100644
index 00000000000..27cc9e3e4a7
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_geolocation_permission_context.h
@@ -0,0 +1,44 @@
+// Copyright 2013 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_GEOLOCATION_PERMISSION_CONTEXT_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GEOLOCATION_PERMISSION_CONTEXT_H_
+
+#include "content/public/browser/geolocation_permission_context.h"
+
+namespace content {
+
+// Browser plugin specific implementation of GeolocationPermissionContext.
+// It manages Geolocation permissions flow for BrowserPluginGuest. When a guest
+// requests gelocation permission, it delegates the request to embedder though
+// embedder's javascript api.
+// This runs on the I/O thread. We have to return to UI thread to talk to a
+// BrowserPluginGuest.
+class BrowserPluginGeolocationPermissionContext :
+ public GeolocationPermissionContext {
+ public:
+ BrowserPluginGeolocationPermissionContext();
+
+ // GeolocationPermissionContext implementation:
+ virtual void RequestGeolocationPermission(
+ int render_process_id,
+ int render_view_id,
+ int bridge_id,
+ const GURL& requesting_frame,
+ base::Callback<void(bool)> callback) OVERRIDE;
+ virtual void CancelGeolocationPermissionRequest(
+ int render_process_id,
+ int render_view_id,
+ int bridge_id,
+ const GURL& requesting_frame) OVERRIDE;
+
+ private:
+ virtual ~BrowserPluginGeolocationPermissionContext();
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginGeolocationPermissionContext);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GEOLOCATION_PERMISSION_CONTEXT_H_
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest.cc b/chromium/content/browser/browser_plugin/browser_plugin_guest.cc
new file mode 100644
index 00000000000..31250b7d626
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -0,0 +1,1667 @@
+// 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 "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/browser_plugin/browser_plugin_embedder.h"
+#include "content/browser/browser_plugin/browser_plugin_guest_helper.h"
+#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
+#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
+#include "content/browser/browser_thread_impl.h"
+#include "content/browser/loader/resource_dispatcher_host_impl.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/browser/web_contents/web_contents_view_guest.h"
+#include "content/common/browser_plugin/browser_plugin_constants.h"
+#include "content/common/browser_plugin/browser_plugin_messages.h"
+#include "content/common/content_constants_internal.h"
+#include "content/common/drag_messages.h"
+#include "content/common/gpu/gpu_messages.h"
+#include "content/common/input_messages.h"
+#include "content/common/view_messages.h"
+#include "content/port/browser/render_view_host_delegate_view.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/geolocation_permission_context.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/resource_request_details.h"
+#include "content/public/browser/user_metrics.h"
+#include "content/public/browser/web_contents_view.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/drop_data.h"
+#include "content/public/common/media_stream_request.h"
+#include "content/public/common/result_codes.h"
+#include "net/url_request/url_request.h"
+#include "third_party/WebKit/public/web/WebCursorInfo.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/surface/transport_dib.h"
+#include "webkit/common/resource_type.h"
+
+#if defined(OS_MACOSX)
+#include "content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h"
+#endif
+
+namespace content {
+
+// static
+BrowserPluginHostFactory* BrowserPluginGuest::factory_ = NULL;
+
+// Parent class for the various types of permission requests, each of which
+// should be able to handle the response to their permission request.
+class BrowserPluginGuest::PermissionRequest :
+ public base::RefCounted<BrowserPluginGuest::PermissionRequest> {
+ public:
+ virtual void Respond(bool should_allow, const std::string& user_input) = 0;
+ protected:
+ PermissionRequest() {
+ RecordAction(UserMetricsAction("BrowserPlugin.Guest.PermissionRequest"));
+ }
+ virtual ~PermissionRequest() {}
+ // Friend RefCounted so that the dtor can be non-public.
+ friend class base::RefCounted<BrowserPluginGuest::PermissionRequest>;
+};
+
+class BrowserPluginGuest::DownloadRequest : public PermissionRequest {
+ public:
+ explicit DownloadRequest(base::Callback<void(bool)> callback)
+ : callback_(callback) {
+ RecordAction(
+ UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Download"));
+ }
+ virtual void Respond(bool should_allow,
+ const std::string& user_input) OVERRIDE {
+ callback_.Run(should_allow);
+ }
+
+ private:
+ virtual ~DownloadRequest() {}
+ base::Callback<void(bool)> callback_;
+};
+
+class BrowserPluginGuest::GeolocationRequest : public PermissionRequest {
+ public:
+ GeolocationRequest(GeolocationCallback callback,
+ int bridge_id,
+ base::WeakPtrFactory<BrowserPluginGuest>* weak_ptr_factory)
+ : callback_(callback),
+ bridge_id_(bridge_id),
+ weak_ptr_factory_(weak_ptr_factory) {
+ RecordAction(
+ UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Geolocation"));
+ }
+
+ virtual void Respond(bool should_allow,
+ const std::string& user_input) OVERRIDE {
+ base::WeakPtr<BrowserPluginGuest> guest(weak_ptr_factory_->GetWeakPtr());
+
+ WebContents* web_contents = guest->embedder_web_contents();
+ if (should_allow && web_contents) {
+ // If renderer side embedder decides to allow gelocation, we need to check
+ // if the app/embedder itself has geolocation access.
+ BrowserContext* browser_context = web_contents->GetBrowserContext();
+ if (browser_context) {
+ GeolocationPermissionContext* geolocation_context =
+ browser_context->GetGeolocationPermissionContext();
+ if (geolocation_context) {
+ base::Callback<void(bool)> geolocation_callback = base::Bind(
+ &BrowserPluginGuest::SetGeolocationPermission,
+ guest,
+ callback_,
+ bridge_id_);
+ geolocation_context->RequestGeolocationPermission(
+ web_contents->GetRenderProcessHost()->GetID(),
+ web_contents->GetRoutingID(),
+ // The geolocation permission request here is not initiated
+ // through WebGeolocationPermissionRequest. We are only interested
+ // in the fact whether the embedder/app has geolocation
+ // permission. Therefore we use an invalid |bridge_id|.
+ -1 /* bridge_id */,
+ web_contents->GetLastCommittedURL(),
+ geolocation_callback);
+ return;
+ }
+ }
+ }
+ guest->SetGeolocationPermission(callback_, bridge_id_, false);
+ }
+
+ private:
+ virtual ~GeolocationRequest() {}
+ base::Callback<void(bool)> callback_;
+ int bridge_id_;
+ base::WeakPtrFactory<BrowserPluginGuest>* weak_ptr_factory_;
+};
+
+class BrowserPluginGuest::MediaRequest : public PermissionRequest {
+ public:
+ MediaRequest(const MediaStreamRequest& request,
+ const MediaResponseCallback& callback,
+ BrowserPluginGuest* guest)
+ : request_(request),
+ callback_(callback),
+ guest_(guest) {
+ RecordAction(
+ UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.Media"));
+ }
+
+ virtual void Respond(bool should_allow,
+ const std::string& user_input) OVERRIDE {
+ WebContentsImpl* web_contents = guest_->embedder_web_contents();
+ if (should_allow && web_contents) {
+ // Re-route the request to the embedder's WebContents; the guest gets the
+ // permission this way.
+ web_contents->RequestMediaAccessPermission(request_, callback_);
+ } else {
+ // Deny the request.
+ callback_.Run(MediaStreamDevices(), scoped_ptr<MediaStreamUI>());
+ }
+ }
+
+ private:
+ virtual ~MediaRequest() {}
+ MediaStreamRequest request_;
+ MediaResponseCallback callback_;
+ BrowserPluginGuest* guest_;
+};
+
+class BrowserPluginGuest::NewWindowRequest : public PermissionRequest {
+ public:
+ NewWindowRequest(int instance_id, BrowserPluginGuest* guest)
+ : instance_id_(instance_id),
+ guest_(guest) {
+ RecordAction(
+ UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.NewWindow"));
+ }
+
+ virtual void Respond(bool should_allow,
+ const std::string& user_input) OVERRIDE {
+ int embedder_render_process_id =
+ guest_->embedder_web_contents()->GetRenderProcessHost()->GetID();
+ BrowserPluginGuest* guest =
+ guest_->GetWebContents()->GetBrowserPluginGuestManager()->
+ GetGuestByInstanceID(instance_id_, embedder_render_process_id);
+ if (!guest) {
+ LOG(INFO) << "Guest not found. Instance ID: " << instance_id_;
+ return;
+ }
+
+ // If we do not destroy the guest then we allow the new window.
+ if (!should_allow)
+ guest->Destroy();
+ }
+
+ private:
+ virtual ~NewWindowRequest() {}
+ int instance_id_;
+ BrowserPluginGuest* guest_;
+};
+
+class BrowserPluginGuest::JavaScriptDialogRequest : public PermissionRequest {
+ public:
+ JavaScriptDialogRequest(const DialogClosedCallback& callback)
+ : callback_(callback) {
+ RecordAction(
+ UserMetricsAction(
+ "BrowserPlugin.Guest.PermissionRequest.JavaScriptDialog"));
+ }
+
+ virtual void Respond(bool should_allow,
+ const std::string& user_input) OVERRIDE {
+ callback_.Run(should_allow, UTF8ToUTF16(user_input));
+ }
+
+ private:
+ virtual ~JavaScriptDialogRequest() {}
+ DialogClosedCallback callback_;
+};
+
+class BrowserPluginGuest::PointerLockRequest : public PermissionRequest {
+ public:
+ PointerLockRequest(BrowserPluginGuest* guest)
+ : guest_(guest) {
+ RecordAction(
+ UserMetricsAction("BrowserPlugin.Guest.PermissionRequest.PointerLock"));
+ }
+
+ virtual void Respond(bool should_allow,
+ const std::string& user_input) OVERRIDE {
+ guest_->SendMessageToEmbedder(
+ new BrowserPluginMsg_SetMouseLock(guest_->instance_id(), should_allow));
+ }
+
+ private:
+ virtual ~PointerLockRequest() {}
+ BrowserPluginGuest* guest_;
+};
+
+namespace {
+const size_t kNumMaxOutstandingPermissionRequests = 1024;
+
+std::string WindowOpenDispositionToString(
+ WindowOpenDisposition window_open_disposition) {
+ switch (window_open_disposition) {
+ case IGNORE_ACTION:
+ return "ignore";
+ case SAVE_TO_DISK:
+ return "save_to_disk";
+ case CURRENT_TAB:
+ return "current_tab";
+ case NEW_BACKGROUND_TAB:
+ return "new_background_tab";
+ case NEW_FOREGROUND_TAB:
+ return "new_foreground_tab";
+ case NEW_WINDOW:
+ return "new_window";
+ case NEW_POPUP:
+ return "new_popup";
+ default:
+ NOTREACHED() << "Unknown Window Open Disposition";
+ return "ignore";
+ }
+}
+
+std::string JavaScriptMessageTypeToString(JavaScriptMessageType message_type) {
+ switch (message_type) {
+ case JAVASCRIPT_MESSAGE_TYPE_ALERT:
+ return "alert";
+ case JAVASCRIPT_MESSAGE_TYPE_CONFIRM:
+ return "confirm";
+ case JAVASCRIPT_MESSAGE_TYPE_PROMPT:
+ return "prompt";
+ default:
+ NOTREACHED() << "Unknown JavaScript Message Type.";
+ return "unknown";
+ }
+}
+
+// Called on IO thread.
+static std::string RetrieveDownloadURLFromRequestId(
+ RenderViewHost* render_view_host,
+ int url_request_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ int render_process_id = render_view_host->GetProcess()->GetID();
+ GlobalRequestID global_id(render_process_id, url_request_id);
+ net::URLRequest* url_request =
+ ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id);
+ if (url_request)
+ return url_request->url().possibly_invalid_spec();
+ return std::string();
+}
+
+} // namespace
+
+class BrowserPluginGuest::EmbedderRenderViewHostObserver
+ : public RenderViewHostObserver {
+ public:
+ explicit EmbedderRenderViewHostObserver(BrowserPluginGuest* guest)
+ : RenderViewHostObserver(
+ guest->embedder_web_contents()->GetRenderViewHost()),
+ browser_plugin_guest_(guest) {
+ }
+
+ virtual ~EmbedderRenderViewHostObserver() {
+ }
+
+ // RenderViewHostObserver:
+ virtual void RenderViewHostDestroyed(
+ RenderViewHost* render_view_host) OVERRIDE {
+ browser_plugin_guest_->embedder_web_contents_ = NULL;
+ browser_plugin_guest_->Destroy();
+ }
+
+ private:
+ BrowserPluginGuest* browser_plugin_guest_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmbedderRenderViewHostObserver);
+};
+
+BrowserPluginGuest::BrowserPluginGuest(
+ int instance_id,
+ WebContentsImpl* web_contents,
+ BrowserPluginGuest* opener,
+ bool has_render_view)
+ : WebContentsObserver(web_contents),
+ weak_ptr_factory_(this),
+ embedder_web_contents_(NULL),
+ instance_id_(instance_id),
+ damage_buffer_sequence_id_(0),
+ damage_buffer_size_(0),
+ damage_buffer_scale_factor_(1.0f),
+ guest_device_scale_factor_(1.0f),
+ guest_hang_timeout_(
+ base::TimeDelta::FromMilliseconds(kHungRendererDelayMs)),
+ focused_(false),
+ mouse_locked_(false),
+ pending_lock_request_(false),
+ embedder_visible_(true),
+ next_permission_request_id_(browser_plugin::kInvalidPermissionRequestID),
+ has_render_view_(has_render_view),
+ last_seen_auto_size_enabled_(false) {
+ DCHECK(web_contents);
+ web_contents->SetDelegate(this);
+ if (opener)
+ opener_ = opener->AsWeakPtr();
+ GetWebContents()->GetBrowserPluginGuestManager()->AddGuest(instance_id_,
+ GetWebContents());
+}
+
+bool BrowserPluginGuest::AddMessageToConsole(WebContents* source,
+ int32 level,
+ const string16& message,
+ int32 line_no,
+ const string16& source_id) {
+ if (!delegate_)
+ return false;
+
+ delegate_->AddMessageToConsole(level, message, line_no, source_id);
+ return true;
+}
+
+void BrowserPluginGuest::DestroyUnattachedWindows() {
+ // Destroy() reaches in and removes the BrowserPluginGuest from its opener's
+ // pending_new_windows_ set. To avoid mutating the set while iterating, we
+ // create a copy of the pending new windows set and iterate over the copy.
+ PendingWindowMap pending_new_windows(pending_new_windows_);
+ // Clean up unattached new windows opened by this guest.
+ for (PendingWindowMap::const_iterator it = pending_new_windows.begin();
+ it != pending_new_windows.end(); ++it) {
+ it->first->Destroy();
+ }
+ // All pending windows should be removed from the set after Destroy() is
+ // called on all of them.
+ DCHECK_EQ(0ul, pending_new_windows_.size());
+}
+
+void BrowserPluginGuest::RespondToPermissionRequest(
+ int request_id,
+ bool should_allow,
+ const std::string& user_input) {
+ RequestMap::iterator request_itr = permission_request_map_.find(request_id);
+ if (request_itr == permission_request_map_.end()) {
+ LOG(INFO) << "Not a valid request ID.";
+ return;
+ }
+ request_itr->second->Respond(should_allow, user_input);
+ permission_request_map_.erase(request_itr);
+}
+
+int BrowserPluginGuest::RequestPermission(
+ BrowserPluginPermissionType permission_type,
+ scoped_refptr<BrowserPluginGuest::PermissionRequest> request,
+ const base::DictionaryValue& request_info) {
+ if (!delegate_) {
+ request->Respond(false, "");
+ return browser_plugin::kInvalidPermissionRequestID;
+ }
+
+ int request_id = ++next_permission_request_id_;
+ permission_request_map_[request_id] = request;
+
+ BrowserPluginGuestDelegate::PermissionResponseCallback callback =
+ base::Bind(&BrowserPluginGuest::RespondToPermissionRequest,
+ AsWeakPtr(),
+ request_id);
+ // If BrowserPluginGuestDelegate hasn't handled the permission then we simply
+ // reject it immediately.
+ if (!delegate_->RequestPermission(permission_type, request_info, callback))
+ callback.Run(false, "");
+
+ return request_id;
+}
+
+void BrowserPluginGuest::Destroy() {
+ if (!attached() && opener())
+ opener()->pending_new_windows_.erase(this);
+ DestroyUnattachedWindows();
+ GetWebContents()->GetBrowserPluginGuestManager()->RemoveGuest(instance_id_);
+ delete GetWebContents();
+}
+
+bool BrowserPluginGuest::OnMessageReceivedFromEmbedder(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuest, message)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_BuffersSwappedACK,
+ OnSwapBuffersACK)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_CompositorFrameACK,
+ OnCompositorFrameACK)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_DragStatusUpdate,
+ OnDragStatusUpdate)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ExecuteEditCommand,
+ OnExecuteEditCommand)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_HandleInputEvent,
+ OnHandleInputEvent)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_LockMouse_ACK, OnLockMouseAck)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_NavigateGuest, OnNavigateGuest)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginDestroyed, OnPluginDestroyed)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_ResizeGuest, OnResizeGuest)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetAutoSize, OnSetSize)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent,
+ OnSetEditCommandsForNextKeyEvent)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetFocus, OnSetFocus)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetName, OnSetName)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetVisibility, OnSetVisibility)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UnlockMouse_ACK, OnUnlockMouseAck)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UpdateGeometry, OnUpdateGeometry)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UpdateRect_ACK, OnUpdateRectACK)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void BrowserPluginGuest::Initialize(
+ WebContentsImpl* embedder_web_contents,
+ const BrowserPluginHostMsg_Attach_Params& params) {
+ focused_ = params.focused;
+ guest_visible_ = params.visible;
+ guest_window_rect_ = params.resize_guest_params.view_rect;
+
+ if (!params.name.empty())
+ name_ = params.name;
+ auto_size_enabled_ = params.auto_size_params.enable;
+ max_auto_size_ = params.auto_size_params.max_size;
+ min_auto_size_ = params.auto_size_params.min_size;
+
+ // Once a BrowserPluginGuest has an embedder WebContents, it's considered to
+ // be attached.
+ embedder_web_contents_ = embedder_web_contents;
+
+ WebContentsViewGuest* new_view =
+ static_cast<WebContentsViewGuest*>(GetWebContents()->GetView());
+ new_view->OnGuestInitialized(embedder_web_contents->GetView());
+
+ // |render_view_host| manages the ownership of this BrowserPluginGuestHelper.
+ new BrowserPluginGuestHelper(this, GetWebContents()->GetRenderViewHost());
+
+ RendererPreferences* renderer_prefs =
+ GetWebContents()->GetMutableRendererPrefs();
+ // Copy renderer preferences (and nothing else) from the embedder's
+ // WebContents to the guest.
+ //
+ // For GTK and Aura this is necessary to get proper renderer configuration
+ // values for caret blinking interval, colors related to selection and
+ // focus.
+ *renderer_prefs = *embedder_web_contents_->GetMutableRendererPrefs();
+
+ // We would like the guest to report changes to frame names so that we can
+ // update the BrowserPlugin's corresponding 'name' attribute.
+ // TODO(fsamuel): Remove this once http://crbug.com/169110 is addressed.
+ renderer_prefs->report_frame_name_changes = true;
+ // Navigation is disabled in Chrome Apps. We want to make sure guest-initiated
+ // navigations still continue to function inside the app.
+ renderer_prefs->browser_handles_all_top_level_requests = false;
+
+ // Listen to embedder visibility changes so that the guest is in a 'shown'
+ // state if both the embedder is visible and the BrowserPlugin is marked as
+ // visible.
+ notification_registrar_.Add(
+ this, NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED,
+ Source<WebContents>(embedder_web_contents_));
+
+ embedder_rvh_observer_.reset(new EmbedderRenderViewHostObserver(this));
+
+ OnSetSize(instance_id_, params.auto_size_params, params.resize_guest_params);
+
+ // Create a swapped out RenderView for the guest in the embedder render
+ // process, so that the embedder can access the guest's window object.
+ int guest_routing_id =
+ GetWebContents()->CreateSwappedOutRenderView(
+ embedder_web_contents_->GetSiteInstance());
+ SendMessageToEmbedder(
+ new BrowserPluginMsg_GuestContentWindowReady(instance_id_,
+ guest_routing_id));
+
+ if (!params.src.empty())
+ OnNavigateGuest(instance_id_, params.src);
+
+ has_render_view_ = true;
+
+ if (!embedder_web_contents_->
+ GetWebkitPrefs().accelerated_compositing_enabled) {
+ WebPreferences prefs = GetWebContents()->GetWebkitPrefs();
+ prefs.accelerated_compositing_enabled = false;
+ GetWebContents()->GetRenderViewHost()->UpdateWebkitPreferences(prefs);
+ }
+
+ // Enable input method for guest if it's enabled for the embedder.
+ if (static_cast<RenderViewHostImpl*>(
+ embedder_web_contents_->GetRenderViewHost())->input_method_active()) {
+ RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
+ GetWebContents()->GetRenderViewHost());
+ guest_rvh->SetInputMethodActive(true);
+ }
+}
+
+BrowserPluginGuest::~BrowserPluginGuest() {
+ while (!pending_messages_.empty()) {
+ delete pending_messages_.front();
+ pending_messages_.pop();
+ }
+}
+
+// static
+BrowserPluginGuest* BrowserPluginGuest::Create(
+ int instance_id,
+ WebContentsImpl* web_contents,
+ scoped_ptr<base::DictionaryValue> extra_params) {
+ RecordAction(UserMetricsAction("BrowserPlugin.Guest.Create"));
+ BrowserPluginGuest* guest = NULL;
+ if (factory_) {
+ guest = factory_->CreateBrowserPluginGuest(instance_id, web_contents);
+ } else {
+ guest = new BrowserPluginGuest(instance_id, web_contents, NULL, false);
+ }
+ web_contents->SetBrowserPluginGuest(guest);
+ BrowserPluginGuestDelegate* delegate = NULL;
+ GetContentClient()->browser()->GuestWebContentsCreated(
+ web_contents, NULL, &delegate, extra_params.Pass());
+ guest->SetDelegate(delegate);
+ return guest;
+}
+
+// static
+BrowserPluginGuest* BrowserPluginGuest::CreateWithOpener(
+ int instance_id,
+ WebContentsImpl* web_contents,
+ BrowserPluginGuest* opener,
+ bool has_render_view) {
+ BrowserPluginGuest* guest =
+ new BrowserPluginGuest(
+ instance_id, web_contents, opener, has_render_view);
+ web_contents->SetBrowserPluginGuest(guest);
+ BrowserPluginGuestDelegate* delegate = NULL;
+ GetContentClient()->browser()->GuestWebContentsCreated(
+ web_contents, opener->GetWebContents(), &delegate,
+ scoped_ptr<base::DictionaryValue>());
+ guest->SetDelegate(delegate);
+ return guest;
+}
+
+RenderWidgetHostView* BrowserPluginGuest::GetEmbedderRenderWidgetHostView() {
+ return embedder_web_contents_->GetRenderWidgetHostView();
+}
+
+void BrowserPluginGuest::UpdateVisibility() {
+ OnSetVisibility(instance_id_, visible());
+}
+
+// screen.
+gfx::Rect BrowserPluginGuest::ToGuestRect(const gfx::Rect& bounds) {
+ gfx::Rect guest_rect(bounds);
+ guest_rect.Offset(guest_window_rect_.OffsetFromOrigin());
+ return guest_rect;
+}
+
+void BrowserPluginGuest::Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED: {
+ DCHECK_EQ(Source<WebContents>(source).ptr(), embedder_web_contents_);
+ embedder_visible_ = *Details<bool>(details).ptr();
+ UpdateVisibility();
+ break;
+ }
+ default:
+ NOTREACHED() << "Unexpected notification sent.";
+ break;
+ }
+}
+
+void BrowserPluginGuest::AddNewContents(WebContents* source,
+ WebContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture,
+ bool* was_blocked) {
+ if (was_blocked)
+ *was_blocked = false;
+ RequestNewWindowPermission(static_cast<WebContentsImpl*>(new_contents),
+ disposition, initial_pos, user_gesture);
+}
+
+void BrowserPluginGuest::CanDownload(
+ RenderViewHost* render_view_host,
+ int request_id,
+ const std::string& request_method,
+ const base::Callback<void(bool)>& callback) {
+ if (permission_request_map_.size() >= kNumMaxOutstandingPermissionRequests) {
+ // Deny the download request.
+ callback.Run(false);
+ return;
+ }
+
+ BrowserThread::PostTaskAndReplyWithResult(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&RetrieveDownloadURLFromRequestId,
+ render_view_host, request_id),
+ base::Bind(&BrowserPluginGuest::DidRetrieveDownloadURLFromRequestId,
+ weak_ptr_factory_.GetWeakPtr(),
+ request_method,
+ callback));
+}
+
+void BrowserPluginGuest::CloseContents(WebContents* source) {
+ if (!delegate_)
+ return;
+
+ delegate_->Close();
+}
+
+JavaScriptDialogManager* BrowserPluginGuest::GetJavaScriptDialogManager() {
+ return this;
+}
+
+bool BrowserPluginGuest::HandleContextMenu(const ContextMenuParams& params) {
+ // TODO(fsamuel): We show the regular page context menu handler for now until
+ // we implement the Apps Context Menu API for Browser Plugin (see
+ // http://crbug.com/140315).
+ return false; // Will be handled by WebContentsViewGuest.
+}
+
+void BrowserPluginGuest::HandleKeyboardEvent(
+ WebContents* source,
+ const NativeWebKeyboardEvent& event) {
+ if (!attached())
+ return;
+
+ if (UnlockMouseIfNecessary(event))
+ return;
+
+ if (delegate_ && delegate_->HandleKeyboardEvent(event))
+ return;
+
+ // Send the unhandled keyboard events back to the embedder to reprocess them.
+ // TODO(fsamuel): This introduces the possibility of out-of-order keyboard
+ // events because the guest may be arbitrarily delayed when responding to
+ // keyboard events. In that time, the embedder may have received and processed
+ // additional key events. This needs to be fixed as soon as possible.
+ // See http://crbug.com/229882.
+ embedder_web_contents_->GetDelegate()->HandleKeyboardEvent(
+ web_contents(), event);
+}
+
+WebContents* BrowserPluginGuest::OpenURLFromTab(WebContents* source,
+ const OpenURLParams& params) {
+ // If the guest wishes to navigate away prior to attachment then we save the
+ // navigation to perform upon attachment. Navigation initializes a lot of
+ // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
+ // Navigation also resumes resource loading which we don't want to allow
+ // until attachment.
+ if (!attached()) {
+ PendingWindowMap::iterator it = opener()->pending_new_windows_.find(this);
+ if (it == opener()->pending_new_windows_.end())
+ return NULL;
+ const NewWindowInfo& old_target_url = it->second;
+ NewWindowInfo new_window_info(params.url, old_target_url.name);
+ new_window_info.changed = new_window_info.url != old_target_url.url;
+ it->second = new_window_info;
+ return NULL;
+ }
+ // This can happen for cross-site redirects.
+ source->GetController().LoadURL(
+ params.url, params.referrer, params.transition, std::string());
+ return source;
+}
+
+void BrowserPluginGuest::WebContentsCreated(WebContents* source_contents,
+ int64 source_frame_id,
+ const string16& frame_name,
+ const GURL& target_url,
+ WebContents* new_contents) {
+ WebContentsImpl* new_contents_impl =
+ static_cast<WebContentsImpl*>(new_contents);
+ BrowserPluginGuest* guest = new_contents_impl->GetBrowserPluginGuest();
+ guest->opener_ = AsWeakPtr();
+ std::string guest_name = UTF16ToUTF8(frame_name);
+ guest->name_ = guest_name;
+ // Take ownership of the new guest until it is attached to the embedder's DOM
+ // tree to avoid leaking a guest if this guest is destroyed before attaching
+ // the new guest.
+ pending_new_windows_.insert(
+ std::make_pair(guest, NewWindowInfo(target_url, guest_name)));
+}
+
+void BrowserPluginGuest::RendererUnresponsive(WebContents* source) {
+ RecordAction(UserMetricsAction("BrowserPlugin.Guest.Hung"));
+ if (!delegate_)
+ return;
+ delegate_->RendererUnresponsive();
+}
+
+void BrowserPluginGuest::RendererResponsive(WebContents* source) {
+ RecordAction(UserMetricsAction("BrowserPlugin.Guest.Responsive"));
+ if (!delegate_)
+ return;
+ delegate_->RendererResponsive();
+}
+
+void BrowserPluginGuest::RunFileChooser(WebContents* web_contents,
+ const FileChooserParams& params) {
+ embedder_web_contents_->GetDelegate()->RunFileChooser(web_contents, params);
+}
+
+bool BrowserPluginGuest::ShouldFocusPageAfterCrash() {
+ // Rather than managing focus in WebContentsImpl::RenderViewReady, we will
+ // manage the focus ourselves.
+ return false;
+}
+
+WebContentsImpl* BrowserPluginGuest::GetWebContents() {
+ return static_cast<WebContentsImpl*>(web_contents());
+}
+
+base::SharedMemory* BrowserPluginGuest::GetDamageBufferFromEmbedder(
+ const BrowserPluginHostMsg_ResizeGuest_Params& params) {
+ if (!attached()) {
+ LOG(WARNING) << "Attempting to map a damage buffer prior to attachment.";
+ return NULL;
+ }
+#if defined(OS_WIN)
+ base::ProcessHandle handle =
+ embedder_web_contents_->GetRenderProcessHost()->GetHandle();
+ scoped_ptr<base::SharedMemory> shared_buf(
+ new base::SharedMemory(params.damage_buffer_handle, false, handle));
+#elif defined(OS_POSIX)
+ scoped_ptr<base::SharedMemory> shared_buf(
+ new base::SharedMemory(params.damage_buffer_handle, false));
+#endif
+ if (!shared_buf->Map(params.damage_buffer_size)) {
+ LOG(WARNING) << "Unable to map the embedder's damage buffer.";
+ return NULL;
+ }
+ return shared_buf.release();
+}
+
+void BrowserPluginGuest::SetDamageBuffer(
+ const BrowserPluginHostMsg_ResizeGuest_Params& params) {
+ damage_buffer_.reset(GetDamageBufferFromEmbedder(params));
+ // Sanity check: Verify that we've correctly shared the damage buffer memory
+ // between the embedder and browser processes.
+ DCHECK(!damage_buffer_ ||
+ *static_cast<unsigned int*>(damage_buffer_->memory()) == 0xdeadbeef);
+ damage_buffer_sequence_id_ = params.damage_buffer_sequence_id;
+ damage_buffer_size_ = params.damage_buffer_size;
+ damage_view_size_ = params.view_rect.size();
+ damage_buffer_scale_factor_ = params.scale_factor;
+}
+
+gfx::Point BrowserPluginGuest::GetScreenCoordinates(
+ const gfx::Point& relative_position) const {
+ gfx::Point screen_pos(relative_position);
+ screen_pos += guest_window_rect_.OffsetFromOrigin();
+ return screen_pos;
+}
+
+bool BrowserPluginGuest::InAutoSizeBounds(const gfx::Size& size) const {
+ return size.width() <= max_auto_size_.width() &&
+ size.height() <= max_auto_size_.height();
+}
+
+void BrowserPluginGuest::RequestNewWindowPermission(
+ WebContentsImpl* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_bounds,
+ bool user_gesture) {
+ BrowserPluginGuest* guest = new_contents->GetBrowserPluginGuest();
+ PendingWindowMap::iterator it = pending_new_windows_.find(guest);
+ if (it == pending_new_windows_.end())
+ return;
+ const NewWindowInfo& new_window_info = it->second;
+
+ base::DictionaryValue request_info;
+ request_info.Set(browser_plugin::kInitialHeight,
+ base::Value::CreateIntegerValue(initial_bounds.height()));
+ request_info.Set(browser_plugin::kInitialWidth,
+ base::Value::CreateIntegerValue(initial_bounds.width()));
+ request_info.Set(browser_plugin::kTargetURL,
+ base::Value::CreateStringValue(new_window_info.url.spec()));
+ request_info.Set(browser_plugin::kName,
+ base::Value::CreateStringValue(new_window_info.name));
+ request_info.Set(browser_plugin::kWindowID,
+ base::Value::CreateIntegerValue(guest->instance_id()));
+ request_info.Set(browser_plugin::kWindowOpenDisposition,
+ base::Value::CreateStringValue(
+ WindowOpenDispositionToString(disposition)));
+
+ RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_NEW_WINDOW,
+ new NewWindowRequest(guest->instance_id(), this),
+ request_info);
+}
+
+bool BrowserPluginGuest::UnlockMouseIfNecessary(
+ const NativeWebKeyboardEvent& event) {
+ if (!mouse_locked_)
+ return false;
+
+ embedder_web_contents()->GotResponseToLockMouseRequest(false);
+ return true;
+}
+
+void BrowserPluginGuest::SendMessageToEmbedder(IPC::Message* msg) {
+ if (!attached()) {
+ // Some pages such as data URLs, javascript URLs, and about:blank
+ // do not load external resources and so they load prior to attachment.
+ // As a result, we must save all these IPCs until attachment and then
+ // forward them so that the embedder gets a chance to see and process
+ // the load events.
+ pending_messages_.push(msg);
+ return;
+ }
+ msg->set_routing_id(embedder_web_contents_->GetRoutingID());
+ embedder_web_contents_->Send(msg);
+}
+
+void BrowserPluginGuest::DragSourceEndedAt(int client_x, int client_y,
+ int screen_x, int screen_y, WebKit::WebDragOperation operation) {
+ web_contents()->GetRenderViewHost()->DragSourceEndedAt(client_x, client_y,
+ screen_x, screen_y, operation);
+}
+
+void BrowserPluginGuest::DragSourceMovedTo(int client_x, int client_y,
+ int screen_x, int screen_y) {
+ web_contents()->GetRenderViewHost()->DragSourceMovedTo(client_x, client_y,
+ screen_x, screen_y);
+}
+
+void BrowserPluginGuest::EndSystemDrag() {
+ RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
+ GetWebContents()->GetRenderViewHost());
+ guest_rvh->DragSourceSystemDragEnded();
+ // Issue a MouseUp event to get out of a selection state.
+ WebKit::WebMouseEvent mouse_event;
+ mouse_event.type = WebKit::WebInputEvent::MouseUp;
+ mouse_event.button = WebKit::WebMouseEvent::ButtonLeft;
+ guest_rvh->ForwardMouseEvent(mouse_event);
+}
+
+void BrowserPluginGuest::SetDelegate(BrowserPluginGuestDelegate* delegate) {
+ DCHECK(!delegate_);
+ delegate_.reset(delegate);
+}
+
+void BrowserPluginGuest::AskEmbedderForGeolocationPermission(
+ int bridge_id,
+ const GURL& requesting_frame,
+ const GeolocationCallback& callback) {
+ if (permission_request_map_.size() >= kNumMaxOutstandingPermissionRequests) {
+ // Deny the geolocation request.
+ callback.Run(false);
+ return;
+ }
+
+ base::DictionaryValue request_info;
+ request_info.Set(browser_plugin::kURL,
+ base::Value::CreateStringValue(requesting_frame.spec()));
+
+ int request_id =
+ RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_GEOLOCATION,
+ new GeolocationRequest(
+ callback, bridge_id, &weak_ptr_factory_),
+ request_info);
+
+ DCHECK(bridge_id_to_request_id_map_.find(bridge_id) ==
+ bridge_id_to_request_id_map_.end());
+ bridge_id_to_request_id_map_[bridge_id] = request_id;
+}
+
+int BrowserPluginGuest::RemoveBridgeID(int bridge_id) {
+ std::map<int, int>::iterator bridge_itr =
+ bridge_id_to_request_id_map_.find(bridge_id);
+ if (bridge_itr == bridge_id_to_request_id_map_.end())
+ return browser_plugin::kInvalidPermissionRequestID;
+
+ int request_id = bridge_itr->second;
+ bridge_id_to_request_id_map_.erase(bridge_itr);
+ return request_id;
+}
+
+void BrowserPluginGuest::CancelGeolocationRequest(int bridge_id) {
+ int request_id = RemoveBridgeID(bridge_id);
+ RequestMap::iterator request_itr = permission_request_map_.find(request_id);
+ if (request_itr == permission_request_map_.end())
+ return;
+ permission_request_map_.erase(request_itr);
+}
+
+void BrowserPluginGuest::SetGeolocationPermission(GeolocationCallback callback,
+ int bridge_id,
+ bool allowed) {
+ callback.Run(allowed);
+ RemoveBridgeID(bridge_id);
+}
+
+void BrowserPluginGuest::SendQueuedMessages() {
+ if (!attached())
+ return;
+
+ while (!pending_messages_.empty()) {
+ IPC::Message* message = pending_messages_.front();
+ pending_messages_.pop();
+ SendMessageToEmbedder(message);
+ }
+}
+
+void BrowserPluginGuest::DidCommitProvisionalLoadForFrame(
+ int64 frame_id,
+ bool is_main_frame,
+ const GURL& url,
+ PageTransition transition_type,
+ RenderViewHost* render_view_host) {
+ RecordAction(UserMetricsAction("BrowserPlugin.Guest.DidNavigate"));
+}
+
+void BrowserPluginGuest::DidStopLoading(RenderViewHost* render_view_host) {
+ bool disable_dragdrop = !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableBrowserPluginDragDrop);
+ if (disable_dragdrop) {
+ // Initiating a drag from inside a guest is currently not supported without
+ // the kEnableBrowserPluginDragDrop flag on a linux platform. So inject some
+ // JS to disable it. http://crbug.com/161112
+ const char script[] = "window.addEventListener('dragstart', function() { "
+ " window.event.preventDefault(); "
+ "});";
+ render_view_host->ExecuteJavascriptInWebFrame(string16(),
+ ASCIIToUTF16(script));
+ }
+}
+
+void BrowserPluginGuest::RenderViewReady() {
+ // TODO(fsamuel): Investigate whether it's possible to update state earlier
+ // here (see http://crbug.com/158151).
+ Send(new InputMsg_SetFocus(routing_id(), focused_));
+ UpdateVisibility();
+ RenderViewHost* rvh = GetWebContents()->GetRenderViewHost();
+ if (auto_size_enabled_)
+ rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
+ else
+ rvh->DisableAutoResize(damage_view_size_);
+
+ Send(new ViewMsg_SetName(routing_id(), name_));
+
+ RenderWidgetHostImpl::From(rvh)->
+ set_hung_renderer_delay_ms(guest_hang_timeout_);
+}
+
+void BrowserPluginGuest::RenderProcessGone(base::TerminationStatus status) {
+ SendMessageToEmbedder(new BrowserPluginMsg_GuestGone(instance_id()));
+ switch (status) {
+ case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
+ RecordAction(UserMetricsAction("BrowserPlugin.Guest.Killed"));
+ break;
+ case base::TERMINATION_STATUS_PROCESS_CRASHED:
+ RecordAction(UserMetricsAction("BrowserPlugin.Guest.Crashed"));
+ break;
+ case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
+ RecordAction(UserMetricsAction("BrowserPlugin.Guest.AbnormalDeath"));
+ break;
+ default:
+ break;
+ }
+ // TODO(fsamuel): Consider whether we should be clearing
+ // |permission_request_map_| here.
+ if (delegate_)
+ delegate_->GuestProcessGone(status);
+}
+
+// static
+void BrowserPluginGuest::AcknowledgeBufferPresent(
+ int route_id,
+ int gpu_host_id,
+ const std::string& mailbox_name,
+ uint32 sync_point) {
+ AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
+ ack_params.mailbox_name = mailbox_name;
+ ack_params.sync_point = sync_point;
+ RenderWidgetHostImpl::AcknowledgeBufferPresent(route_id,
+ gpu_host_id,
+ ack_params);
+}
+
+// static
+bool BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(
+ const IPC::Message& message) {
+ switch (message.type()) {
+ case BrowserPluginHostMsg_BuffersSwappedACK::ID:
+ case BrowserPluginHostMsg_CompositorFrameACK::ID:
+ case BrowserPluginHostMsg_DragStatusUpdate::ID:
+ case BrowserPluginHostMsg_ExecuteEditCommand::ID:
+ case BrowserPluginHostMsg_HandleInputEvent::ID:
+ case BrowserPluginHostMsg_LockMouse_ACK::ID:
+ case BrowserPluginHostMsg_NavigateGuest::ID:
+ case BrowserPluginHostMsg_PluginDestroyed::ID:
+ case BrowserPluginHostMsg_ResizeGuest::ID:
+ case BrowserPluginHostMsg_SetAutoSize::ID:
+ case BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent::ID:
+ case BrowserPluginHostMsg_SetFocus::ID:
+ case BrowserPluginHostMsg_SetName::ID:
+ case BrowserPluginHostMsg_SetVisibility::ID:
+ case BrowserPluginHostMsg_UnlockMouse_ACK::ID:
+ case BrowserPluginHostMsg_UpdateGeometry::ID:
+ case BrowserPluginHostMsg_UpdateRect_ACK::ID:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool BrowserPluginGuest::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuest, message)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers,
+ OnHasTouchEventHandlers)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_LockMouse, OnLockMouse)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnSetCursor)
+ #if defined(OS_MACOSX)
+ // MacOSX creates and populates platform-specific select drop-down menus
+ // whereas other platforms merely create a popup window that the guest
+ // renderer process paints inside.
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ShowPopup, OnShowPopup)
+ #endif
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ShowWidget, OnShowWidget)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_TakeFocus, OnTakeFocus)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UnlockMouse, OnUnlockMouse)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateFrameName, OnUpdateFrameName)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnUpdateRect)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void BrowserPluginGuest::Attach(
+ WebContentsImpl* embedder_web_contents,
+ BrowserPluginHostMsg_Attach_Params params) {
+ if (attached())
+ return;
+
+ // Clear parameters that get inherited from the opener.
+ params.storage_partition_id.clear();
+ params.persist_storage = false;
+ params.src.clear();
+
+ // If a RenderView has already been created for this new window, then we need
+ // to initialize the browser-side state now so that the RenderViewHostManager
+ // does not create a new RenderView on navigation.
+ if (has_render_view_) {
+ static_cast<RenderViewHostImpl*>(
+ GetWebContents()->GetRenderViewHost())->Init();
+ WebContentsViewGuest* new_view =
+ static_cast<WebContentsViewGuest*>(GetWebContents()->GetView());
+ new_view->CreateViewForWidget(web_contents()->GetRenderViewHost());
+ }
+
+ // We need to do a navigation here if the target URL has changed between
+ // the time the WebContents was created and the time it was attached.
+ // We also need to do an initial navigation if a RenderView was never
+ // created for the new window in cases where there is no referrer.
+ PendingWindowMap::iterator it = opener()->pending_new_windows_.find(this);
+ if (it != opener()->pending_new_windows_.end()) {
+ const NewWindowInfo& new_window_info = it->second;
+ if (new_window_info.changed || !has_render_view_)
+ params.src = it->second.url.spec();
+ } else {
+ NOTREACHED();
+ }
+
+ // Once a new guest is attached to the DOM of the embedder page, then the
+ // lifetime of the new guest is no longer managed by the opener guest.
+ opener()->pending_new_windows_.erase(this);
+
+ // The guest's frame name takes precedence over the BrowserPlugin's name.
+ // The guest's frame name is assigned in
+ // BrowserPluginGuest::WebContentsCreated.
+ if (!name_.empty())
+ params.name.clear();
+
+ Initialize(embedder_web_contents, params);
+
+ // Inform the embedder of the guest's information.
+ // We pull the partition information from the site's URL, which is of the form
+ // guest://site/{persist}?{partition_name}.
+ const GURL& site_url = GetWebContents()->GetSiteInstance()->GetSiteURL();
+ BrowserPluginMsg_Attach_ACK_Params ack_params;
+ ack_params.storage_partition_id = site_url.query();
+ ack_params.persist_storage =
+ site_url.path().find("persist") != std::string::npos;
+ ack_params.name = name_;
+ SendMessageToEmbedder(
+ new BrowserPluginMsg_Attach_ACK(instance_id_, ack_params));
+
+ SendQueuedMessages();
+
+ RecordAction(UserMetricsAction("BrowserPlugin.Guest.Attached"));
+}
+
+void BrowserPluginGuest::OnCompositorFrameACK(
+ int instance_id,
+ int route_id,
+ uint32 output_surface_id,
+ int renderer_host_id,
+ const cc::CompositorFrameAck& ack) {
+ RenderWidgetHostImpl::SendSwapCompositorFrameAck(route_id,
+ output_surface_id,
+ renderer_host_id,
+ ack);
+}
+
+void BrowserPluginGuest::OnDragStatusUpdate(int instance_id,
+ WebKit::WebDragStatus drag_status,
+ const DropData& drop_data,
+ WebKit::WebDragOperationsMask mask,
+ const gfx::Point& location) {
+ RenderViewHost* host = GetWebContents()->GetRenderViewHost();
+ switch (drag_status) {
+ case WebKit::WebDragStatusEnter:
+ embedder_web_contents_->GetBrowserPluginEmbedder()->DragEnteredGuest(
+ this);
+ host->DragTargetDragEnter(drop_data, location, location, mask, 0);
+ break;
+ case WebKit::WebDragStatusOver:
+ host->DragTargetDragOver(location, location, mask, 0);
+ break;
+ case WebKit::WebDragStatusLeave:
+ embedder_web_contents_->GetBrowserPluginEmbedder()->DragLeftGuest(this);
+ host->DragTargetDragLeave();
+ break;
+ case WebKit::WebDragStatusDrop:
+ host->DragTargetDrop(location, location, 0);
+ EndSystemDrag();
+ break;
+ case WebKit::WebDragStatusUnknown:
+ NOTREACHED();
+ }
+}
+
+void BrowserPluginGuest::OnExecuteEditCommand(int instance_id,
+ const std::string& name) {
+ Send(new InputMsg_ExecuteEditCommand(routing_id(), name, std::string()));
+}
+
+void BrowserPluginGuest::OnHandleInputEvent(
+ int instance_id,
+ const gfx::Rect& guest_window_rect,
+ const WebKit::WebInputEvent* event) {
+ guest_window_rect_ = guest_window_rect;
+ // If the embedder's RWHV is destroyed then that means that the embedder's
+ // window has been closed but the embedder's WebContents has not yet been
+ // destroyed. Computing screen coordinates of a BrowserPlugin only makes sense
+ // if there is a visible embedder.
+ if (embedder_web_contents_->GetRenderWidgetHostView()) {
+ guest_screen_rect_ = guest_window_rect;
+ guest_screen_rect_.Offset(
+ embedder_web_contents_->GetRenderWidgetHostView()->
+ GetViewBounds().OffsetFromOrigin());
+ }
+ RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
+ GetWebContents()->GetRenderViewHost());
+
+ if (WebKit::WebInputEvent::isMouseEventType(event->type)) {
+ guest_rvh->ForwardMouseEvent(
+ *static_cast<const WebKit::WebMouseEvent*>(event));
+ return;
+ }
+
+ if (event->type == WebKit::WebInputEvent::MouseWheel) {
+ guest_rvh->ForwardWheelEvent(
+ *static_cast<const WebKit::WebMouseWheelEvent*>(event));
+ return;
+ }
+
+ if (WebKit::WebInputEvent::isKeyboardEventType(event->type)) {
+ RenderViewHostImpl* embedder_rvh = static_cast<RenderViewHostImpl*>(
+ embedder_web_contents_->GetRenderViewHost());
+ if (!embedder_rvh->GetLastKeyboardEvent())
+ return;
+ NativeWebKeyboardEvent keyboard_event(
+ *embedder_rvh->GetLastKeyboardEvent());
+ guest_rvh->ForwardKeyboardEvent(keyboard_event);
+ return;
+ }
+
+ if (WebKit::WebInputEvent::isTouchEventType(event->type)) {
+ guest_rvh->ForwardTouchEventWithLatencyInfo(
+ *static_cast<const WebKit::WebTouchEvent*>(event),
+ ui::LatencyInfo());
+ return;
+ }
+
+ if (WebKit::WebInputEvent::isGestureEventType(event->type)) {
+ guest_rvh->ForwardGestureEvent(
+ *static_cast<const WebKit::WebGestureEvent*>(event));
+ return;
+ }
+}
+
+void BrowserPluginGuest::OnLockMouse(bool user_gesture,
+ bool last_unlocked_by_target,
+ bool privileged) {
+ if (pending_lock_request_ ||
+ (permission_request_map_.size() >=
+ kNumMaxOutstandingPermissionRequests)) {
+ // Immediately reject the lock because only one pointerLock may be active
+ // at a time.
+ Send(new ViewMsg_LockMouse_ACK(routing_id(), false));
+ return;
+ }
+ pending_lock_request_ = true;
+ base::DictionaryValue request_info;
+ request_info.Set(browser_plugin::kUserGesture,
+ base::Value::CreateBooleanValue(user_gesture));
+ request_info.Set(browser_plugin::kLastUnlockedBySelf,
+ base::Value::CreateBooleanValue(last_unlocked_by_target));
+ request_info.Set(browser_plugin::kURL,
+ base::Value::CreateStringValue(
+ web_contents()->GetLastCommittedURL().spec()));
+
+ RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_POINTER_LOCK,
+ new PointerLockRequest(this),
+ request_info);
+}
+
+void BrowserPluginGuest::OnLockMouseAck(int instance_id, bool succeeded) {
+ Send(new ViewMsg_LockMouse_ACK(routing_id(), succeeded));
+ pending_lock_request_ = false;
+ if (succeeded)
+ mouse_locked_ = true;
+}
+
+void BrowserPluginGuest::OnNavigateGuest(
+ int instance_id,
+ const std::string& src) {
+ GURL url(src);
+ // We do not load 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. This empty src navigation
+ // should never be sent to BrowserPluginGuest (browser process).
+ DCHECK(!src.empty());
+ if (!src.empty()) {
+ // As guests do not swap processes on navigation, only navigations to
+ // normal web URLs are supported. No protocol handlers are installed for
+ // other schemes (e.g., WebUI or extensions), and no permissions or bindings
+ // can be granted to the guest process.
+ GetWebContents()->GetController().LoadURL(url, Referrer(),
+ PAGE_TRANSITION_AUTO_TOPLEVEL,
+ std::string());
+ }
+}
+
+void BrowserPluginGuest::OnPluginDestroyed(int instance_id) {
+ Destroy();
+}
+
+void BrowserPluginGuest::OnResizeGuest(
+ int instance_id,
+ const BrowserPluginHostMsg_ResizeGuest_Params& params) {
+ if (!params.size_changed)
+ return;
+ // BrowserPlugin manages resize flow control itself and does not depend
+ // on RenderWidgetHost's mechanisms for flow control, so we reset those flags
+ // here. If we are setting the size for the first time before navigating then
+ // BrowserPluginGuest does not yet have a RenderViewHost.
+ if (GetWebContents()->GetRenderViewHost()) {
+ RenderWidgetHostImpl* render_widget_host =
+ RenderWidgetHostImpl::From(GetWebContents()->GetRenderViewHost());
+ render_widget_host->ResetSizeAndRepaintPendingFlags();
+
+ if (guest_device_scale_factor_ != params.scale_factor) {
+ guest_device_scale_factor_ = params.scale_factor;
+ render_widget_host->NotifyScreenInfoChanged();
+ }
+ }
+ // When autosize is turned off and as a result there is a layout change, we
+ // send a sizechanged event.
+ if (!auto_size_enabled_ && last_seen_auto_size_enabled_ &&
+ !params.view_rect.size().IsEmpty() && delegate_) {
+ delegate_->SizeChanged(last_seen_view_size_, params.view_rect.size());
+ last_seen_auto_size_enabled_ = false;
+ }
+ // Invalid damage buffer means we are in HW compositing mode,
+ // so just resize the WebContents and repaint if needed.
+ if (!base::SharedMemory::IsHandleValid(params.damage_buffer_handle)) {
+ if (!params.view_rect.size().IsEmpty())
+ GetWebContents()->GetView()->SizeContents(params.view_rect.size());
+ if (params.repaint)
+ Send(new ViewMsg_Repaint(routing_id(), params.view_rect.size()));
+ return;
+ }
+ SetDamageBuffer(params);
+ GetWebContents()->GetView()->SizeContents(params.view_rect.size());
+ if (params.repaint)
+ Send(new ViewMsg_Repaint(routing_id(), params.view_rect.size()));
+}
+
+void BrowserPluginGuest::OnSetFocus(int instance_id, bool focused) {
+ if (focused_ == focused)
+ return;
+ focused_ = focused;
+ Send(new InputMsg_SetFocus(routing_id(), focused));
+ if (!focused && mouse_locked_)
+ OnUnlockMouse();
+}
+
+void BrowserPluginGuest::OnSetName(int instance_id, const std::string& name) {
+ if (name == name_)
+ return;
+ name_ = name;
+ Send(new ViewMsg_SetName(routing_id(), name));
+}
+
+void BrowserPluginGuest::OnSetSize(
+ int instance_id,
+ const BrowserPluginHostMsg_AutoSize_Params& auto_size_params,
+ const BrowserPluginHostMsg_ResizeGuest_Params& resize_guest_params) {
+ bool old_auto_size_enabled = auto_size_enabled_;
+ gfx::Size old_max_size = max_auto_size_;
+ gfx::Size old_min_size = min_auto_size_;
+ auto_size_enabled_ = auto_size_params.enable;
+ max_auto_size_ = auto_size_params.max_size;
+ min_auto_size_ = auto_size_params.min_size;
+ if (auto_size_enabled_ && (!old_auto_size_enabled ||
+ (old_max_size != max_auto_size_) ||
+ (old_min_size != min_auto_size_))) {
+ GetWebContents()->GetRenderViewHost()->EnableAutoResize(
+ min_auto_size_, max_auto_size_);
+ // TODO(fsamuel): If we're changing autosize parameters, then we force
+ // the guest to completely repaint itself, because BrowserPlugin has
+ // allocated a new damage buffer and expects a full frame of pixels.
+ // Ideally, we shouldn't need to do this because we shouldn't need to
+ // allocate a new damage buffer unless |max_auto_size_| has changed.
+ // However, even in that case, layout may not change and so we may
+ // not get a full frame worth of pixels.
+ Send(new ViewMsg_Repaint(routing_id(), max_auto_size_));
+ } else if (!auto_size_enabled_ && old_auto_size_enabled) {
+ GetWebContents()->GetRenderViewHost()->DisableAutoResize(
+ resize_guest_params.view_rect.size());
+ }
+ OnResizeGuest(instance_id_, resize_guest_params);
+}
+
+void BrowserPluginGuest::OnSetEditCommandsForNextKeyEvent(
+ int instance_id,
+ const std::vector<EditCommand>& edit_commands) {
+ Send(new InputMsg_SetEditCommandsForNextKeyEvent(routing_id(),
+ edit_commands));
+}
+
+void BrowserPluginGuest::OnSetVisibility(int instance_id, bool visible) {
+ guest_visible_ = visible;
+ if (embedder_visible_ && guest_visible_)
+ GetWebContents()->WasShown();
+ else
+ GetWebContents()->WasHidden();
+}
+
+void BrowserPluginGuest::OnSwapBuffersACK(int instance_id,
+ int route_id,
+ int gpu_host_id,
+ const std::string& mailbox_name,
+ uint32 sync_point) {
+ AcknowledgeBufferPresent(route_id, gpu_host_id, mailbox_name, sync_point);
+
+// This is only relevant on MACOSX and WIN when threaded compositing
+// is not enabled. In threaded mode, above ACK is sufficient.
+#if defined(OS_MACOSX) || defined(OS_WIN)
+ RenderWidgetHostImpl* render_widget_host =
+ RenderWidgetHostImpl::From(GetWebContents()->GetRenderViewHost());
+ render_widget_host->AcknowledgeSwapBuffersToRenderer();
+#endif // defined(OS_MACOSX) || defined(OS_WIN)
+}
+
+void BrowserPluginGuest::OnUnlockMouse() {
+ SendMessageToEmbedder(
+ new BrowserPluginMsg_SetMouseLock(instance_id(), false));
+}
+
+void BrowserPluginGuest::OnUnlockMouseAck(int instance_id) {
+ // mouse_locked_ could be false here if the lock attempt was cancelled due
+ // to window focus, or for various other reasons before the guest was informed
+ // of the lock's success.
+ if (mouse_locked_)
+ Send(new ViewMsg_MouseLockLost(routing_id()));
+ mouse_locked_ = false;
+}
+
+void BrowserPluginGuest::OnUpdateRectACK(
+ int instance_id,
+ bool needs_ack,
+ const BrowserPluginHostMsg_AutoSize_Params& auto_size_params,
+ const BrowserPluginHostMsg_ResizeGuest_Params& resize_guest_params) {
+ // Only the software path expects an ACK.
+ if (needs_ack)
+ Send(new ViewMsg_UpdateRect_ACK(routing_id()));
+ OnSetSize(instance_id_, auto_size_params, resize_guest_params);
+}
+
+void BrowserPluginGuest::OnUpdateGeometry(int instance_id,
+ const gfx::Rect& view_rect) {
+ // The plugin has moved within the embedder without resizing or the
+ // embedder/container's view rect changing.
+ guest_window_rect_ = view_rect;
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ GetWebContents()->GetRenderViewHost());
+ if (rvh)
+ rvh->SendScreenRects();
+}
+
+void BrowserPluginGuest::OnHasTouchEventHandlers(bool accept) {
+ SendMessageToEmbedder(
+ new BrowserPluginMsg_ShouldAcceptTouchEvents(instance_id(), accept));
+}
+
+void BrowserPluginGuest::OnSetCursor(const WebCursor& cursor) {
+ SendMessageToEmbedder(new BrowserPluginMsg_SetCursor(instance_id(), cursor));
+}
+
+#if defined(OS_MACOSX)
+void BrowserPluginGuest::OnShowPopup(
+ const ViewHostMsg_ShowPopup_Params& params) {
+ gfx::Rect translated_bounds(params.bounds);
+ translated_bounds.Offset(guest_window_rect_.OffsetFromOrigin());
+ BrowserPluginPopupMenuHelper popup_menu_helper(
+ embedder_web_contents_->GetRenderViewHost(),
+ GetWebContents()->GetRenderViewHost());
+ popup_menu_helper.ShowPopupMenu(translated_bounds,
+ params.item_height,
+ params.item_font_size,
+ params.selected_item,
+ params.popup_items,
+ params.right_aligned,
+ params.allow_multiple_selection);
+}
+#endif
+
+void BrowserPluginGuest::OnShowWidget(int route_id,
+ const gfx::Rect& initial_pos) {
+ GetWebContents()->ShowCreatedWidget(route_id, initial_pos);
+}
+
+void BrowserPluginGuest::OnTakeFocus(bool reverse) {
+ SendMessageToEmbedder(
+ new BrowserPluginMsg_AdvanceFocus(instance_id(), reverse));
+}
+
+void BrowserPluginGuest::OnUpdateFrameName(int frame_id,
+ bool is_top_level,
+ const std::string& name) {
+ if (!is_top_level)
+ return;
+
+ name_ = name;
+ SendMessageToEmbedder(new BrowserPluginMsg_UpdatedName(instance_id_, name));
+}
+
+void BrowserPluginGuest::RequestMediaAccessPermission(
+ WebContents* web_contents,
+ const MediaStreamRequest& request,
+ const MediaResponseCallback& callback) {
+ if (permission_request_map_.size() >= kNumMaxOutstandingPermissionRequests) {
+ // Deny the media request.
+ callback.Run(MediaStreamDevices(), scoped_ptr<MediaStreamUI>());
+ return;
+ }
+
+ base::DictionaryValue request_info;
+ request_info.Set(
+ browser_plugin::kURL,
+ base::Value::CreateStringValue(request.security_origin.spec()));
+
+ RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_MEDIA,
+ new MediaRequest(request, callback, this),
+ request_info);
+}
+
+void BrowserPluginGuest::RunJavaScriptDialog(
+ WebContents* web_contents,
+ const GURL& origin_url,
+ const std::string& accept_lang,
+ JavaScriptMessageType javascript_message_type,
+ const string16& message_text,
+ const string16& default_prompt_text,
+ const DialogClosedCallback& callback,
+ bool* did_suppress_message) {
+ if (permission_request_map_.size() >= kNumMaxOutstandingPermissionRequests) {
+ // Cancel the dialog.
+ callback.Run(false, string16());
+ return;
+ }
+ base::DictionaryValue request_info;
+ request_info.Set(
+ browser_plugin::kDefaultPromptText,
+ base::Value::CreateStringValue(UTF16ToUTF8(default_prompt_text)));
+ request_info.Set(
+ browser_plugin::kMessageText,
+ base::Value::CreateStringValue(UTF16ToUTF8(message_text)));
+ request_info.Set(
+ browser_plugin::kMessageType,
+ base::Value::CreateStringValue(
+ JavaScriptMessageTypeToString(javascript_message_type)));
+ request_info.Set(
+ browser_plugin::kURL,
+ base::Value::CreateStringValue(origin_url.spec()));
+
+ RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_JAVASCRIPT_DIALOG,
+ new JavaScriptDialogRequest(callback),
+ request_info);
+}
+
+void BrowserPluginGuest::RunBeforeUnloadDialog(
+ WebContents* web_contents,
+ const string16& message_text,
+ bool is_reload,
+ const DialogClosedCallback& callback) {
+ // This is called if the guest has a beforeunload event handler.
+ // This callback allows navigation to proceed.
+ callback.Run(true, string16());
+}
+
+bool BrowserPluginGuest::HandleJavaScriptDialog(
+ WebContents* web_contents,
+ bool accept,
+ const string16* prompt_override) {
+ return false;
+}
+
+void BrowserPluginGuest::CancelActiveAndPendingDialogs(
+ WebContents* web_contents) {
+}
+
+void BrowserPluginGuest::WebContentsDestroyed(WebContents* web_contents) {
+}
+
+void BrowserPluginGuest::OnUpdateRect(
+ const ViewHostMsg_UpdateRect_Params& params) {
+ BrowserPluginMsg_UpdateRect_Params relay_params;
+ 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);
+ relay_params.needs_ack = params.needs_ack;
+
+ bool size_changed = last_seen_view_size_ != params.view_size;
+ gfx::Size old_size = last_seen_view_size_;
+ last_seen_view_size_ = params.view_size;
+
+ if ((auto_size_enabled_ || last_seen_auto_size_enabled_) &&
+ size_changed && delegate_) {
+ delegate_->SizeChanged(old_size, last_seen_view_size_);
+ }
+ last_seen_auto_size_enabled_ = auto_size_enabled_;
+
+ // HW accelerated case, acknowledge resize only
+ if (!params.needs_ack || !damage_buffer_) {
+ relay_params.damage_buffer_sequence_id = 0;
+ SendMessageToEmbedder(
+ new BrowserPluginMsg_UpdateRect(instance_id(), relay_params));
+ return;
+ }
+
+ // Only copy damage if the guest is in autosize mode and the guest's view size
+ // is less than the maximum size or 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 (((auto_size_enabled_ && InAutoSizeBounds(params.view_size)) ||
+ (params.view_size == damage_view_size())) &&
+ params.scale_factor == damage_buffer_scale_factor()) {
+ TransportDIB* dib = GetWebContents()->GetRenderProcessHost()->
+ GetTransportDIB(params.bitmap);
+ if (dib) {
+ size_t guest_damage_buffer_size =
+#if defined(OS_WIN)
+ params.bitmap_rect.width() *
+ params.bitmap_rect.height() * 4;
+#else
+ dib->size();
+#endif
+ size_t embedder_damage_buffer_size = damage_buffer_size_;
+ 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);
+ }
+ }
+ relay_params.damage_buffer_sequence_id = damage_buffer_sequence_id_;
+ relay_params.bitmap_rect = params.bitmap_rect;
+ relay_params.scroll_delta = params.scroll_delta;
+ relay_params.scroll_rect = params.scroll_rect;
+ relay_params.copy_rects = params.copy_rects;
+
+ SendMessageToEmbedder(
+ new BrowserPluginMsg_UpdateRect(instance_id(), relay_params));
+}
+
+void BrowserPluginGuest::DidRetrieveDownloadURLFromRequestId(
+ const std::string& request_method,
+ const base::Callback<void(bool)>& callback,
+ const std::string& url) {
+ if (url.empty()) {
+ callback.Run(false);
+ return;
+ }
+
+ base::DictionaryValue request_info;
+ request_info.Set(browser_plugin::kRequestMethod,
+ base::Value::CreateStringValue(request_method));
+ request_info.Set(browser_plugin::kURL, base::Value::CreateStringValue(url));
+
+ RequestPermission(BROWSER_PLUGIN_PERMISSION_TYPE_DOWNLOAD,
+ new DownloadRequest(callback),
+ request_info);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest.h b/chromium/content/browser/browser_plugin/browser_plugin_guest.h
new file mode 100644
index 00000000000..ffe8d566145
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_guest.h
@@ -0,0 +1,534 @@
+// 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 is the browser side of a browser <--> embedder
+// renderer channel. A BrowserPlugin (a WebPlugin) is on the embedder
+// renderer side of browser <--> embedder renderer communication.
+//
+// BrowserPluginGuest lives on the UI thread of the browser process. It has a
+// helper, BrowserPluginGuestHelper, which is a RenderViewHostObserver. The
+// helper object intercepts messages (ViewHostMsg_*) directed at the browser
+// process and redirects them to this class. Any messages about the guest render
+// process that the embedder might be interested in receiving should be listened
+// for here.
+//
+// BrowserPluginGuest is a WebContentsDelegate and WebContentsObserver for the
+// guest WebContents. BrowserPluginGuest operates under the assumption that the
+// guest will be accessible through only one RenderViewHost for the lifetime of
+// the guest WebContents. Thus, cross-process navigation is not supported.
+
+#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_H_
+
+#include <map>
+#include <queue>
+
+#include "base/compiler_specific.h"
+#include "base/id_map.h"
+#include "base/memory/shared_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "content/common/edit_command.h"
+#include "content/port/common/input_event_ack_state.h"
+#include "content/public/browser/browser_plugin_guest_delegate.h"
+#include "content/public/browser/javascript_dialog_manager.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/render_view_host_observer.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/browser_plugin_permission_type.h"
+#include "third_party/WebKit/public/web/WebDragOperation.h"
+#include "third_party/WebKit/public/web/WebDragStatus.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/gfx/rect.h"
+#include "ui/surface/transport_dib.h"
+
+struct BrowserPluginHostMsg_AutoSize_Params;
+struct BrowserPluginHostMsg_Attach_Params;
+struct BrowserPluginHostMsg_ResizeGuest_Params;
+struct ViewHostMsg_CreateWindow_Params;
+#if defined(OS_MACOSX)
+struct ViewHostMsg_ShowPopup_Params;
+#endif
+struct ViewHostMsg_UpdateRect_Params;
+class WebCursor;
+
+namespace cc {
+class CompositorFrameAck;
+}
+
+namespace WebKit {
+class WebInputEvent;
+}
+
+namespace content {
+
+class BrowserPluginHostFactory;
+class BrowserPluginEmbedder;
+class BrowserPluginGuestManager;
+class RenderProcessHost;
+class RenderWidgetHostView;
+struct DropData;
+struct MediaStreamRequest;
+
+// A browser plugin guest provides functionality for WebContents to operate in
+// the guest role and implements guest-specific overrides for ViewHostMsg_*
+// messages.
+//
+// When a guest is initially created, it is in an unattached state. That is,
+// it is not visible anywhere and has no embedder WebContents assigned.
+// A BrowserPluginGuest is said to be "attached" if it has an embedder.
+// A BrowserPluginGuest can also create a new unattached guest via
+// CreateNewWindow. The newly created guest will live in the same partition,
+// which means it can share storage and can script this guest.
+class CONTENT_EXPORT BrowserPluginGuest
+ : public JavaScriptDialogManager,
+ public NotificationObserver,
+ public WebContentsDelegate,
+ public WebContentsObserver,
+ public base::SupportsWeakPtr<BrowserPluginGuest> {
+ public:
+ typedef base::Callback<void(bool)> GeolocationCallback;
+ virtual ~BrowserPluginGuest();
+
+ static BrowserPluginGuest* Create(
+ int instance_id,
+ WebContentsImpl* web_contents,
+ scoped_ptr<base::DictionaryValue> extra_params);
+
+ static BrowserPluginGuest* CreateWithOpener(
+ int instance_id,
+ WebContentsImpl* web_contents,
+ BrowserPluginGuest* opener,
+ bool has_render_view);
+
+ // Destroys the guest WebContents and all its associated state, including
+ // this BrowserPluginGuest, and its new unattached windows.
+ void Destroy();
+
+ // Returns the identifier that uniquely identifies a browser plugin guest
+ // within an embedder.
+ int instance_id() const { return instance_id_; }
+
+ // Overrides factory for testing. Default (NULL) value indicates regular
+ // (non-test) environment.
+ static void set_factory_for_testing(BrowserPluginHostFactory* factory) {
+ BrowserPluginGuest::factory_ = factory;
+ }
+
+ bool OnMessageReceivedFromEmbedder(const IPC::Message& message);
+
+ void Initialize(WebContentsImpl* embedder_web_contents,
+ const BrowserPluginHostMsg_Attach_Params& params);
+
+ void set_guest_hang_timeout_for_testing(const base::TimeDelta& timeout) {
+ guest_hang_timeout_ = timeout;
+ }
+
+ WebContentsImpl* embedder_web_contents() const {
+ return embedder_web_contents_;
+ }
+
+ RenderWidgetHostView* GetEmbedderRenderWidgetHostView();
+
+ bool focused() const { return focused_; }
+ bool visible() const { return guest_visible_; }
+ void clear_damage_buffer() { damage_buffer_.reset(); }
+
+ BrowserPluginGuest* opener() const { return opener_.get(); }
+
+ // Returns whether the mouse pointer was unlocked.
+ bool UnlockMouseIfNecessary(const NativeWebKeyboardEvent& event);
+
+ void UpdateVisibility();
+
+ // NotificationObserver implementation.
+ virtual void Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) OVERRIDE;
+
+ // 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 DidStopLoading(RenderViewHost* render_view_host) OVERRIDE;
+
+ virtual void RenderViewReady() OVERRIDE;
+ virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ // WebContentsDelegate implementation.
+ virtual bool AddMessageToConsole(WebContents* source,
+ int32 level,
+ const string16& message,
+ int32 line_no,
+ const string16& source_id) OVERRIDE;
+ // If a new window is created with target="_blank" and rel="noreferrer", then
+ // this method is called, indicating that the new WebContents is ready to be
+ // attached.
+ virtual void AddNewContents(WebContents* source,
+ WebContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture,
+ bool* was_blocked) OVERRIDE;
+ virtual void CanDownload(RenderViewHost* render_view_host,
+ int request_id,
+ const std::string& request_method,
+ const base::Callback<void(bool)>& callback) OVERRIDE;
+ virtual void CloseContents(WebContents* source) OVERRIDE;
+ virtual JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE;
+ virtual bool HandleContextMenu(const ContextMenuParams& params) OVERRIDE;
+ virtual void HandleKeyboardEvent(
+ WebContents* source,
+ const NativeWebKeyboardEvent& event) OVERRIDE;
+ virtual WebContents* OpenURLFromTab(WebContents* source,
+ const OpenURLParams& params) OVERRIDE;
+ virtual void WebContentsCreated(WebContents* source_contents,
+ int64 source_frame_id,
+ const string16& frame_name,
+ const GURL& target_url,
+ WebContents* new_contents) OVERRIDE;
+ virtual void RendererUnresponsive(WebContents* source) OVERRIDE;
+ virtual void RendererResponsive(WebContents* source) OVERRIDE;
+ virtual void RunFileChooser(WebContents* web_contents,
+ const FileChooserParams& params) OVERRIDE;
+ virtual bool ShouldFocusPageAfterCrash() OVERRIDE;
+ virtual void RequestMediaAccessPermission(
+ WebContents* web_contents,
+ const MediaStreamRequest& request,
+ const MediaResponseCallback& callback) OVERRIDE;
+
+ // JavaScriptDialogManager implementation.
+ virtual void RunJavaScriptDialog(
+ WebContents* web_contents,
+ const GURL& origin_url,
+ const std::string& accept_lang,
+ JavaScriptMessageType javascript_message_type,
+ const string16& message_text,
+ const string16& default_prompt_text,
+ const DialogClosedCallback& callback,
+ bool* did_suppress_message) OVERRIDE;
+ virtual void RunBeforeUnloadDialog(
+ WebContents* web_contents,
+ const string16& message_text,
+ bool is_reload,
+ const DialogClosedCallback& callback) OVERRIDE;
+ virtual bool HandleJavaScriptDialog(WebContents* web_contents,
+ bool accept,
+ const string16* prompt_override) OVERRIDE;
+ virtual void CancelActiveAndPendingDialogs(
+ WebContents* web_contents) OVERRIDE;
+ virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
+
+ // Exposes the protected web_contents() from WebContentsObserver.
+ WebContentsImpl* GetWebContents();
+
+ // Overridden in tests.
+ virtual void SetDamageBuffer(
+ const BrowserPluginHostMsg_ResizeGuest_Params& params);
+
+ gfx::Point GetScreenCoordinates(const gfx::Point& relative_position) const;
+
+ // Helper to send messages to embedder. This methods fills the message with
+ // the correct routing id.
+ // Overridden in test implementation since we want to intercept certain
+ // messages for testing.
+ virtual void SendMessageToEmbedder(IPC::Message* msg);
+
+ // Returns whether the guest is attached to an embedder.
+ bool attached() const { return !!embedder_web_contents_; }
+
+ // Attaches this BrowserPluginGuest to the provided |embedder_web_contents|
+ // and initializes the guest with the provided |params|. Attaching a guest
+ // to an embedder implies that this guest's lifetime is no longer managed
+ // by its opener, and it can begin loading resources.
+ void Attach(WebContentsImpl* embedder_web_contents,
+ BrowserPluginHostMsg_Attach_Params params);
+
+ // Requests geolocation permission through Embedder JavaScript API.
+ void AskEmbedderForGeolocationPermission(int bridge_id,
+ const GURL& requesting_frame,
+ const GeolocationCallback& callback);
+ // Cancels pending geolocation request.
+ void CancelGeolocationRequest(int bridge_id);
+
+ // Allow the embedder to call this for unhandled messages when
+ // BrowserPluginGuest is already destroyed.
+ static void AcknowledgeBufferPresent(int route_id,
+ int gpu_host_id,
+ const std::string& mailbox_name,
+ uint32 sync_point);
+
+ // Returns whether BrowserPluginGuest is interested in receiving the given
+ // |message|.
+ static bool ShouldForwardToBrowserPluginGuest(const IPC::Message& message);
+ gfx::Rect ToGuestRect(const gfx::Rect& rect);
+
+ void DragSourceEndedAt(int client_x, int client_y, int screen_x,
+ int screen_y, WebKit::WebDragOperation operation);
+
+ void DragSourceMovedTo(int client_x, int client_y,
+ int screen_x, int screen_y);
+
+ // Called when the drag started by this guest ends at an OS-level.
+ void EndSystemDrag();
+
+ // |this| takes ownership of |delegate|.
+ void SetDelegate(BrowserPluginGuestDelegate* delegate);
+
+ void RespondToPermissionRequest(int request_id,
+ bool should_allow,
+ const std::string& user_input);
+
+ private:
+ class EmbedderRenderViewHostObserver;
+ friend class TestBrowserPluginGuest;
+
+ class DownloadRequest;
+ class GeolocationRequest;
+ class JavaScriptDialogRequest;
+ // MediaRequest because of naming conflicts with MediaStreamRequest.
+ class MediaRequest;
+ class NewWindowRequest;
+ class PermissionRequest;
+ class PointerLockRequest;
+
+ BrowserPluginGuest(int instance_id,
+ WebContentsImpl* web_contents,
+ BrowserPluginGuest* opener,
+ bool has_render_view);
+
+ // Destroy unattached new windows that have been opened by this
+ // BrowserPluginGuest.
+ void DestroyUnattachedWindows();
+
+ // Bridge IDs correspond to a geolocation request. This method will remove
+ // the bookkeeping for a particular geolocation request associated with the
+ // provided |bridge_id|. It returns the request ID of the geolocation request.
+ int RemoveBridgeID(int bridge_id);
+
+ // Returns the |request_id| generated for the |request| provided.
+ int RequestPermission(
+ BrowserPluginPermissionType permission_type,
+ scoped_refptr<BrowserPluginGuest::PermissionRequest> request,
+ const base::DictionaryValue& request_info);
+
+ base::SharedMemory* 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_;
+ }
+ // Returns the damage buffer corresponding to the handle in resize |params|.
+ base::SharedMemory* GetDamageBufferFromEmbedder(
+ const BrowserPluginHostMsg_ResizeGuest_Params& params);
+
+ bool InAutoSizeBounds(const gfx::Size& size) const;
+
+ void RequestNewWindowPermission(WebContentsImpl* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_bounds,
+ bool user_gesture);
+
+ // Message handlers for messages from embedder.
+
+ void OnCompositorFrameACK(int instance_id,
+ int route_id,
+ uint32 output_surface_id,
+ int renderer_host_id,
+ const cc::CompositorFrameAck& ack);
+
+ // Handles drag events from the embedder.
+ // When dragging, the drag events go to the embedder first, and if the drag
+ // happens on the browser plugin, then the plugin sends a corresponding
+ // drag-message to the guest. This routes the drag-message to the guest
+ // renderer.
+ void OnDragStatusUpdate(int instance_id,
+ WebKit::WebDragStatus drag_status,
+ const DropData& drop_data,
+ WebKit::WebDragOperationsMask drag_mask,
+ const gfx::Point& location);
+ // Instructs the guest to execute an edit command decoded in the embedder.
+ void OnExecuteEditCommand(int instance_id,
+ const std::string& command);
+ // Overriden in tests.
+ virtual void OnHandleInputEvent(int instance_id,
+ const gfx::Rect& guest_window_rect,
+ const WebKit::WebInputEvent* event);
+ void OnLockMouse(bool user_gesture,
+ bool last_unlocked_by_target,
+ bool privileged);
+ void OnLockMouseAck(int instance_id, bool succeeded);
+ void OnNavigateGuest(int instance_id, const std::string& src);
+ void OnPluginDestroyed(int instance_id);
+ // Grab the new damage buffer from the embedder, and resize the guest's
+ // web contents.
+ void OnResizeGuest(int instance_id,
+ const BrowserPluginHostMsg_ResizeGuest_Params& params);
+ // Overriden in tests.
+ virtual void OnSetFocus(int instance_id, bool focused);
+ // Sets the name of the guest so that other guests in the same partition can
+ // access it.
+ void OnSetName(int instance_id, const std::string& name);
+ // Updates the size state of the guest.
+ void OnSetSize(
+ int instance_id,
+ const BrowserPluginHostMsg_AutoSize_Params& auto_size_params,
+ const BrowserPluginHostMsg_ResizeGuest_Params& resize_guest_params);
+ void OnSetEditCommandsForNextKeyEvent(
+ int instance_id,
+ const std::vector<EditCommand>& edit_commands);
+ // The guest WebContents is visible if both its embedder is visible and
+ // the browser plugin element is visible. If either one is not then the
+ // WebContents is marked as hidden. A hidden WebContents will consume
+ // fewer GPU and CPU resources.
+ //
+ // When every WebContents in a RenderProcessHost is hidden, it will lower
+ // the priority of the process (see RenderProcessHostImpl::WidgetHidden).
+ //
+ // It will also send a message to the guest renderer process to cleanup
+ // resources such as dropping back buffers and adjusting memory limits (if in
+ // compositing mode, see CCLayerTreeHost::setVisible).
+ //
+ // Additionally, it will slow down Javascript execution and garbage
+ // collection. See RenderThreadImpl::IdleHandler (executed when hidden) and
+ // RenderThreadImpl::IdleHandlerInForegroundTab (executed when visible).
+ void OnSetVisibility(int instance_id, bool visible);
+ // Message from embedder acknowledging last HW buffer.
+ void OnSwapBuffersACK(int instance_id,
+ int route_id,
+ int gpu_host_id,
+ const std::string& mailbox_name,
+ uint32 sync_point);
+ void OnUnlockMouse();
+ void OnUnlockMouseAck(int instance_id);
+ void OnUpdateGeometry(int instance_id, const gfx::Rect& view_rect);
+ void OnUpdateRectACK(
+ int instance_id,
+ bool needs_ack,
+ const BrowserPluginHostMsg_AutoSize_Params& auto_size_params,
+ const BrowserPluginHostMsg_ResizeGuest_Params& resize_guest_params);
+
+
+ // Message handlers for messages from guest.
+
+ void OnDragStopped();
+ void OnHandleInputEventAck(
+ WebKit::WebInputEvent::Type event_type,
+ InputEventAckState ack_result);
+ void OnHasTouchEventHandlers(bool accept);
+ void OnSetCursor(const WebCursor& cursor);
+ // On MacOSX popups are painted by the browser process. We handle them here
+ // so that they are positioned correctly.
+#if defined(OS_MACOSX)
+ void OnShowPopup(const ViewHostMsg_ShowPopup_Params& params);
+#endif
+ void OnShowWidget(int route_id, const gfx::Rect& initial_pos);
+ // Overriden in tests.
+ virtual void OnTakeFocus(bool reverse);
+ void OnUpdateFrameName(int frame_id,
+ bool is_top_level,
+ const std::string& name);
+ void OnUpdateRect(const ViewHostMsg_UpdateRect_Params& params);
+
+ // Requests download permission through embedder JavaScript API after
+ // retrieving url information from IO thread.
+ void DidRetrieveDownloadURLFromRequestId(
+ const std::string& request_method,
+ const base::Callback<void(bool)>& callback,
+ const std::string& url);
+
+ // Embedder sets permission to allow or deny geolocation request.
+ void SetGeolocationPermission(
+ GeolocationCallback callback, int bridge_id, bool allowed);
+
+ // Forwards all messages from the |pending_messages_| queue to the embedder.
+ void SendQueuedMessages();
+
+ // Weak pointer used to ask GeolocationPermissionContext about geolocation
+ // permission.
+ base::WeakPtrFactory<BrowserPluginGuest> weak_ptr_factory_;
+
+ // Static factory instance (always NULL for non-test).
+ static BrowserPluginHostFactory* factory_;
+
+ NotificationRegistrar notification_registrar_;
+ scoped_ptr<EmbedderRenderViewHostObserver> embedder_rvh_observer_;
+ WebContentsImpl* embedder_web_contents_;
+
+ std::map<int, int> bridge_id_to_request_id_map_;
+
+ // An identifier that uniquely identifies a browser plugin guest within an
+ // embedder.
+ int instance_id_;
+ scoped_ptr<base::SharedMemory> damage_buffer_;
+ // An identifier that uniquely identifies a damage buffer.
+ uint32 damage_buffer_sequence_id_;
+ size_t damage_buffer_size_;
+ gfx::Size damage_view_size_;
+ float damage_buffer_scale_factor_;
+ float guest_device_scale_factor_;
+ gfx::Rect guest_window_rect_;
+ gfx::Rect guest_screen_rect_;
+ base::TimeDelta guest_hang_timeout_;
+ bool focused_;
+ bool mouse_locked_;
+ bool pending_lock_request_;
+ bool guest_visible_;
+ bool embedder_visible_;
+ std::string name_;
+ bool auto_size_enabled_;
+ gfx::Size max_auto_size_;
+ gfx::Size min_auto_size_;
+
+ // Tracks the name, and target URL of the new window and whether or not it has
+ // changed since the WebContents has been created and before the new window
+ // has been attached to a BrowserPlugin. Once the first navigation commits, we
+ // no longer track this information.
+ struct NewWindowInfo {
+ bool changed;
+ GURL url;
+ std::string name;
+ NewWindowInfo(const GURL& url, const std::string& name) :
+ changed(false),
+ url(url),
+ name(name) {}
+ };
+ typedef std::map<BrowserPluginGuest*, NewWindowInfo> PendingWindowMap;
+ PendingWindowMap pending_new_windows_;
+ base::WeakPtr<BrowserPluginGuest> opener_;
+ // A counter to generate a unique request id for a permission request.
+ // We only need the ids to be unique for a given BrowserPluginGuest.
+ int next_permission_request_id_;
+
+ // A map to store relevant info for a request keyed by the request's id.
+ typedef std::map<int, scoped_refptr<PermissionRequest> > RequestMap;
+ RequestMap permission_request_map_;
+
+ // Indicates that this BrowserPluginGuest has associated renderer-side state.
+ // This is used to determine whether or not to create a new RenderView when
+ // this guest is attached.
+ bool has_render_view_;
+
+ // Last seen size of guest contents (by OnUpdateRect).
+ gfx::Size last_seen_view_size_;
+ // Last seen autosize attribute state (by OnUpdateRect).
+ bool last_seen_auto_size_enabled_;
+
+ // This is a queue of messages that are destined to be sent to the embedder
+ // once the guest is attached to a particular embedder.
+ std::queue<IPC::Message*> pending_messages_;
+
+ scoped_ptr<BrowserPluginGuestDelegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuest);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_H_
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest_helper.cc b/chromium/content/browser/browser_plugin/browser_plugin_guest_helper.cc
new file mode 100644
index 00000000000..ee18b6e92e3
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_guest_helper.cc
@@ -0,0 +1,55 @@
+// 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/common/drag_messages.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) {
+ if (ShouldForwardToBrowserPluginGuest(message))
+ return guest_->OnMessageReceived(message);
+ return false;
+}
+
+// static
+bool BrowserPluginGuestHelper::ShouldForwardToBrowserPluginGuest(
+ const IPC::Message& message) {
+ switch (message.type()) {
+ case DragHostMsg_StartDragging::ID:
+ case DragHostMsg_TargetDrop_ACK::ID:
+ case ViewHostMsg_HasTouchEventHandlers::ID:
+ case ViewHostMsg_SetCursor::ID:
+ #if defined(OS_MACOSX)
+ case ViewHostMsg_ShowPopup::ID:
+ #endif
+ case ViewHostMsg_ShowWidget::ID:
+ case ViewHostMsg_TakeFocus::ID:
+ case ViewHostMsg_UpdateFrameName::ID:
+ case ViewHostMsg_UpdateRect::ID:
+ case ViewHostMsg_LockMouse::ID:
+ case ViewHostMsg_UnlockMouse::ID:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest_helper.h b/chromium/content/browser/browser_plugin/browser_plugin_guest_helper.h
new file mode 100644
index 00000000000..dfe2e321691
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_guest_helper.h
@@ -0,0 +1,59 @@
+// 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/port/common/input_event_ack_state.h"
+#include "content/public/browser/render_view_host_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "third_party/WebKit/public/web/WebDragOperation.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+class WebCursor;
+#if defined(OS_MACOSX)
+struct ViewHostMsg_ShowPopup_Params;
+#endif
+struct ViewHostMsg_UpdateRect_Params;
+
+namespace gfx {
+class Size;
+}
+
+namespace content {
+class BrowserPluginGuest;
+class RenderViewHost;
+
+// Helper for BrowserPluginGuest.
+//
+// The purpose of this class is to intercept messages from the guest RenderView
+// before they are handled by the standard message handlers in the browser
+// process. This permits overriding standard behavior with BrowserPlugin-
+// specific behavior.
+//
+// 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:
+ // Returns whether a message should be forward to the helper's associated
+ // BrowserPluginGuest.
+ static bool ShouldForwardToBrowserPluginGuest(const IPC::Message& message);
+
+ BrowserPluginGuest* guest_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuestHelper);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_HELPER_H_
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc b/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc
new file mode 100644
index 00000000000..75de2031333
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.cc
@@ -0,0 +1,274 @@
+// Copyright (c) 2013 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_manager.h"
+
+#include "base/command_line.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/common/browser_plugin/browser_plugin_constants.h"
+#include "content/common/browser_plugin/browser_plugin_messages.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/user_metrics.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/result_codes.h"
+#include "content/public/common/url_constants.h"
+#include "net/base/escape.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+
+namespace content {
+
+// static
+BrowserPluginHostFactory* BrowserPluginGuestManager::factory_ = NULL;
+
+BrowserPluginGuestManager::BrowserPluginGuestManager()
+ : next_instance_id_(browser_plugin::kInstanceIDNone) {
+}
+
+BrowserPluginGuestManager::~BrowserPluginGuestManager() {
+}
+
+// static
+BrowserPluginGuestManager* BrowserPluginGuestManager::Create() {
+ if (factory_)
+ return factory_->CreateBrowserPluginGuestManager();
+ return new BrowserPluginGuestManager();
+}
+
+BrowserPluginGuest* BrowserPluginGuestManager::CreateGuest(
+ SiteInstance* embedder_site_instance,
+ int instance_id,
+ const BrowserPluginHostMsg_Attach_Params& params,
+ scoped_ptr<base::DictionaryValue> extra_params) {
+ SiteInstance* guest_site_instance = NULL;
+ // Validate that the partition id coming from the renderer is valid UTF-8,
+ // since we depend on this in other parts of the code, such as FilePath
+ // creation. If the validation fails, treat it as a bad message and kill the
+ // renderer process.
+ if (!IsStringUTF8(params.storage_partition_id)) {
+ content::RecordAction(UserMetricsAction("BadMessageTerminate_BPGM"));
+ base::KillProcess(
+ embedder_site_instance->GetProcess()->GetHandle(),
+ content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
+ return NULL;
+ }
+
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(switches::kSitePerProcess)) {
+ // When --site-per-process is specified, the behavior of BrowserPlugin
+ // as <webview> is broken and we use it for rendering out-of-process
+ // iframes instead. We use the src URL sent by the renderer to find the
+ // right process in which to place this instance.
+ // Note: Since BrowserPlugin doesn't support cross-process navigation,
+ // the instance will stay in the initially assigned process, regardless
+ // of the site it is navigated to.
+ // TODO(nasko): Fix this, and such that cross-process navigations are
+ // supported.
+ guest_site_instance =
+ embedder_site_instance->GetRelatedSiteInstance(GURL(params.src));
+ } else {
+ const std::string& host = embedder_site_instance->GetSiteURL().host();
+
+ std::string url_encoded_partition = net::EscapeQueryParamValue(
+ params.storage_partition_id, false);
+ // The SiteInstance of a given webview tag is based on the fact that it's
+ // a guest process in addition to which platform application the tag
+ // belongs to and what storage partition is in use, rather than the URL
+ // that the tag is being navigated to.
+ GURL guest_site(
+ base::StringPrintf("%s://%s/%s?%s", chrome::kGuestScheme,
+ host.c_str(),
+ params.persist_storage ? "persist" : "",
+ url_encoded_partition.c_str()));
+
+ // If we already have a webview tag in the same app using the same storage
+ // partition, we should use the same SiteInstance so the existing tag and
+ // the new tag can script each other.
+ guest_site_instance = GetGuestSiteInstance(guest_site);
+ if (!guest_site_instance) {
+ // Create the SiteInstance in a new BrowsingInstance, which will ensure
+ // that webview tags are also not allowed to send messages across
+ // different partitions.
+ guest_site_instance = SiteInstance::CreateForURL(
+ embedder_site_instance->GetBrowserContext(), guest_site);
+ }
+ }
+
+ return WebContentsImpl::CreateGuest(
+ embedder_site_instance->GetBrowserContext(),
+ guest_site_instance,
+ instance_id,
+ extra_params.Pass());
+}
+
+BrowserPluginGuest* BrowserPluginGuestManager::GetGuestByInstanceID(
+ int instance_id,
+ int embedder_render_process_id) const {
+ if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
+ instance_id)) {
+ return NULL;
+ }
+ GuestInstanceMap::const_iterator it =
+ guest_web_contents_by_instance_id_.find(instance_id);
+ if (it == guest_web_contents_by_instance_id_.end())
+ return NULL;
+ return static_cast<WebContentsImpl*>(it->second)->GetBrowserPluginGuest();
+}
+
+void BrowserPluginGuestManager::AddGuest(int instance_id,
+ WebContentsImpl* guest_web_contents) {
+ 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 BrowserPluginGuestManager::RemoveGuest(int instance_id) {
+ DCHECK(guest_web_contents_by_instance_id_.find(instance_id) !=
+ guest_web_contents_by_instance_id_.end());
+ guest_web_contents_by_instance_id_.erase(instance_id);
+}
+
+bool BrowserPluginGuestManager::CanEmbedderAccessInstanceIDMaybeKill(
+ int embedder_render_process_id,
+ int instance_id) const {
+ if (!CanEmbedderAccessInstanceID(embedder_render_process_id, instance_id)) {
+ // The embedder process is trying to access a guest it does not own.
+ content::RecordAction(UserMetricsAction("BadMessageTerminate_BPGM"));
+ base::KillProcess(
+ RenderProcessHost::FromID(embedder_render_process_id)->GetHandle(),
+ content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
+ return false;
+ }
+ return true;
+}
+
+void BrowserPluginGuestManager::OnMessageReceived(const IPC::Message& message,
+ int render_process_id) {
+ if (BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(message)) {
+ int instance_id = 0;
+ // All allowed messages must have instance_id as their first parameter.
+ PickleIterator iter(message);
+ bool success = iter.ReadInt(&instance_id);
+ DCHECK(success);
+ BrowserPluginGuest* guest =
+ GetGuestByInstanceID(instance_id, render_process_id);
+ if (guest && guest->OnMessageReceivedFromEmbedder(message))
+ return;
+ }
+ IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuestManager, message)
+ IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_BuffersSwappedACK,
+ OnUnhandledSwapBuffersACK)
+ IPC_END_MESSAGE_MAP()
+}
+
+// static
+bool BrowserPluginGuestManager::CanEmbedderAccessGuest(
+ int embedder_render_process_id,
+ BrowserPluginGuest* guest) {
+ // The embedder can access the guest if it has not been attached and its
+ // opener's embedder lives in the same process as the given embedder.
+ if (!guest->attached()) {
+ if (!guest->opener())
+ return false;
+
+ return embedder_render_process_id ==
+ guest->opener()->embedder_web_contents()->GetRenderProcessHost()->
+ GetID();
+ }
+
+ return embedder_render_process_id ==
+ guest->embedder_web_contents()->GetRenderProcessHost()->GetID();
+}
+
+bool BrowserPluginGuestManager::CanEmbedderAccessInstanceID(
+ int embedder_render_process_id,
+ int instance_id) const {
+ // The embedder is trying to access a guest with a negative or zero
+ // instance ID.
+ if (instance_id <= browser_plugin::kInstanceIDNone)
+ return false;
+
+ // The embedder is trying to access an instance ID that has not yet been
+ // allocated by BrowserPluginGuestManager. This could cause instance ID
+ // collisions in the future, and potentially give one embedder access to a
+ // guest it does not own.
+ if (instance_id > next_instance_id_)
+ return false;
+
+ GuestInstanceMap::const_iterator it =
+ guest_web_contents_by_instance_id_.find(instance_id);
+ if (it == guest_web_contents_by_instance_id_.end())
+ return true;
+ BrowserPluginGuest* guest =
+ static_cast<WebContentsImpl*>(it->second)->GetBrowserPluginGuest();
+
+ return CanEmbedderAccessGuest(embedder_render_process_id, guest);
+}
+
+SiteInstance* BrowserPluginGuestManager::GetGuestSiteInstance(
+ const GURL& guest_site) {
+ for (GuestInstanceMap::const_iterator it =
+ guest_web_contents_by_instance_id_.begin();
+ it != guest_web_contents_by_instance_id_.end(); ++it) {
+ if (it->second->GetSiteInstance()->GetSiteURL() == guest_site)
+ return it->second->GetSiteInstance();
+ }
+ return NULL;
+}
+
+// We only get here during teardown if we have one last buffer pending,
+// otherwise the ACK is handled by the guest.
+void BrowserPluginGuestManager::OnUnhandledSwapBuffersACK(
+ int instance_id,
+ int route_id,
+ int gpu_host_id,
+ const std::string& mailbox_name,
+ uint32 sync_point) {
+ BrowserPluginGuest::AcknowledgeBufferPresent(route_id,
+ gpu_host_id,
+ mailbox_name,
+ sync_point);
+}
+
+void BrowserPluginGuestManager::DidSendScreenRects(
+ WebContentsImpl* embedder_web_contents) {
+ // TODO(lazyboy): Generalize iterating over guest instances and performing
+ // actions on the guests.
+ for (GuestInstanceMap::iterator it =
+ guest_web_contents_by_instance_id_.begin();
+ it != guest_web_contents_by_instance_id_.end(); ++it) {
+ BrowserPluginGuest* guest = it->second->GetBrowserPluginGuest();
+ if (embedder_web_contents == guest->embedder_web_contents()) {
+ static_cast<RenderViewHostImpl*>(
+ guest->GetWebContents()->GetRenderViewHost())->SendScreenRects();
+ }
+ }
+}
+
+bool BrowserPluginGuestManager::UnlockMouseIfNecessary(
+ WebContentsImpl* embedder_web_contents,
+ const NativeWebKeyboardEvent& event) {
+ if ((event.type != WebKit::WebInputEvent::RawKeyDown) ||
+ (event.windowsKeyCode != ui::VKEY_ESCAPE) ||
+ (event.modifiers & WebKit::WebInputEvent::InputModifiers)) {
+ return false;
+ }
+
+ // TODO(lazyboy): Generalize iterating over guest instances and performing
+ // actions on the guests.
+ for (GuestInstanceMap::iterator it =
+ guest_web_contents_by_instance_id_.begin();
+ it != guest_web_contents_by_instance_id_.end(); ++it) {
+ BrowserPluginGuest* guest = it->second->GetBrowserPluginGuest();
+ if (embedder_web_contents == guest->embedder_web_contents()) {
+ if (guest->UnlockMouseIfNecessary(event))
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.h b/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.h
new file mode 100644
index 00000000000..a657bfa7a67
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_guest_manager.h
@@ -0,0 +1,137 @@
+// Copyright 2013 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 BrowserPluginGuestManager serves as a message router to BrowserPluginGuests
+// for all guests within a given profile.
+// Messages are routed to a particular guest instance based on an instance_id.
+
+#ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_MANAGER_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_MANAGER_H_
+
+#include "base/basictypes.h"
+#include "base/supports_user_data.h"
+#include "base/values.h"
+#include "content/common/content_export.h"
+#include "ipc/ipc_message.h"
+
+struct BrowserPluginHostMsg_Attach_Params;
+struct BrowserPluginHostMsg_ResizeGuest_Params;
+class GURL;
+
+namespace gfx {
+class Point;
+}
+
+namespace IPC {
+class Message;
+} // namespace IPC
+
+namespace content {
+
+class BrowserPluginGuest;
+class BrowserPluginHostFactory;
+class RenderProcessHostImpl;
+class RenderWidgetHostImpl;
+class SiteInstance;
+class WebContents;
+class WebContentsImpl;
+struct NativeWebKeyboardEvent;
+
+class CONTENT_EXPORT BrowserPluginGuestManager :
+ public base::SupportsUserData::Data {
+ public:
+ virtual ~BrowserPluginGuestManager();
+
+ static BrowserPluginGuestManager* Create();
+
+ // Overrides factory for testing. Default (NULL) value indicates regular
+ // (non-test) environment.
+ static void set_factory_for_testing(BrowserPluginHostFactory* factory) {
+ content::BrowserPluginGuestManager::factory_ = factory;
+ }
+
+ // Gets the next available instance id.
+ int get_next_instance_id() { return ++next_instance_id_; }
+
+ // Creates a guest WebContents with the provided |instance_id| and |params|.
+ // If params.src is present, the new guest will also be navigated to the
+ // provided URL. Optionally, the new guest may be attached to a
+ // |guest_opener|, and may be attached to a pre-selected |routing_id|.
+ BrowserPluginGuest* CreateGuest(
+ SiteInstance* embedder_site_instance,
+ int instance_id,
+ const BrowserPluginHostMsg_Attach_Params& params,
+ scoped_ptr<base::DictionaryValue> extra_params);
+
+ // Returns a BrowserPluginGuest given an |instance_id|. Returns NULL if the
+ // guest wasn't found. If the embedder is not permitted to access the given
+ // |instance_id|, the embedder is killed, and NULL is returned.
+ BrowserPluginGuest* GetGuestByInstanceID(
+ int instance_id,
+ int embedder_render_process_id) const;
+
+ // Adds a new |guest_web_contents| to the embedder (overridable in test).
+ virtual void AddGuest(int instance_id, WebContentsImpl* guest_web_contents);
+
+ // Removes the guest with the given |instance_id| from this
+ // BrowserPluginGuestManager.
+ void RemoveGuest(int instance_id);
+
+ // Returns whether the specified embedder is permitted to access the given
+ // |instance_id|, and kills the embedder if not.
+ bool CanEmbedderAccessInstanceIDMaybeKill(int embedder_render_process_id,
+ int instance_id) const;
+
+ void DidSendScreenRects(WebContentsImpl* embedder_web_contents);
+
+ bool UnlockMouseIfNecessary(WebContentsImpl* embedder_web_contents_,
+ const NativeWebKeyboardEvent& event);
+
+ void OnMessageReceived(const IPC::Message& message, int render_process_id);
+
+ private:
+ friend class TestBrowserPluginGuestManager;
+
+ BrowserPluginGuestManager();
+
+ // Returns whether the given embedder process is allowed to access the
+ // provided |guest|.
+ static bool CanEmbedderAccessGuest(int embedder_render_process_id,
+ BrowserPluginGuest* guest);
+
+ // Returns whether the given embedder process is allowed to use the provided
+ // |instance_id| or access the guest associated with the |instance_id|. If the
+ // embedder can, the method returns true. If the guest does not exist but the
+ // embedder can use that |instance_id|, then it returns true. If the embedder
+ // is not permitted to use that instance ID or access the associated guest,
+ // then it returns false.
+ bool CanEmbedderAccessInstanceID(int embedder_render_process_id,
+ int instance_id) const;
+
+ // Returns an existing SiteInstance if the current profile has a guest of the
+ // given |guest_site|.
+ SiteInstance* GetGuestSiteInstance(const GURL& guest_site);
+
+ // Message handlers.
+ void OnUnhandledSwapBuffersACK(int instance_id,
+ int route_id,
+ int gpu_host_id,
+ const std::string& mailbox_name,
+ uint32 sync_point);
+
+ // Static factory instance (always NULL outside of tests).
+ static BrowserPluginHostFactory* factory_;
+
+ // Contains guests' WebContents, mapping from their instance ids.
+ typedef std::map<int, WebContentsImpl*> GuestInstanceMap;
+ GuestInstanceMap guest_web_contents_by_instance_id_;
+ int next_instance_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginGuestManager);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_GUEST_MANAGER_H_
+
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc b/chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc
new file mode 100644
index 00000000000..c50e8cfa8a3
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_host_browsertest.cc
@@ -0,0 +1,814 @@
+// Copyright 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/command_line.h"
+#include "base/memory/singleton.h"
+#include "base/run_loop.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_timeouts.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/browser_plugin/test_browser_plugin_guest_manager.h"
+#include "content/browser/child_process_security_policy_impl.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/browser/render_view_host_observer.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/drop_data.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.h"
+#include "content/test/content_browser_test_utils.h"
+#include "net/base/net_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "net/test/spawned_test_server/spawned_test_server.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using content::BrowserPluginEmbedder;
+using content::BrowserPluginGuest;
+using content::BrowserPluginHostFactory;
+using content::WebContentsImpl;
+
+namespace {
+
+const char kHTMLForGuest[] =
+ "data:text/html,<html><body>hello world</body></html>";
+const char kHTMLForGuestBusyLoop[] =
+ "data:text/html,<html><head><script type=\"text/javascript\">"
+ "function PauseMs(timems) {"
+ " document.title = \"start\";"
+ " var date = new Date();"
+ " var currDate = null;"
+ " do {"
+ " currDate = new Date();"
+ " } while (currDate - date < timems)"
+ "}"
+ "function StartPauseMs(timems) {"
+ " setTimeout(function() { PauseMs(timems); }, 0);"
+ "}"
+ "</script></head><body></body></html>";
+const char kHTMLForGuestTouchHandler[] =
+ "data:text/html,<html><body><div id=\"touch\">With touch</div></body>"
+ "<script type=\"text/javascript\">"
+ "function handler() {}"
+ "function InstallTouchHandler() { "
+ " document.getElementById(\"touch\").addEventListener(\"touchstart\", "
+ " handler);"
+ "}"
+ "function UninstallTouchHandler() { "
+ " document.getElementById(\"touch\").removeEventListener(\"touchstart\", "
+ " handler);"
+ "}"
+ "</script></html>";
+const char kHTMLForGuestWithTitle[] =
+ "data:text/html,"
+ "<html><head><title>%s</title></head>"
+ "<body>hello world</body>"
+ "</html>";
+const char kHTMLForGuestAcceptDrag[] =
+ "data:text/html,<html><body>"
+ "<script>"
+ "function dropped() {"
+ " document.title = \"DROPPED\";"
+ "}"
+ "</script>"
+ "<textarea id=\"text\" style=\"width:100%; height: 100%\""
+ " ondrop=\"dropped();\">"
+ "</textarea>"
+ "</body></html>";
+const char kHTMLForGuestWithSize[] =
+ "data:text/html,"
+ "<html>"
+ "<body style=\"margin: 0px;\">"
+ "<img style=\"width: 100%; height: 400px;\"/>"
+ "</body>"
+ "</html>";
+
+std::string GetHTMLForGuestWithTitle(const std::string& title) {
+ return base::StringPrintf(kHTMLForGuestWithTitle, title.c_str());
+}
+
+} // namespace
+
+namespace content {
+
+// Test factory for creating test instances of BrowserPluginEmbedder and
+// BrowserPluginGuest.
+class TestBrowserPluginHostFactory : public BrowserPluginHostFactory {
+ public:
+ virtual BrowserPluginGuestManager*
+ CreateBrowserPluginGuestManager() OVERRIDE {
+ guest_manager_instance_count_++;
+ if (message_loop_runner_.get())
+ message_loop_runner_->Quit();
+ return new TestBrowserPluginGuestManager();
+ }
+
+ virtual BrowserPluginGuest* CreateBrowserPluginGuest(
+ int instance_id,
+ WebContentsImpl* web_contents) OVERRIDE {
+ return new TestBrowserPluginGuest(instance_id, web_contents);
+ }
+
+ // Also keeps track of number of instances created.
+ virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
+ WebContentsImpl* web_contents) OVERRIDE {
+
+ return new TestBrowserPluginEmbedder(web_contents);
+ }
+
+ // 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 WaitForGuestManagerCreation() {
+ // Check if already have created an instance.
+ if (guest_manager_instance_count_ > 0)
+ return;
+ // Wait otherwise.
+ message_loop_runner_ = new MessageLoopRunner();
+ message_loop_runner_->Run();
+ }
+
+ protected:
+ TestBrowserPluginHostFactory() : guest_manager_instance_count_(0) {}
+ virtual ~TestBrowserPluginHostFactory() {}
+
+ private:
+ // For Singleton.
+ friend struct DefaultSingletonTraits<TestBrowserPluginHostFactory>;
+
+ scoped_refptr<MessageLoopRunner> message_loop_runner_;
+ int guest_manager_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) OVERRIDE {
+ BrowserPluginGuest* guest =
+ new TestBrowserPluginGuest(instance_id, web_contents);
+ 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);
+};
+
+// A transparent observer that can be used to verify that a RenderViewHost
+// received a specific message.
+class RenderViewHostMessageObserver : public RenderViewHostObserver {
+ public:
+ RenderViewHostMessageObserver(RenderViewHost* host,
+ uint32 message_id)
+ : RenderViewHostObserver(host),
+ message_id_(message_id),
+ message_received_(false) {
+ }
+
+ virtual ~RenderViewHostMessageObserver() {}
+
+ void WaitUntilMessageReceived() {
+ if (message_received_)
+ return;
+ message_loop_runner_ = new MessageLoopRunner();
+ message_loop_runner_->Run();
+ }
+
+ void ResetState() {
+ message_received_ = false;
+ }
+
+ // IPC::Listener implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
+ if (message.type() == message_id_) {
+ message_received_ = true;
+ if (message_loop_runner_.get())
+ message_loop_runner_->Quit();
+ }
+ return false;
+ }
+
+ private:
+ scoped_refptr<MessageLoopRunner> message_loop_runner_;
+ uint32 message_id_;
+ bool message_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderViewHostMessageObserver);
+};
+
+class BrowserPluginHostTest : public ContentBrowserTest {
+ public:
+ BrowserPluginHostTest()
+ : test_embedder_(NULL),
+ test_guest_(NULL),
+ test_guest_manager_(NULL) {}
+
+ 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());
+ content::BrowserPluginGuestManager::set_factory_for_testing(
+ TestBrowserPluginHostFactory::GetInstance());
+
+ // On legacy windows, the AcceptDragEvents test needs this to pass.
+#if defined(OS_WIN) && !defined(USE_AURA)
+ UseRealGLBindings();
+#endif
+
+ ContentBrowserTest::SetUp();
+ }
+ virtual void TearDown() OVERRIDE {
+ content::BrowserPluginEmbedder::set_factory_for_testing(NULL);
+ content::BrowserPluginGuest::set_factory_for_testing(NULL);
+
+ ContentBrowserTest::TearDown();
+ }
+
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ // Enable browser plugin in content_shell for running test.
+ command_line->AppendSwitch(switches::kEnableBrowserPluginForAllViewTypes);
+ }
+
+ static void SimulateSpaceKeyPress(WebContents* web_contents) {
+ SimulateKeyPress(web_contents,
+ ui::VKEY_SPACE,
+ false, // control.
+ false, // shift.
+ false, // alt.
+ false); // command.
+ }
+
+ static void SimulateTabKeyPress(WebContents* web_contents) {
+ SimulateKeyPress(web_contents,
+ ui::VKEY_TAB,
+ false, // control.
+ false, // shift.
+ false, // alt.
+ false); // command.
+ }
+
+ // Executes the javascript synchronously and makes sure the returned value is
+ // freed properly.
+ void ExecuteSyncJSFunction(RenderViewHost* rvh, const std::string& jscript) {
+ scoped_ptr<base::Value> value =
+ content::ExecuteScriptAndGetValue(rvh, jscript);
+ }
+
+ bool IsAttributeNull(RenderViewHost* rvh, const std::string& attribute) {
+ scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(rvh,
+ "document.getElementById('plugin').getAttribute('" + attribute + "');");
+ return value->GetType() == Value::TYPE_NULL;
+ }
+
+ // Removes all attributes in the comma-delimited string |attributes|.
+ void RemoveAttributes(RenderViewHost* rvh, const std::string& attributes) {
+ std::vector<std::string> attributes_list;
+ base::SplitString(attributes, ',', &attributes_list);
+ std::vector<std::string>::const_iterator itr;
+ for (itr = attributes_list.begin(); itr != attributes_list.end(); ++itr) {
+ ExecuteSyncJSFunction(rvh, "document.getElementById('plugin')"
+ "." + *itr + " = null;");
+ }
+ }
+
+ // This helper method does the following:
+ // 1. Start the test server and navigate the shell to |embedder_url|.
+ // 2. Execute custom pre-navigation |embedder_code| if provided.
+ // 3. Navigate the guest to the |guest_url|.
+ // 4. Verify that the guest has been created and has completed loading.
+ void StartBrowserPluginTest(const std::string& embedder_url,
+ const std::string& guest_url,
+ bool is_guest_data_url,
+ const std::string& embedder_code) {
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ GURL test_url(embedded_test_server()->GetURL(embedder_url));
+ NavigateToURL(shell(), test_url);
+
+ WebContentsImpl* embedder_web_contents = static_cast<WebContentsImpl*>(
+ shell()->web_contents());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ embedder_web_contents->GetRenderViewHost());
+ // Focus the embedder.
+ rvh->Focus();
+ // Activative IME.
+ rvh->SetInputMethodActive(true);
+
+ // Allow the test to do some operations on the embedder before we perform
+ // the first navigation of the guest.
+ if (!embedder_code.empty())
+ ExecuteSyncJSFunction(rvh, embedder_code);
+
+ if (!is_guest_data_url) {
+ test_url = embedded_test_server()->GetURL(guest_url);
+ ExecuteSyncJSFunction(
+ rvh, base::StringPrintf("SetSrc('%s');", test_url.spec().c_str()));
+ } else {
+ ExecuteSyncJSFunction(
+ rvh, base::StringPrintf("SetSrc('%s');", guest_url.c_str()));
+ }
+
+ // Wait to make sure embedder is created/attached to WebContents.
+ TestBrowserPluginHostFactory::GetInstance()->WaitForGuestManagerCreation();
+
+ test_embedder_ = static_cast<TestBrowserPluginEmbedder*>(
+ embedder_web_contents->GetBrowserPluginEmbedder());
+ ASSERT_TRUE(test_embedder_);
+
+ test_guest_manager_ = static_cast<TestBrowserPluginGuestManager*>(
+ embedder_web_contents->GetBrowserPluginGuestManager());
+ ASSERT_TRUE(test_guest_manager_);
+
+ test_guest_manager_->WaitForGuestAdded();
+
+ // Verify that we have exactly one guest.
+ const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map =
+ test_guest_manager_->guest_web_contents_for_testing();
+ EXPECT_EQ(1u, instance_map.size());
+
+ WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
+ instance_map.begin()->second);
+ test_guest_ = static_cast<TestBrowserPluginGuest*>(
+ test_guest_web_contents->GetBrowserPluginGuest());
+ test_guest_->WaitForLoadStop();
+ }
+
+ TestBrowserPluginEmbedder* test_embedder() const { return test_embedder_; }
+ TestBrowserPluginGuest* test_guest() const { return test_guest_; }
+ TestBrowserPluginGuestManager* test_guest_manager() const {
+ return test_guest_manager_;
+ }
+
+ private:
+ TestBrowserPluginEmbedder* test_embedder_;
+ TestBrowserPluginGuest* test_guest_;
+ TestBrowserPluginGuestManager* test_guest_manager_;
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginHostTest);
+};
+
+// 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) {
+ const gfx::Size nxt_size = gfx::Size(100, 200);
+ const std::string embedder_code = base::StringPrintf(
+ "SetSize(%d, %d);", nxt_size.width(), nxt_size.height());
+ const char kEmbedderURL[] = "/browser_plugin_embedder.html";
+ StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, embedder_code);
+
+ // Wait for the guest to receive a damage buffer of size 100x200.
+ // This means the guest will be painted properly at that size.
+ test_guest()->WaitForDamageBufferWithSize(nxt_size);
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AdvanceFocus) {
+ const char kEmbedderURL[] = "/browser_plugin_focus.html";
+ const char* kGuestURL = "/browser_plugin_focus_child.html";
+ StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
+
+ SimulateMouseClick(test_embedder()->web_contents(), 0,
+ WebKit::WebMouseEvent::ButtonLeft);
+ BrowserPluginHostTest::SimulateTabKeyPress(test_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(test_embedder()->web_contents());
+ BrowserPluginHostTest::SimulateTabKeyPress(test_embedder()->web_contents());
+ BrowserPluginHostTest::SimulateTabKeyPress(test_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) {
+ net::SpawnedTestServer https_server(
+ net::SpawnedTestServer::TYPE_HTTPS,
+ net::SpawnedTestServer::kLocalhost,
+ base::FilePath(FILE_PATH_LITERAL("content/test/data")));
+ ASSERT_TRUE(https_server.Start());
+
+ // 1. Load an embedder page with one guest in it.
+ const char kEmbedderURL[] = "/browser_plugin_embedder.html";
+ StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
+
+ // 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>(test_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(), 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) {
+ const char kEmbedderURL[] = "/browser_plugin_embedder.html";
+ StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
+ WebContentsImpl* embedder_web_contents = test_embedder()->web_contents();
+
+ // 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(embedded_test_server()->GetURL(
+ "/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());
+}
+
+// This test verifies that hiding the embedder also hides the guest.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, BrowserPluginVisibilityChanged) {
+ const char kEmbedderURL[] = "/browser_plugin_embedder.html";
+ StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
+
+ // Hide the Browser Plugin.
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ test_embedder()->web_contents()->GetRenderViewHost());
+ ExecuteSyncJSFunction(
+ rvh, "document.getElementById('plugin').style.visibility = 'hidden'");
+
+ // Make sure that the guest is hidden.
+ test_guest()->WaitUntilHidden();
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, EmbedderVisibilityChanged) {
+ const char kEmbedderURL[] = "/browser_plugin_embedder.html";
+ StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
+
+ // Hide the embedder.
+ test_embedder()->web_contents()->WasHidden();
+
+ // Make sure that hiding the embedder also hides the guest.
+ test_guest()->WaitUntilHidden();
+}
+
+// Verifies that installing/uninstalling touch-event handlers in the guest
+// plugin correctly updates the touch-event handling state in the embedder.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AcceptTouchEvents) {
+ const char kEmbedderURL[] = "/browser_plugin_embedder.html";
+ StartBrowserPluginTest(
+ kEmbedderURL, kHTMLForGuestTouchHandler, true, std::string());
+
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ test_embedder()->web_contents()->GetRenderViewHost());
+ // The embedder should not have any touch event handlers at this point.
+ EXPECT_FALSE(rvh->has_touch_handler());
+
+ // Install the touch handler in the guest. This should cause the embedder to
+ // start listening for touch events too.
+ RenderViewHostMessageObserver observer(rvh,
+ ViewHostMsg_HasTouchEventHandlers::ID);
+ ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(),
+ "InstallTouchHandler();");
+ observer.WaitUntilMessageReceived();
+ EXPECT_TRUE(rvh->has_touch_handler());
+
+ // Uninstalling the touch-handler in guest should cause the embedder to stop
+ // listening for touch events.
+ observer.ResetState();
+ ExecuteSyncJSFunction(test_guest()->web_contents()->GetRenderViewHost(),
+ "UninstallTouchHandler();");
+ observer.WaitUntilMessageReceived();
+ EXPECT_FALSE(rvh->has_touch_handler());
+}
+
+// This tests verifies that reloading the embedder does not crash the browser
+// and that the guest is reset.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, ReloadEmbedder) {
+ const char kEmbedderURL[] = "/browser_plugin_embedder.html";
+ StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ test_embedder()->web_contents()->GetRenderViewHost());
+
+ // Change the title of the page to 'modified' so that we know that
+ // the page has successfully reloaded when it goes back to 'embedder'
+ // in the next step.
+ {
+ const string16 expected_title = ASCIIToUTF16("modified");
+ content::TitleWatcher title_watcher(test_embedder()->web_contents(),
+ expected_title);
+
+ ExecuteSyncJSFunction(rvh,
+ base::StringPrintf("SetTitle('%s');", "modified"));
+
+ string16 actual_title = title_watcher.WaitAndGetTitle();
+ EXPECT_EQ(expected_title, actual_title);
+ }
+
+ // Reload the embedder page, and verify that the reload was successful.
+ // Then navigate the guest to verify that the browser process does not crash.
+ {
+ const string16 expected_title = ASCIIToUTF16("embedder");
+ content::TitleWatcher title_watcher(test_embedder()->web_contents(),
+ expected_title);
+
+ test_embedder()->web_contents()->GetController().Reload(false);
+ string16 actual_title = title_watcher.WaitAndGetTitle();
+ EXPECT_EQ(expected_title, actual_title);
+
+ ExecuteSyncJSFunction(
+ test_embedder()->web_contents()->GetRenderViewHost(),
+ base::StringPrintf("SetSrc('%s');", kHTMLForGuest));
+ test_guest_manager()->WaitForGuestAdded();
+
+ const TestBrowserPluginGuestManager::GuestInstanceMap& instance_map =
+ test_guest_manager()->guest_web_contents_for_testing();
+ WebContentsImpl* test_guest_web_contents = static_cast<WebContentsImpl*>(
+ instance_map.begin()->second);
+ TestBrowserPluginGuest* new_test_guest =
+ static_cast<TestBrowserPluginGuest*>(
+ test_guest_web_contents->GetBrowserPluginGuest());
+ ASSERT_TRUE(new_test_guest != NULL);
+
+ // Wait for the guest to send an UpdateRectMsg, meaning it is ready.
+ new_test_guest->WaitForUpdateRectMsg();
+ }
+}
+
+// Always failing in the win7_aura try bot. See http://crbug.com/181107.
+#if defined(OS_WIN) && defined(USE_AURA)
+#define MAYBE_AcceptDragEvents DISABLED_AcceptDragEvents
+#else
+#define MAYBE_AcceptDragEvents AcceptDragEvents
+#endif
+
+// Tests that a drag-n-drop over the browser plugin in the embedder happens
+// correctly.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, MAYBE_AcceptDragEvents) {
+ const char kEmbedderURL[] = "/browser_plugin_dragging.html";
+ StartBrowserPluginTest(
+ kEmbedderURL, kHTMLForGuestAcceptDrag, true, std::string());
+
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ test_embedder()->web_contents()->GetRenderViewHost());
+
+ // Get a location in the embedder outside of the plugin.
+ base::ListValue *start, *end;
+ scoped_ptr<base::Value> value =
+ content::ExecuteScriptAndGetValue(rvh, "dragLocation()");
+ ASSERT_TRUE(value->GetAsList(&start) && start->GetSize() == 2);
+ double start_x, start_y;
+ ASSERT_TRUE(start->GetDouble(0, &start_x) && start->GetDouble(1, &start_y));
+
+ // Get a location in the embedder that falls inside the plugin.
+ value = content::ExecuteScriptAndGetValue(rvh, "dropLocation()");
+ ASSERT_TRUE(value->GetAsList(&end) && end->GetSize() == 2);
+ double end_x, end_y;
+ ASSERT_TRUE(end->GetDouble(0, &end_x) && end->GetDouble(1, &end_y));
+
+ DropData drop_data;
+ GURL url = GURL("https://www.domain.com/index.html");
+ drop_data.url = url;
+
+ // Pretend that the URL is being dragged over the embedder. Start the drag
+ // from outside the plugin, then move the drag inside the plugin and drop.
+ // This should trigger appropriate messages from the embedder to the guest,
+ // and end with a drop on the guest. The guest changes title when a drop
+ // happens.
+ const string16 expected_title = ASCIIToUTF16("DROPPED");
+ content::TitleWatcher title_watcher(test_guest()->web_contents(),
+ expected_title);
+
+ rvh->DragTargetDragEnter(drop_data, gfx::Point(start_x, start_y),
+ gfx::Point(start_x, start_y), WebKit::WebDragOperationEvery, 0);
+ rvh->DragTargetDragOver(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y),
+ WebKit::WebDragOperationEvery, 0);
+ rvh->DragTargetDrop(gfx::Point(end_x, end_y), gfx::Point(end_x, end_y), 0);
+
+ string16 actual_title = title_watcher.WaitAndGetTitle();
+ EXPECT_EQ(expected_title, actual_title);
+}
+
+// This test verifies that round trip postMessage works as expected.
+// 1. The embedder posts a message 'testing123' to the guest.
+// 2. The guest receives and replies to the message using the event object's
+// source object: event.source.postMessage('foobar', '*')
+// 3. The embedder receives the message and uses the event's source
+// object to do one final reply: 'stop'
+// 4. The guest receives the final 'stop' message.
+// 5. The guest acks the 'stop' message with a 'stop_ack' message.
+// 6. The embedder changes its title to 'main guest' when it sees the 'stop_ack'
+// message.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PostMessage) {
+ const char* kTesting = "testing123";
+ const char* kEmbedderURL = "/browser_plugin_embedder.html";
+ const char* kGuestURL = "/browser_plugin_post_message_guest.html";
+ StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ test_embedder()->web_contents()->GetRenderViewHost());
+ {
+ const string16 expected_title = ASCIIToUTF16("main guest");
+ content::TitleWatcher title_watcher(test_embedder()->web_contents(),
+ expected_title);
+
+ // By the time we get here 'contentWindow' should be ready because the
+ // guest has completed loading.
+ ExecuteSyncJSFunction(
+ rvh, base::StringPrintf("PostMessage('%s, false');", kTesting));
+
+ // The title will be updated to "main guest" at the last stage of the
+ // process described above.
+ string16 actual_title = title_watcher.WaitAndGetTitle();
+ EXPECT_EQ(expected_title, actual_title);
+ }
+}
+
+// This is the same as BrowserPluginHostTest.PostMessage but also
+// posts a message to an iframe.
+// TODO(fsamuel): This test should replace the previous test once postMessage
+// iframe targeting is fixed (see http://crbug.com/153701).
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_PostMessageToIFrame) {
+ const char* kTesting = "testing123";
+ const char* kEmbedderURL = "/browser_plugin_embedder.html";
+ const char* kGuestURL = "/browser_plugin_post_message_guest.html";
+ StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, std::string());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ test_embedder()->web_contents()->GetRenderViewHost());
+ {
+ const string16 expected_title = ASCIIToUTF16("main guest");
+ content::TitleWatcher title_watcher(test_embedder()->web_contents(),
+ expected_title);
+
+ ExecuteSyncJSFunction(
+ rvh, base::StringPrintf("PostMessage('%s, false');", kTesting));
+
+ // The title will be updated to "main guest" at the last stage of the
+ // process described above.
+ string16 actual_title = title_watcher.WaitAndGetTitle();
+ EXPECT_EQ(expected_title, actual_title);
+ }
+ {
+ content::TitleWatcher ready_watcher(test_embedder()->web_contents(),
+ ASCIIToUTF16("ready"));
+
+ RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
+ test_guest()->web_contents()->GetRenderViewHost());
+ GURL test_url = embedded_test_server()->GetURL(
+ "/browser_plugin_post_message_guest.html");
+ ExecuteSyncJSFunction(
+ guest_rvh,
+ base::StringPrintf(
+ "CreateChildFrame('%s');", test_url.spec().c_str()));
+
+ string16 actual_title = ready_watcher.WaitAndGetTitle();
+ EXPECT_EQ(ASCIIToUTF16("ready"), actual_title);
+
+ content::TitleWatcher iframe_watcher(test_embedder()->web_contents(),
+ ASCIIToUTF16("iframe"));
+ ExecuteSyncJSFunction(
+ rvh, base::StringPrintf("PostMessage('%s', true);", kTesting));
+
+ // The title will be updated to "iframe" at the last stage of the
+ // process described above.
+ actual_title = iframe_watcher.WaitAndGetTitle();
+ EXPECT_EQ(ASCIIToUTF16("iframe"), actual_title);
+ }
+}
+
+// This test verifies that if a browser plugin is hidden before navigation,
+// the guest starts off hidden.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, HiddenBeforeNavigation) {
+ const char* kEmbedderURL = "/browser_plugin_embedder.html";
+ const std::string embedder_code =
+ "document.getElementById('plugin').style.visibility = 'hidden'";
+ StartBrowserPluginTest(
+ kEmbedderURL, kHTMLForGuest, true, embedder_code);
+ EXPECT_FALSE(test_guest()->visible());
+}
+
+// This test verifies that if a browser plugin is focused before navigation then
+// the guest starts off focused.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusBeforeNavigation) {
+ const char* kEmbedderURL = "/browser_plugin_embedder.html";
+ const std::string embedder_code =
+ "document.getElementById('plugin').focus();";
+ StartBrowserPluginTest(
+ kEmbedderURL, kHTMLForGuest, true, embedder_code);
+ RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
+ test_guest()->web_contents()->GetRenderViewHost());
+ // Verify that the guest is focused.
+ scoped_ptr<base::Value> value =
+ content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()");
+ bool result = false;
+ ASSERT_TRUE(value->GetAsBoolean(&result));
+ EXPECT_TRUE(result);
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, FocusTracksEmbedder) {
+ const char* kEmbedderURL = "/browser_plugin_embedder.html";
+ StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ test_embedder()->web_contents()->GetRenderViewHost());
+ RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>(
+ test_guest()->web_contents()->GetRenderViewHost());
+ {
+ // Focus the BrowserPlugin. This will have the effect of also focusing the
+ // current guest.
+ ExecuteSyncJSFunction(rvh, "document.getElementById('plugin').focus();");
+ // Verify that key presses go to the guest.
+ SimulateSpaceKeyPress(test_embedder()->web_contents());
+ test_guest()->WaitForInput();
+ // Verify that the guest is focused.
+ scoped_ptr<base::Value> value =
+ content::ExecuteScriptAndGetValue(guest_rvh, "document.hasFocus()");
+ bool result = false;
+ ASSERT_TRUE(value->GetAsBoolean(&result));
+ EXPECT_TRUE(result);
+ }
+ // Blur the embedder.
+ test_embedder()->web_contents()->GetRenderViewHost()->Blur();
+ test_guest()->WaitForBlur();
+}
+
+// Test for regression http://crbug.com/162961.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, GetRenderViewHostAtPositionTest) {
+ const char kEmbedderURL[] = "/browser_plugin_embedder.html";
+ const std::string embedder_code =
+ base::StringPrintf("SetSize(%d, %d);", 100, 100);
+ StartBrowserPluginTest(kEmbedderURL, kHTMLForGuestWithSize, true,
+ embedder_code);
+ // Check for render view host at position (150, 150) that is outside the
+ // bounds of our guest, so this would respond with the render view host of the
+ // embedder.
+ test_embedder()->WaitForRenderViewHostAtPosition(150, 150);
+ ASSERT_EQ(test_embedder()->web_contents()->GetRenderViewHost(),
+ test_embedder()->last_rvh_at_position_response());
+}
+
+// This test verifies that if IME is enabled in the embedder, it is also enabled
+// in the guest.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, VerifyInputMethodActive) {
+ const char* kEmbedderURL = "/browser_plugin_embedder.html";
+ StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ test_guest()->web_contents()->GetRenderViewHost());
+ EXPECT_TRUE(rvh->input_method_active());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_host_factory.h b/chromium/content/browser/browser_plugin/browser_plugin_host_factory.h
new file mode 100644
index 00000000000..9bfc1e2d740
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_host_factory.h
@@ -0,0 +1,41 @@
+// 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/strings/string16.h"
+#include "content/common/content_export.h"
+
+struct BrowserPluginHostMsg_CreateGuest_Params;
+
+namespace content {
+
+class BrowserPluginEmbedder;
+class BrowserPluginGuest;
+class RenderViewHost;
+class WebContentsImpl;
+
+// Factory to create BrowserPlugin embedder and guest.
+class CONTENT_EXPORT BrowserPluginHostFactory {
+ public:
+ virtual BrowserPluginGuestManager* CreateBrowserPluginGuestManager() = 0;
+
+ virtual BrowserPluginGuest* CreateBrowserPluginGuest(
+ int instance_id,
+ WebContentsImpl* web_contents) = 0;
+
+ virtual BrowserPluginEmbedder* CreateBrowserPluginEmbedder(
+ WebContentsImpl* web_contents) = 0;
+
+ protected:
+ virtual ~BrowserPluginHostFactory() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_HOST_FACTORY_H_
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc b/chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc
new file mode 100644
index 00000000000..031797a46c4
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc
@@ -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.
+
+#include "content/browser/browser_plugin/browser_plugin_message_filter.h"
+
+#include "base/supports_user_data.h"
+#include "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/browser_plugin/browser_plugin_constants.h"
+#include "content/common/browser_plugin/browser_plugin_messages.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_view_host.h"
+
+namespace content {
+
+BrowserPluginMessageFilter::BrowserPluginMessageFilter(int render_process_id,
+ bool is_guest)
+ : render_process_id_(render_process_id),
+ is_guest_(is_guest) {
+}
+
+BrowserPluginMessageFilter::~BrowserPluginMessageFilter() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+}
+
+bool BrowserPluginMessageFilter::OnMessageReceived(
+ const IPC::Message& message,
+ bool* message_was_ok) {
+ // Any message requested by a BrowserPluginGuest should be routed through
+ // a BrowserPluginGuestManager.
+ if (BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(message)) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserPluginGuestManager* guest_manager = GetBrowserPluginGuestManager();
+ if (guest_manager)
+ guest_manager->OnMessageReceived(message, render_process_id_);
+ // We always swallow messages destined for BrowserPluginGuestManager because
+ // we're on the UI thread and fallback code is expected to be run on the IO
+ // thread.
+ return true;
+ }
+ return false;
+}
+
+void BrowserPluginMessageFilter::OnDestruct() const {
+ BrowserThread::DeleteOnIOThread::Destruct(this);
+}
+
+void BrowserPluginMessageFilter::OverrideThreadForMessage(
+ const IPC::Message& message, BrowserThread::ID* thread) {
+ if (BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(message))
+ *thread = BrowserThread::UI;
+}
+
+BrowserPluginGuestManager*
+ BrowserPluginMessageFilter::GetBrowserPluginGuestManager() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ RenderProcessHostImpl* host = static_cast<RenderProcessHostImpl*>(
+ RenderProcessHost::FromID(render_process_id_));
+ if (!host)
+ return NULL;
+
+ BrowserContext* browser_context = host->GetBrowserContext();
+ return static_cast<BrowserPluginGuestManager*>(
+ browser_context->GetUserData(
+ browser_plugin::kBrowserPluginGuestManagerKeyName));
+}
+
+} // namespace content
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_message_filter.h b/chromium/content/browser/browser_plugin/browser_plugin_message_filter.h
new file mode 100644
index 00000000000..829c1badcbf
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_message_filter.h
@@ -0,0 +1,45 @@
+// 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_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_MESSAGE_FILTER_H_
+
+#include "content/public/browser/browser_message_filter.h"
+
+namespace content {
+
+class BrowserContext;
+class BrowserPluginGuestManager;
+
+// This class filters out incoming IPC messages for the guest renderer process
+// on the IPC thread before other message filters handle them.
+class BrowserPluginMessageFilter : public BrowserMessageFilter {
+ public:
+ BrowserPluginMessageFilter(int render_process_id, bool is_guest);
+
+ // BrowserMessageFilter implementation.
+ virtual void OverrideThreadForMessage(
+ const IPC::Message& message,
+ BrowserThread::ID* thread) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) OVERRIDE;
+ virtual void OnDestruct() const OVERRIDE;
+
+ private:
+ friend class BrowserThread;
+ friend class base::DeleteHelper<BrowserPluginMessageFilter>;
+
+ virtual ~BrowserPluginMessageFilter();
+
+ BrowserPluginGuestManager* GetBrowserPluginGuestManager();
+
+ int render_process_id_;
+ int is_guest_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginMessageFilter);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_MESSAGE_FILTER_H_
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h b/chromium/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h
new file mode 100644
index 00000000000..2b462371001
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h
@@ -0,0 +1,34 @@
+// 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_POPUP_MENU_HELPER_MAC_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_POPUP_MENU_HELPER_MAC_H_
+
+#include "content/browser/renderer_host/popup_menu_helper_mac.h"
+
+namespace content {
+class RenderViewHost;
+class RenderViewHostImpl;
+
+// This class is similiar to PopupMenuHelperMac but positions the popup relative
+// to the embedder, and issues a reply to the guest.
+class BrowserPluginPopupMenuHelper : public PopupMenuHelper {
+ public:
+ // Creates a BrowserPluginPopupMenuHelper that positions popups relative to
+ // |embedder_rvh| and will notify |guest_rvh| when a user selects or cancels
+ // the popup.
+ BrowserPluginPopupMenuHelper(RenderViewHost* embedder_rvh,
+ RenderViewHost* guest_rvh);
+
+ private:
+ virtual RenderWidgetHostViewMac* GetRenderWidgetHostView() const OVERRIDE;
+
+ RenderViewHostImpl* embedder_rvh_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserPluginPopupMenuHelper);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_POPUP_MENU_HELPER_MAC_H_
diff --git a/chromium/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.mm b/chromium/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.mm
new file mode 100644
index 00000000000..e2b5da55c00
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.mm
@@ -0,0 +1,23 @@
+// 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_popup_menu_helper_mac.h"
+
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_mac.h"
+
+namespace content {
+
+BrowserPluginPopupMenuHelper::BrowserPluginPopupMenuHelper(
+ RenderViewHost* embedder_rvh, RenderViewHost* guest_rvh)
+ : PopupMenuHelper(guest_rvh),
+ embedder_rvh_(static_cast<RenderViewHostImpl*>(embedder_rvh)) {
+}
+
+RenderWidgetHostViewMac*
+ BrowserPluginPopupMenuHelper::GetRenderWidgetHostView() const {
+ return static_cast<RenderWidgetHostViewMac*>(embedder_rvh_->GetView());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.cc b/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.cc
new file mode 100644
index 00000000000..a513d0fd4be
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.cc
@@ -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.
+
+#include "content/browser/browser_plugin/test_browser_plugin_embedder.h"
+
+#include "content/browser/browser_plugin/browser_plugin_embedder.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+
+namespace content {
+
+TestBrowserPluginEmbedder::TestBrowserPluginEmbedder(
+ WebContentsImpl* web_contents)
+ : BrowserPluginEmbedder(web_contents),
+ last_rvh_at_position_response_(NULL) {
+}
+
+TestBrowserPluginEmbedder::~TestBrowserPluginEmbedder() {
+}
+
+void TestBrowserPluginEmbedder::GetRenderViewHostCallback(
+ RenderViewHost* rvh, int x, int y) {
+ last_rvh_at_position_response_ = rvh;
+ if (message_loop_runner_.get())
+ message_loop_runner_->Quit();
+}
+
+void TestBrowserPluginEmbedder::WaitForRenderViewHostAtPosition(int x, int y) {
+ GetRenderViewHostAtPosition(x, y,
+ base::Bind(&TestBrowserPluginEmbedder::GetRenderViewHostCallback,
+ base::Unretained(this)));
+ message_loop_runner_ = new MessageLoopRunner();
+ message_loop_runner_->Run();
+}
+
+WebContentsImpl* TestBrowserPluginEmbedder::web_contents() const {
+ return static_cast<WebContentsImpl*>(BrowserPluginEmbedder::web_contents());
+}
+
+} // namespace content
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.h b/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.h
new file mode 100644
index 00000000000..4673f926305
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/test_browser_plugin_embedder.h
@@ -0,0 +1,47 @@
+// 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"
+
+namespace content {
+
+class BrowserPluginGuest;
+class RenderViewHost;
+class WebContentsImpl;
+
+// 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);
+ virtual ~TestBrowserPluginEmbedder();
+
+ // Asks the renderer process for RenderViewHost at (|x|, |y|) and waits until
+ // the response arrives.
+ void WaitForRenderViewHostAtPosition(int x, int y);
+ RenderViewHost* last_rvh_at_position_response() {
+ return last_rvh_at_position_response_;
+ }
+
+ WebContentsImpl* web_contents() const;
+
+ private:
+ void GetRenderViewHostCallback(RenderViewHost* rvh, int x, int y);
+
+ scoped_refptr<MessageLoopRunner> message_loop_runner_;
+ RenderViewHost* last_rvh_at_position_response_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginEmbedder);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_EMBEDDER_H_
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest.cc b/chromium/content/browser/browser_plugin/test_browser_plugin_guest.cc
new file mode 100644
index 00000000000..a6e62a3ba55
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/test_browser_plugin_guest.cc
@@ -0,0 +1,250 @@
+// 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/renderer_host/render_view_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/browser_plugin/browser_plugin_messages.h"
+#include "content/public/browser/notification_types.h"
+
+namespace content {
+
+class BrowserPluginGuest;
+
+TestBrowserPluginGuest::TestBrowserPluginGuest(
+ int instance_id,
+ WebContentsImpl* web_contents)
+ : BrowserPluginGuest(instance_id, web_contents, NULL, false),
+ update_rect_count_(0),
+ damage_buffer_call_count_(0),
+ exit_observed_(false),
+ focus_observed_(false),
+ blur_observed_(false),
+ advance_focus_observed_(false),
+ was_hidden_observed_(false),
+ set_damage_buffer_observed_(false),
+ input_observed_(false),
+ load_stop_observed_(false),
+ waiting_for_damage_buffer_with_size_(false),
+ last_damage_buffer_size_(gfx::Size()) {
+ // Listen to visibility changes so that a test can wait for these changes.
+ notification_registrar_.Add(this,
+ NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED,
+ Source<WebContents>(web_contents));
+}
+
+TestBrowserPluginGuest::~TestBrowserPluginGuest() {
+}
+
+WebContentsImpl* TestBrowserPluginGuest::web_contents() const {
+ return static_cast<WebContentsImpl*>(BrowserPluginGuest::web_contents());
+}
+
+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_.get())
+ was_hidden_message_loop_runner_->Quit();
+ }
+ return;
+ }
+ }
+
+ BrowserPluginGuest::Observe(type, source, details);
+}
+
+void TestBrowserPluginGuest::SendMessageToEmbedder(IPC::Message* msg) {
+ if (msg->type() == BrowserPluginMsg_UpdateRect::ID) {
+ update_rect_count_++;
+ int instance_id = 0;
+ BrowserPluginMsg_UpdateRect_Params params;
+ BrowserPluginMsg_UpdateRect::Read(msg, &instance_id, &params);
+ last_view_size_observed_ = params.view_size;
+ if (!expected_auto_view_size_.IsEmpty() &&
+ expected_auto_view_size_ == params.view_size) {
+ if (auto_view_size_message_loop_runner_.get())
+ auto_view_size_message_loop_runner_->Quit();
+ }
+ if (send_message_loop_runner_.get())
+ 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::ResetUpdateRectCount() {
+ update_rect_count_ = 0;
+}
+
+void TestBrowserPluginGuest::WaitForDamageBufferWithSize(
+ const gfx::Size& size) {
+ if (damage_buffer_call_count_ > 0 && last_damage_buffer_size_ == size)
+ return;
+
+ expected_damage_buffer_size_ = size;
+ waiting_for_damage_buffer_with_size_ = true;
+ damage_buffer_message_loop_runner_ = new MessageLoopRunner();
+ damage_buffer_message_loop_runner_->Run();
+}
+
+void TestBrowserPluginGuest::RenderProcessGone(base::TerminationStatus status) {
+ exit_observed_ = true;
+ if (status != base::TERMINATION_STATUS_NORMAL_TERMINATION &&
+ status != base::TERMINATION_STATUS_STILL_RUNNING)
+ LOG(INFO) << "Guest crashed status: " << status;
+ if (crash_message_loop_runner_.get())
+ crash_message_loop_runner_->Quit();
+ BrowserPluginGuest::RenderProcessGone(status);
+}
+
+void TestBrowserPluginGuest::OnHandleInputEvent(
+ int instance_id,
+ const gfx::Rect& guest_window_rect,
+ const WebKit::WebInputEvent* event) {
+ BrowserPluginGuest::OnHandleInputEvent(instance_id,
+ guest_window_rect,
+ event);
+ input_observed_ = true;
+ if (input_message_loop_runner_.get())
+ input_message_loop_runner_->Quit();
+}
+
+void TestBrowserPluginGuest::WaitForExit() {
+ // Check if we already observed a guest crash, return immediately if so.
+ if (exit_observed_)
+ return;
+
+ crash_message_loop_runner_ = new MessageLoopRunner();
+ crash_message_loop_runner_->Run();
+}
+
+void TestBrowserPluginGuest::WaitForFocus() {
+ if (focus_observed_) {
+ focus_observed_ = false;
+ return;
+ }
+ focus_message_loop_runner_ = new MessageLoopRunner();
+ focus_message_loop_runner_->Run();
+ focus_observed_ = false;
+}
+
+void TestBrowserPluginGuest::WaitForBlur() {
+ if (blur_observed_) {
+ blur_observed_ = false;
+ return;
+ }
+ blur_message_loop_runner_ = new MessageLoopRunner();
+ blur_message_loop_runner_->Run();
+ blur_observed_ = false;
+}
+
+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::WaitForInput() {
+ if (input_observed_) {
+ input_observed_ = false;
+ return;
+ }
+
+ input_message_loop_runner_ = new MessageLoopRunner();
+ input_message_loop_runner_->Run();
+ input_observed_ = false;
+}
+
+void TestBrowserPluginGuest::WaitForLoadStop() {
+ if (load_stop_observed_) {
+ load_stop_observed_ = false;
+ return;
+ }
+
+ load_stop_message_loop_runner_ = new MessageLoopRunner();
+ load_stop_message_loop_runner_->Run();
+ load_stop_observed_ = false;
+}
+
+void TestBrowserPluginGuest::WaitForViewSize(const gfx::Size& view_size) {
+ if (last_view_size_observed_ == view_size) {
+ last_view_size_observed_ = gfx::Size();
+ return;
+ }
+
+ expected_auto_view_size_ = view_size;
+ auto_view_size_message_loop_runner_ = new MessageLoopRunner();
+ auto_view_size_message_loop_runner_->Run();
+ last_view_size_observed_ = gfx::Size();
+}
+
+void TestBrowserPluginGuest::OnSetFocus(int instance_id, bool focused) {
+ if (focused) {
+ focus_observed_ = true;
+ if (focus_message_loop_runner_.get())
+ focus_message_loop_runner_->Quit();
+ } else {
+ blur_observed_ = true;
+ if (blur_message_loop_runner_.get())
+ blur_message_loop_runner_->Quit();
+ }
+ BrowserPluginGuest::OnSetFocus(instance_id, focused);
+}
+
+void TestBrowserPluginGuest::OnTakeFocus(bool reverse) {
+ advance_focus_observed_ = true;
+ if (advance_focus_message_loop_runner_.get())
+ advance_focus_message_loop_runner_->Quit();
+ BrowserPluginGuest::OnTakeFocus(reverse);
+}
+
+void TestBrowserPluginGuest::SetDamageBuffer(
+ const BrowserPluginHostMsg_ResizeGuest_Params& params) {
+ ++damage_buffer_call_count_;
+ last_damage_buffer_size_ = params.view_rect.size();
+
+ if (waiting_for_damage_buffer_with_size_ &&
+ expected_damage_buffer_size_ == params.view_rect.size() &&
+ damage_buffer_message_loop_runner_.get()) {
+ damage_buffer_message_loop_runner_->Quit();
+ waiting_for_damage_buffer_with_size_ = false;
+ }
+
+ BrowserPluginGuest::SetDamageBuffer(params);
+}
+
+void TestBrowserPluginGuest::DidStopLoading(
+ RenderViewHost* render_view_host) {
+ BrowserPluginGuest::DidStopLoading(render_view_host);
+ load_stop_observed_ = true;
+ if (load_stop_message_loop_runner_.get())
+ load_stop_message_loop_runner_->Quit();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest.h b/chromium/content/browser/browser_plugin/test_browser_plugin_guest.h
new file mode 100644
index 00000000000..7fccb002388
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/test_browser_plugin_guest.h
@@ -0,0 +1,108 @@
+// 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 "content/browser/browser_plugin/browser_plugin_guest.h"
+#include "content/public/test/test_utils.h"
+#include "ui/gfx/size.h"
+
+namespace content {
+
+class RenderProcessHost;
+class RenderViewHost;
+class WebContentsImpl;
+
+// Test class for BrowserPluginGuest.
+//
+// Provides utilities to wait for certain state/messages in BrowserPluginGuest
+// to be used in tests.
+class TestBrowserPluginGuest : public BrowserPluginGuest {
+ public:
+ TestBrowserPluginGuest(int instance_id, WebContentsImpl* web_contents);
+ virtual ~TestBrowserPluginGuest();
+
+ WebContentsImpl* web_contents() const;
+
+ // 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 RenderProcessGone(base::TerminationStatus status) OVERRIDE;
+ virtual void OnHandleInputEvent(int instance_id,
+ const gfx::Rect& guest_window_rect,
+ const WebKit::WebInputEvent* event) OVERRIDE;
+ virtual void OnSetFocus(int instance_id, bool focused) OVERRIDE;
+ virtual void OnTakeFocus(bool reverse) OVERRIDE;
+ virtual void SetDamageBuffer(
+ const BrowserPluginHostMsg_ResizeGuest_Params& params) OVERRIDE;
+ virtual void DidStopLoading(RenderViewHost* render_view_host) 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();
+ void ResetUpdateRectCount();
+ // Waits until a guest receives a damage buffer of the specified |size|.
+ void WaitForDamageBufferWithSize(const gfx::Size& size);
+ // Waits for focus to reach this guest.
+ void WaitForFocus();
+ // Waits for blur to reach this guest.
+ void WaitForBlur();
+ // Waits for focus to move out of this guest.
+ void WaitForAdvanceFocus();
+ // Waits until the guest is hidden.
+ void WaitUntilHidden();
+ // Waits until guest exits.
+ void WaitForExit();
+ // Waits until input is observed.
+ void WaitForInput();
+ // Waits until 'loadstop' is observed.
+ void WaitForLoadStop();
+ // Waits until UpdateRect with a particular |view_size| is observed.
+ void WaitForViewSize(const gfx::Size& view_size);
+
+ private:
+ // Overridden methods from BrowserPluginGuest to intercept in test objects.
+ virtual void SendMessageToEmbedder(IPC::Message* msg) OVERRIDE;
+
+ int update_rect_count_;
+ int damage_buffer_call_count_;
+ bool exit_observed_;
+ bool focus_observed_;
+ bool blur_observed_;
+ bool advance_focus_observed_;
+ bool was_hidden_observed_;
+ bool set_damage_buffer_observed_;
+ bool input_observed_;
+ bool load_stop_observed_;
+ gfx::Size last_view_size_observed_;
+ gfx::Size expected_auto_view_size_;
+
+ // For WaitForDamageBufferWithSize().
+ bool waiting_for_damage_buffer_with_size_;
+ gfx::Size expected_damage_buffer_size_;
+ gfx::Size last_damage_buffer_size_;
+
+ scoped_refptr<MessageLoopRunner> send_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> crash_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> focus_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> blur_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> advance_focus_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> was_hidden_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> damage_buffer_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> input_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> load_stop_message_loop_runner_;
+ scoped_refptr<MessageLoopRunner> auto_view_size_message_loop_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginGuest);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_H_
diff --git a/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.cc b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.cc
new file mode 100644
index 00000000000..7d0b5ac23dd
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 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_manager.h"
+
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/test/test_utils.h"
+
+namespace content {
+
+TestBrowserPluginGuestManager::TestBrowserPluginGuestManager() {
+}
+
+TestBrowserPluginGuestManager::~TestBrowserPluginGuestManager() {
+}
+
+void TestBrowserPluginGuestManager::AddGuest(
+ int instance_id,
+ WebContentsImpl* guest_web_contents) {
+ BrowserPluginGuestManager::AddGuest(instance_id, guest_web_contents);
+ if (message_loop_runner_.get())
+ message_loop_runner_->Quit();
+}
+
+void TestBrowserPluginGuestManager::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/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.h b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.h
new file mode 100644
index 00000000000..d7683cf7ae9
--- /dev/null
+++ b/chromium/content/browser/browser_plugin/test_browser_plugin_guest_manager.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2013 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_MANAGER_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_MANAGER_H_
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
+#include "content/public/test/test_utils.h"
+
+FORWARD_DECLARE_TEST(BrowserPluginHostTest, ReloadEmbedder);
+
+namespace content {
+
+class WebContentsImpl;
+
+// Test class for BrowserPluginGuestManager.
+//
+// Provides utilities to wait for certain state/messages in
+// BrowserPluginGuestManager to be used in tests.
+class TestBrowserPluginGuestManager : public BrowserPluginGuestManager {
+ public:
+ typedef BrowserPluginGuestManager::GuestInstanceMap GuestInstanceMap;
+
+ TestBrowserPluginGuestManager();
+ virtual ~TestBrowserPluginGuestManager();
+
+ const GuestInstanceMap& guest_web_contents_for_testing() const {
+ return guest_web_contents_by_instance_id_;
+ }
+
+ // Waits until at least one guest is added to the guest manager.
+ void WaitForGuestAdded();
+
+ private:
+ // BrowserPluginHostTest.ReloadEmbedder needs access to the GuestInstanceMap.
+ FRIEND_TEST_ALL_PREFIXES(BrowserPluginHostTest, ReloadEmbedder);
+
+ // Overriden to intercept in test.
+ virtual void AddGuest(int instance_id,
+ WebContentsImpl* guest_web_contents) OVERRIDE;
+
+ scoped_refptr<MessageLoopRunner> message_loop_runner_;
+ DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginGuestManager);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_MANAGER_H_