blob: cfaad3d8549d8ee5d58ad71d22f6520e2df5ee4b [file] [log] [blame]
// Copyright (c) 2011 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 "chrome/renderer/extensions/extension_dispatcher.h"
#include "base/command_line.h"
#include "chrome/common/child_process_logging.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/renderer/extension_groups.h"
#include "chrome/renderer/extensions/chrome_app_bindings.h"
#include "chrome/renderer/extensions/event_bindings.h"
#include "chrome/renderer/extensions/extension_process_bindings.h"
#include "chrome/renderer/extensions/js_only_v8_extensions.h"
#include "chrome/renderer/extensions/renderer_extension_bindings.h"
#include "chrome/renderer/render_thread.h"
#include "chrome/renderer/user_script_slave.h"
#include "v8/include/v8.h"
namespace {
static const double kInitialExtensionIdleHandlerDelayS = 5.0 /* seconds */;
static const int64 kMaxExtensionIdleHandlerDelayS = 5*60 /* seconds */;
static ExtensionDispatcher* g_extension_dispatcher;
}
ExtensionDispatcher* ExtensionDispatcher::Get() {
return g_extension_dispatcher;
}
ExtensionDispatcher::ExtensionDispatcher() {
DCHECK(!g_extension_dispatcher);
g_extension_dispatcher = this;
std::string type_str = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kProcessType);
is_extension_process_ = type_str == switches::kExtensionProcess ||
CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
if (is_extension_process_) {
RenderThread::current()->set_idle_notification_delay_in_s(
kInitialExtensionIdleHandlerDelayS);
}
user_script_slave_.reset(new UserScriptSlave(&extensions_));
}
ExtensionDispatcher::~ExtensionDispatcher() {
g_extension_dispatcher = NULL;
}
bool ExtensionDispatcher::OnControlMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ExtensionDispatcher, message)
IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnMessageInvoke)
IPC_MESSAGE_HANDLER(ExtensionMsg_SetFunctionNames, OnSetFunctionNames)
IPC_MESSAGE_HANDLER(ExtensionMsg_Loaded, OnLoaded)
IPC_MESSAGE_HANDLER(ExtensionMsg_Unloaded, OnUnloaded)
IPC_MESSAGE_HANDLER(ExtensionMsg_SetScriptingWhitelist,
OnSetScriptingWhitelist)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePageActions, OnPageActionsUpdated)
IPC_MESSAGE_HANDLER(ExtensionMsg_SetAPIPermissions, OnSetAPIPermissions)
IPC_MESSAGE_HANDLER(ExtensionMsg_SetHostPermissions, OnSetHostPermissions)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void ExtensionDispatcher::OnRenderProcessShutdown() {
delete this;
}
void ExtensionDispatcher::WebKitInitialized() {
// For extensions, we want to ensure we call the IdleHandler every so often,
// even if the extension keeps up activity.
if (is_extension_process_) {
forced_idle_timer_.Start(
base::TimeDelta::FromSeconds(kMaxExtensionIdleHandlerDelayS),
RenderThread::current(), &RenderThread::IdleHandler);
}
RegisterExtension(extensions_v8::ChromeAppExtension::Get(), false);
// Add v8 extensions related to chrome extensions.
RegisterExtension(ExtensionProcessBindings::Get(), true);
RegisterExtension(BaseJsV8Extension::Get(), true);
RegisterExtension(JsonSchemaJsV8Extension::Get(), true);
RegisterExtension(EventBindings::Get(), true);
RegisterExtension(RendererExtensionBindings::Get(), true);
RegisterExtension(ExtensionApiTestV8Extension::Get(), true);
}
bool ExtensionDispatcher::AllowScriptExtension(
const std::string& v8_extension_name,
const GURL& url,
int extension_group) {
// If the V8 extension is not restricted, allow it to run anywhere.
if (!restricted_v8_extensions_.count(v8_extension_name))
return true;
// Extension-only bindings should be restricted to content scripts and
// extension-blessed URLs.
if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS ||
extensions_.ExtensionBindingsAllowed(url)) {
return true;
}
return false;
}
void ExtensionDispatcher::IdleNotification() {
if (is_extension_process_) {
// Dampen the forced delay as well if the extension stays idle for long
// periods of time.
int64 forced_delay_s = std::max(static_cast<int64>(
RenderThread::current()->idle_notification_delay_in_s()),
kMaxExtensionIdleHandlerDelayS);
forced_idle_timer_.Stop();
forced_idle_timer_.Start(
base::TimeDelta::FromSeconds(forced_delay_s),
RenderThread::current(), &RenderThread::IdleHandler);
}
}
void ExtensionDispatcher::OnSetFunctionNames(
const std::vector<std::string>& names) {
ExtensionProcessBindings::SetFunctionNames(names);
}
void ExtensionDispatcher::OnMessageInvoke(const std::string& extension_id,
const std::string& function_name,
const ListValue& args,
const GURL& event_url) {
RendererExtensionBindings::Invoke(
extension_id, function_name, args, NULL, event_url);
// Reset the idle handler each time there's any activity like event or message
// dispatch, for which Invoke is the chokepoint.
if (is_extension_process_) {
RenderThread::current()->ScheduleIdleHandler(
kInitialExtensionIdleHandlerDelayS);
}
}
void ExtensionDispatcher::OnLoaded(const ExtensionMsg_Loaded_Params& params) {
scoped_refptr<const Extension> extension(params.ConvertToExtension());
if (!extension) {
// This can happen if extension parsing fails for any reason. One reason
// this can legitimately happen is if the
// --enable-experimental-extension-apis changes at runtime, which happens
// during browser tests. Existing renderers won't know about the change.
return;
}
extensions_.Insert(extension);
}
void ExtensionDispatcher::OnUnloaded(const std::string& id) {
extensions_.Remove(id);
}
void ExtensionDispatcher::OnSetScriptingWhitelist(
const Extension::ScriptingWhitelist& extension_ids) {
Extension::SetScriptingWhitelist(extension_ids);
}
void ExtensionDispatcher::OnPageActionsUpdated(
const std::string& extension_id,
const std::vector<std::string>& page_actions) {
ExtensionProcessBindings::SetPageActions(extension_id, page_actions);
}
void ExtensionDispatcher::OnSetAPIPermissions(
const std::string& extension_id,
const std::set<std::string>& permissions) {
ExtensionProcessBindings::SetAPIPermissions(extension_id, permissions);
// This is called when starting a new extension page, so start the idle
// handler ticking.
RenderThread::current()->ScheduleIdleHandler(
kInitialExtensionIdleHandlerDelayS);
UpdateActiveExtensions();
}
void ExtensionDispatcher::OnSetHostPermissions(
const GURL& extension_url, const std::vector<URLPattern>& permissions) {
ExtensionProcessBindings::SetHostPermissions(extension_url, permissions);
}
void ExtensionDispatcher::OnUpdateUserScripts(
base::SharedMemoryHandle scripts) {
DCHECK(base::SharedMemory::IsHandleValid(scripts)) << "Bad scripts handle";
user_script_slave_->UpdateScripts(scripts);
UpdateActiveExtensions();
}
void ExtensionDispatcher::UpdateActiveExtensions() {
// In single-process mode, the browser process reports the active extensions.
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess))
return;
std::set<std::string> active_extensions;
user_script_slave_->GetActiveExtensions(&active_extensions);
ExtensionProcessBindings::GetActiveExtensions(&active_extensions);
child_process_logging::SetActiveExtensions(active_extensions);
}
void ExtensionDispatcher::RegisterExtension(v8::Extension* extension,
bool restrict_to_extensions) {
if (restrict_to_extensions)
restricted_v8_extensions_.insert(extension->name());
RenderThread::current()->RegisterExtension(extension);
}