blob: a34a8ac157eb10b35ce6136272eb67af6f47071c [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 "chrome/browser/renderer_host/chrome_extension_message_filter.h"
#include <stdint.h>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/activity_log/activity_action_constants.h"
#include "chrome/browser/extensions/activity_log/activity_actions.h"
#include "chrome/browser/extensions/activity_log/activity_log.h"
#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
#include "chrome/browser/extensions/api/messaging/message_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/extensions/chrome_extension_messages.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/api/messaging/message.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/file_util.h"
#include "extensions/common/manifest_handlers/default_locale_handler.h"
#include "extensions/common/message_bundle.h"
using content::BrowserThread;
namespace {
const uint32_t kFilteredMessageClasses[] = {
ChromeExtensionMsgStart, ExtensionMsgStart,
};
// Logs an action to the extension activity log for the specified profile.
void AddActionToExtensionActivityLog(Profile* profile,
extensions::ActivityLog* activity_log,
scoped_refptr<extensions::Action> action) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// If the action included a URL, check whether it is for an incognito
// profile. The check is performed here so that it can safely be done from
// the UI thread.
if (action->page_url().is_valid() || !action->page_title().empty())
action->set_page_incognito(profile->IsOffTheRecord());
activity_log->LogAction(action);
}
} // namespace
ChromeExtensionMessageFilter::ChromeExtensionMessageFilter(
int render_process_id,
Profile* profile)
: BrowserMessageFilter(kFilteredMessageClasses,
arraysize(kFilteredMessageClasses)),
render_process_id_(render_process_id),
profile_(profile),
activity_log_(extensions::ActivityLog::GetInstance(profile)),
extension_info_map_(
extensions::ExtensionSystem::Get(profile)->info_map()) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
notification_registrar_.Add(this,
chrome::NOTIFICATION_PROFILE_DESTROYED,
content::Source<Profile>(profile));
}
ChromeExtensionMessageFilter::~ChromeExtensionMessageFilter() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
bool ChromeExtensionMessageFilter::OnMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ChromeExtensionMessageFilter, message)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension,
OnOpenChannelToExtension)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToTab, OnOpenChannelToTab)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToNativeApp,
OnOpenChannelToNativeApp)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenMessagePort, OnOpenMessagePort)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_CloseMessagePort, OnCloseMessagePort)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_PostMessage, OnPostMessage)
IPC_MESSAGE_HANDLER_DELAY_REPLY(ExtensionHostMsg_GetMessageBundle,
OnGetExtMessageBundle)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddAPIActionToActivityLog,
OnAddAPIActionToExtensionActivityLog);
IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddDOMActionToActivityLog,
OnAddDOMActionToExtensionActivityLog);
IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddEventToActivityLog,
OnAddEventToExtensionActivityLog);
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void ChromeExtensionMessageFilter::OverrideThreadForMessage(
const IPC::Message& message, BrowserThread::ID* thread) {
switch (message.type()) {
case ExtensionHostMsg_OpenMessagePort::ID:
case ExtensionHostMsg_CloseMessagePort::ID:
case ExtensionHostMsg_PostMessage::ID:
case ExtensionHostMsg_AddAPIActionToActivityLog::ID:
case ExtensionHostMsg_AddDOMActionToActivityLog::ID:
case ExtensionHostMsg_AddEventToActivityLog::ID:
*thread = BrowserThread::UI;
break;
default:
break;
}
}
void ChromeExtensionMessageFilter::OnDestruct() const {
if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
delete this;
} else {
BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
}
}
void ChromeExtensionMessageFilter::OnOpenChannelToExtension(
int routing_id,
const ExtensionMsg_ExternalConnectionInfo& info,
const std::string& channel_name,
bool include_tls_channel_id,
int* port_id) {
int port2_id;
extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(
&ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread,
this, render_process_id_, routing_id, port2_id, info,
channel_name, include_tls_channel_id));
}
void ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread(
int source_process_id, int source_routing_id,
int receiver_port_id,
const ExtensionMsg_ExternalConnectionInfo& info,
const std::string& channel_name,
bool include_tls_channel_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (profile_) {
extensions::MessageService::Get(profile_)
->OpenChannelToExtension(source_process_id,
source_routing_id,
receiver_port_id,
info.source_id,
info.target_id,
info.source_url,
channel_name,
include_tls_channel_id);
}
}
void ChromeExtensionMessageFilter::OnOpenChannelToNativeApp(
int routing_id,
const std::string& native_app_name,
int* port_id) {
int port2_id;
extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(
&ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread,
this, routing_id, port2_id, native_app_name));
}
void ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread(
int source_routing_id,
int receiver_port_id,
const std::string& native_app_name) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (profile_) {
extensions::MessageService::Get(profile_)
->OpenChannelToNativeApp(render_process_id_,
source_routing_id,
receiver_port_id,
native_app_name);
}
}
void ChromeExtensionMessageFilter::OnOpenChannelToTab(
int routing_id,
const ExtensionMsg_TabTargetConnectionInfo& info,
const std::string& extension_id,
const std::string& channel_name,
int* port_id) {
int port2_id;
extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread,
this, render_process_id_, routing_id, port2_id, info,
extension_id, channel_name));
}
void ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread(
int source_process_id,
int source_routing_id,
int receiver_port_id,
const ExtensionMsg_TabTargetConnectionInfo& info,
const std::string& extension_id,
const std::string& channel_name) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (profile_) {
extensions::MessageService::Get(profile_)
->OpenChannelToTab(source_process_id,
source_routing_id,
receiver_port_id,
info.tab_id,
info.frame_id,
extension_id,
channel_name);
}
}
void ChromeExtensionMessageFilter::OnOpenMessagePort(int routing_id,
int port_id) {
if (!profile_)
return;
extensions::MessageService::Get(profile_)->OpenPort(
port_id, render_process_id_, routing_id);
}
void ChromeExtensionMessageFilter::OnCloseMessagePort(int routing_id,
int port_id,
bool force_close) {
if (!profile_)
return;
extensions::MessageService::Get(profile_)->ClosePort(
port_id, render_process_id_, routing_id, force_close);
}
void ChromeExtensionMessageFilter::OnPostMessage(
int port_id,
const extensions::Message& message) {
if (!profile_)
return;
extensions::MessageService::Get(profile_)->PostMessage(port_id, message);
}
void ChromeExtensionMessageFilter::OnGetExtMessageBundle(
const std::string& extension_id, IPC::Message* reply_msg) {
BrowserThread::PostBlockingPoolTask(
FROM_HERE,
base::Bind(
&ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool,
this, extension_id, reply_msg));
}
void ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool(
const std::string& extension_id,
IPC::Message* reply_msg) {
DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
const extensions::ExtensionSet& extension_set =
extension_info_map_->extensions();
std::unique_ptr<extensions::MessageBundle::SubstitutionMap> dictionary_map(
extensions::file_util::LoadMessageBundleSubstitutionMapWithImports(
extension_id, extension_set));
ExtensionHostMsg_GetMessageBundle::WriteReplyParams(reply_msg,
*dictionary_map);
Send(reply_msg);
}
void ChromeExtensionMessageFilter::OnAddAPIActionToExtensionActivityLog(
const std::string& extension_id,
const ExtensionHostMsg_APIActionOrEvent_Params& params) {
if (!ShouldLogExtensionAction(extension_id))
return;
scoped_refptr<extensions::Action> action = new extensions::Action(
extension_id, base::Time::Now(), extensions::Action::ACTION_API_CALL,
params.api_call);
action->set_args(base::WrapUnique(params.arguments.DeepCopy()));
if (!params.extra.empty()) {
action->mutable_other()->SetString(
activity_log_constants::kActionExtra, params.extra);
}
AddActionToExtensionActivityLog(profile_, activity_log_, action);
}
void ChromeExtensionMessageFilter::OnAddDOMActionToExtensionActivityLog(
const std::string& extension_id,
const ExtensionHostMsg_DOMAction_Params& params) {
if (!ShouldLogExtensionAction(extension_id))
return;
scoped_refptr<extensions::Action> action = new extensions::Action(
extension_id, base::Time::Now(), extensions::Action::ACTION_DOM_ACCESS,
params.api_call);
action->set_args(base::WrapUnique(params.arguments.DeepCopy()));
action->set_page_url(params.url);
action->set_page_title(base::UTF16ToUTF8(params.url_title));
action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
params.call_type);
AddActionToExtensionActivityLog(profile_, activity_log_, action);
}
void ChromeExtensionMessageFilter::OnAddEventToExtensionActivityLog(
const std::string& extension_id,
const ExtensionHostMsg_APIActionOrEvent_Params& params) {
if (!ShouldLogExtensionAction(extension_id))
return;
scoped_refptr<extensions::Action> action = new extensions::Action(
extension_id, base::Time::Now(), extensions::Action::ACTION_API_EVENT,
params.api_call);
action->set_args(base::WrapUnique(params.arguments.DeepCopy()));
if (!params.extra.empty()) {
action->mutable_other()->SetString(activity_log_constants::kActionExtra,
params.extra);
}
AddActionToExtensionActivityLog(profile_, activity_log_, action);
}
void ChromeExtensionMessageFilter::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
profile_ = NULL;
activity_log_ = nullptr;
}
bool ChromeExtensionMessageFilter::ShouldLogExtensionAction(
const std::string& extension_id) const {
// We only send these IPCs if activity logging is enabled, but due to race
// conditions (e.g. logging gets disabled but the renderer sends the message
// before it gets updated), we still need this check here.
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return profile_ &&
g_browser_process->profile_manager()->IsValidProfile(profile_) &&
activity_log_ && activity_log_->ShouldLog(extension_id);
}