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.
5 #include "chrome/renderer/chrome_render_process_observer.h"
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/metrics/variations/variations_util.h"
27 #include "chrome/common/net/net_resource_provider.h"
28 #include "chrome/common/render_messages.h"
29 #include "chrome/common/url_constants.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"
48 #include "base/win/iat_patch_function.h"
51 using blink::WebCache
;
52 using blink::WebRuntimeFeatures
;
53 using blink::WebSecurityPolicy
;
54 using blink::WebString
;
55 using content::RenderThread
;
59 const int kCacheStatsDelayMS
= 2000;
60 const size_t kUnitializedCacheCapacity
= UINT_MAX
;
62 class RendererResourceDelegate
: public content::ResourceDispatcherDelegate
{
64 RendererResourceDelegate()
65 : weak_factory_(this) {
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(
77 base::Bind(&RendererResourceDelegate::InformHostOfCacheStats
,
78 weak_factory_
.GetWeakPtr()),
79 base::TimeDelta::FromMilliseconds(kCacheStatsDelayMS
));
82 if (error_code
== net::ERR_ABORTED
) {
86 // Resource canceled with a specific error are filtered.
87 return SecurityFilterPeer::CreateSecurityFilterPeerForDeniedRequest(
88 resource_type
, current_peer
, error_code
);
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
);
100 void InformHostOfCacheStats() {
101 WebCache::UsageStats stats
;
102 WebCache::getUsageStats(&stats
);
103 RenderThread::Get()->Send(new ChromeViewHostMsg_UpdatedCacheStats(stats
));
106 base::WeakPtrFactory
<RendererResourceDelegate
> weak_factory_
;
108 DISALLOW_COPY_AND_ASSIGN(RendererResourceDelegate
);
112 static base::win::IATPatchFunction g_iat_patch_createdca
;
113 HDC WINAPI
CreateDCAPatch(LPCSTR driver_name
,
116 const void* init_data
) {
117 DCHECK(std::string("DISPLAY") == std::string(driver_name
));
118 DCHECK(!device_name
);
122 // CreateDC fails behind the sandbox, but not CreateCompatibleDC.
123 return CreateCompatibleDC(NULL
);
126 static base::win::IATPatchFunction g_iat_patch_get_font_data
;
127 DWORD WINAPI
GetFontDataPatch(HDC hdc
,
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
));
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();
148 static const int kWaitForWorkersStatsTimeoutMS
= 20;
150 class HeapStatisticsCollector
{
152 HeapStatisticsCollector() : round_id_(0) {}
154 void InitiateCollection();
155 static HeapStatisticsCollector
* Instance();
158 void CollectOnWorkerThread(scoped_refptr
<base::TaskRunner
> master
,
160 void ReceiveStats(int round_id
, size_t total_size
, size_t used_size
);
161 void SendStatsToBrowser(int round_id
);
169 HeapStatisticsCollector
* HeapStatisticsCollector::Instance() {
170 CR_DEFINE_STATIC_LOCAL(HeapStatisticsCollector
, instance
, ());
174 void 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(),
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(
190 base::Bind(&HeapStatisticsCollector::SendStatsToBrowser
,
191 base::Unretained(this),
193 base::TimeDelta::FromMilliseconds(kWaitForWorkersStatsTimeoutMS
));
195 // No worker threads so just send out the main thread data right away.
196 SendStatsToBrowser(round_id_
);
200 void HeapStatisticsCollector::CollectOnWorkerThread(
201 scoped_refptr
<base::TaskRunner
> master
,
204 size_t total_bytes
= 0;
205 size_t used_bytes
= 0;
206 v8::Isolate
* isolate
= v8::Isolate::GetCurrent();
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();
215 base::Bind(&HeapStatisticsCollector::ReceiveStats
,
216 base::Unretained(this),
222 void HeapStatisticsCollector::ReceiveStats(int round_id
,
225 if (round_id
!= round_id_
)
227 total_bytes_
+= total_bytes
;
228 used_bytes_
+= used_bytes
;
229 if (!--workers_to_go_
)
230 SendStatsToBrowser(round_id
);
233 void HeapStatisticsCollector::SendStatsToBrowser(int round_id
) {
234 if (round_id
!= round_id_
)
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_
));
246 bool ChromeRenderProcessObserver::is_incognito_process_
= false;
248 ChromeRenderProcessObserver::ChromeRenderProcessObserver(
249 ChromeContentRendererClient
* 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.
261 #if defined(ENABLE_AUTOFILL_DIALOG)
262 WebRuntimeFeatures::enableRequestAutocomplete(true);
265 RenderThread
* thread
= RenderThread::Get();
266 resource_delegate_
.reset(new RendererResourceDelegate());
267 thread
->SetResourceDispatcherDelegate(resource_delegate_
.get());
269 // Configure modules that need access to resources.
270 net::NetModule::SetResourceProvider(chrome_common_net::NetResourceProvider
);
273 // Need to patch a few functions for font loading to work correctly.
275 if (PathService::Get(chrome::FILE_PDF_PLUGIN
, &pdf
) &&
276 base::PathExists(pdf
)) {
277 g_iat_patch_createdca
.Patch(
278 pdf
.value().c_str(), "gdi32.dll", "CreateDCA", CreateDCAPatch
);
279 g_iat_patch_get_font_data
.Patch(
280 pdf
.value().c_str(), "gdi32.dll", "GetFontData", GetFontDataPatch
);
284 #if defined(OS_POSIX) && !defined(OS_MACOSX) && defined(USE_NSS)
285 // On platforms where we use system NSS shared libraries,
286 // initialize NSS now because it won't be able to load the .so's
287 // after we engage the sandbox.
288 if (!command_line
.HasSwitch(switches::kSingleProcess
))
289 crypto::InitNSSSafely();
290 #elif defined(OS_WIN)
291 // crypt32.dll is used to decode X509 certificates for Chromoting.
292 // Only load this library when the feature is enabled.
293 base::LoadNativeLibrary(base::FilePath(L
"crypt32.dll"), NULL
);
295 // Setup initial set of crash dump data for Field Trials in this renderer.
296 chrome_variations::SetChildProcessLoggingVariationList();
299 ChromeRenderProcessObserver::~ChromeRenderProcessObserver() {
302 bool ChromeRenderProcessObserver::OnControlMessageReceived(
303 const IPC::Message
& message
) {
305 IPC_BEGIN_MESSAGE_MAP(ChromeRenderProcessObserver
, message
)
306 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetIsIncognitoProcess
,
307 OnSetIsIncognitoProcess
)
308 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetCacheCapacities
, OnSetCacheCapacities
)
309 IPC_MESSAGE_HANDLER(ChromeViewMsg_ClearCache
, OnClearCache
)
310 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetFieldTrialGroup
, OnSetFieldTrialGroup
)
311 IPC_MESSAGE_HANDLER(ChromeViewMsg_GetV8HeapStats
, OnGetV8HeapStats
)
312 IPC_MESSAGE_HANDLER(ChromeViewMsg_GetCacheResourceStats
,
313 OnGetCacheResourceStats
)
314 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetContentSettingRules
,
315 OnSetContentSettingRules
)
316 IPC_MESSAGE_UNHANDLED(handled
= false)
317 IPC_END_MESSAGE_MAP()
321 void ChromeRenderProcessObserver::WebKitInitialized() {
322 webkit_initialized_
= true;
323 if (pending_cache_capacity_
!= kUnitializedCacheCapacity
) {
324 WebCache::setCapacities(pending_cache_min_dead_capacity_
,
325 pending_cache_max_dead_capacity_
,
326 pending_cache_capacity_
);
329 // chrome-native: is a scheme used for placeholder navigations that allow
330 // UIs to be drawn with platform native widgets instead of HTML. These pages
331 // should not be accessible, and should also be treated as empty documents
332 // that can commit synchronously. No code should be runnable in these pages,
333 // so it should not need to access anything nor should it allow javascript
334 // URLs since it should never be visible to the user.
335 WebString
native_scheme(base::ASCIIToUTF16(chrome::kChromeNativeScheme
));
336 WebSecurityPolicy::registerURLSchemeAsDisplayIsolated(native_scheme
);
337 WebSecurityPolicy::registerURLSchemeAsEmptyDocument(native_scheme
);
338 WebSecurityPolicy::registerURLSchemeAsNoAccess(native_scheme
);
339 WebSecurityPolicy::registerURLSchemeAsNotAllowingJavascriptURLs(
343 void ChromeRenderProcessObserver::OnRenderProcessShutdown() {
344 webkit_initialized_
= false;
347 void ChromeRenderProcessObserver::OnSetIsIncognitoProcess(
348 bool is_incognito_process
) {
349 is_incognito_process_
= is_incognito_process
;
352 void ChromeRenderProcessObserver::OnSetContentSettingRules(
353 const RendererContentSettingRules
& rules
) {
354 content_setting_rules_
= rules
;
357 void ChromeRenderProcessObserver::OnSetCacheCapacities(size_t min_dead_capacity
,
358 size_t max_dead_capacity
,
360 if (!webkit_initialized_
) {
361 pending_cache_min_dead_capacity_
= min_dead_capacity
;
362 pending_cache_max_dead_capacity_
= max_dead_capacity
;
363 pending_cache_capacity_
= capacity
;
367 WebCache::setCapacities(
368 min_dead_capacity
, max_dead_capacity
, capacity
);
371 void ChromeRenderProcessObserver::OnClearCache(bool on_navigation
) {
372 if (on_navigation
|| !webkit_initialized_
) {
373 clear_cache_pending_
= true;
379 void ChromeRenderProcessObserver::OnGetCacheResourceStats() {
380 WebCache::ResourceTypeStats stats
;
381 if (webkit_initialized_
)
382 WebCache::getResourceTypeStats(&stats
);
383 RenderThread::Get()->Send(new ChromeViewHostMsg_ResourceTypeStats(stats
));
386 void ChromeRenderProcessObserver::OnSetFieldTrialGroup(
387 const std::string
& field_trial_name
,
388 const std::string
& group_name
) {
389 base::FieldTrial
* trial
=
390 base::FieldTrialList::CreateFieldTrial(field_trial_name
, group_name
);
391 // TODO(mef): Remove this check after the investigation of 359406 is complete.
392 CHECK(trial
) << field_trial_name
<< ":" << group_name
;
393 // Ensure the trial is marked as "used" by calling group() on it. This is
394 // needed to ensure the trial is properly reported in renderer crash reports.
396 chrome_variations::SetChildProcessLoggingVariationList();
399 void ChromeRenderProcessObserver::OnGetV8HeapStats() {
400 HeapStatisticsCollector::Instance()->InitiateCollection();
403 void ChromeRenderProcessObserver::ExecutePendingClearCache() {
404 if (clear_cache_pending_
&& webkit_initialized_
) {
405 clear_cache_pending_
= false;
410 const RendererContentSettingRules
*
411 ChromeRenderProcessObserver::content_setting_rules() const {
412 return &content_setting_rules_
;