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