chrome_extension_message_filter.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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/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"
27
28using content::BrowserThread;
29using extensions::APIPermission;
30
31namespace {
32
33const uint32 kFilteredMessageClasses[] = {
34  ChromeMsgStart,
35  ExtensionMsgStart,
36};
37
38// Logs an action to the extension activity log for the specified profile.  Can
39// be called from any thread.
40void AddActionToExtensionActivityLog(
41    Profile* profile,
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));
49  } else {
50    if (!g_browser_process->profile_manager()->IsValidProfile(profile))
51      return;
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
54    // the UI thread.
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);
60  }
61}
62
63}  // namespace
64
65ChromeExtensionMessageFilter::ChromeExtensionMessageFilter(
66    int render_process_id,
67    Profile* profile)
68    : BrowserMessageFilter(kFilteredMessageClasses,
69                           arraysize(kFilteredMessageClasses)),
70      render_process_id_(render_process_id),
71      profile_(profile),
72      extension_info_map_(
73          extensions::ExtensionSystem::Get(profile)->info_map()) {
74}
75
76ChromeExtensionMessageFilter::~ChromeExtensionMessageFilter() {
77}
78
79bool ChromeExtensionMessageFilter::OnMessageReceived(
80    const IPC::Message& message) {
81  bool handled = true;
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()
103
104  return handled;
105}
106
107void ChromeExtensionMessageFilter::OverrideThreadForMessage(
108    const IPC::Message& message, BrowserThread::ID* thread) {
109  switch (message.type()) {
110    case ExtensionHostMsg_CloseChannel::ID:
111      *thread = BrowserThread::UI;
112      break;
113    default:
114      break;
115  }
116}
117
118void ChromeExtensionMessageFilter::OnCanTriggerClipboardRead(
119    const GURL& origin, bool* allowed) {
120  *allowed = extension_info_map_->SecurityOriginHasAPIPermission(
121      origin, render_process_id_, APIPermission::kClipboardRead);
122}
123
124void 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));
131}
132
133void ChromeExtensionMessageFilter::OnOpenChannelToExtension(
134    int routing_id,
135    const ExtensionMsg_ExternalConnectionInfo& info,
136    const std::string& channel_name,
137    bool include_tls_channel_id,
138    int* port_id) {
139  int port2_id;
140  extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
141
142  BrowserThread::PostTask(
143      BrowserThread::UI, FROM_HERE,
144      base::Bind(
145          &ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread,
146          this, render_process_id_, routing_id, port2_id, info,
147          channel_name, include_tls_channel_id));
148}
149
150void 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);
161}
162
163void ChromeExtensionMessageFilter::OnOpenChannelToNativeApp(
164    int routing_id,
165    const std::string& source_extension_id,
166    const std::string& native_app_name,
167    int* port_id) {
168  int port2_id;
169  extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
170
171  BrowserThread::PostTask(
172      BrowserThread::UI, FROM_HERE,
173      base::Bind(
174          &ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread,
175          this, routing_id, port2_id, source_extension_id, native_app_name));
176}
177
178void 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);
187}
188
189void ChromeExtensionMessageFilter::OnOpenChannelToTab(
190    int routing_id, int tab_id, const std::string& extension_id,
191    const std::string& channel_name, int* port_id) {
192  int port2_id;
193  extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
194
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));
200}
201
202void ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread(
203    int source_process_id, int source_routing_id,
204    int receiver_port_id,
205    int tab_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);
212}
213
214void 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;
220  if (extension) {
221    extension_path = extension->path();
222    default_locale = extensions::LocaleInfo::GetDefaultLocale(extension);
223  }
224
225  BrowserThread::PostBlockingPoolTask(
226      FROM_HERE,
227      base::Bind(
228          &ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool,
229          this, extension_path, extension_id, default_locale, reply_msg));
230}
231
232void 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());
238
239  scoped_ptr<extensions::MessageBundle::SubstitutionMap> dictionary_map(
240      extensions::file_util::LoadMessageBundleSubstitutionMap(
241          extension_path, extension_id, default_locale));
242
243  ExtensionHostMsg_GetMessageBundle::WriteReplyParams(reply_msg,
244                                                      *dictionary_map);
245  Send(reply_msg);
246}
247
248void ChromeExtensionMessageFilter::OnExtensionCloseChannel(
249    int port_id,
250    const std::string& error_message) {
251  if (!content::RenderProcessHost::FromID(render_process_id_))
252    return;  // To guard against crash in browser_tests shutdown.
253
254  extensions::MessageService* message_service =
255      extensions::MessageService::Get(profile_);
256  if (message_service)
257    message_service->CloseChannel(port_id, error_message);
258}
259
260void 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,
265      params.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);
270  }
271  AddActionToExtensionActivityLog(profile_, action);
272}
273
274void 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,
279      params.api_call);
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,
284                                      params.call_type);
285  AddActionToExtensionActivityLog(profile_, action);
286}
287
288void 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,
293      params.api_call);
294  action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
295  if (!params.extra.empty()) {
296    action->mutable_other()->SetString(activity_log_constants::kActionExtra,
297                                       params.extra);
298  }
299  AddActionToExtensionActivityLog(profile_, action);
300}
301