blob: e1b85d3a9faede7613e843a3fc2a1f9611253def [file] [log] [blame]
// 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 <memory>
#include <utility>
#include "base/lazy_instance.h"
#include "base/macros.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/zoom/page_zoom.h"
#include "components/zoom/zoom_controller.h"
#include "content/public/browser/file_select_listener.h"
#include "content/public/browser/guest_mode.h"
#include "content/public/browser/navigation_handle.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.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/site_instance.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/blink/public/platform/web_gesture_event.h"
using content::WebContents;
namespace guest_view {
namespace {
using WebContentsGuestViewMap = std::map<const WebContents*, GuestViewBase*>;
base::LazyInstance<WebContentsGuestViewMap>::Leaky g_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 or when its page scale factor changes so the guest can change
// itself accordingly.
class GuestViewBase::OwnerContentsObserver : public WebContentsObserver {
public:
OwnerContentsObserver(GuestViewBase* guest,
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 DidFinishNavigation(
content::NavigationHandle* navigation_handle) override {
// If the embedder navigates to a different page then destroy the guest.
if (!navigation_handle->IsInMainFrame() ||
!navigation_handle->HasCommitted() ||
navigation_handle->IsSameDocument()) {
return;
}
Destroy();
}
void RenderProcessGone(base::TerminationStatus status) override {
if (destroyed_)
return;
// If the embedder process is destroyed, then destroy the guest.
Destroy();
}
void DidToggleFullscreenModeForTab(bool entered_fullscreen,
bool will_cause_resize) override {
if (destroyed_)
return;
is_fullscreen_ = entered_fullscreen;
guest_->EmbedderFullscreenToggled(is_fullscreen_);
}
void MainFrameWasResized(bool width_changed) override {
if (destroyed_ || !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_);
}
}
void DidUpdateAudioMutingState(bool muted) override {
if (destroyed_)
return;
guest_->web_contents()->SetAudioMuted(muted);
}
void RenderFrameDeleted(content::RenderFrameHost* rfh) override {
guest_->OnRenderFrameHostDeleted(rfh->GetProcess()->GetID(),
rfh->GetRoutingID());
}
private:
bool is_fullscreen_;
bool destroyed_;
GuestViewBase* guest_;
void Destroy() {
if (destroyed_)
return;
destroyed_ = true;
bool also_delete = true;
WebContents* guest_web_contents = guest_->web_contents();
if (content::GuestMode::IsCrossProcessFrameGuest(guest_web_contents)) {
// The outer WebContents have ownership of attached OOPIF-based guests, so
// we are not responsible for their deletion.
if (guest_web_contents->GetOuterWebContents())
also_delete = false;
}
guest_->Destroy(also_delete);
}
DISALLOW_COPY_AND_ASSIGN(OwnerContentsObserver);
};
// This observer ensures that the GuestViewBase destroys itself if its opener
// WebContents goes away before the GuestViewBase is attached.
class GuestViewBase::OpenerLifetimeObserver : public WebContentsObserver {
public:
explicit 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(true);
}
private:
GuestViewBase* guest_;
DISALLOW_COPY_AND_ASSIGN(OpenerLifetimeObserver);
};
GuestViewBase::GuestViewBase(WebContents* owner_web_contents)
: owner_web_contents_(owner_web_contents),
browser_context_(owner_web_contents->GetBrowserContext()),
guest_instance_id_(GetGuestViewManager()->GetNextInstanceID()),
view_instance_id_(kInstanceIDNone),
element_instance_id_(kInstanceIDNone),
attach_in_progress_(false),
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) {
SetOwnerHost();
}
GuestViewBase::~GuestViewBase() {}
void GuestViewBase::Init(const base::DictionaryValue& create_params,
WebContentsCreatedCallback callback) {
if (initialized_)
return;
initialized_ = true;
if (!GetGuestViewManager()->IsGuestAvailableToContext(this)) {
// The derived class did not create a WebContents so this class serves no
// purpose. Let's self-destruct.
delete this;
std::move(callback).Run(nullptr);
return;
}
std::unique_ptr<base::DictionaryValue> params(create_params.DeepCopy());
CreateWebContents(create_params,
base::BindOnce(&GuestViewBase::CompleteInit,
weak_ptr_factory_.GetWeakPtr(),
std::move(params), std::move(callback)));
}
void GuestViewBase::InitWithWebContents(
const base::DictionaryValue& create_params,
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 DidFinishNavigation, but since ZoomController always resets to default
// zoom mode on this event, GuestViewBase would need to do so after
// ZoomController::DidFinishNavigation has completed.
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_ =
std::make_unique<OwnerContentsObserver>(this, owner_web_contents_);
WebContentsObserver::Observe(guest_web_contents);
guest_web_contents->SetDelegate(this);
g_webcontents_guestview_map.Get().insert(
std::make_pair(guest_web_contents, this));
GetGuestViewManager()->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_);
SetUpSizing(create_params);
// Observe guest zoom changes.
auto* zoom_controller = 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.
auto args = std::make_unique<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(
std::make_unique<GuestViewEvent>(kEventResize, std::move(args)));
}
gfx::Size GuestViewBase::GetDefaultSize() const {
if (!is_full_page_plugin())
return gfx::Size(kDefaultWidth, kDefaultHeight);
// Full page plugins default to the size of the owner's viewport.
return owner_web_contents()
->GetRenderWidgetHostView()
->GetVisibleViewportSize();
}
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::RenderWidgetHostView* rwhv =
web_contents()->GetRenderWidgetHostView();
if (enable_auto_size) {
// Autosize is being enabled.
if (rwhv)
rwhv->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();
}
bool changed_due_to_auto_resize = false;
if (auto_size_enabled_) {
// Autosize was previously enabled.
if (rwhv)
rwhv->DisableAutoResize(new_size);
changed_due_to_auto_resize = true;
} else {
// Autosize was already disabled.
guest_host_->SizeContents(new_size);
}
UpdateGuestSize(new_size, changed_due_to_auto_resize);
}
auto_size_enabled_ = enable_auto_size;
}
// static
void GuestViewBase::CleanUp(content::BrowserContext* browser_context,
int embedder_process_id,
int view_instance_id) {
// TODO(paulmeyer): Add in any general GuestView cleanup work here.
}
// static
GuestViewBase* GuestViewBase::FromWebContents(const WebContents* web_contents) {
WebContentsGuestViewMap* guest_map = g_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;
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::ZoomPropagatesFromEmbedderToGuest() const {
return true;
}
GuestViewManager* GuestViewBase::GetGuestViewManager() {
return GuestViewManager::FromBrowserContext(browser_context());
}
WebContents* GuestViewBase::CreateNewGuestWindow(
const WebContents::CreateParams& create_params) {
return GetGuestViewManager()->CreateGuestWithWebContentsParams(
GetViewType(), owner_web_contents(), create_params);
}
void GuestViewBase::OnRenderFrameHostDeleted(int process_id, int routing_id) {}
void GuestViewBase::DidAttach(int guest_proxy_routing_id) {
DCHECK(attach_in_progress_);
// Clear this flag here, as functions called below may check attached().
attach_in_progress_ = false;
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());
// The guest should have the same muting state as the owner.
web_contents()->SetAudioMuted(owner_web_contents()->IsAudioMuted());
// Give the derived class an opportunity to perform some actions.
DidAttachToEmbedder();
// Inform the associated GuestViewContainer that the contentWindow is ready.
GetOwnerRenderWidgetHost()->Send(new GuestViewMsg_GuestAttached(
element_instance_id_, guest_proxy_routing_id));
SendQueuedEvents();
}
// TODO(wjmaclean): Delete this when browser plugin goes away;
// https://crbug.com/533069 .
void GuestViewBase::DidDetach() {
GuestViewManager::FromBrowserContext(browser_context_)->DetachGuest(this);
StopTrackingEmbedderZoomLevel();
owner_web_contents()->GetRenderViewHost()->Send(
new GuestViewMsg_GuestDetached(element_instance_id_));
element_instance_id_ = kInstanceIDNone;
if (ShouldDestroyOnDetach())
Destroy(true);
}
WebContents* GuestViewBase::GetOwnerWebContents() const {
return owner_web_contents_;
}
const GURL& GuestViewBase::GetOwnerSiteURL() const {
return owner_web_contents()->GetLastCommittedURL();
}
bool GuestViewBase::ShouldDestroyOnDetach() const {
return false;
}
void GuestViewBase::Destroy(bool also_delete) {
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;
g_webcontents_guestview_map.Get().erase(web_contents());
GetGuestViewManager()->RemoveGuest(guest_instance_id_);
pending_events_.clear();
if (also_delete)
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_ =
std::make_unique<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(WebContents* embedder_web_contents,
int element_instance_id,
bool is_full_page_plugin,
base::OnceClosure completion_callback) {
WillAttach(embedder_web_contents, nullptr, element_instance_id,
is_full_page_plugin, std::move(completion_callback));
}
void GuestViewBase::WillAttach(WebContents* embedder_web_contents,
content::RenderFrameHost* outer_contents_frame,
int element_instance_id,
bool is_full_page_plugin,
base::OnceClosure completion_callback) {
// Stop tracking the old embedder's zoom level.
if (owner_web_contents())
StopTrackingEmbedderZoomLevel();
if (owner_web_contents_ != embedder_web_contents) {
DCHECK_EQ(owner_contents_observer_->web_contents(), owner_web_contents_);
owner_web_contents_ = embedder_web_contents;
owner_contents_observer_ =
std::make_unique<OwnerContentsObserver>(this, embedder_web_contents);
SetOwnerHost();
}
// Start tracking the new embedder's zoom level.
StartTrackingEmbedderZoomLevel();
attach_in_progress_ = true;
element_instance_id_ = element_instance_id;
is_full_page_plugin_ = is_full_page_plugin;
WillAttachToEmbedder();
if (content::GuestMode::IsCrossProcessFrameGuest(web_contents())) {
web_contents()->AttachToOuterWebContentsFrame(
base::WrapUnique<WebContents>(web_contents()), outer_contents_frame);
// TODO(ekaramad): MimeHandlerViewGuest might not need this ACK
// (https://crbug.com/659750).
// We don't ACK until after AttachToOuterWebContentsFrame, so that
// |outer_contents_frame| gets swapped before the AttachIframeGuest callback
// is run. We also need to send the ACK before queued events are sent in
// DidAttach.
embedder_web_contents->GetMainFrame()->Send(
new GuestViewMsg_AttachToEmbedderFrame_ACK(element_instance_id));
}
// Completing attachment will resume suspended resource loads and then send
// queued events.
SignalWhenReady(std::move(completion_callback));
}
void GuestViewBase::SignalWhenReady(base::OnceClosure callback) {
// The default behavior is to call the |callback| immediately. Derived classes
// can implement an alternative signal for readiness.
std::move(callback).Run();
}
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();
GuestViewDidStopLoading();
}
void GuestViewBase::RenderViewReady() {
GuestReady();
}
void GuestViewBase::WebContentsDestroyed() {
Destroy(false);
// 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::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted())
return;
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::ContentsMouseEvent(WebContents* source,
bool motion,
bool exited) {
if (!attached() || !embedder_web_contents()->GetDelegate())
return;
embedder_web_contents()->GetDelegate()->ContentsMouseEvent(
embedder_web_contents(), motion, exited);
}
void GuestViewBase::ContentsZoomChange(bool zoom_in) {
if (!attached() || !embedder_web_contents()->GetDelegate())
return;
embedder_web_contents()->GetDelegate()->ContentsZoomChange(zoom_in);
}
bool GuestViewBase::HandleKeyboardEvent(
WebContents* source,
const content::NativeWebKeyboardEvent& event) {
if (!attached())
return false;
// Send the keyboard events back to the embedder to reprocess them.
return embedder_web_contents()->GetDelegate()->HandleKeyboardEvent(
embedder_web_contents(), event);
}
void GuestViewBase::LoadingStateChanged(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<blink::mojom::ColorSuggestionPtr>& suggestions) {
if (!attached() || !embedder_web_contents()->GetDelegate())
return nullptr;
return embedder_web_contents()->GetDelegate()->OpenColorChooser(
web_contents, color, suggestions);
}
void GuestViewBase::ResizeDueToAutoResize(WebContents* web_contents,
const gfx::Size& new_size) {
UpdateGuestSize(new_size, auto_size_enabled_);
}
void GuestViewBase::RunFileChooser(
content::RenderFrameHost* render_frame_host,
std::unique_ptr<content::FileSelectListener> listener,
const blink::mojom::FileChooserParams& params) {
if (!attached() || !embedder_web_contents()->GetDelegate()) {
listener->FileSelectionCanceled();
return;
}
embedder_web_contents()->GetDelegate()->RunFileChooser(
render_frame_host, std::move(listener), params);
}
bool GuestViewBase::ShouldFocusPageAfterCrash() {
// Focus is managed elsewhere.
return false;
}
bool GuestViewBase::PreHandleGestureEvent(WebContents* source,
const blink::WebGestureEvent& event) {
// Pinch events which cause a scale change should not be routed to a guest.
// We still allow synthetic wheel events for touchpad pinch to go to the page.
DCHECK(!blink::WebInputEvent::IsPinchGestureEventType(event.GetType()) ||
(event.SourceDevice() ==
blink::WebGestureDevice::kWebGestureDeviceTouchpad &&
event.NeedsWheelEvent()));
return false;
}
void GuestViewBase::UpdatePreferredSize(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(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;
}
content::RenderWidgetHost* GuestViewBase::GetOwnerRenderWidgetHost() {
// We assume guests live inside an owner RenderFrame but the RenderFrame may
// not be cross-process. In case a type of guest should be allowed to be
// embedded in a cross-process frame, this method should be overrode for that
// specific guest type. For all other guests, the owner RenderWidgetHost is
// that of the owner WebContents.
auto* owner = GetOwnerWebContents();
if (owner && owner->GetRenderWidgetHostView())
return owner->GetRenderWidgetHostView()->GetRenderWidgetHost();
return nullptr;
}
content::SiteInstance* GuestViewBase::GetOwnerSiteInstance() {
// We assume guests live inside an owner RenderFrame but the RenderFrame may
// not be cross-process. In case a type of guest should be allowed to be
// embedded in a cross-process frame, this method should be overrode for that
// specific guest type. For all other guests, the owner site instance can be
// from the owner WebContents.
if (auto* owner_contents = GetOwnerWebContents())
return owner_contents->GetSiteInstance();
return nullptr;
}
void GuestViewBase::AttachToOuterWebContentsFrame(
content::RenderFrameHost* embedder_frame,
int32_t element_instance_id,
bool is_full_page_plugin) {
auto completion_callback =
base::BindOnce(&GuestViewBase::DidAttach, weak_ptr_factory_.GetWeakPtr(),
MSG_ROUTING_NONE);
WillAttach(WebContents::FromRenderFrameHost(embedder_frame), embedder_frame,
element_instance_id, is_full_page_plugin,
std::move(completion_callback));
}
void GuestViewBase::OnZoomChanged(
const zoom::ZoomController::ZoomChangedEventData& data) {
if (data.web_contents == embedder_web_contents()) {
// The embedder's zoom level has changed.
auto* guest_zoom_controller =
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);
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(
std::unique_ptr<GuestViewEvent> event) {
event->Dispatch(this, guest_instance_id_);
}
void GuestViewBase::DispatchEventToView(std::unique_ptr<GuestViewEvent> event) {
if ((attached() && pending_events_.empty()) ||
(can_owner_receive_events() &&
!content::GuestMode::IsCrossProcessFrameGuest(web_contents()))) {
event->Dispatch(this, view_instance_id_);
return;
}
pending_events_.push_back(std::move(event));
}
void GuestViewBase::SendQueuedEvents() {
if (!attached())
return;
while (!pending_events_.empty()) {
std::unique_ptr<GuestViewEvent> event_ptr =
std::move(pending_events_.front());
pending_events_.pop_front();
event_ptr->Dispatch(this, view_instance_id_);
}
}
void GuestViewBase::CompleteInit(
std::unique_ptr<base::DictionaryValue> create_params,
WebContentsCreatedCallback callback,
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;
std::move(callback).Run(nullptr);
return;
}
InitWithWebContents(*create_params, guest_web_contents);
std::move(callback).Run(guest_web_contents);
}
double GuestViewBase::GetEmbedderZoomFactor() const {
if (!embedder_web_contents())
return 1.0;
return content::ZoomLevelToZoomFactor(
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 =
zoom::ZoomController::FromWebContents(owner_web_contents());
if (!embedder_zoom_controller)
return;
zoom::ZoomController::FromWebContents(web_contents())
->SetZoomLevel(embedder_zoom_controller->GetZoomLevel());
}
void GuestViewBase::StartTrackingEmbedderZoomLevel() {
if (!ZoomPropagatesFromEmbedderToGuest())
return;
auto* embedder_zoom_controller =
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() {
// TODO(wjmaclean): Remove the observer any time the GuestWebView transitions
// from propagating to not-propagating the zoom from the embedder.
auto* embedder_zoom_controller =
zoom::ZoomController::FromWebContents(owner_web_contents());
// Chrome Apps do not have a ZoomController.
if (!embedder_zoom_controller)
return;
// It is safe to remove an observer that was never registed.
embedder_zoom_controller->RemoveObserver(this);
}
void GuestViewBase::UpdateGuestSize(const gfx::Size& new_size,
bool due_to_auto_resize) {
if (due_to_auto_resize)
GuestSizeChangedDueToAutoSize(guest_size_, new_size);
DispatchOnResizeEvent(guest_size_, new_size);
guest_size_ = new_size;
}
void GuestViewBase::SetOwnerHost() {
auto* manager = GuestViewManager::FromBrowserContext(browser_context_);
owner_host_ = manager->IsOwnedByExtension(this)
? owner_web_contents()->GetLastCommittedURL().host()
: std::string();
}
} // namespace guest_view