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