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