• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
RendererResourceDelegate()66   RendererResourceDelegate()
67       : weak_factory_(this) {
68   }
69 
OnRequestComplete(content::RequestPeer * current_peer,content::ResourceType resource_type,int error_code)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 
OnReceivedResponse(content::RequestPeer * current_peer,const std::string & mime_type,const GURL & url)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:
InformHostOfCacheStats()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;
CreateDCAPatch(LPCSTR driver_name,LPCSTR device_name,LPCSTR output,const void * init_data)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;
GetFontDataPatch(HDC hdc,DWORD table,DWORD offset,LPVOID buffer,DWORD length)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:
HeapStatisticsCollector()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 
Instance()175 HeapStatisticsCollector* HeapStatisticsCollector::Instance() {
176   CR_DEFINE_STATIC_LOCAL(HeapStatisticsCollector, instance, ());
177   return &instance;
178 }
179 
InitiateCollection()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 
CollectOnWorkerThread(scoped_refptr<base::TaskRunner> master,int round_id)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 
ReceiveStats(int round_id,size_t total_bytes,size_t used_bytes)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 
SendStatsToBrowser(int round_id)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 
ChromeRenderProcessObserver(ChromeContentRendererClient * client)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 
~ChromeRenderProcessObserver()306 ChromeRenderProcessObserver::~ChromeRenderProcessObserver() {
307 }
308 
OnControlMessageReceived(const IPC::Message & message)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 
WebKitInitialized()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 
OnRenderProcessShutdown()342 void ChromeRenderProcessObserver::OnRenderProcessShutdown() {
343   webkit_initialized_ = false;
344 }
345 
OnSetIsIncognitoProcess(bool is_incognito_process)346 void ChromeRenderProcessObserver::OnSetIsIncognitoProcess(
347     bool is_incognito_process) {
348   is_incognito_process_ = is_incognito_process;
349 }
350 
OnSetContentSettingRules(const RendererContentSettingRules & rules)351 void ChromeRenderProcessObserver::OnSetContentSettingRules(
352     const RendererContentSettingRules& rules) {
353   content_setting_rules_ = rules;
354 }
355 
OnGetCacheResourceStats()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 
OnSetFieldTrialGroup(const std::string & field_trial_name,const std::string & group_name)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 
OnGetV8HeapStats()376 void ChromeRenderProcessObserver::OnGetV8HeapStats() {
377   HeapStatisticsCollector::Instance()->InitiateCollection();
378 }
379 
380 const RendererContentSettingRules*
content_setting_rules() const381 ChromeRenderProcessObserver::content_setting_rules() const {
382   return &content_setting_rules_;
383 }
384