Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / renderer / chrome_render_process_observer.cc
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/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
51 using blink::WebCache;
52 using blink::WebRuntimeFeatures;
53 using blink::WebSecurityPolicy;
54 using blink::WebString;
55 using content::RenderThread;
56
57 namespace {
58
59 const int kCacheStatsDelayMS = 2000;
60 const size_t kUnitializedCacheCapacity = UINT_MAX;
61
62 class 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)
112 static base::win::IATPatchFunction g_iat_patch_createdca;
113 HDC 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
126 static base::win::IATPatchFunction g_iat_patch_get_font_data;
127 DWORD 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
148 static const int kWaitForWorkersStatsTimeoutMS = 20;
149
150 class 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
169 HeapStatisticsCollector* HeapStatisticsCollector::Instance() {
170   CR_DEFINE_STATIC_LOCAL(HeapStatisticsCollector, instance, ());
171   return &instance;
172 }
173
174 void 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
200 void 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
222 void 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
233 void 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
246 bool ChromeRenderProcessObserver::is_incognito_process_ = false;
247
248 ChromeRenderProcessObserver::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   RenderThread* thread = RenderThread::Get();
266   resource_delegate_.reset(new RendererResourceDelegate());
267   thread->SetResourceDispatcherDelegate(resource_delegate_.get());
268
269   // Configure modules that need access to resources.
270   net::NetModule::SetResourceProvider(chrome_common_net::NetResourceProvider);
271
272 #if defined(OS_WIN)
273   // Need to patch a few functions for font loading to work correctly.
274   base::FilePath pdf;
275   if (PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf) &&
276       base::PathExists(pdf)) {
277     g_iat_patch_createdca.Patch(
278         pdf.value().c_str(), "gdi32.dll", "CreateDCA", CreateDCAPatch);
279     g_iat_patch_get_font_data.Patch(
280         pdf.value().c_str(), "gdi32.dll", "GetFontData", GetFontDataPatch);
281   }
282 #endif
283
284 #if defined(OS_POSIX) && !defined(OS_MACOSX) && defined(USE_NSS)
285   // On platforms where we use system NSS shared libraries,
286   // initialize NSS now because it won't be able to load the .so's
287   // after we engage the sandbox.
288   if (!command_line.HasSwitch(switches::kSingleProcess))
289     crypto::InitNSSSafely();
290 #elif defined(OS_WIN)
291   // crypt32.dll is used to decode X509 certificates for Chromoting.
292   // Only load this library when the feature is enabled.
293   base::LoadNativeLibrary(base::FilePath(L"crypt32.dll"), NULL);
294 #endif
295   // Setup initial set of crash dump data for Field Trials in this renderer.
296   chrome_variations::SetChildProcessLoggingVariationList();
297 }
298
299 ChromeRenderProcessObserver::~ChromeRenderProcessObserver() {
300 }
301
302 bool ChromeRenderProcessObserver::OnControlMessageReceived(
303     const IPC::Message& message) {
304   bool handled = true;
305   IPC_BEGIN_MESSAGE_MAP(ChromeRenderProcessObserver, message)
306     IPC_MESSAGE_HANDLER(ChromeViewMsg_SetIsIncognitoProcess,
307                         OnSetIsIncognitoProcess)
308     IPC_MESSAGE_HANDLER(ChromeViewMsg_SetCacheCapacities, OnSetCacheCapacities)
309     IPC_MESSAGE_HANDLER(ChromeViewMsg_ClearCache, OnClearCache)
310     IPC_MESSAGE_HANDLER(ChromeViewMsg_SetFieldTrialGroup, OnSetFieldTrialGroup)
311     IPC_MESSAGE_HANDLER(ChromeViewMsg_GetV8HeapStats, OnGetV8HeapStats)
312     IPC_MESSAGE_HANDLER(ChromeViewMsg_GetCacheResourceStats,
313                         OnGetCacheResourceStats)
314     IPC_MESSAGE_HANDLER(ChromeViewMsg_SetContentSettingRules,
315                         OnSetContentSettingRules)
316     IPC_MESSAGE_UNHANDLED(handled = false)
317   IPC_END_MESSAGE_MAP()
318   return handled;
319 }
320
321 void ChromeRenderProcessObserver::WebKitInitialized() {
322   webkit_initialized_ = true;
323   if (pending_cache_capacity_ != kUnitializedCacheCapacity) {
324     WebCache::setCapacities(pending_cache_min_dead_capacity_,
325                             pending_cache_max_dead_capacity_,
326                             pending_cache_capacity_);
327   }
328
329   // chrome-native: is a scheme used for placeholder navigations that allow
330   // UIs to be drawn with platform native widgets instead of HTML.  These pages
331   // should not be accessible, and should also be treated as empty documents
332   // that can commit synchronously.  No code should be runnable in these pages,
333   // so it should not need to access anything nor should it allow javascript
334   // URLs since it should never be visible to the user.
335   WebString native_scheme(base::ASCIIToUTF16(chrome::kChromeNativeScheme));
336   WebSecurityPolicy::registerURLSchemeAsDisplayIsolated(native_scheme);
337   WebSecurityPolicy::registerURLSchemeAsEmptyDocument(native_scheme);
338   WebSecurityPolicy::registerURLSchemeAsNoAccess(native_scheme);
339   WebSecurityPolicy::registerURLSchemeAsNotAllowingJavascriptURLs(
340       native_scheme);
341 }
342
343 void ChromeRenderProcessObserver::OnRenderProcessShutdown() {
344   webkit_initialized_ = false;
345 }
346
347 void ChromeRenderProcessObserver::OnSetIsIncognitoProcess(
348     bool is_incognito_process) {
349   is_incognito_process_ = is_incognito_process;
350 }
351
352 void ChromeRenderProcessObserver::OnSetContentSettingRules(
353     const RendererContentSettingRules& rules) {
354   content_setting_rules_ = rules;
355 }
356
357 void ChromeRenderProcessObserver::OnSetCacheCapacities(size_t min_dead_capacity,
358                                                        size_t max_dead_capacity,
359                                                        size_t capacity) {
360   if (!webkit_initialized_) {
361     pending_cache_min_dead_capacity_ = min_dead_capacity;
362     pending_cache_max_dead_capacity_ = max_dead_capacity;
363     pending_cache_capacity_ = capacity;
364     return;
365   }
366
367   WebCache::setCapacities(
368       min_dead_capacity, max_dead_capacity, capacity);
369 }
370
371 void ChromeRenderProcessObserver::OnClearCache(bool on_navigation) {
372   if (on_navigation || !webkit_initialized_) {
373     clear_cache_pending_ = true;
374   } else {
375     WebCache::clear();
376   }
377 }
378
379 void ChromeRenderProcessObserver::OnGetCacheResourceStats() {
380   WebCache::ResourceTypeStats stats;
381   if (webkit_initialized_)
382     WebCache::getResourceTypeStats(&stats);
383   RenderThread::Get()->Send(new ChromeViewHostMsg_ResourceTypeStats(stats));
384 }
385
386 void ChromeRenderProcessObserver::OnSetFieldTrialGroup(
387     const std::string& field_trial_name,
388     const std::string& group_name) {
389   base::FieldTrial* trial =
390       base::FieldTrialList::CreateFieldTrial(field_trial_name, group_name);
391   // TODO(mef): Remove this check after the investigation of 359406 is complete.
392   CHECK(trial) << field_trial_name << ":" << group_name;
393   // Ensure the trial is marked as "used" by calling group() on it. This is
394   // needed to ensure the trial is properly reported in renderer crash reports.
395   trial->group();
396   chrome_variations::SetChildProcessLoggingVariationList();
397 }
398
399 void ChromeRenderProcessObserver::OnGetV8HeapStats() {
400   HeapStatisticsCollector::Instance()->InitiateCollection();
401 }
402
403 void ChromeRenderProcessObserver::ExecutePendingClearCache() {
404   if (clear_cache_pending_ && webkit_initialized_) {
405     clear_cache_pending_ = false;
406     WebCache::clear();
407   }
408 }
409
410 const RendererContentSettingRules*
411 ChromeRenderProcessObserver::content_setting_rules() const {
412   return &content_setting_rules_;
413 }