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