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