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