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/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