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