blob: d36dc13e3d14792ad1dcc494be964beb4cb2b25a [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
#include "content/browser/browser_plugin/browser_plugin_guest.h"
#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/browser_plugin/browser_plugin_constants.h"
#include "content/common/browser_plugin/browser_plugin_messages.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_plugin_guest_manager_delegate.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_client.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
#include "net/base/escape.h"
namespace content {
// static
BrowserPluginHostFactory* BrowserPluginGuestManager::factory_ = NULL;
BrowserPluginGuestManager::BrowserPluginGuestManager(BrowserContext* context)
: context_(context) {}
BrowserPluginGuestManagerDelegate*
BrowserPluginGuestManager::GetDelegate() const {
return context_->GetGuestManagerDelegate();
}
BrowserPluginGuestManager::~BrowserPluginGuestManager() {
}
// static.
BrowserPluginGuestManager* BrowserPluginGuestManager::FromBrowserContext(
BrowserContext* context) {
BrowserPluginGuestManager* guest_manager =
static_cast<BrowserPluginGuestManager*>(
context->GetUserData(
browser_plugin::kBrowserPluginGuestManagerKeyName));
if (!guest_manager) {
guest_manager = BrowserPluginGuestManager::Create(context);
context->SetUserData(browser_plugin::kBrowserPluginGuestManagerKeyName,
guest_manager);
}
return guest_manager;
}
// static
BrowserPluginGuestManager* BrowserPluginGuestManager::Create(
BrowserContext* context) {
if (factory_)
return factory_->CreateBrowserPluginGuestManager(context);
return new BrowserPluginGuestManager(context);
}
int BrowserPluginGuestManager::GetNextInstanceID() {
if (!GetDelegate())
return 0;
return GetDelegate()->GetNextInstanceID();
}
BrowserPluginGuest* BrowserPluginGuestManager::CreateGuest(
SiteInstance* embedder_site_instance,
int instance_id,
const BrowserPluginHostMsg_Attach_Params& params,
scoped_ptr<base::DictionaryValue> extra_params) {
RenderProcessHost* embedder_process_host =
embedder_site_instance->GetProcess();
// Validate that the partition id coming from the renderer is valid UTF-8,
// since we depend on this in other parts of the code, such as FilePath
// creation. If the validation fails, treat it as a bad message and kill the
// renderer process.
if (!base::IsStringUTF8(params.storage_partition_id)) {
content::RecordAction(
base::UserMetricsAction("BadMessageTerminate_BPGM"));
base::KillProcess(
embedder_process_host->GetHandle(),
content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
return NULL;
}
const GURL& embedder_site_url = embedder_site_instance->GetSiteURL();
const std::string& host = embedder_site_url.host();
std::string url_encoded_partition = net::EscapeQueryParamValue(
params.storage_partition_id, false);
// The SiteInstance of a given webview tag is based on the fact that it's
// a guest process in addition to which platform application the tag
// belongs to and what storage partition is in use, rather than the URL
// that the tag is being navigated to.
GURL guest_site(base::StringPrintf("%s://%s/%s?%s",
kGuestScheme,
host.c_str(),
params.persist_storage ? "persist" : "",
url_encoded_partition.c_str()));
// If we already have a webview tag in the same app using the same storage
// partition, we should use the same SiteInstance so the existing tag and
// the new tag can script each other.
SiteInstance* guest_site_instance = GetGuestSiteInstance(guest_site);
if (!guest_site_instance) {
// Create the SiteInstance in a new BrowsingInstance, which will ensure
// that webview tags are also not allowed to send messages across
// different partitions.
guest_site_instance = SiteInstance::CreateForURL(
embedder_site_instance->GetBrowserContext(), guest_site);
}
return WebContentsImpl::CreateGuest(
embedder_site_instance->GetBrowserContext(),
guest_site_instance,
instance_id,
extra_params.Pass());
}
static void BrowserPluginGuestByInstanceIDCallback(
const BrowserPluginGuestManager::GuestByInstanceIDCallback& callback,
WebContents* guest_web_contents) {
if (!guest_web_contents) {
callback.Run(NULL);
return;
}
callback.Run(static_cast<WebContentsImpl*>(guest_web_contents)->
GetBrowserPluginGuest());
}
void BrowserPluginGuestManager::MaybeGetGuestByInstanceIDOrKill(
int instance_id,
int embedder_render_process_id,
const GuestByInstanceIDCallback& callback) const {
if (!GetDelegate()) {
callback.Run(NULL);
return;
}
GetDelegate()->MaybeGetGuestByInstanceIDOrKill(
instance_id,
embedder_render_process_id,
base::Bind(&BrowserPluginGuestByInstanceIDCallback,
callback));
}
void BrowserPluginGuestManager::AddGuest(int instance_id,
WebContents* guest_web_contents) {
if (!GetDelegate())
return;
GetDelegate()->AddGuest(instance_id, guest_web_contents);
}
void BrowserPluginGuestManager::RemoveGuest(int instance_id) {
if (!GetDelegate())
return;
GetDelegate()->RemoveGuest(instance_id);
}
static void BrowserPluginGuestMessageCallback(const IPC::Message& message,
BrowserPluginGuest* guest) {
if (!guest)
return;
guest->OnMessageReceivedFromEmbedder(message);
}
void BrowserPluginGuestManager::OnMessageReceived(const IPC::Message& message,
int render_process_id) {
DCHECK(BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(message));
int instance_id = 0;
// All allowed messages must have instance_id as their first parameter.
PickleIterator iter(message);
bool success = iter.ReadInt(&instance_id);
DCHECK(success);
MaybeGetGuestByInstanceIDOrKill(instance_id,
render_process_id,
base::Bind(&BrowserPluginGuestMessageCallback,
message));
}
SiteInstance* BrowserPluginGuestManager::GetGuestSiteInstance(
const GURL& guest_site) {
if (!GetDelegate())
return NULL;
return GetDelegate()->GetGuestSiteInstance(guest_site);
}
static bool BrowserPluginGuestCallback(
const BrowserPluginGuestManager::GuestCallback& callback,
WebContents* guest_web_contents) {
return callback.Run(static_cast<WebContentsImpl*>(guest_web_contents)
->GetBrowserPluginGuest());
}
bool BrowserPluginGuestManager::ForEachGuest(
WebContents* embedder_web_contents, const GuestCallback& callback) {
if (!GetDelegate())
return false;
return GetDelegate()->ForEachGuest(embedder_web_contents,
base::Bind(&BrowserPluginGuestCallback,
callback));
}
} // namespace content