summaryrefslogtreecommitdiffstats
path: root/components/guest_view/browser
diff options
context:
space:
mode:
authorfsamuel <fsamuel@chromium.org>2015-05-04 18:00:39 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-05 01:01:10 +0000
commit8dfa19acbffebaeafd38e8c3f4ef136c3fc83734 (patch)
tree9c62f75554ab826e9a49d0f2ee390b7d6cb2df1b /components/guest_view/browser
parentec5b7687d06564998e7f1b96465878a391edc723 (diff)
downloadchromium_src-8dfa19acbffebaeafd38e8c3f4ef136c3fc83734.zip
chromium_src-8dfa19acbffebaeafd38e8c3f4ef136c3fc83734.tar.gz
chromium_src-8dfa19acbffebaeafd38e8c3f4ef136c3fc83734.tar.bz2
Move GuestView layer in browser to components
In addition to moving the browser-side of the GuestView layer into components, this CL also: 1. Replaces some loops to the top level web contents with GuestViewBase::GetTopLevelWebContents. 2. Installs the ExtensionsGuestViewMessageFilter in appshell. 3. Move some files out of extensions/common/guest_view to components/guest_view/common BUG=444869 Review URL: https://codereview.chromium.org/1102173002 Cr-Commit-Position: refs/heads/master@{#328243}
Diffstat (limited to 'components/guest_view/browser')
-rw-r--r--components/guest_view/browser/DEPS8
-rw-r--r--components/guest_view/browser/guest_view.h70
-rw-r--r--components/guest_view/browser/guest_view_base.cc821
-rw-r--r--components/guest_view/browser/guest_view_base.h458
-rw-r--r--components/guest_view/browser/guest_view_event.cc28
-rw-r--r--components/guest_view/browser/guest_view_event.h41
-rw-r--r--components/guest_view/browser/guest_view_manager.cc376
-rw-r--r--components/guest_view/browser/guest_view_manager.h212
-rw-r--r--components/guest_view/browser/guest_view_manager_delegate.cc23
-rw-r--r--components/guest_view/browser/guest_view_manager_delegate.h53
-rw-r--r--components/guest_view/browser/guest_view_manager_factory.h30
-rw-r--r--components/guest_view/browser/guest_view_manager_unittest.cc84
-rw-r--r--components/guest_view/browser/guest_view_message_filter.cc80
-rw-r--r--components/guest_view/browser/guest_view_message_filter.h67
-rw-r--r--components/guest_view/browser/test_guest_view_manager.cc99
-rw-r--r--components/guest_view/browser/test_guest_view_manager.h82
16 files changed, 2532 insertions, 0 deletions
diff --git a/components/guest_view/browser/DEPS b/components/guest_view/browser/DEPS
new file mode 100644
index 0000000..f0c51e4
--- /dev/null
+++ b/components/guest_view/browser/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+components/ui/zoom",
+ "+content/public/browser",
+ "+content/public/common",
+ "+content/public/test",
+ "+ipc",
+ '+third_party/WebKit/public/web/WebInputEvent.h',
+]
diff --git a/components/guest_view/browser/guest_view.h b/components/guest_view/browser/guest_view.h
new file mode 100644
index 0000000..f99fe83
--- /dev/null
+++ b/components/guest_view/browser/guest_view.h
@@ -0,0 +1,70 @@
+// Copyright 2014 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 COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_H_
+#define COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_H_
+
+#include "components/guest_view/browser/guest_view_base.h"
+#include "components/guest_view/browser/guest_view_manager.h"
+#include "content/public/browser/render_frame_host.h"
+
+namespace guest_view {
+
+// A GuestView is the templated base class for out-of-process frames in the
+// chrome layer. GuestView is templated on its derived type to allow for type-
+// safe access. See GuestViewBase for more information.
+template <typename T>
+class GuestView : public GuestViewBase {
+ public:
+ static T* From(int embedder_process_id, int guest_instance_id) {
+ auto guest = GuestViewBase::From(embedder_process_id, guest_instance_id);
+ if (!guest)
+ return nullptr;
+ return guest->As<T>();
+ }
+
+ static T* FromWebContents(const content::WebContents* contents) {
+ auto guest = GuestViewBase::FromWebContents(contents);
+ return guest ? guest->As<T>() : nullptr;
+ }
+
+ static T* FromFrameID(int render_process_id, int render_frame_id) {
+ auto render_frame_host =
+ content::RenderFrameHost::FromID(render_process_id, render_frame_id);
+ if (!render_frame_host)
+ return nullptr;
+
+ auto web_contents =
+ content::WebContents::FromRenderFrameHost(render_frame_host);
+ return FromWebContents(web_contents);
+ }
+
+ T* GetOpener() const {
+ GuestViewBase* guest = GuestViewBase::GetOpener();
+ if (!guest)
+ return nullptr;
+ return guest->As<T>();
+ }
+
+ void SetOpener(T* opener) {
+ GuestViewBase::SetOpener(opener);
+ }
+
+ // GuestViewBase implementation.
+ const char* GetViewType() const final {
+ return T::Type;
+ }
+
+ protected:
+ explicit GuestView(content::WebContents* owner_web_contents)
+ : GuestViewBase(owner_web_contents) {}
+ ~GuestView() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GuestView);
+};
+
+} // namespace guest_view
+
+#endif // COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_H_
diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc
new file mode 100644
index 0000000..1b1b5ed
--- /dev/null
+++ b/components/guest_view/browser/guest_view_base.cc
@@ -0,0 +1,821 @@
+// Copyright 2014 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 "components/guest_view/browser/guest_view_base.h"
+
+#include "base/lazy_instance.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/guest_view/browser/guest_view_event.h"
+#include "components/guest_view/browser/guest_view_manager.h"
+#include "components/guest_view/common/guest_view_constants.h"
+#include "components/guest_view/common/guest_view_messages.h"
+#include "components/ui/zoom/page_zoom.h"
+#include "components/ui/zoom/zoom_controller.h"
+#include "content/public/browser/navigation_details.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/page_zoom.h"
+#include "content/public/common/url_constants.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+using content::WebContents;
+
+namespace content {
+struct FrameNavigateParams;
+}
+
+namespace guest_view {
+
+namespace {
+
+using WebContentsGuestViewMap = std::map<const WebContents*, GuestViewBase*>;
+static base::LazyInstance<WebContentsGuestViewMap> webcontents_guestview_map =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+SetSizeParams::SetSizeParams() {
+}
+SetSizeParams::~SetSizeParams() {
+}
+
+// This observer ensures that the GuestViewBase destroys itself when its
+// embedder goes away. It also tracks when the embedder's fullscreen is
+// toggled so the guest can change itself accordingly.
+class GuestViewBase::OwnerContentsObserver : public WebContentsObserver {
+ public:
+ OwnerContentsObserver(GuestViewBase* guest,
+ content::WebContents* embedder_web_contents)
+ : WebContentsObserver(embedder_web_contents),
+ is_fullscreen_(false),
+ destroyed_(false),
+ guest_(guest) {}
+
+ ~OwnerContentsObserver() override {}
+
+ // WebContentsObserver implementation.
+ void WebContentsDestroyed() override {
+ // If the embedder is destroyed then destroy the guest.
+ Destroy();
+ }
+
+ void DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) override {
+ // If the embedder navigates to a different page then destroy the guest.
+ if (details.is_navigation_to_different_page())
+ Destroy();
+ }
+
+ void RenderProcessGone(base::TerminationStatus status) override {
+ // If the embedder crashes, then destroy the guest.
+ Destroy();
+ }
+
+ void DidToggleFullscreenModeForTab(bool entered_fullscreen) override {
+ if (destroyed_)
+ return;
+
+ is_fullscreen_ = entered_fullscreen;
+ guest_->EmbedderFullscreenToggled(is_fullscreen_);
+ }
+
+ void MainFrameWasResized(bool width_changed) override {
+ if (destroyed_)
+ return;
+
+ if (!web_contents()->GetDelegate())
+ return;
+
+ bool current_fullscreen =
+ web_contents()->GetDelegate()->IsFullscreenForTabOrPending(
+ web_contents());
+ if (is_fullscreen_ && !current_fullscreen) {
+ is_fullscreen_ = false;
+ guest_->EmbedderFullscreenToggled(is_fullscreen_);
+ }
+ }
+
+ private:
+ bool is_fullscreen_;
+ bool destroyed_;
+ GuestViewBase* guest_;
+
+ void Destroy() {
+ if (destroyed_)
+ return;
+
+ destroyed_ = true;
+ guest_->EmbedderWillBeDestroyed();
+ guest_->Destroy();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(OwnerContentsObserver);
+};
+
+// This observer ensures that the GuestViewBase destroys itself when its
+// embedder goes away.
+class GuestViewBase::OpenerLifetimeObserver : public WebContentsObserver {
+ public:
+ OpenerLifetimeObserver(GuestViewBase* guest)
+ : WebContentsObserver(guest->GetOpener()->web_contents()),
+ guest_(guest) {}
+
+ ~OpenerLifetimeObserver() override {}
+
+ // WebContentsObserver implementation.
+ void WebContentsDestroyed() override {
+ if (guest_->attached())
+ return;
+
+ // If the opener is destroyed then destroy the guest.
+ guest_->Destroy();
+ }
+
+ private:
+ GuestViewBase* guest_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpenerLifetimeObserver);
+};
+
+GuestViewBase::GuestViewBase(content::WebContents* owner_web_contents)
+ : owner_web_contents_(owner_web_contents),
+ browser_context_(owner_web_contents->GetBrowserContext()),
+ guest_instance_id_(
+ GuestViewManager::FromBrowserContext(browser_context_)->
+ GetNextInstanceID()),
+ view_instance_id_(kInstanceIDNone),
+ element_instance_id_(kInstanceIDNone),
+ initialized_(false),
+ is_being_destroyed_(false),
+ guest_host_(nullptr),
+ auto_size_enabled_(false),
+ is_full_page_plugin_(false),
+ guest_proxy_routing_id_(MSG_ROUTING_NONE),
+ weak_ptr_factory_(this) {
+ owner_host_ = GuestViewManager::FromBrowserContext(browser_context_)->
+ IsOwnedByExtension(this) ?
+ owner_web_contents->GetLastCommittedURL().host() : std::string();
+}
+
+void GuestViewBase::Init(const base::DictionaryValue& create_params,
+ const WebContentsCreatedCallback& callback) {
+ if (initialized_)
+ return;
+ initialized_ = true;
+
+ if (!GuestViewManager::FromBrowserContext(browser_context_)->
+ IsGuestAvailableToContext(this)) {
+ // The derived class did not create a WebContents so this class serves no
+ // purpose. Let's self-destruct.
+ delete this;
+ callback.Run(nullptr);
+ return;
+ }
+
+ scoped_ptr<base::DictionaryValue> params(create_params.DeepCopy());
+ CreateWebContents(create_params,
+ base::Bind(&GuestViewBase::CompleteInit,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(&params),
+ callback));
+}
+
+void GuestViewBase::InitWithWebContents(
+ const base::DictionaryValue& create_params,
+ content::WebContents* guest_web_contents) {
+ DCHECK(guest_web_contents);
+
+ // Create a ZoomController to allow the guest's contents to be zoomed.
+ // Do this before adding the GuestView as a WebContents Observer so that
+ // the GuestView and its derived classes can re-configure the ZoomController
+ // after the latter has handled WebContentsObserver events (observers are
+ // notified of events in the same order they are added as observers). For
+ // example, GuestViewBase may wish to put its guest into isolated zoom mode
+ // in DidNavigateMainFrame, but since ZoomController always resets to default
+ // zoom mode on this event, GuestViewBase would need to do so after
+ // ZoomController::DidNavigateMainFrame has completed.
+ ui_zoom::ZoomController::CreateForWebContents(guest_web_contents);
+
+ // At this point, we have just created the guest WebContents, we need to add
+ // an observer to the owner WebContents. This observer will be responsible
+ // for destroying the guest WebContents if the owner goes away.
+ owner_contents_observer_.reset(
+ new OwnerContentsObserver(this, owner_web_contents_));
+
+ WebContentsObserver::Observe(guest_web_contents);
+ guest_web_contents->SetDelegate(this);
+ webcontents_guestview_map.Get().insert(
+ std::make_pair(guest_web_contents, this));
+ GuestViewManager::FromBrowserContext(browser_context_)->
+ AddGuest(guest_instance_id_, guest_web_contents);
+
+ // Populate the view instance ID if we have it on creation.
+ create_params.GetInteger(kParameterInstanceId, &view_instance_id_);
+
+ if (CanRunInDetachedState())
+ SetUpSizing(create_params);
+
+ // Observe guest zoom changes.
+ auto zoom_controller =
+ ui_zoom::ZoomController::FromWebContents(web_contents());
+ zoom_controller->AddObserver(this);
+
+ // Give the derived class an opportunity to perform additional initialization.
+ DidInitialize(create_params);
+}
+
+void GuestViewBase::LoadURLWithParams(
+ const content::NavigationController::LoadURLParams& load_params) {
+ int guest_proxy_routing_id = host()->LoadURLWithParams(load_params);
+ DCHECK(guest_proxy_routing_id_ == MSG_ROUTING_NONE ||
+ guest_proxy_routing_id == guest_proxy_routing_id_);
+ guest_proxy_routing_id_ = guest_proxy_routing_id;
+}
+
+void GuestViewBase::DispatchOnResizeEvent(const gfx::Size& old_size,
+ const gfx::Size& new_size) {
+ if (new_size == old_size)
+ return;
+
+ // Dispatch the onResize event.
+ scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
+ args->SetInteger(kOldWidth, old_size.width());
+ args->SetInteger(kOldHeight, old_size.height());
+ args->SetInteger(kNewWidth, new_size.width());
+ args->SetInteger(kNewHeight, new_size.height());
+ DispatchEventToGuestProxy(new GuestViewEvent(kEventResize, args.Pass()));
+}
+
+gfx::Size GuestViewBase::GetDefaultSize() const {
+ if (is_full_page_plugin()) {
+ // Full page plugins default to the size of the owner's viewport.
+ return owner_web_contents()
+ ->GetRenderWidgetHostView()
+ ->GetVisibleViewportSize();
+ } else {
+ return gfx::Size(kDefaultWidth, kDefaultHeight);
+ }
+}
+
+void GuestViewBase::SetSize(const SetSizeParams& params) {
+ bool enable_auto_size =
+ params.enable_auto_size ? *params.enable_auto_size : auto_size_enabled_;
+ gfx::Size min_size = params.min_size ? *params.min_size : min_auto_size_;
+ gfx::Size max_size = params.max_size ? *params.max_size : max_auto_size_;
+
+ if (params.normal_size)
+ normal_size_ = *params.normal_size;
+
+ min_auto_size_ = min_size;
+ min_auto_size_.SetToMin(max_size);
+ max_auto_size_ = max_size;
+ max_auto_size_.SetToMax(min_size);
+
+ enable_auto_size &= !min_auto_size_.IsEmpty() && !max_auto_size_.IsEmpty() &&
+ IsAutoSizeSupported();
+
+ content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
+ if (enable_auto_size) {
+ // Autosize is being enabled.
+ rvh->EnableAutoResize(min_auto_size_, max_auto_size_);
+ normal_size_.SetSize(0, 0);
+ } else {
+ // Autosize is being disabled.
+ // Use default width/height if missing from partially defined normal size.
+ if (normal_size_.width() && !normal_size_.height())
+ normal_size_.set_height(GetDefaultSize().height());
+ if (!normal_size_.width() && normal_size_.height())
+ normal_size_.set_width(GetDefaultSize().width());
+
+ gfx::Size new_size;
+ if (!normal_size_.IsEmpty()) {
+ new_size = normal_size_;
+ } else if (!guest_size_.IsEmpty()) {
+ new_size = guest_size_;
+ } else {
+ new_size = GetDefaultSize();
+ }
+
+ if (auto_size_enabled_) {
+ // Autosize was previously enabled.
+ rvh->DisableAutoResize(new_size);
+ GuestSizeChangedDueToAutoSize(guest_size_, new_size);
+ } else {
+ // Autosize was already disabled.
+ guest_host_->SizeContents(new_size);
+ }
+
+ DispatchOnResizeEvent(guest_size_, new_size);
+ guest_size_ = new_size;
+ }
+
+ auto_size_enabled_ = enable_auto_size;
+}
+
+// static
+GuestViewBase* GuestViewBase::FromWebContents(const WebContents* web_contents) {
+ WebContentsGuestViewMap* guest_map = webcontents_guestview_map.Pointer();
+ auto it = guest_map->find(web_contents);
+ return it == guest_map->end() ? nullptr : it->second;
+}
+
+// static
+GuestViewBase* GuestViewBase::From(int owner_process_id,
+ int guest_instance_id) {
+ auto host = content::RenderProcessHost::FromID(owner_process_id);
+ if (!host)
+ return nullptr;
+
+ content::WebContents* guest_web_contents =
+ GuestViewManager::FromBrowserContext(
+ host->GetBrowserContext())->
+ GetGuestByInstanceIDSafely(guest_instance_id, owner_process_id);
+ if (!guest_web_contents)
+ return nullptr;
+
+ return GuestViewBase::FromWebContents(guest_web_contents);
+}
+
+// static
+WebContents* GuestViewBase::GetTopLevelWebContents(WebContents* web_contents) {
+ while (GuestViewBase* guest = FromWebContents(web_contents))
+ web_contents = guest->owner_web_contents();
+ return web_contents;
+}
+
+// static
+bool GuestViewBase::IsGuest(WebContents* web_contents) {
+ return !!GuestViewBase::FromWebContents(web_contents);
+}
+
+bool GuestViewBase::IsAutoSizeSupported() const {
+ return false;
+}
+
+bool GuestViewBase::IsPreferredSizeModeEnabled() const {
+ return false;
+}
+
+bool GuestViewBase::IsDragAndDropEnabled() const {
+ return false;
+}
+
+bool GuestViewBase::ZoomPropagatesFromEmbedderToGuest() const {
+ return true;
+}
+
+content::WebContents* GuestViewBase::CreateNewGuestWindow(
+ const content::WebContents::CreateParams& create_params) {
+ auto guest_manager = GuestViewManager::FromBrowserContext(browser_context());
+ return guest_manager->CreateGuestWithWebContentsParams(
+ GetViewType(),
+ owner_web_contents(),
+ create_params);
+}
+
+void GuestViewBase::DidAttach(int guest_proxy_routing_id) {
+ DCHECK(guest_proxy_routing_id_ == MSG_ROUTING_NONE ||
+ guest_proxy_routing_id == guest_proxy_routing_id_);
+ guest_proxy_routing_id_ = guest_proxy_routing_id;
+
+ opener_lifetime_observer_.reset();
+
+ SetUpSizing(*attach_params());
+
+ // Give the derived class an opportunity to perform some actions.
+ DidAttachToEmbedder();
+
+ // Inform the associated GuestViewContainer that the contentWindow is ready.
+ embedder_web_contents()->Send(new GuestViewMsg_GuestAttached(
+ element_instance_id_,
+ guest_proxy_routing_id));
+
+ SendQueuedEvents();
+}
+
+void GuestViewBase::DidDetach() {
+ GuestViewManager::FromBrowserContext(browser_context_)->DetachGuest(this);
+ StopTrackingEmbedderZoomLevel();
+ owner_web_contents()->Send(new GuestViewMsg_GuestDetached(
+ element_instance_id_));
+ element_instance_id_ = kInstanceIDNone;
+}
+
+WebContents* GuestViewBase::GetOwnerWebContents() const {
+ return owner_web_contents_;
+}
+
+void GuestViewBase::GuestSizeChanged(const gfx::Size& new_size) {
+ if (!auto_size_enabled_)
+ return;
+ GuestSizeChangedDueToAutoSize(guest_size_, new_size);
+ DispatchOnResizeEvent(guest_size_, new_size);
+ guest_size_ = new_size;
+}
+
+const GURL& GuestViewBase::GetOwnerSiteURL() const {
+ return owner_web_contents()->GetLastCommittedURL();
+}
+
+void GuestViewBase::Destroy() {
+ if (is_being_destroyed_)
+ return;
+
+ is_being_destroyed_ = true;
+
+ // It is important to clear owner_web_contents_ after the call to
+ // StopTrackingEmbedderZoomLevel(), but before the rest of
+ // the statements in this function.
+ StopTrackingEmbedderZoomLevel();
+ owner_web_contents_ = nullptr;
+
+ DCHECK(web_contents());
+
+ // Give the derived class an opportunity to perform some cleanup.
+ WillDestroy();
+
+ // Invalidate weak pointers now so that bound callbacks cannot be called late
+ // into destruction. We must call this after WillDestroy because derived types
+ // may wish to access their openers.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+
+ // Give the content module an opportunity to perform some cleanup.
+ guest_host_->WillDestroy();
+ guest_host_ = nullptr;
+
+ webcontents_guestview_map.Get().erase(web_contents());
+ GuestViewManager::FromBrowserContext(browser_context_)->
+ RemoveGuest(guest_instance_id_);
+ pending_events_.clear();
+
+ delete web_contents();
+}
+
+void GuestViewBase::SetAttachParams(const base::DictionaryValue& params) {
+ attach_params_.reset(params.DeepCopy());
+ attach_params_->GetInteger(kParameterInstanceId, &view_instance_id_);
+}
+
+void GuestViewBase::SetOpener(GuestViewBase* guest) {
+ if (guest && guest->IsViewType(GetViewType())) {
+ opener_ = guest->weak_ptr_factory_.GetWeakPtr();
+ if (!attached())
+ opener_lifetime_observer_.reset(new OpenerLifetimeObserver(this));
+ return;
+ }
+ opener_ = base::WeakPtr<GuestViewBase>();
+ opener_lifetime_observer_.reset();
+}
+
+void GuestViewBase::SetGuestHost(content::GuestHost* guest_host) {
+ guest_host_ = guest_host;
+}
+
+void GuestViewBase::WillAttach(content::WebContents* embedder_web_contents,
+ int element_instance_id,
+ bool is_full_page_plugin) {
+ if (owner_web_contents_ != embedder_web_contents) {
+ DCHECK_EQ(owner_contents_observer_->web_contents(), owner_web_contents_);
+ // Stop tracking the old embedder's zoom level.
+ StopTrackingEmbedderZoomLevel();
+ owner_web_contents_ = embedder_web_contents;
+ owner_contents_observer_.reset(
+ new OwnerContentsObserver(this, embedder_web_contents));
+ owner_host_ = GuestViewManager::FromBrowserContext(browser_context_)->
+ IsOwnedByExtension(this) ?
+ owner_web_contents()->GetLastCommittedURL().host() : std::string();
+ }
+
+ // Start tracking the new embedder's zoom level.
+ StartTrackingEmbedderZoomLevel();
+ element_instance_id_ = element_instance_id;
+ is_full_page_plugin_ = is_full_page_plugin;
+
+ WillAttachToEmbedder();
+}
+
+int GuestViewBase::LogicalPixelsToPhysicalPixels(double logical_pixels) const {
+ DCHECK(logical_pixels >= 0);
+ double zoom_factor = GetEmbedderZoomFactor();
+ return lround(logical_pixels * zoom_factor);
+}
+
+double GuestViewBase::PhysicalPixelsToLogicalPixels(int physical_pixels) const {
+ DCHECK(physical_pixels >= 0);
+ double zoom_factor = GetEmbedderZoomFactor();
+ return physical_pixels / zoom_factor;
+}
+
+void GuestViewBase::DidStopLoading() {
+ content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
+
+ if (IsPreferredSizeModeEnabled())
+ rvh->EnablePreferredSizeMode();
+ if (!IsDragAndDropEnabled()) {
+ const char script[] =
+ "window.addEventListener('dragstart', function() { "
+ " window.event.preventDefault(); "
+ "});";
+ rvh->GetMainFrame()->ExecuteJavaScript(base::ASCIIToUTF16(script));
+ }
+ GuestViewDidStopLoading();
+}
+
+void GuestViewBase::RenderViewReady() {
+ GuestReady();
+}
+
+void GuestViewBase::WebContentsDestroyed() {
+ // Let the derived class know that its WebContents is in the process of
+ // being destroyed. web_contents() is still valid at this point.
+ // TODO(fsamuel): This allows for reentrant code into WebContents during
+ // destruction. This could potentially lead to bugs. Perhaps we should get rid
+ // of this?
+ GuestDestroyed();
+
+ // Self-destruct.
+ delete this;
+}
+
+void GuestViewBase::DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) {
+ if (attached() && ZoomPropagatesFromEmbedderToGuest())
+ SetGuestZoomLevelToMatchEmbedder();
+}
+
+void GuestViewBase::ActivateContents(WebContents* web_contents) {
+ if (!attached() || !embedder_web_contents()->GetDelegate())
+ return;
+
+ embedder_web_contents()->GetDelegate()->ActivateContents(
+ embedder_web_contents());
+}
+
+void GuestViewBase::DeactivateContents(WebContents* web_contents) {
+ if (!attached() || !embedder_web_contents()->GetDelegate())
+ return;
+
+ embedder_web_contents()->GetDelegate()->DeactivateContents(
+ embedder_web_contents());
+}
+
+void GuestViewBase::ContentsMouseEvent(content::WebContents* source,
+ const gfx::Point& location,
+ bool motion) {
+ if (!attached() || !embedder_web_contents()->GetDelegate())
+ return;
+
+ embedder_web_contents()->GetDelegate()->ContentsMouseEvent(
+ embedder_web_contents(), location, motion);
+}
+
+void GuestViewBase::ContentsZoomChange(bool zoom_in) {
+ ui_zoom::PageZoom::Zoom(
+ embedder_web_contents(),
+ zoom_in ? content::PAGE_ZOOM_IN : content::PAGE_ZOOM_OUT);
+}
+
+void GuestViewBase::HandleKeyboardEvent(
+ WebContents* source,
+ const content::NativeWebKeyboardEvent& event) {
+ if (!attached())
+ return;
+
+ // Send the keyboard events back to the embedder to reprocess them.
+ embedder_web_contents()->GetDelegate()->
+ HandleKeyboardEvent(embedder_web_contents(), event);
+}
+
+void GuestViewBase::LoadingStateChanged(content::WebContents* source,
+ bool to_different_document) {
+ if (!attached() || !embedder_web_contents()->GetDelegate())
+ return;
+
+ embedder_web_contents()->GetDelegate()->LoadingStateChanged(
+ embedder_web_contents(), to_different_document);
+}
+
+content::ColorChooser* GuestViewBase::OpenColorChooser(
+ WebContents* web_contents,
+ SkColor color,
+ const std::vector<content::ColorSuggestion>& suggestions) {
+ if (!attached() || !embedder_web_contents()->GetDelegate())
+ return nullptr;
+
+ return embedder_web_contents()->GetDelegate()->OpenColorChooser(
+ web_contents, color, suggestions);
+}
+
+void GuestViewBase::RunFileChooser(WebContents* web_contents,
+ const content::FileChooserParams& params) {
+ if (!attached() || !embedder_web_contents()->GetDelegate())
+ return;
+
+ embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents, params);
+}
+
+bool GuestViewBase::ShouldFocusPageAfterCrash() {
+ // Focus is managed elsewhere.
+ return false;
+}
+
+bool GuestViewBase::PreHandleGestureEvent(content::WebContents* source,
+ const blink::WebGestureEvent& event) {
+ return event.type == blink::WebGestureEvent::GesturePinchBegin ||
+ event.type == blink::WebGestureEvent::GesturePinchUpdate ||
+ event.type == blink::WebGestureEvent::GesturePinchEnd;
+}
+
+void GuestViewBase::UpdatePreferredSize(
+ content::WebContents* target_web_contents,
+ const gfx::Size& pref_size) {
+ // In theory it's not necessary to check IsPreferredSizeModeEnabled() because
+ // there will only be events if it was enabled in the first place. However,
+ // something else may have turned on preferred size mode, so double check.
+ DCHECK_EQ(web_contents(), target_web_contents);
+ if (IsPreferredSizeModeEnabled()) {
+ OnPreferredSizeChanged(pref_size);
+ }
+}
+
+void GuestViewBase::UpdateTargetURL(content::WebContents* source,
+ const GURL& url) {
+ if (!attached() || !embedder_web_contents()->GetDelegate())
+ return;
+
+ embedder_web_contents()->GetDelegate()->UpdateTargetURL(
+ embedder_web_contents(), url);
+}
+
+bool GuestViewBase::ShouldResumeRequestsForCreatedWindow() {
+ return false;
+}
+
+GuestViewBase::~GuestViewBase() {
+}
+
+void GuestViewBase::OnZoomChanged(
+ const ui_zoom::ZoomController::ZoomChangedEventData& data) {
+ if (data.web_contents == embedder_web_contents()) {
+ // The embedder's zoom level has changed.
+ auto guest_zoom_controller =
+ ui_zoom::ZoomController::FromWebContents(web_contents());
+ if (content::ZoomValuesEqual(data.new_zoom_level,
+ guest_zoom_controller->GetZoomLevel())) {
+ return;
+ }
+ // When the embedder's zoom level doesn't match the guest's, then update the
+ // guest's zoom level to match.
+ guest_zoom_controller->SetZoomLevel(data.new_zoom_level);
+
+ EmbedderZoomChanged(data.old_zoom_level, data.new_zoom_level);
+ return;
+ }
+
+ if (data.web_contents == web_contents()) {
+ // The guest's zoom level has changed.
+ GuestZoomChanged(data.old_zoom_level, data.new_zoom_level);
+ }
+}
+
+void GuestViewBase::DispatchEventToGuestProxy(GuestViewEvent* event) {
+ event->Dispatch(this, guest_instance_id_);
+}
+
+void GuestViewBase::DispatchEventToView(GuestViewEvent* event) {
+ if (!attached() &&
+ (!CanRunInDetachedState() || !can_owner_receive_events())) {
+ pending_events_.push_back(linked_ptr<GuestViewEvent>(event));
+ return;
+ }
+
+ event->Dispatch(this, view_instance_id_);
+}
+
+void GuestViewBase::SendQueuedEvents() {
+ if (!attached())
+ return;
+ while (!pending_events_.empty()) {
+ linked_ptr<GuestViewEvent> event_ptr = pending_events_.front();
+ pending_events_.pop_front();
+ event_ptr.release()->Dispatch(this, view_instance_id_);
+ }
+}
+
+void GuestViewBase::CompleteInit(
+ scoped_ptr<base::DictionaryValue> create_params,
+ const WebContentsCreatedCallback& callback,
+ content::WebContents* guest_web_contents) {
+ if (!guest_web_contents) {
+ // The derived class did not create a WebContents so this class serves no
+ // purpose. Let's self-destruct.
+ delete this;
+ callback.Run(nullptr);
+ return;
+ }
+ InitWithWebContents(*create_params, guest_web_contents);
+ callback.Run(guest_web_contents);
+}
+
+double GuestViewBase::GetEmbedderZoomFactor() const {
+ if (!embedder_web_contents())
+ return 1.0;
+
+ return content::ZoomLevelToZoomFactor(
+ ui_zoom::ZoomController::GetZoomLevelForWebContents(
+ embedder_web_contents()));
+}
+
+void GuestViewBase::SetUpSizing(const base::DictionaryValue& params) {
+ // Read the autosize parameters passed in from the embedder.
+ bool auto_size_enabled = auto_size_enabled_;
+ params.GetBoolean(kAttributeAutoSize, &auto_size_enabled);
+
+ int max_height = max_auto_size_.height();
+ int max_width = max_auto_size_.width();
+ params.GetInteger(kAttributeMaxHeight, &max_height);
+ params.GetInteger(kAttributeMaxWidth, &max_width);
+
+ int min_height = min_auto_size_.height();
+ int min_width = min_auto_size_.width();
+ params.GetInteger(kAttributeMinHeight, &min_height);
+ params.GetInteger(kAttributeMinWidth, &min_width);
+
+ double element_height = 0.0;
+ double element_width = 0.0;
+ params.GetDouble(kElementHeight, &element_height);
+ params.GetDouble(kElementWidth, &element_width);
+
+ // Set the normal size to the element size so that the guestview will fit
+ // the element initially if autosize is disabled.
+ int normal_height = normal_size_.height();
+ int normal_width = normal_size_.width();
+ // If the element size was provided in logical units (versus physical), then
+ // it will be converted to physical units.
+ bool element_size_is_logical = false;
+ params.GetBoolean(kElementSizeIsLogical, &element_size_is_logical);
+ if (element_size_is_logical) {
+ // Convert the element size from logical pixels to physical pixels.
+ normal_height = LogicalPixelsToPhysicalPixels(element_height);
+ normal_width = LogicalPixelsToPhysicalPixels(element_width);
+ } else {
+ normal_height = lround(element_height);
+ normal_width = lround(element_width);
+ }
+
+ SetSizeParams set_size_params;
+ set_size_params.enable_auto_size.reset(new bool(auto_size_enabled));
+ set_size_params.min_size.reset(new gfx::Size(min_width, min_height));
+ set_size_params.max_size.reset(new gfx::Size(max_width, max_height));
+ set_size_params.normal_size.reset(new gfx::Size(normal_width, normal_height));
+
+ // Call SetSize to apply all the appropriate validation and clipping of
+ // values.
+ SetSize(set_size_params);
+}
+
+void GuestViewBase::SetGuestZoomLevelToMatchEmbedder() {
+ auto embedder_zoom_controller =
+ ui_zoom::ZoomController::FromWebContents(owner_web_contents());
+ if (!embedder_zoom_controller)
+ return;
+
+ ui_zoom::ZoomController::FromWebContents(web_contents())
+ ->SetZoomLevel(embedder_zoom_controller->GetZoomLevel());
+}
+
+void GuestViewBase::StartTrackingEmbedderZoomLevel() {
+ if (!ZoomPropagatesFromEmbedderToGuest())
+ return;
+
+ auto embedder_zoom_controller =
+ ui_zoom::ZoomController::FromWebContents(owner_web_contents());
+ // Chrome Apps do not have a ZoomController.
+ if (!embedder_zoom_controller)
+ return;
+ // Listen to the embedder's zoom changes.
+ embedder_zoom_controller->AddObserver(this);
+
+ // Set the guest's initial zoom level to be equal to the embedder's.
+ SetGuestZoomLevelToMatchEmbedder();
+}
+
+void GuestViewBase::StopTrackingEmbedderZoomLevel() {
+ if (!attached() || !ZoomPropagatesFromEmbedderToGuest())
+ return;
+
+ auto embedder_zoom_controller =
+ ui_zoom::ZoomController::FromWebContents(owner_web_contents());
+ // Chrome Apps do not have a ZoomController.
+ if (!embedder_zoom_controller)
+ return;
+ embedder_zoom_controller->RemoveObserver(this);
+}
+
+} // namespace guest_view
diff --git a/components/guest_view/browser/guest_view_base.h b/components/guest_view/browser/guest_view_base.h
new file mode 100644
index 0000000..74fb59c
--- /dev/null
+++ b/components/guest_view/browser/guest_view_base.h
@@ -0,0 +1,458 @@
+// Copyright 2014 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 COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_BASE_H_
+#define COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_BASE_H_
+
+#include <queue>
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "components/guest_view/common/guest_view_constants.h"
+#include "components/ui/zoom/zoom_observer.h"
+#include "content/public/browser/browser_plugin_guest_delegate.h"
+#include "content/public/browser/guest_host.h"
+#include "content/public/browser/render_process_host_observer.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/browser/web_contents_observer.h"
+
+struct RendererContentSettingRules;
+
+namespace guest_view {
+
+class GuestViewEvent;
+
+// A struct of parameters for SetSize(). The parameters are all declared as
+// scoped pointers since they are all optional. Null pointers indicate that the
+// parameter has not been provided, and the last used value should be used. Note
+// that when |enable_auto_size| is true, providing |normal_size| is not
+// meaningful. This is because the normal size of the guestview is overridden
+// whenever autosizing occurs.
+struct SetSizeParams {
+ SetSizeParams();
+ ~SetSizeParams();
+
+ scoped_ptr<bool> enable_auto_size;
+ scoped_ptr<gfx::Size> min_size;
+ scoped_ptr<gfx::Size> max_size;
+ scoped_ptr<gfx::Size> normal_size;
+};
+
+// A GuestViewBase is the base class browser-side API implementation for a
+// <*view> tag. GuestViewBase maintains an association between a guest
+// WebContents and an owner WebContents. It receives events issued from
+// the guest and relays them to the owner. GuestViewBase tracks the lifetime
+// of its owner. A GuestViewBase's owner is referred to as an embedder if
+// it is attached to a container within the owner's WebContents.
+class GuestViewBase : public content::BrowserPluginGuestDelegate,
+ public content::WebContentsDelegate,
+ public content::WebContentsObserver,
+ public ui_zoom::ZoomObserver {
+ public:
+ // Returns a *ViewGuest if this GuestView is of the given view type.
+ template <typename T>
+ T* As() {
+ if (IsViewType(T::Type))
+ return static_cast<T*>(this);
+
+ return nullptr;
+ }
+
+ static GuestViewBase* FromWebContents(
+ const content::WebContents* web_contents);
+
+ static GuestViewBase* From(int owner_process_id, int instance_id);
+
+ // Given a |web_contents|, returns the top level owner WebContents. If
+ // |web_contents| does not belong to a GuestView, it will be returned
+ // unchanged.
+ static content::WebContents* GetTopLevelWebContents(
+ content::WebContents* web_contents);
+
+ static bool IsGuest(content::WebContents* web_contents);
+
+ virtual const char* GetViewType() const = 0;
+
+ // This method is called after the guest has been attached to an embedder and
+ // suspended resource loads have been resumed.
+ //
+ // This method can be overriden by subclasses. This gives the derived class
+ // an opportunity to perform setup actions after attachment.
+ virtual void DidAttachToEmbedder() {}
+
+ // This method is called after this GuestViewBase has been initiated.
+ //
+ // This gives the derived class an opportunity to perform additional
+ // initialization.
+ virtual void DidInitialize(const base::DictionaryValue& create_params) {}
+
+ // This method is called when the initial set of frames within the page have
+ // completed loading.
+ virtual void GuestViewDidStopLoading() {}
+
+ // This method is called before the embedder is destroyed.
+ // |owner_web_contents_| should still be valid during this call. This
+ // allows the derived class to perform some cleanup related to the embedder
+ // web contents.
+ virtual void EmbedderWillBeDestroyed() {}
+
+ // This method is called when the embedder's zoom changes.
+ virtual void EmbedderZoomChanged(double old_zoom_level,
+ double new_zoom_level) {}
+
+ // This method is called when the guest WebContents has been destroyed. This
+ // object will be destroyed after this call returns.
+ //
+ // This gives the derived class an opportunity to perform some cleanup.
+ virtual void GuestDestroyed() {}
+
+ // This method is invoked when the guest RenderView is ready, e.g. because we
+ // recreated it after a crash or after reattachment.
+ //
+ // This gives the derived class an opportunity to perform some initialization
+ // work.
+ virtual void GuestReady() {}
+
+ // This method is called when the guest's zoom changes.
+ virtual void GuestZoomChanged(double old_zoom_level, double new_zoom_level) {}
+
+ // This method is called when embedder WebContents's fullscreen is toggled.
+ //
+ // If the guest asked the embedder to enter fullscreen, the guest uses this
+ // signal to exit fullscreen state.
+ virtual void EmbedderFullscreenToggled(bool entered_fullscreen) {}
+
+ // This method is invoked when the contents auto-resized to give the container
+ // an opportunity to match it if it wishes.
+ //
+ // This gives the derived class an opportunity to inform its container element
+ // or perform other actions.
+ virtual void GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
+ const gfx::Size& new_size) {}
+
+ // This method queries whether autosize is supported for this particular view.
+ // By default, autosize is not supported. Derived classes can override this
+ // behavior to support autosize.
+ virtual bool IsAutoSizeSupported() const;
+
+ // This method is invoked when the contents preferred size changes. This will
+ // only ever fire if IsPreferredSizeSupported returns true.
+ virtual void OnPreferredSizeChanged(const gfx::Size& pref_size) {}
+
+ // This method queries whether preferred size events are enabled for this
+ // view. By default, preferred size events are disabled, since they add a
+ // small amount of overhead.
+ virtual bool IsPreferredSizeModeEnabled() const;
+
+ // This method queries whether drag-and-drop is enabled for this particular
+ // view. By default, drag-and-drop is disabled. Derived classes can override
+ // this behavior to enable drag-and-drop.
+ virtual bool IsDragAndDropEnabled() const;
+
+ // This method is called immediately before suspended resource loads have been
+ // resumed on attachment to an embedder.
+ //
+ // This method can be overriden by subclasses. This gives the derived class
+ // an opportunity to perform setup actions before attachment.
+ virtual void WillAttachToEmbedder() {}
+
+ // This method is called when the guest WebContents is about to be destroyed.
+ //
+ // This gives the derived class an opportunity to perform some cleanup prior
+ // to destruction.
+ virtual void WillDestroy() {}
+
+ // This method is to be implemented by the derived class. This indicates
+ // whether zoom should propagate from the embedder to the guest content.
+ virtual bool ZoomPropagatesFromEmbedderToGuest() const;
+
+ // This method is to be implemented by the derived class. Access to guest
+ // views are determined by the availability of the internal extension API
+ // used to implement the guest view.
+ //
+ // This should be the name of the API as it appears in the _api_features.json
+ // file.
+ virtual const char* GetAPINamespace() const = 0;
+
+ // This method is to be implemented by the derived class. This method is the
+ // task prefix to show for a task produced by this GuestViewBase's derived
+ // type.
+ virtual int GetTaskPrefix() const = 0;
+
+ // This method is to be implemented by the derived class. Given a set of
+ // initialization parameters, a concrete subclass of GuestViewBase can
+ // create a specialized WebContents that it returns back to GuestViewBase.
+ using WebContentsCreatedCallback =
+ base::Callback<void(content::WebContents*)>;
+ virtual void CreateWebContents(
+ const base::DictionaryValue& create_params,
+ const WebContentsCreatedCallback& callback) = 0;
+
+ // This creates a WebContents and initializes |this| GuestViewBase to use the
+ // newly created WebContents.
+ void Init(const base::DictionaryValue& create_params,
+ const WebContentsCreatedCallback& callback);
+
+ void InitWithWebContents(const base::DictionaryValue& create_params,
+ content::WebContents* guest_web_contents);
+
+ void LoadURLWithParams(
+ const content::NavigationController::LoadURLParams& load_params);
+
+ bool IsViewType(const char* const view_type) const {
+ return !strcmp(GetViewType(), view_type);
+ }
+
+ // Used to toggle autosize mode for this GuestView, and set both the automatic
+ // and normal sizes.
+ void SetSize(const SetSizeParams& params);
+
+ bool initialized() const { return initialized_; }
+
+ content::WebContents* embedder_web_contents() const {
+ return attached() ? owner_web_contents_ : nullptr;
+ }
+
+ content::WebContents* owner_web_contents() const {
+ return owner_web_contents_;
+ }
+
+ content::GuestHost* host() const {
+ return guest_host_;
+ }
+
+ // Returns the parameters associated with the element hosting this GuestView
+ // passed in from JavaScript.
+ base::DictionaryValue* attach_params() const { return attach_params_.get(); }
+
+ // Returns whether this guest has an associated embedder.
+ bool attached() const {
+ return element_instance_id_ != kInstanceIDNone;
+ }
+
+ // Returns the instance ID of the <*view> element.
+ int view_instance_id() const { return view_instance_id_; }
+
+ // Returns the instance ID of this GuestViewBase.
+ int guest_instance_id() const { return guest_instance_id_; }
+
+ // Returns the instance ID of the GuestViewBase's element.
+ int element_instance_id() const { return element_instance_id_; }
+
+ bool can_owner_receive_events() const { return !!view_instance_id_; }
+
+ // Returns the user browser context of the embedder.
+ content::BrowserContext* browser_context() const { return browser_context_; }
+
+ GuestViewBase* GetOpener() const {
+ return opener_.get();
+ }
+
+ // Returns the URL of the owner WebContents.
+ const GURL& GetOwnerSiteURL() const;
+
+ // Returns the host of the owner WebContents. For extensions, this is the
+ // extension ID.
+ std::string owner_host() const { return owner_host_; }
+
+ // Whether the guest view is inside a plugin document.
+ bool is_full_page_plugin() const { return is_full_page_plugin_; }
+
+ // Returns the routing ID of the guest proxy in the owner's renderer process.
+ // This value is only valid after attachment or first navigation.
+ int proxy_routing_id() const { return guest_proxy_routing_id_; }
+
+ // Destroy this guest.
+ void Destroy();
+
+ // Saves the attach state of the custom element hosting this GuestView.
+ void SetAttachParams(const base::DictionaryValue& params);
+ void SetOpener(GuestViewBase* opener);
+
+ // BrowserPluginGuestDelegate implementation.
+ content::WebContents* CreateNewGuestWindow(
+ const content::WebContents::CreateParams& create_params) final;
+ void DidAttach(int guest_proxy_routing_id) final;
+ void DidDetach() final;
+ content::WebContents* GetOwnerWebContents() const final;
+ void GuestSizeChanged(const gfx::Size& new_size) final;
+ void SetGuestHost(content::GuestHost* guest_host) final;
+ void WillAttach(content::WebContents* embedder_web_contents,
+ int browser_plugin_instance_id,
+ bool is_full_page_plugin) final;
+
+ // ui_zoom::ZoomObserver implementation.
+ void OnZoomChanged(
+ const ui_zoom::ZoomController::ZoomChangedEventData& data) final;
+
+ // Dispatches an event to the guest proxy.
+ void DispatchEventToGuestProxy(GuestViewEvent* event);
+
+ // Dispatches an event to the view.
+ void DispatchEventToView(GuestViewEvent* event);
+
+ protected:
+ explicit GuestViewBase(content::WebContents* owner_web_contents);
+
+ ~GuestViewBase() override;
+
+ // Convert sizes in pixels from logical to physical numbers of pixels.
+ // Note that a size can consist of a fractional number of logical pixels
+ // (hence |logical_pixels| is represented as a double), but will always
+ // consist of an integral number of physical pixels (hence the return value
+ // is represented as an int).
+ int LogicalPixelsToPhysicalPixels(double logical_pixels) const;
+
+ // Convert sizes in pixels from physical to logical numbers of pixels.
+ // Note that a size can consist of a fractional number of logical pixels
+ // (hence the return value is represented as a double), but will always
+ // consist of an integral number of physical pixels (hence |physical_pixels|
+ // is represented as an int).
+ double PhysicalPixelsToLogicalPixels(int physical_pixels) const;
+
+ // WebContentsObserver implementation.
+ void DidStopLoading() final;
+ void RenderViewReady() final;
+ void WebContentsDestroyed() final;
+ void DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) override;
+
+ // WebContentsDelegate implementation.
+ void ActivateContents(content::WebContents* contents) final;
+ void DeactivateContents(content::WebContents* contents) final;
+ void ContentsMouseEvent(content::WebContents* source,
+ const gfx::Point& location,
+ bool motion) override;
+ void ContentsZoomChange(bool zoom_in) override;
+ void HandleKeyboardEvent(
+ content::WebContents* source,
+ const content::NativeWebKeyboardEvent& event) override;
+ void LoadingStateChanged(content::WebContents* source,
+ bool to_different_document) final;
+ content::ColorChooser* OpenColorChooser(
+ content::WebContents* web_contents,
+ SkColor color,
+ const std::vector<content::ColorSuggestion>& suggestions) override;
+ void RunFileChooser(content::WebContents* web_contents,
+ const content::FileChooserParams& params) override;
+ bool ShouldFocusPageAfterCrash() final;
+ bool PreHandleGestureEvent(content::WebContents* source,
+ const blink::WebGestureEvent& event) override;
+ void UpdatePreferredSize(content::WebContents* web_contents,
+ const gfx::Size& pref_size) final;
+ void UpdateTargetURL(content::WebContents* source, const GURL& url) override;
+ bool ShouldResumeRequestsForCreatedWindow() override;
+
+ void SetGuestZoomLevelToMatchEmbedder();
+
+ private:
+ class OwnerContentsObserver;
+
+ class OpenerLifetimeObserver;
+
+ void SendQueuedEvents();
+
+ void CompleteInit(scoped_ptr<base::DictionaryValue> create_params,
+ const WebContentsCreatedCallback& callback,
+ content::WebContents* guest_web_contents);
+
+ // Dispatches the onResize event to the embedder.
+ void DispatchOnResizeEvent(const gfx::Size& old_size,
+ const gfx::Size& new_size);
+
+ // Returns the default size of the guestview.
+ gfx::Size GetDefaultSize() const;
+
+ // Get the zoom factor for the embedder's web contents.
+ double GetEmbedderZoomFactor() const;
+
+ void SetUpSizing(const base::DictionaryValue& params);
+
+ void StartTrackingEmbedderZoomLevel();
+ void StopTrackingEmbedderZoomLevel();
+
+ // This guest tracks the lifetime of the WebContents specified by
+ // |owner_web_contents_|. If |owner_web_contents_| is destroyed then this
+ // guest will also self-destruct.
+ content::WebContents* owner_web_contents_;
+ std::string owner_host_;
+ content::BrowserContext* const browser_context_;
+
+ // |guest_instance_id_| is a profile-wide unique identifier for a guest
+ // WebContents.
+ const int guest_instance_id_;
+
+ // |view_instance_id_| is an identifier that's unique within a particular
+ // embedder RenderViewHost for a particular <*view> instance.
+ int view_instance_id_;
+
+ // |element_instance_id_| is an identifer that's unique to a particular
+ // GuestViewContainer element.
+ int element_instance_id_;
+
+ // |initialized_| indicates whether GuestViewBase::Init has been called for
+ // this object.
+ bool initialized_;
+
+ // Indicates that this guest is in the process of being destroyed.
+ bool is_being_destroyed_;
+
+ // This is a queue of Events that are destined to be sent to the embedder once
+ // the guest is attached to a particular embedder.
+ std::deque<linked_ptr<GuestViewEvent> > pending_events_;
+
+ // The opener guest view.
+ base::WeakPtr<GuestViewBase> opener_;
+
+ // The parameters associated with the element hosting this GuestView that
+ // are passed in from JavaScript. This will typically be the view instance ID,
+ // and element-specific parameters. These parameters are passed along to new
+ // guests that are created from this guest.
+ scoped_ptr<base::DictionaryValue> attach_params_;
+
+ // This observer ensures that this guest self-destructs if the embedder goes
+ // away.
+ scoped_ptr<OwnerContentsObserver> owner_contents_observer_;
+
+ // This observer ensures that if the guest is unattached and its opener goes
+ // away then this guest also self-destructs.
+ scoped_ptr<OpenerLifetimeObserver> opener_lifetime_observer_;
+
+ // The size of the guest content. Note: In autosize mode, the container
+ // element may not match the size of the guest.
+ gfx::Size guest_size_;
+
+ // A pointer to the guest_host.
+ content::GuestHost* guest_host_;
+
+ // Indicates whether autosize mode is enabled or not.
+ bool auto_size_enabled_;
+
+ // The maximum size constraints of the container element in autosize mode.
+ gfx::Size max_auto_size_;
+
+ // The minimum size constraints of the container element in autosize mode.
+ gfx::Size min_auto_size_;
+
+ // The size that will be used when autosize mode is disabled.
+ gfx::Size normal_size_;
+
+ // Whether the guest view is inside a plugin document.
+ bool is_full_page_plugin_;
+
+ // The routing ID of the proxy to the guest in the owner's renderer process.
+ int guest_proxy_routing_id_;
+
+ // This is used to ensure pending tasks will not fire after this object is
+ // destroyed.
+ base::WeakPtrFactory<GuestViewBase> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GuestViewBase);
+};
+
+} // namespace guest_view
+
+#endif // COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_BASE_H_
diff --git a/components/guest_view/browser/guest_view_event.cc b/components/guest_view/browser/guest_view_event.cc
new file mode 100644
index 0000000..a88d62f
--- /dev/null
+++ b/components/guest_view/browser/guest_view_event.cc
@@ -0,0 +1,28 @@
+// Copyright 2015 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 "components/guest_view/browser/guest_view_event.h"
+
+#include "components/guest_view/browser/guest_view_base.h"
+#include "components/guest_view/browser/guest_view_manager.h"
+
+namespace guest_view {
+
+GuestViewEvent::GuestViewEvent(const std::string& name,
+ scoped_ptr<base::DictionaryValue> args)
+ : name_(name),
+ args_(args.Pass()) {
+}
+
+GuestViewEvent::~GuestViewEvent() {
+}
+
+void GuestViewEvent::Dispatch(GuestViewBase* guest, int instance_id) {
+ GuestViewManager::FromBrowserContext(guest->browser_context())->
+ DispatchEvent(name_, args_.Pass(), guest, instance_id);
+
+ delete this;
+}
+
+} // namespace guest_view
diff --git a/components/guest_view/browser/guest_view_event.h b/components/guest_view/browser/guest_view_event.h
new file mode 100644
index 0000000..7f51d46
--- /dev/null
+++ b/components/guest_view/browser/guest_view_event.h
@@ -0,0 +1,41 @@
+// Copyright 2015 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 COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_EVENT_H_
+#define COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_EVENT_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+namespace guest_view {
+
+class GuestViewBase;
+
+// A GuestViewEvent is a wrapper class for a GuestView event.
+// GuestViewEvents may be queued until the guest is attached to a container.
+// This wrapper class holds all the necessary information to fire the event
+// on attachment. GuestViewEvents are owned by GuestViewBase.
+class GuestViewEvent {
+ public:
+ GuestViewEvent(const std::string& name,
+ scoped_ptr<base::DictionaryValue> args);
+ ~GuestViewEvent();
+
+ // This method will dispatch the event to the specified |guest|'s embedder and
+ // use the provided |instance_id| for routing. After dispatch, this object
+ // will self-destruct.
+ void Dispatch(GuestViewBase* guest, int instance_id);
+
+private:
+ const std::string name_;
+ scoped_ptr<base::DictionaryValue> args_;
+
+ DISALLOW_COPY_AND_ASSIGN(GuestViewEvent);
+};
+
+} // namespace guest_view
+
+#endif // COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_EVENT_H_
diff --git a/components/guest_view/browser/guest_view_manager.cc b/components/guest_view/browser/guest_view_manager.cc
new file mode 100644
index 0000000..04cc220
--- /dev/null
+++ b/components/guest_view/browser/guest_view_manager.cc
@@ -0,0 +1,376 @@
+// Copyright 2014 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 "components/guest_view/browser/guest_view_manager.h"
+
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "components/guest_view/browser/guest_view_base.h"
+#include "components/guest_view/browser/guest_view_manager_delegate.h"
+#include "components/guest_view/browser/guest_view_manager_factory.h"
+#include "components/guest_view/common/guest_view_constants.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/user_metrics.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/child_process_host.h"
+#include "content/public/common/result_codes.h"
+#include "content/public/common/url_constants.h"
+#include "url/gurl.h"
+
+using content::BrowserContext;
+using content::SiteInstance;
+using content::WebContents;
+
+namespace guest_view {
+
+// static
+GuestViewManagerFactory* GuestViewManager::factory_ = nullptr;
+
+GuestViewManager::GuestViewManager(
+ content::BrowserContext* context,
+ scoped_ptr<GuestViewManagerDelegate> delegate)
+ : current_instance_id_(0),
+ last_instance_id_removed_(0),
+ context_(context),
+ delegate_(delegate.Pass()) {
+}
+
+GuestViewManager::~GuestViewManager() {}
+
+// static
+GuestViewManager* GuestViewManager::CreateWithDelegate(
+ BrowserContext* context,
+ scoped_ptr<GuestViewManagerDelegate> delegate) {
+ GuestViewManager* guest_manager = FromBrowserContext(context);
+ if (!guest_manager) {
+ if (factory_) {
+ guest_manager =
+ factory_->CreateGuestViewManager(context, delegate.Pass());
+ } else {
+ guest_manager = new GuestViewManager(context, delegate.Pass());
+ }
+ context->SetUserData(kGuestViewManagerKeyName, guest_manager);
+ }
+ return guest_manager;
+}
+
+// static
+GuestViewManager* GuestViewManager::FromBrowserContext(
+ BrowserContext* context) {
+ return static_cast<GuestViewManager*>(context->GetUserData(
+ kGuestViewManagerKeyName));
+}
+
+content::WebContents* GuestViewManager::GetGuestByInstanceIDSafely(
+ int guest_instance_id,
+ int embedder_render_process_id) {
+ if (!CanEmbedderAccessInstanceIDMaybeKill(embedder_render_process_id,
+ guest_instance_id)) {
+ return nullptr;
+ }
+ return GetGuestByInstanceID(guest_instance_id);
+}
+
+void GuestViewManager::AttachGuest(int embedder_process_id,
+ int element_instance_id,
+ int guest_instance_id,
+ const base::DictionaryValue& attach_params) {
+ auto guest_view = GuestViewBase::From(embedder_process_id, guest_instance_id);
+ if (!guest_view)
+ return;
+
+ ElementInstanceKey key(embedder_process_id, element_instance_id);
+ auto it = instance_id_map_.find(key);
+ // If there is an existing guest attached to the element, then destroy the
+ // existing guest.
+ if (it != instance_id_map_.end()) {
+ int old_guest_instance_id = it->second;
+ if (old_guest_instance_id == guest_instance_id)
+ return;
+
+ auto old_guest_view = GuestViewBase::From(embedder_process_id,
+ old_guest_instance_id);
+ old_guest_view->Destroy();
+ }
+ instance_id_map_[key] = guest_instance_id;
+ reverse_instance_id_map_[guest_instance_id] = key;
+ guest_view->SetAttachParams(attach_params);
+}
+
+void GuestViewManager::DetachGuest(GuestViewBase* guest) {
+ if (!guest->attached())
+ return;
+
+ auto reverse_it = reverse_instance_id_map_.find(guest->guest_instance_id());
+ if (reverse_it == reverse_instance_id_map_.end())
+ return;
+
+ const ElementInstanceKey& key = reverse_it->second;
+
+ auto it = instance_id_map_.find(key);
+ DCHECK(it != instance_id_map_.end());
+
+ reverse_instance_id_map_.erase(reverse_it);
+ instance_id_map_.erase(it);
+}
+
+bool GuestViewManager::IsOwnedByExtension(GuestViewBase* guest) {
+ return delegate_->IsOwnedByExtension(guest);
+}
+
+int GuestViewManager::GetNextInstanceID() {
+ return ++current_instance_id_;
+}
+
+void GuestViewManager::CreateGuest(const std::string& view_type,
+ content::WebContents* owner_web_contents,
+ const base::DictionaryValue& create_params,
+ const WebContentsCreatedCallback& callback) {
+ GuestViewBase* guest = CreateGuestInternal(owner_web_contents, view_type);
+ if (!guest) {
+ callback.Run(nullptr);
+ return;
+ }
+ guest->Init(create_params, callback);
+}
+
+content::WebContents* GuestViewManager::CreateGuestWithWebContentsParams(
+ const std::string& view_type,
+ content::WebContents* owner_web_contents,
+ const content::WebContents::CreateParams& create_params) {
+ auto guest = CreateGuestInternal(owner_web_contents, view_type);
+ if (!guest)
+ return nullptr;
+ content::WebContents::CreateParams guest_create_params(create_params);
+ guest_create_params.guest_delegate = guest;
+ auto guest_web_contents = WebContents::Create(guest_create_params);
+ guest->InitWithWebContents(base::DictionaryValue(), guest_web_contents);
+ return guest_web_contents;
+}
+
+content::WebContents* GuestViewManager::GetGuestByInstanceID(
+ int owner_process_id,
+ int element_instance_id) {
+ int guest_instance_id = GetGuestInstanceIDForElementID(owner_process_id,
+ element_instance_id);
+ if (guest_instance_id == kInstanceIDNone)
+ return nullptr;
+
+ return GetGuestByInstanceID(guest_instance_id);
+}
+
+int GuestViewManager::GetGuestInstanceIDForElementID(int owner_process_id,
+ int element_instance_id) {
+ auto iter = instance_id_map_.find(
+ ElementInstanceKey(owner_process_id, element_instance_id));
+ if (iter == instance_id_map_.end())
+ return kInstanceIDNone;
+ return iter->second;
+}
+
+SiteInstance* GuestViewManager::GetGuestSiteInstance(
+ const GURL& guest_site) {
+ for (const auto& guest : guest_web_contents_by_instance_id_) {
+ if (guest.second->GetSiteInstance()->GetSiteURL() == guest_site)
+ return guest.second->GetSiteInstance();
+ }
+ return nullptr;
+}
+
+bool GuestViewManager::ForEachGuest(WebContents* owner_web_contents,
+ const GuestCallback& callback) {
+ for (const auto& guest : guest_web_contents_by_instance_id_) {
+ auto guest_view = GuestViewBase::FromWebContents(guest.second);
+ if (guest_view->owner_web_contents() != owner_web_contents)
+ continue;
+
+ if (callback.Run(guest_view->web_contents()))
+ return true;
+ }
+ return false;
+}
+
+WebContents* GuestViewManager::GetFullPageGuest(
+ WebContents* embedder_web_contents) {
+ WebContents* result = nullptr;
+ ForEachGuest(embedder_web_contents,
+ base::Bind(&GuestViewManager::GetFullPageGuestHelper, &result));
+ return result;
+}
+
+void GuestViewManager::AddGuest(int guest_instance_id,
+ WebContents* guest_web_contents) {
+ CHECK(!ContainsKey(guest_web_contents_by_instance_id_, guest_instance_id));
+ CHECK(CanUseGuestInstanceID(guest_instance_id));
+ guest_web_contents_by_instance_id_[guest_instance_id] = guest_web_contents;
+}
+
+void GuestViewManager::RemoveGuest(int guest_instance_id) {
+ auto it = guest_web_contents_by_instance_id_.find(guest_instance_id);
+ DCHECK(it != guest_web_contents_by_instance_id_.end());
+ guest_web_contents_by_instance_id_.erase(it);
+
+ auto id_iter = reverse_instance_id_map_.find(guest_instance_id);
+ if (id_iter != reverse_instance_id_map_.end()) {
+ const ElementInstanceKey& instance_id_key = id_iter->second;
+ instance_id_map_.erase(instance_id_map_.find(instance_id_key));
+ reverse_instance_id_map_.erase(id_iter);
+ }
+
+ // All the instance IDs that lie within [0, last_instance_id_removed_]
+ // are invalid.
+ // The remaining sparse invalid IDs are kept in |removed_instance_ids_| set.
+ // The following code compacts the set by incrementing
+ // |last_instance_id_removed_|.
+ if (guest_instance_id == last_instance_id_removed_ + 1) {
+ ++last_instance_id_removed_;
+ // Compact.
+ auto iter = removed_instance_ids_.begin();
+ while (iter != removed_instance_ids_.end()) {
+ int instance_id = *iter;
+ // The sparse invalid IDs must not lie within
+ // [0, last_instance_id_removed_]
+ DCHECK(instance_id > last_instance_id_removed_);
+ if (instance_id != last_instance_id_removed_ + 1)
+ break;
+ ++last_instance_id_removed_;
+ removed_instance_ids_.erase(iter++);
+ }
+ } else {
+ removed_instance_ids_.insert(guest_instance_id);
+ }
+}
+
+GuestViewBase* GuestViewManager::CreateGuestInternal(
+ content::WebContents* owner_web_contents,
+ const std::string& view_type) {
+ if (guest_view_registry_.empty())
+ RegisterGuestViewTypes();
+
+ auto it = guest_view_registry_.find(view_type);
+ if (it == guest_view_registry_.end()) {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ return it->second.Run(owner_web_contents);
+}
+
+void GuestViewManager::RegisterGuestViewTypes() {
+ delegate_->RegisterAdditionalGuestViewTypes();
+}
+
+bool GuestViewManager::IsGuestAvailableToContext(GuestViewBase* guest) {
+ return delegate_->IsGuestAvailableToContext(guest);
+}
+
+void GuestViewManager::DispatchEvent(const std::string& event_name,
+ scoped_ptr<base::DictionaryValue> args,
+ GuestViewBase* guest,
+ int instance_id) {
+ // TODO(fsamuel): GuestViewManager should probably do something more useful
+ // here like log an error if the event could not be dispatched.
+ delegate_->DispatchEvent(event_name, args.Pass(), guest, instance_id);
+}
+
+content::WebContents* GuestViewManager::GetGuestByInstanceID(
+ int guest_instance_id) {
+ auto it = guest_web_contents_by_instance_id_.find(guest_instance_id);
+ if (it == guest_web_contents_by_instance_id_.end())
+ return nullptr;
+ return it->second;
+}
+
+bool GuestViewManager::CanEmbedderAccessInstanceIDMaybeKill(
+ int embedder_render_process_id,
+ int guest_instance_id) {
+ if (!CanEmbedderAccessInstanceID(embedder_render_process_id,
+ guest_instance_id)) {
+ // The embedder process is trying to access a guest it does not own.
+ content::RecordAction(
+ base::UserMetricsAction("BadMessageTerminate_BPGM"));
+ content::RenderProcessHost::FromID(embedder_render_process_id)
+ ->Shutdown(content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
+ return false;
+ }
+ return true;
+}
+
+bool GuestViewManager::CanUseGuestInstanceID(int guest_instance_id) {
+ if (guest_instance_id <= last_instance_id_removed_)
+ return false;
+ return !ContainsKey(removed_instance_ids_, guest_instance_id);
+}
+
+// static
+bool GuestViewManager::GetFullPageGuestHelper(
+ content::WebContents** result,
+ content::WebContents* guest_web_contents) {
+ auto guest_view = GuestViewBase::FromWebContents(guest_web_contents);
+ if (guest_view && guest_view->is_full_page_plugin()) {
+ *result = guest_web_contents;
+ return true;
+ }
+ return false;
+}
+
+bool GuestViewManager::CanEmbedderAccessInstanceID(
+ int embedder_render_process_id,
+ int guest_instance_id) {
+ // The embedder is trying to access a guest with a negative or zero
+ // instance ID.
+ if (guest_instance_id <= kInstanceIDNone)
+ return false;
+
+ // The embedder is trying to access an instance ID that has not yet been
+ // allocated by GuestViewManager. This could cause instance ID
+ // collisions in the future, and potentially give one embedder access to a
+ // guest it does not own.
+ if (guest_instance_id > current_instance_id_)
+ return false;
+
+ // We might get some late arriving messages at tear down. Let's let the
+ // embedder tear down in peace.
+ auto it = guest_web_contents_by_instance_id_.find(guest_instance_id);
+ if (it == guest_web_contents_by_instance_id_.end())
+ return true;
+
+ auto guest_view = GuestViewBase::FromWebContents(it->second);
+ if (!guest_view)
+ return false;
+
+ return embedder_render_process_id ==
+ guest_view->owner_web_contents()->GetRenderProcessHost()->GetID();
+}
+
+GuestViewManager::ElementInstanceKey::ElementInstanceKey()
+ : embedder_process_id(content::ChildProcessHost::kInvalidUniqueID),
+ element_instance_id(content::ChildProcessHost::kInvalidUniqueID) {
+}
+
+GuestViewManager::ElementInstanceKey::ElementInstanceKey(
+ int embedder_process_id,
+ int element_instance_id)
+ : embedder_process_id(embedder_process_id),
+ element_instance_id(element_instance_id) {
+}
+
+bool GuestViewManager::ElementInstanceKey::operator<(
+ const GuestViewManager::ElementInstanceKey& other) const {
+ if (embedder_process_id != other.embedder_process_id)
+ return embedder_process_id < other.embedder_process_id;
+
+ return element_instance_id < other.element_instance_id;
+}
+
+bool GuestViewManager::ElementInstanceKey::operator==(
+ const GuestViewManager::ElementInstanceKey& other) const {
+ return (embedder_process_id == other.embedder_process_id) &&
+ (element_instance_id == other.element_instance_id);
+}
+
+} // namespace guest_view
diff --git a/components/guest_view/browser/guest_view_manager.h b/components/guest_view/browser/guest_view_manager.h
new file mode 100644
index 0000000..f51d0a6
--- /dev/null
+++ b/components/guest_view/browser/guest_view_manager.h
@@ -0,0 +1,212 @@
+// Copyright 2014 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 COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MANAGER_H_
+#define COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MANAGER_H_
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/gtest_prod_util.h"
+#include "base/lazy_instance.h"
+#include "base/macros.h"
+#include "content/public/browser/browser_plugin_guest_manager.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+
+class GURL;
+
+namespace content {
+class BrowserContext;
+class WebContents;
+} // namespace content
+
+namespace guest_view {
+
+class GuestViewBase;
+class GuestViewManagerDelegate;
+class GuestViewManagerFactory;
+
+class GuestViewManager : public content::BrowserPluginGuestManager,
+ public base::SupportsUserData::Data {
+ public:
+ GuestViewManager(content::BrowserContext* context,
+ scoped_ptr<GuestViewManagerDelegate> delegate);
+ ~GuestViewManager() override;
+
+ // Returns the GuestViewManager associated with |context|. If one isn't
+ // available, then it is created and returned.
+ static GuestViewManager* CreateWithDelegate(
+ content::BrowserContext* context,
+ scoped_ptr<GuestViewManagerDelegate> delegate);
+
+ // Returns the GuestViewManager associated with |context|. If one isn't
+ // available, then nullptr is returned.
+ static GuestViewManager* FromBrowserContext(content::BrowserContext* context);
+
+ // Overrides factory for testing. Default (NULL) value indicates regular
+ // (non-test) environment.
+ static void set_factory_for_testing(GuestViewManagerFactory* factory) {
+ GuestViewManager::factory_ = factory;
+ }
+ // Returns the guest WebContents associated with the given |guest_instance_id|
+ // if the provided |embedder_render_process_id| is allowed to access it.
+ // If the embedder is not allowed access, the embedder will be killed, and
+ // this method will return NULL. If no WebContents exists with the given
+ // instance ID, then NULL will also be returned.
+ content::WebContents* GetGuestByInstanceIDSafely(
+ int guest_instance_id,
+ int embedder_render_process_id);
+
+ // Associates the Browser Plugin with |element_instance_id| to a
+ // guest that has ID of |guest_instance_id| and sets initialization
+ // parameters, |params| for it.
+ void AttachGuest(int embedder_process_id,
+ int element_instance_id,
+ int guest_instance_id,
+ const base::DictionaryValue& attach_params);
+
+ // Removes the association between |element_instance_id| and a guest instance
+ // ID if one exists.
+ void DetachGuest(GuestViewBase* guest);
+
+ // Indicates whether the |guest| is owned by an extension or Chrome App.
+ bool IsOwnedByExtension(GuestViewBase* guest);
+
+ int GetNextInstanceID();
+ int GetGuestInstanceIDForElementID(
+ int owner_process_id,
+ int element_instance_id);
+
+ template <typename T>
+ void RegisterGuestViewType() {
+ auto it = guest_view_registry_.find(T::Type);
+ DCHECK(it == guest_view_registry_.end());
+ guest_view_registry_[T::Type] = base::Bind(&T::Create);
+ }
+
+ using WebContentsCreatedCallback =
+ base::Callback<void(content::WebContents*)>;
+ void CreateGuest(const std::string& view_type,
+ content::WebContents* owner_web_contents,
+ const base::DictionaryValue& create_params,
+ const WebContentsCreatedCallback& callback);
+
+ content::WebContents* CreateGuestWithWebContentsParams(
+ const std::string& view_type,
+ content::WebContents* owner_web_contents,
+ const content::WebContents::CreateParams& create_params);
+
+ content::SiteInstance* GetGuestSiteInstance(
+ const GURL& guest_site);
+
+ // BrowserPluginGuestManager implementation.
+ content::WebContents* GetGuestByInstanceID(
+ int owner_process_id,
+ int element_instance_id) override;
+ bool ForEachGuest(content::WebContents* owner_web_contents,
+ const GuestCallback& callback) override;
+ content::WebContents* GetFullPageGuest(
+ content::WebContents* embedder_web_contents) override;
+
+ protected:
+ friend class GuestViewBase;
+ friend class GuestViewEvent;
+
+ // Can be overriden in tests.
+ virtual void AddGuest(int guest_instance_id,
+ content::WebContents* guest_web_contents);
+
+ // Can be overriden in tests.
+ virtual void RemoveGuest(int guest_instance_id);
+
+ // Creates a guest of the provided |view_type|.
+ GuestViewBase* CreateGuestInternal(content::WebContents* owner_web_contents,
+ const std::string& view_type);
+
+ // Adds GuestView types to the GuestView registry.
+ void RegisterGuestViewTypes();
+
+ // Indicates whether the provided |guest| can be used in the context it has
+ // been created.
+ bool IsGuestAvailableToContext(GuestViewBase* guest);
+
+ // Dispatches the event with |name| with the provided |args| to the embedder
+ // of the given |guest| with |instance_id| for routing.
+ void DispatchEvent(const std::string& event_name,
+ scoped_ptr<base::DictionaryValue> args,
+ GuestViewBase* guest,
+ int instance_id);
+
+ content::WebContents* GetGuestByInstanceID(int guest_instance_id);
+
+ bool CanEmbedderAccessInstanceIDMaybeKill(
+ int embedder_render_process_id,
+ int guest_instance_id);
+
+ bool CanEmbedderAccessInstanceID(int embedder_render_process_id,
+ int guest_instance_id);
+
+ // Returns true if |guest_instance_id| can be used to add a new guest to this
+ // manager.
+ // We disallow adding new guest with instance IDs that were previously removed
+ // from this manager using RemoveGuest.
+ bool CanUseGuestInstanceID(int guest_instance_id);
+
+ static bool GetFullPageGuestHelper(content::WebContents** result,
+ content::WebContents* guest_web_contents);
+
+ // Static factory instance (always NULL for non-test).
+ static GuestViewManagerFactory* factory_;
+
+ // Contains guests' WebContents, mapping from their instance ids.
+ using GuestInstanceMap = std::map<int, content::WebContents*>;
+ GuestInstanceMap guest_web_contents_by_instance_id_;
+
+ struct ElementInstanceKey {
+ int embedder_process_id;
+ int element_instance_id;
+
+ ElementInstanceKey();
+ ElementInstanceKey(int embedder_process_id,
+ int element_instance_id);
+
+ bool operator<(const ElementInstanceKey& other) const;
+ bool operator==(const ElementInstanceKey& other) const;
+ };
+
+ using GuestInstanceIDMap = std::map<ElementInstanceKey, int>;
+ GuestInstanceIDMap instance_id_map_;
+
+ // The reverse map of GuestInstanceIDMap.
+ using GuestInstanceIDReverseMap = std::map<int, ElementInstanceKey>;
+ GuestInstanceIDReverseMap reverse_instance_id_map_;
+
+ using GuestCreationCallback =
+ base::Callback<GuestViewBase*(content::WebContents*)>;
+ using GuestViewCreationMap =
+ std::map<std::string, GuestViewManager::GuestCreationCallback>;
+ GuestViewCreationMap guest_view_registry_;
+
+ int current_instance_id_;
+
+ // Any instance ID whose number not greater than this was removed via
+ // RemoveGuest.
+ // This is used so that we don't have store all removed instance IDs in
+ // |removed_instance_ids_|.
+ int last_instance_id_removed_;
+ // The remaining instance IDs that are greater than
+ // |last_instance_id_removed_| are kept here.
+ std::set<int> removed_instance_ids_;
+
+ content::BrowserContext* context_;
+
+ scoped_ptr<GuestViewManagerDelegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(GuestViewManager);
+};
+
+} // namespace guest_view
+
+#endif // COMPONETS_GUEST_VIEW_BROWSER_GUEST_VIEW_MANAGER_H_
diff --git a/components/guest_view/browser/guest_view_manager_delegate.cc b/components/guest_view/browser/guest_view_manager_delegate.cc
new file mode 100644
index 0000000..244c516
--- /dev/null
+++ b/components/guest_view/browser/guest_view_manager_delegate.cc
@@ -0,0 +1,23 @@
+// Copyright 2015 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 "components/guest_view/browser/guest_view_manager_delegate.h"
+
+namespace guest_view {
+
+GuestViewManagerDelegate::GuestViewManagerDelegate() {
+}
+
+GuestViewManagerDelegate::~GuestViewManagerDelegate() {
+}
+
+bool GuestViewManagerDelegate::IsGuestAvailableToContext(GuestViewBase* guest) {
+ return false;
+}
+
+bool GuestViewManagerDelegate::IsOwnedByExtension(GuestViewBase* guest) {
+ return false;
+}
+
+} // namespace guest
diff --git a/components/guest_view/browser/guest_view_manager_delegate.h b/components/guest_view/browser/guest_view_manager_delegate.h
new file mode 100644
index 0000000..a46c278d
--- /dev/null
+++ b/components/guest_view/browser/guest_view_manager_delegate.h
@@ -0,0 +1,53 @@
+// Copyright 2015 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 COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MANAGER_DELEGATE_H_
+#define COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MANAGER_DELEGATE_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+namespace base {
+class DictionaryValue;
+} // namespace base
+
+namespace guest_view {
+
+class GuestViewBase;
+
+// A GuestViewManagerDelegate interface allows GuestViewManager to delegate
+// responsibilities to other modules in Chromium. Different builds of Chromium
+// may use different GuestViewManagerDelegate implementations. For example,
+// mobile builds of Chromium do not include an extensions module and so
+// permission checks would be different, and IsOwnedByExtension would always
+// return false.
+class GuestViewManagerDelegate {
+ public:
+ GuestViewManagerDelegate();
+ virtual ~GuestViewManagerDelegate();
+
+ // Dispatches the event with |name| with the provided |args| to the embedder
+ // of the given |guest| with |instance_id| for routing.
+ virtual void DispatchEvent(const std::string& event_name,
+ scoped_ptr<base::DictionaryValue> args,
+ GuestViewBase* guest,
+ int instance_id) {}
+
+ // Indicates whether the |guest| can be used within the context of where it
+ // was created.
+ virtual bool IsGuestAvailableToContext(GuestViewBase* guest);
+
+ // Indicates whether the |guest| is owned by an extension or Chrome App.
+ virtual bool IsOwnedByExtension(GuestViewBase* guest);
+
+ // Registers additional GuestView types the delegator (GuestViewManger) can
+ // create.
+ virtual void RegisterAdditionalGuestViewTypes() {}
+};
+
+} // namespace guest_view
+
+#endif // COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MANAGER_DELEGATE_H_
diff --git a/components/guest_view/browser/guest_view_manager_factory.h b/components/guest_view/browser/guest_view_manager_factory.h
new file mode 100644
index 0000000..0d127a3
--- /dev/null
+++ b/components/guest_view/browser/guest_view_manager_factory.h
@@ -0,0 +1,30 @@
+// Copyright 2014 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 COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MANAGER_FACTORY_H_
+#define COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MANAGER_FACTORY_H_
+
+namespace content {
+class BrowserContext;
+}
+
+namespace guest_view {
+
+class GuestViewManager;
+class GuestViewManagerDelegate;
+
+class GuestViewManagerFactory {
+ public:
+ virtual GuestViewManager* CreateGuestViewManager(
+ content::BrowserContext* context,
+ scoped_ptr<GuestViewManagerDelegate> delegate) = 0;
+
+ protected:
+ virtual ~GuestViewManagerFactory() {}
+};
+
+} // namespace guest_view
+
+#endif // COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MANAGER_FACTORY_H_
+
diff --git a/components/guest_view/browser/guest_view_manager_unittest.cc b/components/guest_view/browser/guest_view_manager_unittest.cc
new file mode 100644
index 0000000..1d43dbd
--- /dev/null
+++ b/components/guest_view/browser/guest_view_manager_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright 2014 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 "components/guest_view/browser/guest_view_manager.h"
+#include "components/guest_view/browser/guest_view_manager_delegate.h"
+#include "components/guest_view/browser/test_guest_view_manager.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::WebContents;
+using content::WebContentsTester;
+
+namespace guest_view {
+
+namespace {
+
+class GuestViewManagerTest : public content::RenderViewHostTestHarness {
+ public:
+ GuestViewManagerTest() {}
+ ~GuestViewManagerTest() override {}
+
+ scoped_ptr<WebContents> CreateWebContents() {
+ return scoped_ptr<WebContents>(
+ WebContentsTester::CreateTestWebContents(&browser_context_, NULL));
+ }
+
+ private:
+ content::TestBrowserContext browser_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(GuestViewManagerTest);
+};
+
+} // namespace
+
+TEST_F(GuestViewManagerTest, AddRemove) {
+ content::TestBrowserContext browser_context;
+ scoped_ptr<GuestViewManagerDelegate> delegate(
+ new GuestViewManagerDelegate());
+ scoped_ptr<TestGuestViewManager> manager(
+ new TestGuestViewManager(&browser_context, delegate.Pass()));
+
+ scoped_ptr<WebContents> web_contents1(CreateWebContents());
+ scoped_ptr<WebContents> web_contents2(CreateWebContents());
+ scoped_ptr<WebContents> web_contents3(CreateWebContents());
+
+ EXPECT_EQ(0, manager->last_instance_id_removed());
+
+ EXPECT_TRUE(manager->CanUseGuestInstanceID(1));
+ EXPECT_TRUE(manager->CanUseGuestInstanceID(2));
+ EXPECT_TRUE(manager->CanUseGuestInstanceID(3));
+
+ manager->AddGuest(1, web_contents1.get());
+ manager->AddGuest(2, web_contents2.get());
+ manager->RemoveGuest(2);
+
+ // Since we removed 2, it would be an invalid ID.
+ EXPECT_TRUE(manager->CanUseGuestInstanceID(1));
+ EXPECT_FALSE(manager->CanUseGuestInstanceID(2));
+ EXPECT_TRUE(manager->CanUseGuestInstanceID(3));
+
+ EXPECT_EQ(0, manager->last_instance_id_removed());
+
+ EXPECT_TRUE(manager->CanUseGuestInstanceID(3));
+
+ manager->AddGuest(3, web_contents3.get());
+ manager->RemoveGuest(1);
+ EXPECT_FALSE(manager->CanUseGuestInstanceID(1));
+ EXPECT_FALSE(manager->CanUseGuestInstanceID(2));
+
+ EXPECT_EQ(2, manager->last_instance_id_removed());
+ manager->RemoveGuest(3);
+ EXPECT_EQ(3, manager->last_instance_id_removed());
+
+ EXPECT_FALSE(manager->CanUseGuestInstanceID(1));
+ EXPECT_FALSE(manager->CanUseGuestInstanceID(2));
+ EXPECT_FALSE(manager->CanUseGuestInstanceID(3));
+
+ EXPECT_EQ(0u, manager->GetNumRemovedInstanceIDs());
+}
+
+} // namespace guest_view
diff --git a/components/guest_view/browser/guest_view_message_filter.cc b/components/guest_view/browser/guest_view_message_filter.cc
new file mode 100644
index 0000000..d313f53
--- /dev/null
+++ b/components/guest_view/browser/guest_view_message_filter.cc
@@ -0,0 +1,80 @@
+// Copyright 2015 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 "components/guest_view/browser/guest_view_message_filter.h"
+
+#include "components/guest_view/browser/guest_view_base.h"
+#include "components/guest_view/browser/guest_view_manager.h"
+#include "components/guest_view/common/guest_view_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "ipc/ipc_message_macros.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+using content::RenderFrameHost;
+using content::WebContents;
+
+namespace guest_view {
+
+GuestViewMessageFilter::GuestViewMessageFilter(int render_process_id,
+ BrowserContext* context)
+ : BrowserMessageFilter(GuestViewMsgStart),
+ render_process_id_(render_process_id),
+ browser_context_(context),
+ weak_ptr_factory_(this) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+GuestViewMessageFilter::~GuestViewMessageFilter() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+}
+
+void GuestViewMessageFilter::OverrideThreadForMessage(
+ const IPC::Message& message,
+ BrowserThread::ID* thread) {
+ switch (message.type()) {
+ case GuestViewHostMsg_AttachGuest::ID:
+ *thread = BrowserThread::UI;
+ break;
+ default:
+ break;
+ }
+}
+
+void GuestViewMessageFilter::OnDestruct() const {
+ // Destroy the filter on the IO thread since that's where its weak pointers
+ // are being used.
+ BrowserThread::DeleteOnIOThread::Destruct(this);
+}
+
+bool GuestViewMessageFilter::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(GuestViewMessageFilter, message)
+ IPC_MESSAGE_HANDLER(GuestViewHostMsg_AttachGuest, OnAttachGuest)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void GuestViewMessageFilter::OnAttachGuest(
+ int element_instance_id,
+ int guest_instance_id,
+ const base::DictionaryValue& params) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ auto manager = GuestViewManager::FromBrowserContext(browser_context_);
+ // We should have a GuestViewManager at this point. If we don't then the
+ // embedder is misbehaving.
+ if (!manager)
+ return;
+
+ manager->AttachGuest(render_process_id_,
+ element_instance_id,
+ guest_instance_id,
+ params);
+}
+
+} // namespace guest_view
diff --git a/components/guest_view/browser/guest_view_message_filter.h b/components/guest_view/browser/guest_view_message_filter.h
new file mode 100644
index 0000000..7d2448f
--- /dev/null
+++ b/components/guest_view/browser/guest_view_message_filter.h
@@ -0,0 +1,67 @@
+// Copyright 2015 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 COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MESSAGE_FILTER_H_
+#define COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MESSAGE_FILTER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/browser_message_filter.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace content {
+class BrowserContext;
+class WebContents;
+}
+
+namespace gfx {
+class Size;
+}
+
+namespace guest_view {
+
+// This class filters out incoming GuestView-specific IPC messages from the
+// renderer process. It is created on the UI thread. Messages may be handled on
+// the IO thread or the UI thread.
+class GuestViewMessageFilter : public content::BrowserMessageFilter {
+ public:
+ GuestViewMessageFilter(int render_process_id,
+ content::BrowserContext* context);
+
+ private:
+ friend class content::BrowserThread;
+ friend class base::DeleteHelper<GuestViewMessageFilter>;
+
+ ~GuestViewMessageFilter() override;
+
+ // content::BrowserMessageFilter implementation.
+ void OverrideThreadForMessage(const IPC::Message& message,
+ content::BrowserThread::ID* thread) override;
+ void OnDestruct() const override;
+ bool OnMessageReceived(const IPC::Message& message) override;
+
+ // Message handlers on the UI thread.
+ void OnAttachGuest(int element_instance_id,
+ int guest_instance_id,
+ const base::DictionaryValue& attach_params);
+
+ const int render_process_id_;
+
+ // Should only be accessed on the UI thread.
+ content::BrowserContext* const browser_context_;
+
+ // Weak pointers produced by this factory are bound to the IO thread.
+ base::WeakPtrFactory<GuestViewMessageFilter> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GuestViewMessageFilter);
+};
+
+} // namespace guest_view
+
+#endif // COMPONENTS_GUEST_VIEW_BROWSER_GUEST_VIEW_MESSAGE_FILTER_H_
diff --git a/components/guest_view/browser/test_guest_view_manager.cc b/components/guest_view/browser/test_guest_view_manager.cc
new file mode 100644
index 0000000..90a1023
--- /dev/null
+++ b/components/guest_view/browser/test_guest_view_manager.cc
@@ -0,0 +1,99 @@
+// Copyright 2014 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 "components/guest_view/browser/test_guest_view_manager.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "components/guest_view/browser/guest_view_manager_delegate.h"
+
+namespace guest_view {
+
+TestGuestViewManager::TestGuestViewManager(
+ content::BrowserContext* context,
+ scoped_ptr<GuestViewManagerDelegate> delegate)
+ : GuestViewManager(context, delegate.Pass()),
+ num_guests_created_(0) {
+}
+
+TestGuestViewManager::~TestGuestViewManager() {
+}
+
+size_t TestGuestViewManager::GetNumGuestsActive() const {
+ return guest_web_contents_by_instance_id_.size();
+}
+
+size_t TestGuestViewManager::GetNumRemovedInstanceIDs() const {
+ return removed_instance_ids_.size();
+}
+
+content::WebContents* TestGuestViewManager::GetLastGuestCreated() {
+ content::WebContents* web_contents = nullptr;
+ for (int i = current_instance_id_; i >= 0; i--) {
+ web_contents = GetGuestByInstanceID(i);
+ if (web_contents) {
+ break;
+ }
+ }
+ return web_contents;
+}
+
+void TestGuestViewManager::WaitForAllGuestsDeleted() {
+ // Make sure that every guest that was created have been removed.
+ for (auto& watcher : guest_web_contents_watchers_)
+ watcher->Wait();
+}
+
+void TestGuestViewManager::WaitForGuestCreated() {
+ created_message_loop_runner_ = new content::MessageLoopRunner;
+ created_message_loop_runner_->Run();
+}
+
+content::WebContents* TestGuestViewManager::WaitForSingleGuestCreated() {
+ if (!GetNumGuestsActive()) {
+ // Guests have been created and subsequently destroyed.
+ if (num_guests_created() > 0)
+ return nullptr;
+ WaitForGuestCreated();
+ }
+
+ return GetLastGuestCreated();
+}
+
+void TestGuestViewManager::AddGuest(int guest_instance_id,
+ content::WebContents* guest_web_contents) {
+ GuestViewManager::AddGuest(guest_instance_id, guest_web_contents);
+
+ guest_web_contents_watchers_.push_back(
+ linked_ptr<content::WebContentsDestroyedWatcher>(
+ new content::WebContentsDestroyedWatcher(guest_web_contents)));
+
+ ++num_guests_created_;
+
+ if (created_message_loop_runner_.get())
+ created_message_loop_runner_->Quit();
+}
+
+void TestGuestViewManager::RemoveGuest(int guest_instance_id) {
+ GuestViewManager::RemoveGuest(guest_instance_id);
+}
+
+// Test factory for creating test instances of GuestViewManager.
+TestGuestViewManagerFactory::TestGuestViewManagerFactory()
+ : test_guest_view_manager_(NULL) {
+}
+
+TestGuestViewManagerFactory::~TestGuestViewManagerFactory() {
+}
+
+GuestViewManager* TestGuestViewManagerFactory::CreateGuestViewManager(
+ content::BrowserContext* context,
+ scoped_ptr<GuestViewManagerDelegate> delegate) {
+ if (!test_guest_view_manager_) {
+ test_guest_view_manager_ =
+ new TestGuestViewManager(context, delegate.Pass());
+ }
+ return test_guest_view_manager_;
+}
+
+} // namespace guest_view
diff --git a/components/guest_view/browser/test_guest_view_manager.h b/components/guest_view/browser/test_guest_view_manager.h
new file mode 100644
index 0000000..cbf942f
--- /dev/null
+++ b/components/guest_view/browser/test_guest_view_manager.h
@@ -0,0 +1,82 @@
+// Copyright 2014 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 COMPONENTS_GUEST_VIEW_BROWSER_TEST_GUEST_VIEW_MANAGER_H_
+#define COMPONENTS_GUEST_VIEW_BROWSER_TEST_GUEST_VIEW_MANAGER_H_
+
+#include "base/memory/linked_ptr.h"
+#include "components/guest_view/browser/guest_view_manager.h"
+#include "components/guest_view/browser/guest_view_manager_factory.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+
+namespace guest_view {
+
+class TestGuestViewManager : public GuestViewManager {
+ public:
+ TestGuestViewManager(
+ content::BrowserContext* context,
+ scoped_ptr<GuestViewManagerDelegate> delegate);
+ ~TestGuestViewManager() override;
+
+ void WaitForAllGuestsDeleted();
+ content::WebContents* WaitForSingleGuestCreated();
+
+ content::WebContents* GetLastGuestCreated();
+
+ // Returns the number of guests currently still alive at the time of calling
+ // this method.
+ size_t GetNumGuestsActive() const;
+
+ // Returns the size of the set of removed instance IDs.
+ size_t GetNumRemovedInstanceIDs() const;
+
+ // Returns the number of guests that have been created since the creation of
+ // this GuestViewManager.
+ int num_guests_created() const { return num_guests_created_; }
+
+ // Returns the last guest instance ID removed from the manager.
+ int last_instance_id_removed() const { return last_instance_id_removed_; }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(GuestViewManagerTest, AddRemove);
+
+ // GuestViewManager override:
+ void AddGuest(int guest_instance_id,
+ content::WebContents* guest_web_contents) override;
+ void RemoveGuest(int guest_instance_id) override;
+
+ void WaitForGuestCreated();
+
+ using GuestViewManager::last_instance_id_removed_;
+ using GuestViewManager::removed_instance_ids_;
+
+ int num_guests_created_;
+
+ std::vector<linked_ptr<content::WebContentsDestroyedWatcher>>
+ guest_web_contents_watchers_;
+ scoped_refptr<content::MessageLoopRunner> created_message_loop_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestGuestViewManager);
+};
+
+// Test factory for creating test instances of GuestViewManager.
+class TestGuestViewManagerFactory : public GuestViewManagerFactory {
+ public:
+ TestGuestViewManagerFactory();
+ ~TestGuestViewManagerFactory() override;
+
+ GuestViewManager* CreateGuestViewManager(
+ content::BrowserContext* context,
+ scoped_ptr<GuestViewManagerDelegate> delegate) override;
+
+ private:
+ TestGuestViewManager* test_guest_view_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestGuestViewManagerFactory);
+};
+
+} // namespace guest_view
+
+#endif // COMPONENTS_GUEST_VIEW_BROWSER_TEST_GUEST_VIEW_MANAGER_H_