chrome_render_message_filter.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright (c) 2012 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_render_message_filter.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/metrics/histogram.h"
12#include "base/strings/utf_string_conversions.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/chrome_notification_types.h"
15#include "chrome/browser/content_settings/cookie_settings.h"
16#include "chrome/browser/content_settings/tab_specific_content_settings.h"
17#include "chrome/browser/extensions/activity_log/activity_action_constants.h"
18#include "chrome/browser/extensions/activity_log/activity_actions.h"
19#include "chrome/browser/extensions/activity_log/activity_log.h"
20#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
21#include "chrome/browser/extensions/api/messaging/message_service.h"
22#include "chrome/browser/net/chrome_url_request_context.h"
23#include "chrome/browser/net/predictor.h"
24#include "chrome/browser/profiles/profile_manager.h"
25#include "chrome/browser/task_manager/task_manager.h"
26#include "chrome/common/extensions/api/i18n/default_locale_handler.h"
27#include "chrome/common/extensions/extension_file_util.h"
28#include "chrome/common/extensions/message_bundle.h"
29#include "chrome/common/render_messages.h"
30#include "content/public/browser/notification_service.h"
31#include "content/public/browser/render_process_host.h"
32#include "extensions/browser/extension_system.h"
33#include "extensions/common/constants.h"
34#include "extensions/common/extension_messages.h"
35
36#if defined(USE_TCMALLOC)
37#include "chrome/browser/browser_about_handler.h"
38#endif
39
40using content::BrowserThread;
41using extensions::APIPermission;
42using blink::WebCache;
43
44namespace {
45
46const uint32 kFilteredMessageClasses[] = {
47  ChromeMsgStart,
48  ExtensionMsgStart,
49};
50
51// Logs an action to the extension activity log for the specified profile.  Can
52// be called from any thread.
53void AddActionToExtensionActivityLog(
54    Profile* profile,
55    scoped_refptr<extensions::Action> action) {
56#if defined(ENABLE_EXTENSIONS)
57  // The ActivityLog can only be accessed from the main (UI) thread.  If we're
58  // running on the wrong thread, re-dispatch from the main thread.
59  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
60    BrowserThread::PostTask(
61        BrowserThread::UI, FROM_HERE,
62        base::Bind(&AddActionToExtensionActivityLog, profile, action));
63  } else {
64    if (!g_browser_process->profile_manager()->IsValidProfile(profile))
65      return;
66    // If the action included a URL, check whether it is for an incognito
67    // profile.  The check is performed here so that it can safely be done from
68    // the UI thread.
69    if (action->page_url().is_valid() || !action->page_title().empty())
70      action->set_page_incognito(profile->IsOffTheRecord());
71    extensions::ActivityLog* activity_log =
72        extensions::ActivityLog::GetInstance(profile);
73    activity_log->LogAction(action);
74  }
75#endif
76}
77
78} // namespace
79
80ChromeRenderMessageFilter::ChromeRenderMessageFilter(
81    int render_process_id,
82    Profile* profile,
83    net::URLRequestContextGetter* request_context)
84    : BrowserMessageFilter(
85          kFilteredMessageClasses, arraysize(kFilteredMessageClasses)),
86      render_process_id_(render_process_id),
87      profile_(profile),
88      off_the_record_(profile_->IsOffTheRecord()),
89      predictor_(profile_->GetNetworkPredictor()),
90      request_context_(request_context),
91      extension_info_map_(
92          extensions::ExtensionSystem::Get(profile)->info_map()),
93      cookie_settings_(CookieSettings::Factory::GetForProfile(profile)),
94      weak_ptr_factory_(this) {
95}
96
97ChromeRenderMessageFilter::~ChromeRenderMessageFilter() {
98}
99
100bool ChromeRenderMessageFilter::OnMessageReceived(const IPC::Message& message,
101                                                  bool* message_was_ok) {
102  bool handled = true;
103  IPC_BEGIN_MESSAGE_MAP_EX(ChromeRenderMessageFilter, message, *message_was_ok)
104    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DnsPrefetch, OnDnsPrefetch)
105    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_Preconnect, OnPreconnect)
106    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_ResourceTypeStats,
107                        OnResourceTypeStats)
108    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_UpdatedCacheStats,
109                        OnUpdatedCacheStats)
110    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FPS, OnFPS)
111    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_V8HeapStats, OnV8HeapStats)
112    IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension,
113                        OnOpenChannelToExtension)
114    IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToTab, OnOpenChannelToTab)
115    IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToNativeApp,
116                        OnOpenChannelToNativeApp)
117    IPC_MESSAGE_HANDLER_DELAY_REPLY(ExtensionHostMsg_GetMessageBundle,
118                                    OnGetExtensionMessageBundle)
119    IPC_MESSAGE_HANDLER(ExtensionHostMsg_CloseChannel, OnExtensionCloseChannel)
120    IPC_MESSAGE_HANDLER(ExtensionHostMsg_RequestForIOThread,
121                        OnExtensionRequestForIOThread)
122    IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddAPIActionToActivityLog,
123                        OnAddAPIActionToExtensionActivityLog);
124    IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddDOMActionToActivityLog,
125                        OnAddDOMActionToExtensionActivityLog);
126    IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddEventToActivityLog,
127                        OnAddEventToExtensionActivityLog);
128    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_AllowDatabase, OnAllowDatabase)
129    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_AllowDOMStorage, OnAllowDOMStorage)
130    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_AllowFileSystem, OnAllowFileSystem)
131    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_AllowIndexedDB, OnAllowIndexedDB)
132    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CanTriggerClipboardRead,
133                        OnCanTriggerClipboardRead)
134    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CanTriggerClipboardWrite,
135                        OnCanTriggerClipboardWrite)
136    IPC_MESSAGE_UNHANDLED(handled = false)
137  IPC_END_MESSAGE_MAP()
138
139  return handled;
140}
141
142void ChromeRenderMessageFilter::OverrideThreadForMessage(
143    const IPC::Message& message, BrowserThread::ID* thread) {
144  switch (message.type()) {
145    case ChromeViewHostMsg_ResourceTypeStats::ID:
146    case ExtensionHostMsg_CloseChannel::ID:
147    case ChromeViewHostMsg_UpdatedCacheStats::ID:
148      *thread = BrowserThread::UI;
149      break;
150    default:
151      break;
152  }
153}
154
155net::HostResolver* ChromeRenderMessageFilter::GetHostResolver() {
156  return request_context_->GetURLRequestContext()->host_resolver();
157}
158
159void ChromeRenderMessageFilter::OnDnsPrefetch(
160    const std::vector<std::string>& hostnames) {
161  if (predictor_)
162    predictor_->DnsPrefetchList(hostnames);
163}
164
165void ChromeRenderMessageFilter::OnPreconnect(const GURL& url) {
166  if (predictor_)
167    predictor_->PreconnectUrl(
168        url, GURL(), chrome_browser_net::UrlInfo::MOUSE_OVER_MOTIVATED, 1);
169}
170
171void ChromeRenderMessageFilter::OnResourceTypeStats(
172    const WebCache::ResourceTypeStats& stats) {
173  HISTOGRAM_COUNTS("WebCoreCache.ImagesSizeKB",
174                   static_cast<int>(stats.images.size / 1024));
175  HISTOGRAM_COUNTS("WebCoreCache.CSSStylesheetsSizeKB",
176                   static_cast<int>(stats.cssStyleSheets.size / 1024));
177  HISTOGRAM_COUNTS("WebCoreCache.ScriptsSizeKB",
178                   static_cast<int>(stats.scripts.size / 1024));
179  HISTOGRAM_COUNTS("WebCoreCache.XSLStylesheetsSizeKB",
180                   static_cast<int>(stats.xslStyleSheets.size / 1024));
181  HISTOGRAM_COUNTS("WebCoreCache.FontsSizeKB",
182                   static_cast<int>(stats.fonts.size / 1024));
183
184  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
185#if defined(ENABLE_TASK_MANAGER)
186  TaskManager::GetInstance()->model()->NotifyResourceTypeStats(peer_pid(),
187                                                               stats);
188#endif  // defined(ENABLE_TASK_MANAGER)
189}
190
191void ChromeRenderMessageFilter::OnUpdatedCacheStats(
192    const WebCache::UsageStats& stats) {
193  WebCacheManager::GetInstance()->ObserveStats(render_process_id_, stats);
194}
195
196void ChromeRenderMessageFilter::OnFPS(int routing_id, float fps) {
197  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
198    BrowserThread::PostTask(
199        BrowserThread::UI, FROM_HERE,
200        base::Bind(
201            &ChromeRenderMessageFilter::OnFPS, this,
202            routing_id, fps));
203    return;
204  }
205
206  base::ProcessId renderer_id = peer_pid();
207
208#if defined(ENABLE_TASK_MANAGER)
209  TaskManager::GetInstance()->model()->NotifyFPS(
210      renderer_id, routing_id, fps);
211#endif  // defined(ENABLE_TASK_MANAGER)
212
213  FPSDetails details(routing_id, fps);
214  content::NotificationService::current()->Notify(
215      chrome::NOTIFICATION_RENDERER_FPS_COMPUTED,
216      content::Source<const base::ProcessId>(&renderer_id),
217      content::Details<const FPSDetails>(&details));
218}
219
220void ChromeRenderMessageFilter::OnV8HeapStats(int v8_memory_allocated,
221                                              int v8_memory_used) {
222  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
223    BrowserThread::PostTask(
224        BrowserThread::UI, FROM_HERE,
225        base::Bind(&ChromeRenderMessageFilter::OnV8HeapStats, this,
226                   v8_memory_allocated, v8_memory_used));
227    return;
228  }
229
230  base::ProcessId renderer_id = peer_pid();
231
232#if defined(ENABLE_TASK_MANAGER)
233  TaskManager::GetInstance()->model()->NotifyV8HeapStats(
234      renderer_id,
235      static_cast<size_t>(v8_memory_allocated),
236      static_cast<size_t>(v8_memory_used));
237#endif  // defined(ENABLE_TASK_MANAGER)
238
239  V8HeapStatsDetails details(v8_memory_allocated, v8_memory_used);
240  content::NotificationService::current()->Notify(
241      chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED,
242      content::Source<const base::ProcessId>(&renderer_id),
243      content::Details<const V8HeapStatsDetails>(&details));
244}
245
246void ChromeRenderMessageFilter::OnOpenChannelToExtension(
247    int routing_id,
248    const ExtensionMsg_ExternalConnectionInfo& info,
249    const std::string& channel_name,
250    bool include_tls_channel_id,
251    int* port_id) {
252  int port2_id;
253  extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
254
255  BrowserThread::PostTask(
256      BrowserThread::UI, FROM_HERE,
257      base::Bind(&ChromeRenderMessageFilter::OpenChannelToExtensionOnUIThread,
258                 this, render_process_id_, routing_id, port2_id, info,
259                 channel_name, include_tls_channel_id));
260}
261
262void ChromeRenderMessageFilter::OpenChannelToExtensionOnUIThread(
263    int source_process_id, int source_routing_id,
264    int receiver_port_id,
265    const ExtensionMsg_ExternalConnectionInfo& info,
266    const std::string& channel_name,
267    bool include_tls_channel_id) {
268  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
269  extensions::MessageService::Get(profile_)->OpenChannelToExtension(
270      source_process_id, source_routing_id, receiver_port_id,
271      info.source_id, info.target_id, info.source_url, channel_name,
272      include_tls_channel_id);
273}
274
275void ChromeRenderMessageFilter::OnOpenChannelToNativeApp(
276    int routing_id,
277    const std::string& source_extension_id,
278    const std::string& native_app_name,
279    int* port_id) {
280  int port2_id;
281  extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
282
283  BrowserThread::PostTask(
284      BrowserThread::UI, FROM_HERE,
285      base::Bind(&ChromeRenderMessageFilter::OpenChannelToNativeAppOnUIThread,
286                 this, routing_id, port2_id, source_extension_id,
287                 native_app_name));
288}
289
290void ChromeRenderMessageFilter::OpenChannelToNativeAppOnUIThread(
291    int source_routing_id,
292    int receiver_port_id,
293    const std::string& source_extension_id,
294    const std::string& native_app_name) {
295  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
296  extensions::MessageService::Get(profile_)->OpenChannelToNativeApp(
297      render_process_id_, source_routing_id, receiver_port_id,
298      source_extension_id, native_app_name);
299}
300
301void ChromeRenderMessageFilter::OnOpenChannelToTab(
302    int routing_id, int tab_id, const std::string& extension_id,
303    const std::string& channel_name, int* port_id) {
304  int port2_id;
305  extensions::MessageService::AllocatePortIdPair(port_id, &port2_id);
306
307  BrowserThread::PostTask(
308      BrowserThread::UI, FROM_HERE,
309      base::Bind(&ChromeRenderMessageFilter::OpenChannelToTabOnUIThread, this,
310                 render_process_id_, routing_id, port2_id, tab_id, extension_id,
311                 channel_name));
312}
313
314void ChromeRenderMessageFilter::OpenChannelToTabOnUIThread(
315    int source_process_id, int source_routing_id,
316    int receiver_port_id,
317    int tab_id,
318    const std::string& extension_id,
319    const std::string& channel_name) {
320  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
321  extensions::MessageService::Get(profile_)->OpenChannelToTab(
322      source_process_id, source_routing_id, receiver_port_id,
323      tab_id, extension_id, channel_name);
324}
325
326void ChromeRenderMessageFilter::OnGetExtensionMessageBundle(
327    const std::string& extension_id, IPC::Message* reply_msg) {
328  const extensions::Extension* extension =
329      extension_info_map_->extensions().GetByID(extension_id);
330  base::FilePath extension_path;
331  std::string default_locale;
332  if (extension) {
333    extension_path = extension->path();
334    default_locale = extensions::LocaleInfo::GetDefaultLocale(extension);
335  }
336
337  BrowserThread::PostTask(
338      BrowserThread::FILE, FROM_HERE,
339      base::Bind(
340          &ChromeRenderMessageFilter::OnGetExtensionMessageBundleOnFileThread,
341          this, extension_path, extension_id, default_locale, reply_msg));
342}
343
344void ChromeRenderMessageFilter::OnGetExtensionMessageBundleOnFileThread(
345    const base::FilePath& extension_path,
346    const std::string& extension_id,
347    const std::string& default_locale,
348    IPC::Message* reply_msg) {
349  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
350
351  scoped_ptr<extensions::MessageBundle::SubstitutionMap> dictionary_map(
352      extension_file_util::LoadMessageBundleSubstitutionMap(
353          extension_path, extension_id, default_locale));
354
355  ExtensionHostMsg_GetMessageBundle::WriteReplyParams(reply_msg,
356                                                      *dictionary_map);
357  Send(reply_msg);
358}
359
360void ChromeRenderMessageFilter::OnExtensionCloseChannel(
361    int port_id,
362    const std::string& error_message) {
363  if (!content::RenderProcessHost::FromID(render_process_id_))
364    return;  // To guard against crash in browser_tests shutdown.
365
366  extensions::MessageService* message_service =
367      extensions::MessageService::Get(profile_);
368  if (message_service)
369    message_service->CloseChannel(port_id, error_message);
370}
371
372void ChromeRenderMessageFilter::OnExtensionRequestForIOThread(
373    int routing_id,
374    const ExtensionHostMsg_Request_Params& params) {
375  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
376
377  ExtensionFunctionDispatcher::DispatchOnIOThread(
378      extension_info_map_.get(), profile_, render_process_id_,
379      weak_ptr_factory_.GetWeakPtr(), routing_id, params);
380}
381
382void ChromeRenderMessageFilter::OnAddAPIActionToExtensionActivityLog(
383    const std::string& extension_id,
384    const ExtensionHostMsg_APIActionOrEvent_Params& params) {
385  scoped_refptr<extensions::Action> action = new extensions::Action(
386      extension_id, base::Time::Now(), extensions::Action::ACTION_API_CALL,
387      params.api_call);
388  action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
389  if (!params.extra.empty()) {
390    action->mutable_other()->SetString(
391        activity_log_constants::kActionExtra, params.extra);
392  }
393  AddActionToExtensionActivityLog(profile_, action);
394}
395
396void ChromeRenderMessageFilter::OnAddDOMActionToExtensionActivityLog(
397    const std::string& extension_id,
398    const ExtensionHostMsg_DOMAction_Params& params) {
399  scoped_refptr<extensions::Action> action = new extensions::Action(
400      extension_id, base::Time::Now(), extensions::Action::ACTION_DOM_ACCESS,
401      params.api_call);
402  action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
403  action->set_page_url(params.url);
404  action->set_page_title(base::UTF16ToUTF8(params.url_title));
405  action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb,
406                                      params.call_type);
407  AddActionToExtensionActivityLog(profile_, action);
408}
409
410void ChromeRenderMessageFilter::OnAddEventToExtensionActivityLog(
411    const std::string& extension_id,
412    const ExtensionHostMsg_APIActionOrEvent_Params& params) {
413  scoped_refptr<extensions::Action> action = new extensions::Action(
414      extension_id, base::Time::Now(), extensions::Action::ACTION_API_EVENT,
415      params.api_call);
416  action->set_args(make_scoped_ptr(params.arguments.DeepCopy()));
417  if (!params.extra.empty()) {
418    action->mutable_other()->SetString(activity_log_constants::kActionExtra,
419                                       params.extra);
420  }
421  AddActionToExtensionActivityLog(profile_, action);
422}
423
424void ChromeRenderMessageFilter::OnAllowDatabase(
425    int render_frame_id,
426    const GURL& origin_url,
427    const GURL& top_origin_url,
428    const base::string16& name,
429    const base::string16& display_name,
430    bool* allowed) {
431  *allowed =
432      cookie_settings_->IsSettingCookieAllowed(origin_url, top_origin_url);
433  BrowserThread::PostTask(
434      BrowserThread::UI, FROM_HERE,
435      base::Bind(&TabSpecificContentSettings::WebDatabaseAccessed,
436                 render_process_id_, render_frame_id, origin_url, name,
437                 display_name, !*allowed));
438}
439
440void ChromeRenderMessageFilter::OnAllowDOMStorage(int render_frame_id,
441                                                  const GURL& origin_url,
442                                                  const GURL& top_origin_url,
443                                                  bool local,
444                                                  bool* allowed) {
445  *allowed =
446      cookie_settings_->IsSettingCookieAllowed(origin_url, top_origin_url);
447  // Record access to DOM storage for potential display in UI.
448  BrowserThread::PostTask(
449      BrowserThread::UI, FROM_HERE,
450      base::Bind(&TabSpecificContentSettings::DOMStorageAccessed,
451                 render_process_id_, render_frame_id, origin_url, local,
452                 !*allowed));
453}
454
455void ChromeRenderMessageFilter::OnAllowFileSystem(int render_frame_id,
456                                                  const GURL& origin_url,
457                                                  const GURL& top_origin_url,
458                                                  bool* allowed) {
459  *allowed =
460      cookie_settings_->IsSettingCookieAllowed(origin_url, top_origin_url);
461  // Record access to file system for potential display in UI.
462  BrowserThread::PostTask(
463      BrowserThread::UI, FROM_HERE,
464      base::Bind(&TabSpecificContentSettings::FileSystemAccessed,
465                 render_process_id_, render_frame_id, origin_url, !*allowed));
466}
467
468void ChromeRenderMessageFilter::OnAllowIndexedDB(int render_frame_id,
469                                                 const GURL& origin_url,
470                                                 const GURL& top_origin_url,
471                                                 const base::string16& name,
472                                                 bool* allowed) {
473  *allowed =
474      cookie_settings_->IsSettingCookieAllowed(origin_url, top_origin_url);
475  BrowserThread::PostTask(
476      BrowserThread::UI, FROM_HERE,
477      base::Bind(&TabSpecificContentSettings::IndexedDBAccessed,
478                 render_process_id_, render_frame_id, origin_url, name,
479                 !*allowed));
480}
481
482void ChromeRenderMessageFilter::OnCanTriggerClipboardRead(
483    const GURL& origin, bool* allowed) {
484  *allowed = extension_info_map_->SecurityOriginHasAPIPermission(
485      origin, render_process_id_, APIPermission::kClipboardRead);
486}
487
488void ChromeRenderMessageFilter::OnCanTriggerClipboardWrite(
489    const GURL& origin, bool* allowed) {
490  // Since all extensions could historically write to the clipboard, preserve it
491  // for compatibility.
492  *allowed = (origin.SchemeIs(extensions::kExtensionScheme) ||
493      extension_info_map_->SecurityOriginHasAPIPermission(
494          origin, render_process_id_, APIPermission::kClipboardWrite));
495}
496