1// Copyright 2014 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/web_contents_resource_provider.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/strings/utf_string_conversions.h" 10#include "chrome/browser/browser_process.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/task_manager/renderer_resource.h" 16#include "chrome/browser/task_manager/task_manager.h" 17#include "chrome/browser/task_manager/task_manager_util.h" 18#include "chrome/browser/task_manager/web_contents_information.h" 19#include "chrome/grit/generated_resources.h" 20#include "content/public/browser/render_frame_host.h" 21#include "content/public/browser/render_process_host.h" 22#include "content/public/browser/render_view_host.h" 23#include "content/public/browser/render_widget_host_iterator.h" 24#include "content/public/browser/site_instance.h" 25#include "content/public/browser/web_contents.h" 26#include "content/public/browser/web_contents_observer.h" 27#include "ui/base/l10n/l10n_util.h" 28#include "ui/gfx/image/image_skia.h" 29 30using content::RenderViewHost; 31using content::RenderFrameHost; 32using content::SiteInstance; 33using content::WebContents; 34 35namespace task_manager { 36 37// A resource for a process hosting out-of-process iframes. 38class SubframeResource : public RendererResource { 39 public: 40 explicit SubframeResource(WebContents* web_contents, 41 SiteInstance* site_instance, 42 RenderFrameHost* example_rfh); 43 virtual ~SubframeResource() {} 44 45 // Resource methods: 46 virtual Type GetType() const OVERRIDE; 47 virtual base::string16 GetTitle() const OVERRIDE; 48 virtual gfx::ImageSkia GetIcon() const OVERRIDE; 49 virtual WebContents* GetWebContents() const OVERRIDE; 50 51 private: 52 WebContents* web_contents_; 53 base::string16 title_; 54 DISALLOW_COPY_AND_ASSIGN(SubframeResource); 55}; 56 57SubframeResource::SubframeResource(WebContents* web_contents, 58 SiteInstance* subframe_site_instance, 59 RenderFrameHost* example_rfh) 60 : RendererResource(subframe_site_instance->GetProcess()->GetHandle(), 61 example_rfh->GetRenderViewHost()), 62 web_contents_(web_contents) { 63 int message_id = subframe_site_instance->GetBrowserContext()->IsOffTheRecord() 64 ? IDS_TASK_MANAGER_SUBFRAME_INCOGNITO_PREFIX 65 : IDS_TASK_MANAGER_SUBFRAME_PREFIX; 66 title_ = l10n_util::GetStringFUTF16( 67 message_id, 68 base::UTF8ToUTF16(subframe_site_instance->GetSiteURL().spec())); 69} 70 71Resource::Type SubframeResource::GetType() const { 72 return RENDERER; 73} 74 75base::string16 SubframeResource::GetTitle() const { 76 return title_; 77} 78 79gfx::ImageSkia SubframeResource::GetIcon() const { 80 return gfx::ImageSkia(); 81} 82 83WebContents* SubframeResource::GetWebContents() const { 84 return web_contents_; 85} 86 87// Tracks changes to one WebContents, and manages task manager resources for 88// that WebContents, on behalf of a WebContentsResourceProvider. 89class TaskManagerWebContentsEntry : public content::WebContentsObserver { 90 public: 91 typedef std::multimap<SiteInstance*, RendererResource*> ResourceMap; 92 typedef std::pair<ResourceMap::iterator, ResourceMap::iterator> ResourceRange; 93 94 TaskManagerWebContentsEntry(WebContents* web_contents, 95 WebContentsResourceProvider* provider) 96 : content::WebContentsObserver(web_contents), 97 provider_(provider), 98 main_frame_site_instance_(NULL) {} 99 100 virtual ~TaskManagerWebContentsEntry() { 101 for (ResourceMap::iterator j = resources_by_site_instance_.begin(); 102 j != resources_by_site_instance_.end();) { 103 RendererResource* resource = j->second; 104 105 // Advance to next non-duplicate entry. 106 do { 107 ++j; 108 } while (j != resources_by_site_instance_.end() && resource == j->second); 109 110 delete resource; 111 } 112 } 113 114 // content::WebContentsObserver implementation. 115 virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE { 116 ClearResourceForFrame(render_frame_host); 117 } 118 119 virtual void RenderFrameHostChanged(RenderFrameHost* old_host, 120 RenderFrameHost* new_host) OVERRIDE { 121 if (old_host) 122 ClearResourceForFrame(old_host); 123 CreateResourceForFrame(new_host); 124 } 125 126 virtual void RenderViewReady() OVERRIDE { 127 ClearAllResources(); 128 CreateAllResources(); 129 } 130 131 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE { 132 ClearAllResources(); 133 } 134 135 virtual void WebContentsDestroyed() OVERRIDE { 136 ClearAllResources(); 137 provider_->DeleteEntry(web_contents(), this); // Deletes |this|. 138 } 139 140 // Called by WebContentsResourceProvider. 141 RendererResource* GetResourceForSiteInstance(SiteInstance* site_instance) { 142 ResourceMap::iterator i = resources_by_site_instance_.find(site_instance); 143 if (i == resources_by_site_instance_.end()) 144 return NULL; 145 return i->second; 146 } 147 148 void CreateAllResources() { 149 // We'll show one row per SiteInstance in the task manager. 150 DCHECK(web_contents()->GetMainFrame() != NULL); 151 web_contents()->ForEachFrame( 152 base::Bind(&TaskManagerWebContentsEntry::CreateResourceForFrame, 153 base::Unretained(this))); 154 } 155 156 void ClearAllResources() { 157 for (ResourceMap::iterator j = resources_by_site_instance_.begin(); 158 j != resources_by_site_instance_.end();) { 159 RendererResource* resource = j->second; 160 161 // Advance to next non-duplicate entry. 162 do { 163 ++j; 164 } while (j != resources_by_site_instance_.end() && resource == j->second); 165 166 // Remove the resource from the Task Manager. 167 task_manager()->RemoveResource(resource); 168 delete resource; 169 } 170 resources_by_site_instance_.clear(); 171 tracked_frame_hosts_.clear(); 172 } 173 174 void ClearResourceForFrame(RenderFrameHost* render_frame_host) { 175 SiteInstance* site_instance = render_frame_host->GetSiteInstance(); 176 std::set<RenderFrameHost*>::iterator frame_set_iterator = 177 tracked_frame_hosts_.find(render_frame_host); 178 if (frame_set_iterator == tracked_frame_hosts_.end()) { 179 // We weren't tracking this RenderFrameHost. 180 return; 181 } 182 tracked_frame_hosts_.erase(frame_set_iterator); 183 ResourceRange resource_range = 184 resources_by_site_instance_.equal_range(site_instance); 185 if (resource_range.first == resource_range.second) { 186 NOTREACHED(); 187 return; 188 } 189 RendererResource* resource = resource_range.first->second; 190 resources_by_site_instance_.erase(resource_range.first++); 191 if (resource_range.first == resource_range.second) { 192 // The removed entry was the sole remaining reference to that resource, so 193 // actually destroy it. 194 task_manager()->RemoveResource(resource); 195 delete resource; 196 if (site_instance == main_frame_site_instance_) { 197 main_frame_site_instance_ = NULL; 198 } 199 } 200 } 201 202 void CreateResourceForFrame(RenderFrameHost* render_frame_host) { 203 SiteInstance* site_instance = render_frame_host->GetSiteInstance(); 204 205 DCHECK_EQ(0u, tracked_frame_hosts_.count(render_frame_host)); 206 tracked_frame_hosts_.insert(render_frame_host); 207 208 ResourceRange existing_resource_range = 209 resources_by_site_instance_.equal_range(site_instance); 210 bool existing_resource = 211 (existing_resource_range.first != existing_resource_range.second); 212 bool is_main_frame = (render_frame_host == web_contents()->GetMainFrame()); 213 bool site_instance_is_main = (site_instance == main_frame_site_instance_); 214 scoped_ptr<RendererResource> new_resource; 215 if (!existing_resource || (is_main_frame && !site_instance_is_main)) { 216 if (is_main_frame) { 217 new_resource = info()->MakeResource(web_contents()); 218 main_frame_site_instance_ = site_instance; 219 } else { 220 new_resource.reset(new SubframeResource( 221 web_contents(), site_instance, render_frame_host)); 222 } 223 } 224 225 if (existing_resource) { 226 RendererResource* old_resource = existing_resource_range.first->second; 227 if (!new_resource) { 228 resources_by_site_instance_.insert( 229 std::make_pair(site_instance, old_resource)); 230 } else { 231 for (ResourceMap::iterator it = existing_resource_range.first; 232 it != existing_resource_range.second; 233 ++it) { 234 it->second = new_resource.get(); 235 } 236 task_manager()->RemoveResource(old_resource); 237 delete old_resource; 238 } 239 } 240 241 if (new_resource) { 242 task_manager()->AddResource(new_resource.get()); 243 resources_by_site_instance_.insert( 244 std::make_pair(site_instance, new_resource.release())); 245 } 246 } 247 248 private: 249 TaskManager* task_manager() { return provider_->task_manager(); } 250 251 WebContentsInformation* info() { return provider_->info(); } 252 253 WebContentsResourceProvider* const provider_; 254 std::set<RenderFrameHost*> tracked_frame_hosts_; 255 ResourceMap resources_by_site_instance_; 256 SiteInstance* main_frame_site_instance_; 257}; 258 259//////////////////////////////////////////////////////////////////////////////// 260// WebContentsResourceProvider class 261//////////////////////////////////////////////////////////////////////////////// 262 263WebContentsResourceProvider::WebContentsResourceProvider( 264 TaskManager* task_manager, 265 scoped_ptr<WebContentsInformation> info) 266 : task_manager_(task_manager), info_(info.Pass()) { 267} 268 269WebContentsResourceProvider::~WebContentsResourceProvider() {} 270 271RendererResource* WebContentsResourceProvider::GetResource(int origin_pid, 272 int child_id, 273 int route_id) { 274 RenderFrameHost* rfh = RenderFrameHost::FromID(child_id, route_id); 275 WebContents* web_contents = WebContents::FromRenderFrameHost(rfh); 276 277 // If an origin PID was specified then the request originated in a plugin 278 // working on the WebContents's behalf, so ignore it. 279 if (origin_pid) 280 return NULL; 281 282 EntryMap::const_iterator web_contents_it = entries_.find(web_contents); 283 284 if (web_contents_it == entries_.end()) { 285 // Can happen if the tab was closed while a network request was being 286 // performed. 287 return NULL; 288 } 289 290 return web_contents_it->second->GetResourceForSiteInstance( 291 rfh->GetSiteInstance()); 292} 293 294void WebContentsResourceProvider::StartUpdating() { 295 WebContentsInformation::NewWebContentsCallback new_web_contents_callback = 296 base::Bind(&WebContentsResourceProvider::OnWebContentsCreated, this); 297 info_->GetAll(new_web_contents_callback); 298 info_->StartObservingCreation(new_web_contents_callback); 299} 300 301void WebContentsResourceProvider::StopUpdating() { 302 info_->StopObservingCreation(); 303 304 // Delete all entries; this dissassociates them from the WebContents too. 305 STLDeleteValues(&entries_); 306} 307 308void WebContentsResourceProvider::OnWebContentsCreated( 309 WebContents* web_contents) { 310 // Don't add dead tabs or tabs that haven't yet connected. 311 if (!web_contents->GetRenderProcessHost()->GetHandle() || 312 !web_contents->WillNotifyDisconnection()) { 313 return; 314 } 315 316 DCHECK(info_->CheckOwnership(web_contents)); 317 if (entries_.count(web_contents)) { 318 // The case may happen that we have added a WebContents as part of the 319 // iteration performed during StartUpdating() call but the notification that 320 // it has connected was not fired yet. So when the notification happens, we 321 // are already observing this WebContents and just ignore it. 322 return; 323 } 324 scoped_ptr<TaskManagerWebContentsEntry> entry( 325 new TaskManagerWebContentsEntry(web_contents, this)); 326 entry->CreateAllResources(); 327 entries_[web_contents] = entry.release(); 328} 329 330void WebContentsResourceProvider::DeleteEntry( 331 WebContents* web_contents, 332 TaskManagerWebContentsEntry* entry) { 333 if (!entries_.erase(web_contents)) { 334 NOTREACHED(); 335 return; 336 } 337 delete entry; // Typically, this is our caller. Deletion is okay. 338} 339 340} // namespace task_manager 341