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.
4
5 #include "chrome/browser/renderer_host/chrome_extension_message_filter.h"
6
7 #include "base/bind.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/chrome_notification_types.h"
13 #include "chrome/browser/extensions/activity_log/activity_action_constants.h"
14 #include "chrome/browser/extensions/activity_log/activity_actions.h"
15 #include "chrome/browser/extensions/activity_log/activity_log.h"
16 #include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
17 #include "chrome/browser/extensions/api/messaging/message_service.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/profiles/profile_manager.h"
20 #include "chrome/common/extensions/api/i18n/default_locale_handler.h"
21 #include "chrome/common/render_messages.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/common/constants.h"
26 #include "extensions/common/extension_messages.h"
27 #include "extensions/common/file_util.h"
28 #include "extensions/common/message_bundle.h"
29
30 using content::BrowserThread;
31 using extensions::APIPermission;
32
33 namespace {
34
35 const uint32 kFilteredMessageClasses[] = {
36 ChromeMsgStart,
37 ExtensionMsgStart,
38 };
39
40 // Logs an action to the extension activity log for the specified profile. Can
41 // be called from any thread.
AddActionToExtensionActivityLog(Profile * profile,scoped_refptr<extensions::Action> action)42 void AddActionToExtensionActivityLog(
43 Profile* profile,
44 scoped_refptr<extensions::Action> action) {
45 // The ActivityLog can only be accessed from the main (UI) thread. If we're
46 // running on the wrong thread, re-dispatch from the main thread.
47 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
48 BrowserThread::PostTask(
49 BrowserThread::UI, FROM_HERE,
50 base::Bind(&AddActionToExtensionActivityLog, profile, action));
51 } else {
52 if (!g_browser_process->profile_manager()->IsValidProfile(profile))
53 return;
54 // If the action included a URL, check whether it is for an incognito
55 // profile. The check is performed here so that it can safely be done from
56 // the UI thread.
57 if (action->page_url().is_valid() || !action->page_title().empty())
58 action->set_page_incognito(profile->IsOffTheRecord());
59 extensions::ActivityLog* activity_log =
60 extensions::ActivityLog::GetInstance(profile);
61 activity_log->LogAction(action);
62 }
63 }
64
65 } // namespace
66
ChromeExtensionMessageFilter(int render_process_id,Profile * profile)67 ChromeExtensionMessageFilter::ChromeExtensionMessageFilter(
68 int render_process_id,
69 Profile* profile)
70 : BrowserMessageFilter(kFilteredMessageClasses,
71 arraysize(kFilteredMessageClasses)),
72 render_process_id_(render_process_id),
73 profile_(profile),
74 extension_info_map_(
75 extensions::ExtensionSystem::Get(profile)->info_map()) {
76 DCHECK_CURRENTLY_ON(BrowserThread::UI);
77 notification_registrar_.Add(this,
78 chrome::NOTIFICATION_PROFILE_DESTROYED,
79 content::Source<Profile>(profile));
80 }
81
~ChromeExtensionMessageFilter()82 ChromeExtensionMessageFilter::~ChromeExtensionMessageFilter() {
83 DCHECK_CURRENTLY_ON(BrowserThread::UI);
84 }
85
OnMessageReceived(const IPC::Message & message)86 bool ChromeExtensionMessageFilter::OnMessageReceived(
87 const IPC::Message& message) {
88 bool handled = true;
89 IPC_BEGIN_MESSAGE_MAP(ChromeExtensionMessageFilter, message)
90 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CanTriggerClipboardRead,
91 OnCanTriggerClipboardRead)
92 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CanTriggerClipboardWrite,
93 OnCanTriggerClipboardWrite)
94 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension,
95 OnOpenChannelToExtension)
96 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToTab, OnOpenChannelToTab)
97 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToNativeApp,
98 OnOpenChannelToNativeApp)
99 IPC_MESSAGE_HANDLER_DELAY_REPLY(ExtensionHostMsg_GetMessageBundle,
100 OnGetExtMessageBundle)
101 IPC_MESSAGE_HANDLER(ExtensionHostMsg_CloseChannel, OnExtensionCloseChannel)
102 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddAPIActionToActivityLog,
103 OnAddAPIActionToExtensionActivityLog);
104 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddDOMActionToActivityLog,
105 OnAddDOMActionToExtensionActivityLog);
106 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddEventToActivityLog,
107 OnAddEventToExtensionActivityLog);
108 IPC_MESSAGE_UNHANDLED(handled = false)
109 IPC_END_MESSAGE_MAP()
110
111 return handled;
112 }
113
OverrideThreadForMessage(const IPC::Message & message,BrowserThread::ID * thread)114 void ChromeExtensionMessageFilter::OverrideThreadForMessage(
115 const IPC::Message& message, BrowserThread::ID* thread) {
116 switch (message.type()) {
117 case ExtensionHostMsg_CloseChannel::ID:
118 *thread = BrowserThread::UI;
119 break;
120 default:
121 break;
122 }
123 }
124
OnDestruct() const125 void ChromeExtensionMessageFilter::OnDestruct() const {
126 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
127 delete this;
128 } else {
129 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
130 }
131 }
132
OnCanTriggerClipboardRead(const GURL & origin,bool * allowed)133 void ChromeExtensionMessageFilter::OnCanTriggerClipboardRead(
134 const GURL& origin, bool* allowed) {
135 *allowed = extension_info_map_->SecurityOriginHasAPIPermission(
136 origin, render_process_id_, APIPermission::kClipboardRead);
137 }
138
OnCanTriggerClipboardWrite(const GURL & origin,bool * allowed)139 void ChromeExtensionMessageFilter::OnCanTriggerClipboardWrite(
140 const GURL& origin, bool* allowed) {
141 // Since all extensions could historically write to the clipboard, preserve it
142 // for compatibility.
143 *allowed = (origin.SchemeIs(extensions::kExtensionScheme) ||
144 extension_info_map_->SecurityOriginHasAPIPermission(
145 origin, render_process_id_, APIPermission::kClipboardWrite));
146 }
147
OnOpenChannelToExtension(int routing_id,const ExtensionMsg_ExternalConnectionInfo & info,const std::string & channel_name,bool include_tls_channel_id,int * port_id)148 void ChromeExtensionMessageFilter::OnOpenChannelToExtension(
149 int routing_id,
150 const ExtensionMsg_ExternalConnectionInfo& info,
151 const std::string& channel_name,
152 bool include_tls_channel_id,
153 int* port_id) {
154 int port2_id;
155 extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
156
157 BrowserThread::PostTask(
158 BrowserThread::UI, FROM_HERE,
159 base::Bind(
160 &ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread,
161 this, render_process_id_, routing_id, port2_id, info,
162 channel_name, include_tls_channel_id));
163 }
164
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)165 void ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread(
166 int source_process_id, int source_routing_id,
167 int receiver_port_id,
168 const ExtensionMsg_ExternalConnectionInfo& info,
169 const std::string& channel_name,
170 bool include_tls_channel_id) {
171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
172 if (profile_) {
173 extensions::MessageService::Get(profile_)
174 ->OpenChannelToExtension(source_process_id,
175 source_routing_id,
176 receiver_port_id,
177 info.source_id,
178 info.target_id,
179 info.source_url,
180 channel_name,
181 include_tls_channel_id);
182 }
183 }
184
OnOpenChannelToNativeApp(int routing_id,const std::string & source_extension_id,const std::string & native_app_name,int * port_id)185 void ChromeExtensionMessageFilter::OnOpenChannelToNativeApp(
186 int routing_id,
187 const std::string& source_extension_id,
188 const std::string& native_app_name,
189 int* port_id) {
190 int port2_id;
191 extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
192
193 BrowserThread::PostTask(
194 BrowserThread::UI, FROM_HERE,
195 base::Bind(
196 &ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread,
197 this, routing_id, port2_id, source_extension_id, native_app_name));
198 }
199
OpenChannelToNativeAppOnUIThread(int source_routing_id,int receiver_port_id,const std::string & source_extension_id,const std::string & native_app_name)200 void ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread(
201 int source_routing_id,
202 int receiver_port_id,
203 const std::string& source_extension_id,
204 const std::string& native_app_name) {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
206 if (profile_) {
207 extensions::MessageService::Get(profile_)
208 ->OpenChannelToNativeApp(render_process_id_,
209 source_routing_id,
210 receiver_port_id,
211 source_extension_id,
212 native_app_name);
213 }
214 }
215
OnOpenChannelToTab(int routing_id,int tab_id,const std::string & extension_id,const std::string & channel_name,int * port_id)216 void ChromeExtensionMessageFilter::OnOpenChannelToTab(
217 int routing_id, int tab_id, const std::string& extension_id,
218 const std::string& channel_name, int* port_id) {
219 int port2_id;
220 extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
221
222 BrowserThread::PostTask(
223 BrowserThread::UI, FROM_HERE,
224 base::Bind(&ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread,
225 this, render_process_id_, routing_id, port2_id, tab_id,
226 extension_id, channel_name));
227 }
228
OpenChannelToTabOnUIThread(int source_process_id,int source_routing_id,int receiver_port_id,int tab_id,const std::string & extension_id,const std::string & channel_name)229 void ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread(
230 int source_process_id, int source_routing_id,
231 int receiver_port_id,
232 int tab_id,
233 const std::string& extension_id,
234 const std::string& channel_name) {
235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
236 if (profile_) {
237 extensions::MessageService::Get(profile_)
238 ->OpenChannelToTab(source_process_id,
239 source_routing_id,
240 receiver_port_id,
241 tab_id,
242 extension_id,
243 channel_name);
244 }
245 }
246
OnGetExtMessageBundle(const std::string & extension_id,IPC::Message * reply_msg)247 void ChromeExtensionMessageFilter::OnGetExtMessageBundle(
248 const std::string& extension_id, IPC::Message* reply_msg) {
249 const extensions::Extension* extension =
250 extension_info_map_->extensions().GetByID(extension_id);
251 base::FilePath extension_path;
252 std::string default_locale;
253 if (extension) {
254 extension_path = extension->path();
255 default_locale = extensions::LocaleInfo::GetDefaultLocale(extension);
256 }
257
258 BrowserThread::PostBlockingPoolTask(
259 FROM_HERE,
260 base::Bind(
261 &ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool,
262 this, extension_path, extension_id, default_locale, reply_msg));
263 }
264
OnGetExtMessageBundleOnBlockingPool(const base::FilePath & extension_path,const std::string & extension_id,const std::string & default_locale,IPC::Message * reply_msg)265 void ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool(
266 const base::FilePath& extension_path,
267 const std::string& extension_id,
268 const std::string& default_locale,
269 IPC::Message* reply_msg) {
270 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
271
272 scoped_ptr<extensions::MessageBundle::SubstitutionMap> dictionary_map(
273 extensions::file_util::LoadMessageBundleSubstitutionMap(
274 extension_path, extension_id, default_locale));
275
276 ExtensionHostMsg_GetMessageBundle::WriteReplyParams(reply_msg,
277 *dictionary_map);
278 Send(reply_msg);
279 }
280
OnExtensionCloseChannel(int port_id,const std::string & error_message)281 void ChromeExtensionMessageFilter::OnExtensionCloseChannel(
282 int port_id,
283 const std::string& error_message) {
284 if (!content::RenderProcessHost::FromID(render_process_id_))
285 return; // To guard against crash in browser_tests shutdown.
286
287 extensions::MessageService* message_service =
288 extensions::MessageService::Get(profile_);
289 if (message_service)
290 message_service->CloseChannel(port_id, error_message);
291 }
292
OnAddAPIActionToExtensionActivityLog(const std::string & extension_id,const ExtensionHostMsg_APIActionOrEvent_Params & params)293 void ChromeExtensionMessageFilter::OnAddAPIActionToExtensionActivityLog(
294 const std::string& extension_id,
295 const ExtensionHostMsg_APIActionOrEvent_Params& params) {
296 scoped_refptr<extensions::Action> action = new extensions::Action(
297 extension_id, base::Time::Now(), extensions::Action::ACTION_API_CALL,
298 params.api_call);
299 action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
300 if (!params.extra.empty()) {
301 action->mutable_other()->SetString(
302 activity_log_constants::kActionExtra, params.extra);
303 }
304 AddActionToExtensionActivityLog(profile_, action);
305 }
306
OnAddDOMActionToExtensionActivityLog(const std::string & extension_id,const ExtensionHostMsg_DOMAction_Params & params)307 void ChromeExtensionMessageFilter::OnAddDOMActionToExtensionActivityLog(
308 const std::string& extension_id,
309 const ExtensionHostMsg_DOMAction_Params& params) {
310 scoped_refptr<extensions::Action> action = new extensions::Action(
311 extension_id, base::Time::Now(), extensions::Action::ACTION_DOM_ACCESS,
312 params.api_call);
313 action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
314 action->set_page_url(params.url);
315 action->set_page_title(base::UTF16ToUTF8(params.url_title));
316 action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
317 params.call_type);
318 AddActionToExtensionActivityLog(profile_, action);
319 }
320
OnAddEventToExtensionActivityLog(const std::string & extension_id,const ExtensionHostMsg_APIActionOrEvent_Params & params)321 void ChromeExtensionMessageFilter::OnAddEventToExtensionActivityLog(
322 const std::string& extension_id,
323 const ExtensionHostMsg_APIActionOrEvent_Params& params) {
324 scoped_refptr<extensions::Action> action = new extensions::Action(
325 extension_id, base::Time::Now(), extensions::Action::ACTION_API_EVENT,
326 params.api_call);
327 action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
328 if (!params.extra.empty()) {
329 action->mutable_other()->SetString(activity_log_constants::kActionExtra,
330 params.extra);
331 }
332 AddActionToExtensionActivityLog(profile_, action);
333 }
334
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)335 void ChromeExtensionMessageFilter::Observe(
336 int type,
337 const content::NotificationSource& source,
338 const content::NotificationDetails& details) {
339 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
340 profile_ = NULL;
341 }
342