blob: 7f80a4700234dd8b47ab77e9154ce41936c08840 [file] [log] [blame]
// 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 <memory>
#include "components/guest_view/browser/bad_message.h"
#include "components/guest_view/browser/guest_view_base.h"
#include "components/guest_view/browser/guest_view_manager.h"
#include "components/guest_view/browser/guest_view_manager_delegate.h"
#include "components/guest_view/common/guest_view_messages.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(
const uint32_t* message_classes_to_filter,
size_t num_message_classes_to_filter,
int render_process_id,
BrowserContext* context)
: BrowserMessageFilter(message_classes_to_filter,
num_message_classes_to_filter),
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);
}
GuestViewManager* GuestViewMessageFilter::GetOrCreateGuestViewManager() {
auto* manager = GuestViewManager::FromBrowserContext(browser_context_);
if (!manager) {
manager = GuestViewManager::CreateWithDelegate(
browser_context_, std::make_unique<GuestViewManagerDelegate>());
}
return manager;
}
GuestViewManager* GuestViewMessageFilter::GetGuestViewManagerOrKill() {
auto* manager = GuestViewManager::FromBrowserContext(browser_context_);
if (!manager) {
bad_message::ReceivedBadMessage(
this, bad_message::GVMF_UNEXPECTED_MESSAGE_BEFORE_GVM_CREATION);
}
return manager;
}
void GuestViewMessageFilter::OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) {
if (IPC_MESSAGE_CLASS(message) == GuestViewMsgStart)
*thread = BrowserThread::UI;
}
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_HANDLER(GuestViewHostMsg_AttachToEmbedderFrame,
OnAttachToEmbedderFrame)
IPC_MESSAGE_HANDLER(GuestViewHostMsg_ViewCreated, OnViewCreated)
IPC_MESSAGE_HANDLER(GuestViewHostMsg_ViewGarbageCollected,
OnViewGarbageCollected)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void GuestViewMessageFilter::OnViewCreated(int view_instance_id,
const std::string& view_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
GetOrCreateGuestViewManager()->ViewCreated(render_process_id_,
view_instance_id, view_type);
}
void GuestViewMessageFilter::OnViewGarbageCollected(int view_instance_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
GetOrCreateGuestViewManager()->ViewGarbageCollected(render_process_id_,
view_instance_id);
}
void GuestViewMessageFilter::OnAttachGuest(
int element_instance_id,
int guest_instance_id,
const base::DictionaryValue& params) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// We should have a GuestViewManager at this point. If we don't then the
// embedder is misbehaving.
auto* manager = GetGuestViewManagerOrKill();
if (!manager)
return;
manager->AttachGuest(render_process_id_,
element_instance_id,
guest_instance_id,
params);
}
void GuestViewMessageFilter::AttachToEmbedderFrame(
int embedder_local_render_frame_id,
int element_instance_id,
int guest_instance_id,
const base::DictionaryValue& params,
bool is_full_page_plugin) {
// We should have a GuestViewManager at this point. If we don't then the
// embedder is misbehaving.
auto* manager = GetGuestViewManagerOrKill();
if (!manager)
return;
content::WebContents* guest_web_contents =
manager->GetGuestByInstanceIDSafely(guest_instance_id,
render_process_id_);
if (!guest_web_contents)
return;
auto* guest = GuestViewBase::FromWebContents(guest_web_contents);
content::WebContents* owner_web_contents = guest->owner_web_contents();
DCHECK(owner_web_contents);
auto* embedder_frame = RenderFrameHost::FromID(
render_process_id_, embedder_local_render_frame_id);
// If we're creating a new guest through window.open /
// RenderViewImpl::CreateView, the embedder may not be the same as
// the original creator/owner, so update embedder_web_contents here.
content::WebContents* embedder_web_contents =
WebContents::FromRenderFrameHost(embedder_frame);
// Update the guest manager about the attachment.
// This sets up the embedder and guest pairing information inside
// the manager.
manager->AttachGuest(render_process_id_, element_instance_id,
guest_instance_id, params);
base::OnceClosure perform_attach = base::BindOnce(
[](content::WebContents* guest_web_contents,
content::WebContents* embedder_web_contents,
content::RenderFrameHost* embedder_frame, int element_instance_id) {
// Attach this inner WebContents |guest_web_contents| to the outer
// WebContents |embedder_web_contents|. The outer WebContents's
// frame |embedder_frame| hosts the inner WebContents.
// NOTE: this must be called after WillAttach, because it could unblock
// pending requests which depend on the WebViewGuest being initialized.
guest_web_contents->AttachToOuterWebContentsFrame(embedder_web_contents,
embedder_frame);
// We don't ACK until after AttachToOuterWebContentsFrame, so that
// |embedder_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));
},
guest_web_contents, embedder_web_contents, embedder_frame,
element_instance_id);
guest->WillAttach(
embedder_web_contents, element_instance_id, is_full_page_plugin,
std::move(perform_attach),
base::Bind(&GuestViewBase::DidAttach,
guest->weak_ptr_factory_.GetWeakPtr(), MSG_ROUTING_NONE));
}
void GuestViewMessageFilter::OnAttachToEmbedderFrame(
int embedder_local_render_frame_id,
int element_instance_id,
int guest_instance_id,
const base::DictionaryValue& params) {
AttachToEmbedderFrame(embedder_local_render_frame_id, element_instance_id,
guest_instance_id, params,
false /* is_full_page_plugin */);
}
} // namespace guest_view