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/renderer/chrome_render_process_observer.h"
6
7#include <limits>
8#include <vector>
9
10#include "base/allocator/allocator_extension.h"
11#include "base/bind.h"
12#include "base/command_line.h"
13#include "base/files/file_util.h"
14#include "base/memory/weak_ptr.h"
15#include "base/message_loop/message_loop.h"
16#include "base/metrics/field_trial.h"
17#include "base/metrics/histogram.h"
18#include "base/metrics/statistics_recorder.h"
19#include "base/native_library.h"
20#include "base/path_service.h"
21#include "base/strings/utf_string_conversions.h"
22#include "base/threading/platform_thread.h"
23#include "chrome/common/child_process_logging.h"
24#include "chrome/common/chrome_paths.h"
25#include "chrome/common/chrome_switches.h"
26#include "chrome/common/net/net_resource_provider.h"
27#include "chrome/common/render_messages.h"
28#include "chrome/common/url_constants.h"
29#include "chrome/common/variations/variations_util.h"
30#include "chrome/renderer/content_settings_observer.h"
31#include "chrome/renderer/security_filter_peer.h"
32#include "content/public/child/resource_dispatcher_delegate.h"
33#include "content/public/renderer/render_thread.h"
34#include "content/public/renderer/render_view.h"
35#include "content/public/renderer/render_view_visitor.h"
36#include "crypto/nss_util.h"
37#include "net/base/net_errors.h"
38#include "net/base/net_module.h"
39#include "third_party/WebKit/public/web/WebCache.h"
40#include "third_party/WebKit/public/web/WebDocument.h"
41#include "third_party/WebKit/public/web/WebFrame.h"
42#include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
43#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
44#include "third_party/WebKit/public/web/WebView.h"
45
46#if defined(OS_WIN)
47#include "base/win/iat_patch_function.h"
48#endif
49
50#if defined(ENABLE_EXTENSIONS)
51#include "chrome/renderer/extensions/extension_localization_peer.h"
52#endif
53
54using blink::WebCache;
55using blink::WebRuntimeFeatures;
56using blink::WebSecurityPolicy;
57using blink::WebString;
58using content::RenderThread;
59
60namespace {
61
62const int kCacheStatsDelayMS = 2000;
63
64class RendererResourceDelegate : public content::ResourceDispatcherDelegate {
65 public:
66  RendererResourceDelegate()
67      : weak_factory_(this) {
68  }
69
70  virtual content::RequestPeer* OnRequestComplete(
71      content::RequestPeer* current_peer,
72      content::ResourceType resource_type,
73      int error_code) OVERRIDE {
74    // Update the browser about our cache.
75    // Rate limit informing the host of our cache stats.
76    if (!weak_factory_.HasWeakPtrs()) {
77      base::MessageLoop::current()->PostDelayedTask(
78          FROM_HERE,
79          base::Bind(&RendererResourceDelegate::InformHostOfCacheStats,
80                     weak_factory_.GetWeakPtr()),
81          base::TimeDelta::FromMilliseconds(kCacheStatsDelayMS));
82    }
83
84    if (error_code == net::ERR_ABORTED) {
85      return NULL;
86    }
87
88    // Resource canceled with a specific error are filtered.
89    return SecurityFilterPeer::CreateSecurityFilterPeerForDeniedRequest(
90        resource_type, current_peer, error_code);
91  }
92
93  virtual content::RequestPeer* OnReceivedResponse(
94      content::RequestPeer* current_peer,
95      const std::string& mime_type,
96      const GURL& url) OVERRIDE {
97#if defined(ENABLE_EXTENSIONS)
98    return ExtensionLocalizationPeer::CreateExtensionLocalizationPeer(
99        current_peer, RenderThread::Get(), mime_type, url);
100#else
101    return NULL;
102#endif
103  }
104
105 private:
106  void InformHostOfCacheStats() {
107    WebCache::UsageStats stats;
108    WebCache::getUsageStats(&stats);
109    RenderThread::Get()->Send(new ChromeViewHostMsg_UpdatedCacheStats(stats));
110  }
111
112  base::WeakPtrFactory<RendererResourceDelegate> weak_factory_;
113
114  DISALLOW_COPY_AND_ASSIGN(RendererResourceDelegate);
115};
116
117#if defined(OS_WIN)
118static base::win::IATPatchFunction g_iat_patch_createdca;
119HDC WINAPI CreateDCAPatch(LPCSTR driver_name,
120                          LPCSTR device_name,
121                          LPCSTR output,
122                          const void* init_data) {
123  DCHECK(std::string("DISPLAY") == std::string(driver_name));
124  DCHECK(!device_name);
125  DCHECK(!output);
126  DCHECK(!init_data);
127
128  // CreateDC fails behind the sandbox, but not CreateCompatibleDC.
129  return CreateCompatibleDC(NULL);
130}
131
132static base::win::IATPatchFunction g_iat_patch_get_font_data;
133DWORD WINAPI GetFontDataPatch(HDC hdc,
134                              DWORD table,
135                              DWORD offset,
136                              LPVOID buffer,
137                              DWORD length) {
138  int rv = GetFontData(hdc, table, offset, buffer, length);
139  if (rv == GDI_ERROR && hdc) {
140    HFONT font = static_cast<HFONT>(GetCurrentObject(hdc, OBJ_FONT));
141
142    LOGFONT logfont;
143    if (GetObject(font, sizeof(LOGFONT), &logfont)) {
144      std::vector<char> font_data;
145      RenderThread::Get()->PreCacheFont(logfont);
146      rv = GetFontData(hdc, table, offset, buffer, length);
147      RenderThread::Get()->ReleaseCachedFonts();
148    }
149  }
150  return rv;
151}
152#endif  // OS_WIN
153
154static const int kWaitForWorkersStatsTimeoutMS = 20;
155
156class HeapStatisticsCollector {
157 public:
158  HeapStatisticsCollector() : round_id_(0) {}
159
160  void InitiateCollection();
161  static HeapStatisticsCollector* Instance();
162
163 private:
164  void CollectOnWorkerThread(scoped_refptr<base::TaskRunner> master,
165                             int round_id);
166  void ReceiveStats(int round_id, size_t total_size, size_t used_size);
167  void SendStatsToBrowser(int round_id);
168
169  size_t total_bytes_;
170  size_t used_bytes_;
171  int workers_to_go_;
172  int round_id_;
173};
174
175HeapStatisticsCollector* HeapStatisticsCollector::Instance() {
176  CR_DEFINE_STATIC_LOCAL(HeapStatisticsCollector, instance, ());
177  return &instance;
178}
179
180void HeapStatisticsCollector::InitiateCollection() {
181  v8::HeapStatistics heap_stats;
182  v8::Isolate::GetCurrent()->GetHeapStatistics(&heap_stats);
183  total_bytes_ = heap_stats.total_heap_size();
184  used_bytes_ = heap_stats.used_heap_size();
185  base::Closure collect = base::Bind(
186      &HeapStatisticsCollector::CollectOnWorkerThread,
187      base::Unretained(this),
188      base::MessageLoopProxy::current(),
189      round_id_);
190  workers_to_go_ = RenderThread::Get()->PostTaskToAllWebWorkers(collect);
191  if (workers_to_go_) {
192    // The guard task to send out partial stats
193    // in case some workers are not responsive.
194    base::MessageLoopProxy::current()->PostDelayedTask(
195        FROM_HERE,
196        base::Bind(&HeapStatisticsCollector::SendStatsToBrowser,
197                   base::Unretained(this),
198                   round_id_),
199        base::TimeDelta::FromMilliseconds(kWaitForWorkersStatsTimeoutMS));
200  } else {
201    // No worker threads so just send out the main thread data right away.
202    SendStatsToBrowser(round_id_);
203  }
204}
205
206void HeapStatisticsCollector::CollectOnWorkerThread(
207    scoped_refptr<base::TaskRunner> master,
208    int round_id) {
209
210  size_t total_bytes = 0;
211  size_t used_bytes = 0;
212  v8::Isolate* isolate = v8::Isolate::GetCurrent();
213  if (isolate) {
214    v8::HeapStatistics heap_stats;
215    isolate->GetHeapStatistics(&heap_stats);
216    total_bytes = heap_stats.total_heap_size();
217    used_bytes = heap_stats.used_heap_size();
218  }
219  master->PostTask(
220      FROM_HERE,
221      base::Bind(&HeapStatisticsCollector::ReceiveStats,
222                 base::Unretained(this),
223                 round_id,
224                 total_bytes,
225                 used_bytes));
226}
227
228void HeapStatisticsCollector::ReceiveStats(int round_id,
229                                           size_t total_bytes,
230                                           size_t used_bytes) {
231  if (round_id != round_id_)
232    return;
233  total_bytes_ += total_bytes;
234  used_bytes_ += used_bytes;
235  if (!--workers_to_go_)
236    SendStatsToBrowser(round_id);
237}
238
239void HeapStatisticsCollector::SendStatsToBrowser(int round_id) {
240  if (round_id != round_id_)
241    return;
242  // TODO(alph): Do caching heap stats and use the cache if we haven't got
243  //             reply from a worker.
244  //             Currently a busy worker stats are not counted.
245  RenderThread::Get()->Send(new ChromeViewHostMsg_V8HeapStats(
246      total_bytes_, used_bytes_));
247  ++round_id_;
248}
249
250}  // namespace
251
252bool ChromeRenderProcessObserver::is_incognito_process_ = false;
253
254ChromeRenderProcessObserver::ChromeRenderProcessObserver(
255    ChromeContentRendererClient* client)
256    : client_(client),
257      webkit_initialized_(false) {
258  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
259
260#if defined(ENABLE_AUTOFILL_DIALOG)
261  WebRuntimeFeatures::enableRequestAutocomplete(true);
262#endif
263
264  if (command_line.HasSwitch(switches::kEnableShowModalDialog))
265    WebRuntimeFeatures::enableShowModalDialog(true);
266
267  if (command_line.HasSwitch(switches::kJavaScriptHarmony)) {
268    std::string flag("--harmony");
269    v8::V8::SetFlagsFromString(flag.c_str(), static_cast<int>(flag.size()));
270  }
271
272  RenderThread* thread = RenderThread::Get();
273  resource_delegate_.reset(new RendererResourceDelegate());
274  thread->SetResourceDispatcherDelegate(resource_delegate_.get());
275
276  // Configure modules that need access to resources.
277  net::NetModule::SetResourceProvider(chrome_common_net::NetResourceProvider);
278
279#if defined(OS_WIN)
280  // Need to patch a few functions for font loading to work correctly.
281  base::FilePath pdf;
282  if (PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf) &&
283      base::PathExists(pdf)) {
284    g_iat_patch_createdca.Patch(
285        pdf.value().c_str(), "gdi32.dll", "CreateDCA", CreateDCAPatch);
286    g_iat_patch_get_font_data.Patch(
287        pdf.value().c_str(), "gdi32.dll", "GetFontData", GetFontDataPatch);
288  }
289#endif
290
291#if defined(OS_POSIX) && !defined(OS_MACOSX) && defined(USE_NSS)
292  // On platforms where we use system NSS shared libraries,
293  // initialize NSS now because it won't be able to load the .so's
294  // after we engage the sandbox.
295  if (!command_line.HasSwitch(switches::kSingleProcess))
296    crypto::InitNSSSafely();
297#elif defined(OS_WIN)
298  // crypt32.dll is used to decode X509 certificates for Chromoting.
299  // Only load this library when the feature is enabled.
300  base::LoadNativeLibrary(base::FilePath(L"crypt32.dll"), NULL);
301#endif
302  // Setup initial set of crash dump data for Field Trials in this renderer.
303  chrome_variations::SetChildProcessLoggingVariationList();
304}
305
306ChromeRenderProcessObserver::~ChromeRenderProcessObserver() {
307}
308
309bool ChromeRenderProcessObserver::OnControlMessageReceived(
310    const IPC::Message& message) {
311  bool handled = true;
312  IPC_BEGIN_MESSAGE_MAP(ChromeRenderProcessObserver, message)
313    IPC_MESSAGE_HANDLER(ChromeViewMsg_SetIsIncognitoProcess,
314                        OnSetIsIncognitoProcess)
315    IPC_MESSAGE_HANDLER(ChromeViewMsg_SetFieldTrialGroup, OnSetFieldTrialGroup)
316    IPC_MESSAGE_HANDLER(ChromeViewMsg_GetV8HeapStats, OnGetV8HeapStats)
317    IPC_MESSAGE_HANDLER(ChromeViewMsg_GetCacheResourceStats,
318                        OnGetCacheResourceStats)
319    IPC_MESSAGE_HANDLER(ChromeViewMsg_SetContentSettingRules,
320                        OnSetContentSettingRules)
321    IPC_MESSAGE_UNHANDLED(handled = false)
322  IPC_END_MESSAGE_MAP()
323  return handled;
324}
325
326void ChromeRenderProcessObserver::WebKitInitialized() {
327  webkit_initialized_ = true;
328  // chrome-native: is a scheme used for placeholder navigations that allow
329  // UIs to be drawn with platform native widgets instead of HTML.  These pages
330  // should not be accessible, and should also be treated as empty documents
331  // that can commit synchronously.  No code should be runnable in these pages,
332  // so it should not need to access anything nor should it allow javascript
333  // URLs since it should never be visible to the user.
334  WebString native_scheme(base::ASCIIToUTF16(chrome::kChromeNativeScheme));
335  WebSecurityPolicy::registerURLSchemeAsDisplayIsolated(native_scheme);
336  WebSecurityPolicy::registerURLSchemeAsEmptyDocument(native_scheme);
337  WebSecurityPolicy::registerURLSchemeAsNoAccess(native_scheme);
338  WebSecurityPolicy::registerURLSchemeAsNotAllowingJavascriptURLs(
339      native_scheme);
340}
341
342void ChromeRenderProcessObserver::OnRenderProcessShutdown() {
343  webkit_initialized_ = false;
344}
345
346void ChromeRenderProcessObserver::OnSetIsIncognitoProcess(
347    bool is_incognito_process) {
348  is_incognito_process_ = is_incognito_process;
349}
350
351void ChromeRenderProcessObserver::OnSetContentSettingRules(
352    const RendererContentSettingRules& rules) {
353  content_setting_rules_ = rules;
354}
355
356void ChromeRenderProcessObserver::OnGetCacheResourceStats() {
357  WebCache::ResourceTypeStats stats;
358  if (webkit_initialized_)
359    WebCache::getResourceTypeStats(&stats);
360  RenderThread::Get()->Send(new ChromeViewHostMsg_ResourceTypeStats(stats));
361}
362
363void ChromeRenderProcessObserver::OnSetFieldTrialGroup(
364    const std::string& field_trial_name,
365    const std::string& group_name) {
366  base::FieldTrial* trial =
367      base::FieldTrialList::CreateFieldTrial(field_trial_name, group_name);
368  // TODO(mef): Remove this check after the investigation of 359406 is complete.
369  CHECK(trial) << field_trial_name << ":" << group_name;
370  // Ensure the trial is marked as "used" by calling group() on it. This is
371  // needed to ensure the trial is properly reported in renderer crash reports.
372  trial->group();
373  chrome_variations::SetChildProcessLoggingVariationList();
374}
375
376void ChromeRenderProcessObserver::OnGetV8HeapStats() {
377  HeapStatisticsCollector::Instance()->InitiateCollection();
378}
379
380const RendererContentSettingRules*
381ChromeRenderProcessObserver::content_setting_rules() const {
382  return &content_setting_rules_;
383}
384