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