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 base::string16 GetTitle() const OVERRIDE; 79 virtual base::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 base::string16 TabContentsResource::GetTitle() const { 123 // Fall back on the URL if there's no title. 124 GURL url = web_contents_->GetURL(); 125 base::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 base::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 368