- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / task_manager / tab_contents_resource_provider.cc
1 // Copyright 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/browser/task_manager/tab_contents_resource_provider.h"
6
7 #include "chrome/browser/browser_process.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/devtools/devtools_window.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/favicon/favicon_tab_helper.h"
12 #include "chrome/browser/prerender/prerender_manager.h"
13 #include "chrome/browser/prerender/prerender_manager_factory.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile_manager.h"
16 #include "chrome/browser/search/instant_service.h"
17 #include "chrome/browser/search/instant_service_factory.h"
18 #include "chrome/browser/search/search.h"
19 #include "chrome/browser/tab_contents/tab_util.h"
20 #include "chrome/browser/task_manager/renderer_resource.h"
21 #include "chrome/browser/task_manager/resource_provider.h"
22 #include "chrome/browser/task_manager/task_manager.h"
23 #include "chrome/browser/task_manager/task_manager_util.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_finder.h"
26 #include "chrome/browser/ui/browser_iterator.h"
27 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
28 #include "content/public/browser/notification_service.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "content/public/browser/web_contents.h"
31 #include "extensions/common/constants.h"
32 #include "grit/theme_resources.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/resource/resource_bundle.h"
35 #include "ui/gfx/image/image_skia.h"
36
37 #if defined(ENABLE_FULL_PRINTING)
38 #include "chrome/browser/printing/background_printing_manager.h"
39 #endif
40
41 using content::WebContents;
42 using extensions::Extension;
43
44 namespace {
45
46 bool IsContentsPrerendering(WebContents* web_contents) {
47   Profile* profile =
48       Profile::FromBrowserContext(web_contents->GetBrowserContext());
49   prerender::PrerenderManager* prerender_manager =
50       prerender::PrerenderManagerFactory::GetForProfile(profile);
51   return prerender_manager &&
52          prerender_manager->IsWebContentsPrerendering(web_contents, NULL);
53 }
54
55 bool IsContentsBackgroundPrinted(WebContents* web_contents) {
56 #if defined(ENABLE_FULL_PRINTING)
57   printing::BackgroundPrintingManager* printing_manager =
58       g_browser_process->background_printing_manager();
59   return printing_manager->HasPrintPreviewDialog(web_contents);
60 #else
61   return false;
62 #endif
63 }
64
65 }  // namespace
66
67 namespace task_manager {
68
69 // Tracks a single tab contents, prerendered page, Instant page, or background
70 // printing page.
71 class TabContentsResource : public RendererResource {
72  public:
73   explicit TabContentsResource(content::WebContents* web_contents);
74   virtual ~TabContentsResource();
75
76   // Resource methods:
77   virtual Type GetType() const OVERRIDE;
78   virtual string16 GetTitle() const OVERRIDE;
79   virtual string16 GetProfileName() const OVERRIDE;
80   virtual gfx::ImageSkia GetIcon() const OVERRIDE;
81   virtual content::WebContents* GetWebContents() const OVERRIDE;
82   virtual const extensions::Extension* GetExtension() const OVERRIDE;
83
84  private:
85   // Returns true if contains content rendered by an extension.
86   bool HostsExtension() const;
87
88   static gfx::ImageSkia* prerender_icon_;
89   content::WebContents* web_contents_;
90   Profile* profile_;
91   bool is_instant_ntp_;
92
93   DISALLOW_COPY_AND_ASSIGN(TabContentsResource);
94 };
95
96 gfx::ImageSkia* TabContentsResource::prerender_icon_ = NULL;
97
98 TabContentsResource::TabContentsResource(
99     WebContents* web_contents)
100     : RendererResource(web_contents->GetRenderProcessHost()->GetHandle(),
101                        web_contents->GetRenderViewHost()),
102       web_contents_(web_contents),
103       profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
104       is_instant_ntp_(chrome::IsPreloadedInstantExtendedNTP(web_contents)) {
105   if (!prerender_icon_) {
106     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
107     prerender_icon_ = rb.GetImageSkiaNamed(IDR_PRERENDER);
108   }
109 }
110
111 TabContentsResource::~TabContentsResource() {
112 }
113
114 bool TabContentsResource::HostsExtension() const {
115   return web_contents_->GetURL().SchemeIs(extensions::kExtensionScheme);
116 }
117
118 Resource::Type TabContentsResource::GetType() const {
119   return HostsExtension() ? EXTENSION : RENDERER;
120 }
121
122 string16 TabContentsResource::GetTitle() const {
123   // Fall back on the URL if there's no title.
124   GURL url = web_contents_->GetURL();
125   string16 tab_title = util::GetTitleFromWebContents(web_contents_);
126
127   // Only classify as an app if the URL is an app and the tab is hosting an
128   // extension process.  (It's possible to be showing the URL from before it
129   // was installed as an app.)
130   ExtensionService* extension_service = profile_->GetExtensionService();
131   extensions::ProcessMap* process_map = extension_service->process_map();
132   bool is_app = extension_service->IsInstalledApp(url) &&
133       process_map->Contains(web_contents_->GetRenderProcessHost()->GetID());
134
135   int message_id = util::GetMessagePrefixID(
136       is_app,
137       HostsExtension(),
138       profile_->IsOffTheRecord(),
139       IsContentsPrerendering(web_contents_),
140       is_instant_ntp_,
141       false);  // is_background
142   return l10n_util::GetStringFUTF16(message_id, tab_title);
143 }
144
145 string16 TabContentsResource::GetProfileName() const {
146   return util::GetProfileNameFromInfoCache(profile_);
147 }
148
149 gfx::ImageSkia TabContentsResource::GetIcon() const {
150   if (IsContentsPrerendering(web_contents_))
151     return *prerender_icon_;
152   FaviconTabHelper::CreateForWebContents(web_contents_);
153   return FaviconTabHelper::FromWebContents(web_contents_)->
154       GetFavicon().AsImageSkia();
155 }
156
157 WebContents* TabContentsResource::GetWebContents() const {
158   return web_contents_;
159 }
160
161 const Extension* TabContentsResource::GetExtension() const {
162   if (HostsExtension()) {
163     ExtensionService* extension_service = profile_->GetExtensionService();
164     return extension_service->extensions()->GetByID(
165         web_contents_->GetURL().host());
166   }
167
168   return NULL;
169 }
170
171 ////////////////////////////////////////////////////////////////////////////////
172 // TabContentsResourceProvider class
173 ////////////////////////////////////////////////////////////////////////////////
174
175 TabContentsResourceProvider::
176     TabContentsResourceProvider(TaskManager* task_manager)
177     :  updating_(false),
178        task_manager_(task_manager) {
179 }
180
181 TabContentsResourceProvider::~TabContentsResourceProvider() {
182 }
183
184 Resource* TabContentsResourceProvider::GetResource(
185     int origin_pid,
186     int render_process_host_id,
187     int routing_id) {
188   WebContents* web_contents =
189       tab_util::GetWebContentsByID(render_process_host_id, routing_id);
190   if (!web_contents)  // Not one of our resource.
191     return NULL;
192
193   // If an origin PID was specified then the request originated in a plugin
194   // working on the WebContents's behalf, so ignore it.
195   if (origin_pid)
196     return NULL;
197
198   std::map<WebContents*, TabContentsResource*>::iterator
199       res_iter = resources_.find(web_contents);
200   if (res_iter == resources_.end()) {
201     // Can happen if the tab was closed while a network request was being
202     // performed.
203     return NULL;
204   }
205   return res_iter->second;
206 }
207
208 void TabContentsResourceProvider::StartUpdating() {
209   DCHECK(!updating_);
210   updating_ = true;
211
212   // The contents that are tracked by this resource provider are those that
213   // are tab contents (WebContents serving as a tab in a Browser), Instant
214   // pages, prerender pages, and background printed pages.
215
216   // Add all the existing WebContentses.
217   for (TabContentsIterator iterator; !iterator.done(); iterator.Next()) {
218     Add(*iterator);
219     DevToolsWindow* docked =
220         DevToolsWindow::GetDockedInstanceForInspectedTab(*iterator);
221     if (docked)
222       Add(docked->web_contents());
223   }
224
225   // Add all the prerender pages.
226   std::vector<Profile*> profiles(
227       g_browser_process->profile_manager()->GetLoadedProfiles());
228   for (size_t i = 0; i < profiles.size(); ++i) {
229     prerender::PrerenderManager* prerender_manager =
230         prerender::PrerenderManagerFactory::GetForProfile(profiles[i]);
231     if (prerender_manager) {
232       const std::vector<content::WebContents*> contentses =
233           prerender_manager->GetAllPrerenderingContents();
234       for (size_t j = 0; j < contentses.size(); ++j)
235         Add(contentses[j]);
236     }
237   }
238
239   // Add all the Instant Extended prerendered NTPs.
240   for (size_t i = 0; i < profiles.size(); ++i) {
241     const InstantService* instant_service =
242         InstantServiceFactory::GetForProfile(profiles[i]);
243     if (instant_service && instant_service->GetNTPContents())
244       Add(instant_service->GetNTPContents());
245   }
246
247 #if defined(ENABLE_FULL_PRINTING)
248   // Add all the pages being background printed.
249   printing::BackgroundPrintingManager* printing_manager =
250       g_browser_process->background_printing_manager();
251   for (printing::BackgroundPrintingManager::WebContentsSet::iterator i =
252            printing_manager->begin();
253        i != printing_manager->end(); ++i) {
254     Add(*i);
255   }
256 #endif
257
258   // Then we register for notifications to get new web contents.
259   registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
260                  content::NotificationService::AllBrowserContextsAndSources());
261   registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
262                  content::NotificationService::AllBrowserContextsAndSources());
263   registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
264                  content::NotificationService::AllBrowserContextsAndSources());
265 }
266
267 void TabContentsResourceProvider::StopUpdating() {
268   DCHECK(updating_);
269   updating_ = false;
270
271   // Then we unregister for notifications to get new web contents.
272   registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
273       content::NotificationService::AllBrowserContextsAndSources());
274   registrar_.Remove(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
275       content::NotificationService::AllBrowserContextsAndSources());
276   registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
277       content::NotificationService::AllBrowserContextsAndSources());
278
279   // Delete all the resources.
280   STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end());
281
282   resources_.clear();
283 }
284
285 void TabContentsResourceProvider::AddToTaskManager(WebContents* web_contents) {
286   TabContentsResource* resource = new TabContentsResource(web_contents);
287   resources_[web_contents] = resource;
288   task_manager_->AddResource(resource);
289 }
290
291 void TabContentsResourceProvider::Add(WebContents* web_contents) {
292   if (!updating_)
293     return;
294
295   // The contents that are tracked by this resource provider are those that
296   // are tab contents (WebContents serving as a tab in a Browser), Instant
297   // pages, prerender pages, and background printed pages.
298   if (!chrome::FindBrowserWithWebContents(web_contents) &&
299       !IsContentsPrerendering(web_contents) &&
300       !chrome::IsPreloadedInstantExtendedNTP(web_contents) &&
301       !IsContentsBackgroundPrinted(web_contents) &&
302       !DevToolsWindow::IsDevToolsWindow(web_contents->GetRenderViewHost())) {
303     return;
304   }
305
306   // Don't add dead tabs or tabs that haven't yet connected.
307   if (!web_contents->GetRenderProcessHost()->GetHandle() ||
308       !web_contents->WillNotifyDisconnection()) {
309     return;
310   }
311
312   if (resources_.count(web_contents)) {
313     // The case may happen that we have added a WebContents as part of the
314     // iteration performed during StartUpdating() call but the notification that
315     // it has connected was not fired yet. So when the notification happens, we
316     // already know about this tab and just ignore it.
317     return;
318   }
319   AddToTaskManager(web_contents);
320 }
321
322 void TabContentsResourceProvider::Remove(WebContents* web_contents) {
323   if (!updating_)
324     return;
325   std::map<WebContents*, TabContentsResource*>::iterator
326       iter = resources_.find(web_contents);
327   if (iter == resources_.end()) {
328     // Since WebContents are destroyed asynchronously (see TabContentsCollector
329     // in navigation_controller.cc), we can be notified of a tab being removed
330     // that we don't know.  This can happen if the user closes a tab and quickly
331     // opens the task manager, before the tab is actually destroyed.
332     return;
333   }
334
335   // Remove the resource from the Task Manager.
336   TabContentsResource* resource = iter->second;
337   task_manager_->RemoveResource(resource);
338   // And from the provider.
339   resources_.erase(iter);
340   // Finally, delete the resource.
341   delete resource;
342 }
343
344 void TabContentsResourceProvider::Observe(
345     int type,
346     const content::NotificationSource& source,
347     const content::NotificationDetails& details) {
348   WebContents* web_contents = content::Source<WebContents>(source).ptr();
349
350   switch (type) {
351     case content::NOTIFICATION_WEB_CONTENTS_CONNECTED:
352       Add(web_contents);
353       break;
354     case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED:
355       Remove(web_contents);
356       Add(web_contents);
357       break;
358     case content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED:
359       Remove(web_contents);
360       break;
361     default:
362       NOTREACHED() << "Unexpected notification.";
363       return;
364   }
365 }
366
367 }  // namespace task_manager