1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/renderer_host/chrome_extension_message_filter.h"
8 #include "base/bind_helpers.h"
9 #include "base/files/file_path.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
13 #include "chrome/browser/extensions/activity_log/activity_actions.h"
14 #include "chrome/browser/extensions/activity_log/activity_log.h"
15 #include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
16 #include "chrome/browser/extensions/api/messaging/message_service.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "chrome/common/extensions/api/i18n/default_locale_handler.h"
20 #include "chrome/common/render_messages.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "extensions/browser/extension_system.h"
23 #include "extensions/common/constants.h"
24 #include "extensions/common/extension_messages.h"
25 #include "extensions/common/file_util.h"
26 #include "extensions/common/message_bundle.h"
28 using content::BrowserThread;
29 using extensions::APIPermission;
33 const uint32 kFilteredMessageClasses[] = {
38 // Logs an action to the extension activity log for the specified profile. Can
39 // be called from any thread.
40 void AddActionToExtensionActivityLog(
42 scoped_refptr<extensions::Action> action) {
43 // The ActivityLog can only be accessed from the main (UI) thread. If we're
44 // running on the wrong thread, re-dispatch from the main thread.
45 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
46 BrowserThread::PostTask(
47 BrowserThread::UI, FROM_HERE,
48 base::Bind(&AddActionToExtensionActivityLog, profile, action));
50 if (!g_browser_process->profile_manager()->IsValidProfile(profile))
52 // If the action included a URL, check whether it is for an incognito
53 // profile. The check is performed here so that it can safely be done from
55 if (action->page_url().is_valid() || !action->page_title().empty())
56 action->set_page_incognito(profile->IsOffTheRecord());
57 extensions::ActivityLog* activity_log =
58 extensions::ActivityLog::GetInstance(profile);
59 activity_log->LogAction(action);
65 ChromeExtensionMessageFilter::ChromeExtensionMessageFilter(
66 int render_process_id,
68 : BrowserMessageFilter(kFilteredMessageClasses,
69 arraysize(kFilteredMessageClasses)),
70 render_process_id_(render_process_id),
73 extensions::ExtensionSystem::Get(profile)->info_map()) {
76 ChromeExtensionMessageFilter::~ChromeExtensionMessageFilter() {
79 bool ChromeExtensionMessageFilter::OnMessageReceived(
80 const IPC::Message& message) {
82 IPC_BEGIN_MESSAGE_MAP(ChromeExtensionMessageFilter, message)
83 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CanTriggerClipboardRead,
84 OnCanTriggerClipboardRead)
85 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CanTriggerClipboardWrite,
86 OnCanTriggerClipboardWrite)
87 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension,
88 OnOpenChannelToExtension)
89 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToTab, OnOpenChannelToTab)
90 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToNativeApp,
91 OnOpenChannelToNativeApp)
92 IPC_MESSAGE_HANDLER_DELAY_REPLY(ExtensionHostMsg_GetMessageBundle,
93 OnGetExtMessageBundle)
94 IPC_MESSAGE_HANDLER(ExtensionHostMsg_CloseChannel, OnExtensionCloseChannel)
95 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddAPIActionToActivityLog,
96 OnAddAPIActionToExtensionActivityLog);
97 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddDOMActionToActivityLog,
98 OnAddDOMActionToExtensionActivityLog);
99 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddEventToActivityLog,
100 OnAddEventToExtensionActivityLog);
101 IPC_MESSAGE_UNHANDLED(handled = false)
102 IPC_END_MESSAGE_MAP()
107 void ChromeExtensionMessageFilter::OverrideThreadForMessage(
108 const IPC::Message& message, BrowserThread::ID* thread) {
109 switch (message.type()) {
110 case ExtensionHostMsg_CloseChannel::ID:
111 *thread = BrowserThread::UI;
118 void ChromeExtensionMessageFilter::OnCanTriggerClipboardRead(
119 const GURL& origin, bool* allowed) {
120 *allowed = extension_info_map_->SecurityOriginHasAPIPermission(
121 origin, render_process_id_, APIPermission::kClipboardRead);
124 void ChromeExtensionMessageFilter::OnCanTriggerClipboardWrite(
125 const GURL& origin, bool* allowed) {
126 // Since all extensions could historically write to the clipboard, preserve it
127 // for compatibility.
128 *allowed = (origin.SchemeIs(extensions::kExtensionScheme) ||
129 extension_info_map_->SecurityOriginHasAPIPermission(
130 origin, render_process_id_, APIPermission::kClipboardWrite));
133 void ChromeExtensionMessageFilter::OnOpenChannelToExtension(
135 const ExtensionMsg_ExternalConnectionInfo& info,
136 const std::string& channel_name,
137 bool include_tls_channel_id,
140 extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
142 BrowserThread::PostTask(
143 BrowserThread::UI, FROM_HERE,
145 &ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread,
146 this, render_process_id_, routing_id, port2_id, info,
147 channel_name, include_tls_channel_id));
150 void ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread(
151 int source_process_id, int source_routing_id,
152 int receiver_port_id,
153 const ExtensionMsg_ExternalConnectionInfo& info,
154 const std::string& channel_name,
155 bool include_tls_channel_id) {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
157 extensions::MessageService::Get(profile_)->OpenChannelToExtension(
158 source_process_id, source_routing_id, receiver_port_id,
159 info.source_id, info.target_id, info.source_url, channel_name,
160 include_tls_channel_id);
163 void ChromeExtensionMessageFilter::OnOpenChannelToNativeApp(
165 const std::string& source_extension_id,
166 const std::string& native_app_name,
169 extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
171 BrowserThread::PostTask(
172 BrowserThread::UI, FROM_HERE,
174 &ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread,
175 this, routing_id, port2_id, source_extension_id, native_app_name));
178 void ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread(
179 int source_routing_id,
180 int receiver_port_id,
181 const std::string& source_extension_id,
182 const std::string& native_app_name) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
184 extensions::MessageService::Get(profile_)->OpenChannelToNativeApp(
185 render_process_id_, source_routing_id, receiver_port_id,
186 source_extension_id, native_app_name);
189 void ChromeExtensionMessageFilter::OnOpenChannelToTab(
190 int routing_id, int tab_id, const std::string& extension_id,
191 const std::string& channel_name, int* port_id) {
193 extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
195 BrowserThread::PostTask(
196 BrowserThread::UI, FROM_HERE,
197 base::Bind(&ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread,
198 this, render_process_id_, routing_id, port2_id, tab_id,
199 extension_id, channel_name));
202 void ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread(
203 int source_process_id, int source_routing_id,
204 int receiver_port_id,
206 const std::string& extension_id,
207 const std::string& channel_name) {
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
209 extensions::MessageService::Get(profile_)->OpenChannelToTab(
210 source_process_id, source_routing_id, receiver_port_id,
211 tab_id, extension_id, channel_name);
214 void ChromeExtensionMessageFilter::OnGetExtMessageBundle(
215 const std::string& extension_id, IPC::Message* reply_msg) {
216 const extensions::Extension* extension =
217 extension_info_map_->extensions().GetByID(extension_id);
218 base::FilePath extension_path;
219 std::string default_locale;
221 extension_path = extension->path();
222 default_locale = extensions::LocaleInfo::GetDefaultLocale(extension);
225 BrowserThread::PostBlockingPoolTask(
228 &ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool,
229 this, extension_path, extension_id, default_locale, reply_msg));
232 void ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool(
233 const base::FilePath& extension_path,
234 const std::string& extension_id,
235 const std::string& default_locale,
236 IPC::Message* reply_msg) {
237 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
239 scoped_ptr<extensions::MessageBundle::SubstitutionMap> dictionary_map(
240 extensions::file_util::LoadMessageBundleSubstitutionMap(
241 extension_path, extension_id, default_locale));
243 ExtensionHostMsg_GetMessageBundle::WriteReplyParams(reply_msg,
248 void ChromeExtensionMessageFilter::OnExtensionCloseChannel(
250 const std::string& error_message) {
251 if (!content::RenderProcessHost::FromID(render_process_id_))
252 return; // To guard against crash in browser_tests shutdown.
254 extensions::MessageService* message_service =
255 extensions::MessageService::Get(profile_);
257 message_service->CloseChannel(port_id, error_message);
260 void ChromeExtensionMessageFilter::OnAddAPIActionToExtensionActivityLog(
261 const std::string& extension_id,
262 const ExtensionHostMsg_APIActionOrEvent_Params& params) {
263 scoped_refptr<extensions::Action> action = new extensions::Action(
264 extension_id, base::Time::Now(), extensions::Action::ACTION_API_CALL,
266 action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
267 if (!params.extra.empty()) {
268 action->mutable_other()->SetString(
269 activity_log_constants::kActionExtra, params.extra);
271 AddActionToExtensionActivityLog(profile_, action);
274 void ChromeExtensionMessageFilter::OnAddDOMActionToExtensionActivityLog(
275 const std::string& extension_id,
276 const ExtensionHostMsg_DOMAction_Params& params) {
277 scoped_refptr<extensions::Action> action = new extensions::Action(
278 extension_id, base::Time::Now(), extensions::Action::ACTION_DOM_ACCESS,
280 action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
281 action->set_page_url(params.url);
282 action->set_page_title(base::UTF16ToUTF8(params.url_title));
283 action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
285 AddActionToExtensionActivityLog(profile_, action);
288 void ChromeExtensionMessageFilter::OnAddEventToExtensionActivityLog(
289 const std::string& extension_id,
290 const ExtensionHostMsg_APIActionOrEvent_Params& params) {
291 scoped_refptr<extensions::Action> action = new extensions::Action(
292 extension_id, base::Time::Now(), extensions::Action::ACTION_API_EVENT,
294 action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
295 if (!params.extra.empty()) {
296 action->mutable_other()->SetString(activity_log_constants::kActionExtra,
299 AddActionToExtensionActivityLog(profile_, action);