- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / memory_internals / memory_internals_proxy.cc
1 // Copyright 2013 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/browser/ui/webui/memory_internals/memory_internals_proxy.h"
6
7 #include <set>
8 #include <string>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/strings/string16.h"
13 #include "base/sys_info.h"
14 #include "base/values.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/memory_details.h"
18 #include "chrome/browser/prerender/prerender_manager.h"
19 #include "chrome/browser/prerender/prerender_manager_factory.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/profiles/profile_manager.h"
22 #include "chrome/browser/renderer_host/chrome_render_message_filter.h"
23 #include "chrome/browser/search/instant_service.h"
24 #include "chrome/browser/search/instant_service_factory.h"
25 #include "chrome/browser/ui/android/tab_model/tab_model.h"
26 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
27 #include "chrome/browser/ui/browser.h"
28 #include "chrome/browser/ui/browser_instant_controller.h"
29 #include "chrome/browser/ui/browser_iterator.h"
30 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
31 #include "chrome/browser/ui/webui/memory_internals/memory_internals_handler.h"
32 #include "chrome/common/render_messages.h"
33 #include "content/public/browser/navigation_controller.h"
34 #include "content/public/browser/navigation_entry.h"
35 #include "content/public/browser/notification_observer.h"
36 #include "content/public/browser/notification_registrar.h"
37 #include "content/public/browser/notification_service.h"
38 #include "content/public/browser/render_process_host.h"
39 #include "content/public/browser/render_view_host.h"
40 #include "content/public/browser/web_contents.h"
41 #include "content/public/browser/web_ui.h"
42
43 #if defined(ENABLE_FULL_PRINTING)
44 #include "chrome/browser/printing/background_printing_manager.h"
45 #endif
46
47 using content::BrowserThread;
48
49 class Profile;
50
51 namespace {
52
53 class ProcessDetails : public MemoryDetails {
54  public:
55   typedef base::Callback<void(const ProcessData&)> DataCallback;
56   explicit ProcessDetails(const DataCallback& callback)
57       : callback_(callback) {}
58   // MemoryDetails:
59   virtual void OnDetailsAvailable() OVERRIDE {
60     const std::vector<ProcessData>& browser_processes = processes();
61     // [0] means Chrome.
62     callback_.Run(browser_processes[0]);
63   }
64
65  private:
66   virtual ~ProcessDetails() {}
67
68   DataCallback callback_;
69
70   DISALLOW_COPY_AND_ASSIGN(ProcessDetails);
71 };
72
73 base::DictionaryValue* FindProcessFromPid(base::ListValue* processes,
74                                           base::ProcessId pid) {
75   const size_t n = processes->GetSize();
76   for (size_t i = 0; i < n; ++i) {
77     base::DictionaryValue* process;
78     if (!processes->GetDictionary(i, &process))
79       return NULL;
80     int id;
81     if (process->GetInteger("pid", &id) && id == static_cast<int>(pid))
82       return process;
83   }
84   return NULL;
85 }
86
87 void GetAllWebContents(std::set<content::WebContents*>* web_contents) {
88   // Add all the existing WebContentses.
89 #if defined(OS_ANDROID)
90   for (TabModelList::const_iterator iter = TabModelList::begin();
91        iter != TabModelList::end(); ++iter) {
92     TabModel* model = *iter;
93     for (int i = 0; i < model->GetTabCount(); ++i)
94       web_contents->insert(model->GetWebContentsAt(i));
95   }
96 #else
97   for (TabContentsIterator iter; !iter.done(); iter.Next())
98     web_contents->insert(*iter);
99 #endif
100   // Add all the prerender pages.
101   std::vector<Profile*> profiles(
102       g_browser_process->profile_manager()->GetLoadedProfiles());
103   for (size_t i = 0; i < profiles.size(); ++i) {
104     prerender::PrerenderManager* prerender_manager =
105         prerender::PrerenderManagerFactory::GetForProfile(profiles[i]);
106     if (!prerender_manager)
107       continue;
108     const std::vector<content::WebContents*> contentses =
109         prerender_manager->GetAllPrerenderingContents();
110     for (size_t j = 0; j < contentses.size(); ++j)
111       web_contents->insert(contentses[j]);
112   }
113   // Add all the Instant Extended prerendered NTPs.
114   for (size_t i = 0; i < profiles.size(); ++i) {
115     const InstantService* instant_service =
116         InstantServiceFactory::GetForProfile(profiles[i]);
117     if (instant_service && instant_service->GetNTPContents())
118       web_contents->insert(instant_service->GetNTPContents());
119   }
120 #if defined(ENABLE_FULL_PRINTING)
121   // Add all the pages being background printed.
122   printing::BackgroundPrintingManager* printing_manager =
123       g_browser_process->background_printing_manager();
124   for (printing::BackgroundPrintingManager::WebContentsSet::const_iterator
125            iter = printing_manager->begin();
126        iter != printing_manager->end(); ++iter) {
127     web_contents->insert(*iter);
128   }
129 #endif
130 }
131
132 }  // namespace
133
134 class RendererDetails : public content::NotificationObserver {
135  public:
136   typedef base::Callback<void(const base::ProcessId pid,
137                               const size_t v8_allocated,
138                               const size_t v8_used)> V8DataCallback;
139
140   explicit RendererDetails(const V8DataCallback& callback)
141       : callback_(callback) {
142     registrar_.Add(this, chrome::NOTIFICATION_RENDERER_V8_HEAP_STATS_COMPUTED,
143                    content::NotificationService::AllSources());
144   }
145   virtual ~RendererDetails() {}
146
147   void Request() {
148     for (std::set<content::WebContents*>::iterator iter = web_contents_.begin();
149          iter != web_contents_.end(); ++iter)
150       (*iter)->GetRenderViewHost()->Send(new ChromeViewMsg_GetV8HeapStats);
151   }
152
153   void AddWebContents(content::WebContents* content) {
154     web_contents_.insert(content);
155   }
156
157   void Clear() {
158     web_contents_.clear();
159   }
160
161   void RemoveWebContents(base::ProcessId) {
162     // We don't have to detect which content is the caller of this method.
163     if (!web_contents_.empty())
164       web_contents_.erase(web_contents_.begin());
165   }
166
167   int IsClean() {
168     return web_contents_.empty();
169   }
170
171  private:
172   // NotificationObserver:
173   virtual void Observe(int type,
174                        const content::NotificationSource& source,
175                        const content::NotificationDetails& details) OVERRIDE {
176     const base::ProcessId* pid =
177         content::Source<const base::ProcessId>(source).ptr();
178     const ChromeRenderMessageFilter::V8HeapStatsDetails* v8_heap =
179         content::Details<const ChromeRenderMessageFilter::V8HeapStatsDetails>(
180             details).ptr();
181     callback_.Run(*pid,
182                   v8_heap->v8_memory_allocated(),
183                   v8_heap->v8_memory_used());
184   }
185
186   V8DataCallback callback_;
187   content::NotificationRegistrar registrar_;
188   std::set<content::WebContents*> web_contents_;  // This class does not own
189
190   DISALLOW_COPY_AND_ASSIGN(RendererDetails);
191 };
192
193 MemoryInternalsProxy::MemoryInternalsProxy()
194     : information_(new base::DictionaryValue()),
195       renderer_details_(new RendererDetails(
196           base::Bind(&MemoryInternalsProxy::OnRendererAvailable, this))) {}
197
198 void MemoryInternalsProxy::Attach(MemoryInternalsHandler* handler) {
199   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
200   handler_ = handler;
201 }
202
203 void MemoryInternalsProxy::Detach() {
204   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
205   handler_ = NULL;
206 }
207
208 void MemoryInternalsProxy::StartFetch(const base::ListValue* list) {
209   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210
211   // Clear previous information before fetching new information.
212   information_->Clear();
213   scoped_refptr<ProcessDetails> process(new ProcessDetails(
214       base::Bind(&MemoryInternalsProxy::OnProcessAvailable, this)));
215   process->StartFetch(MemoryDetails::SKIP_USER_METRICS);
216 }
217
218 MemoryInternalsProxy::~MemoryInternalsProxy() {}
219
220 void MemoryInternalsProxy::RequestRendererDetails() {
221   renderer_details_->Clear();
222
223 #if defined(OS_ANDROID)
224   for (TabModelList::const_iterator iter = TabModelList::begin();
225        iter != TabModelList::end(); ++iter) {
226     TabModel* model = *iter;
227     for (int i = 0; i < model->GetTabCount(); ++i)
228       renderer_details_->AddWebContents(model->GetWebContentsAt(i));
229   }
230 #else
231   for (TabContentsIterator iter; !iter.done(); iter.Next())
232     renderer_details_->AddWebContents(*iter);
233 #endif
234
235   renderer_details_->Request();
236 }
237
238 void MemoryInternalsProxy::OnProcessAvailable(const ProcessData& browser) {
239   base::ListValue* process_info = new ListValue();
240   base::ListValue* extension_info = new ListValue();
241   information_->Set("processes", process_info);
242   information_->Set("extensions", extension_info);
243   for (PMIIterator iter = browser.processes.begin();
244        iter != browser.processes.end(); ++iter) {
245     base::DictionaryValue* process = new DictionaryValue();
246     if (iter->renderer_type == ProcessMemoryInformation::RENDERER_EXTENSION)
247       extension_info->Append(process);
248     else
249       process_info->Append(process);
250
251     // From MemoryDetails.
252     process->SetInteger("pid", iter->pid);
253     process->SetString("type",
254                        ProcessMemoryInformation::GetFullTypeNameInEnglish(
255                            iter->process_type, iter->renderer_type));
256     process->SetInteger("memory_private", iter->working_set.priv);
257
258     base::ListValue* titles = new ListValue();
259     process->Set("titles", titles);
260     for (size_t i = 0; i < iter->titles.size(); ++i)
261       titles->AppendString(iter->titles[i]);
262   }
263
264   std::set<content::WebContents*> web_contents;
265   GetAllWebContents(&web_contents);
266   ConvertTabsInformation(web_contents, process_info);
267
268   RequestRendererDetails();
269 }
270
271 void MemoryInternalsProxy::OnRendererAvailable(const base::ProcessId pid,
272                                                const size_t v8_allocated,
273                                                const size_t v8_used) {
274   // Do not update while no renderers are registered.
275   if (renderer_details_->IsClean())
276     return;
277
278   base::ListValue* processes;
279   if (!information_->GetList("processes", &processes))
280     return;
281
282   const size_t size = processes->GetSize();
283   for (size_t i = 0; i < size; ++i) {
284     base::DictionaryValue* process;
285     processes->GetDictionary(i, &process);
286     int id;
287     if (!process->GetInteger("pid", &id) || id != static_cast<int>(pid))
288       continue;
289     // Convert units from Bytes to KiB.
290     process->SetInteger("v8_alloc", v8_allocated / 1024);
291     process->SetInteger("v8_used", v8_used / 1024);
292     break;
293   }
294
295   renderer_details_->RemoveWebContents(pid);
296   if (renderer_details_->IsClean())
297     FinishCollection();
298 }
299
300 void MemoryInternalsProxy::ConvertTabsInformation(
301     const std::set<content::WebContents*>& web_contents,
302     base::ListValue* processes) {
303   for (std::set<content::WebContents*>::const_iterator
304            iter = web_contents.begin(); iter != web_contents.end(); ++iter) {
305     content::WebContents* web = *iter;
306     const base::ProcessId pid = base::GetProcId(
307         web->GetRenderProcessHost()->GetHandle());
308
309     // Find which process renders the web contents.
310     base::DictionaryValue* process = FindProcessFromPid(processes, pid);
311     if (!process)
312       continue;
313
314     // Prepare storage to register navigation histories.
315     base::ListValue* tabs;
316     if (!process->GetList("history", &tabs)) {
317       tabs = new base::ListValue();
318       process->Set("history", tabs);
319     }
320
321     base::DictionaryValue* tab = new base::DictionaryValue();
322     tabs->Append(tab);
323
324     base::ListValue* histories = new base::ListValue();
325     tab->Set("history", histories);
326
327     const content::NavigationController& controller = web->GetController();
328     const int entry_size = controller.GetEntryCount();
329     for (int i = 0; i < entry_size; ++i) {
330       content::NavigationEntry *entry = controller.GetEntryAtIndex(i);
331       base::DictionaryValue* history = new base::DictionaryValue();
332       histories->Append(history);
333       history->SetString("url", entry->GetURL().spec());
334       history->SetString("title", entry->GetTitle());
335       history->SetInteger("time", (base::Time::Now() -
336                                    entry->GetTimestamp()).InSeconds());
337     }
338     tab->SetInteger("index", controller.GetCurrentEntryIndex());
339   }
340 }
341
342 void MemoryInternalsProxy::FinishCollection() {
343   information_->SetInteger("uptime", base::SysInfo::Uptime());
344   information_->SetString("os", base::SysInfo::OperatingSystemName());
345   information_->SetString("os_version",
346                           base::SysInfo::OperatingSystemVersion());
347
348   CallJavaScriptFunctionOnUIThread("g_main_view.onSetSnapshot", *information_);
349 }
350
351 void MemoryInternalsProxy::CallJavaScriptFunctionOnUIThread(
352     const std::string& function, const base::Value& args) {
353   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354
355   std::vector<const base::Value*> args_vector(1, &args);
356   string16 update = content::WebUI::GetJavascriptCall(function, args_vector);
357   // Don't forward updates to a destructed UI.
358   if (handler_)
359     handler_->OnUpdate(update);
360 }