Home | History | Annotate | Download | only in renderer
      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