- add sources.
[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/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 WebKit::WebCache;
57 using WebKit::WebCrossOriginPreflightResultCache;
58 using WebKit::WebFontCache;
59 using WebKit::WebRuntimeFeatures;
60 using WebKit::WebSecurityPolicy;
61 using WebKit::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     chrome::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 }