| OLD | NEW |
| (Empty) |
| 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 <utility> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/bind_helpers.h" | |
| 11 #include "base/macros.h" | |
| 12 #include "base/strings/utf_string_conversions.h" | |
| 13 #include "chrome/browser/browser_process.h" | |
| 14 #include "chrome/browser/prerender/prerender_manager.h" | |
| 15 #include "chrome/browser/prerender/prerender_manager_factory.h" | |
| 16 #include "chrome/browser/profiles/profile.h" | |
| 17 #include "chrome/browser/profiles/profile_manager.h" | |
| 18 #include "chrome/browser/task_manager/renderer_resource.h" | |
| 19 #include "chrome/browser/task_manager/task_manager.h" | |
| 20 #include "chrome/browser/task_manager/task_manager_util.h" | |
| 21 #include "chrome/browser/task_manager/web_contents_information.h" | |
| 22 #include "chrome/grit/generated_resources.h" | |
| 23 #include "content/public/browser/render_frame_host.h" | |
| 24 #include "content/public/browser/render_process_host.h" | |
| 25 #include "content/public/browser/render_process_host_observer.h" | |
| 26 #include "content/public/browser/render_view_host.h" | |
| 27 #include "content/public/browser/render_widget_host_iterator.h" | |
| 28 #include "content/public/browser/site_instance.h" | |
| 29 #include "content/public/browser/web_contents.h" | |
| 30 #include "content/public/browser/web_contents_observer.h" | |
| 31 #include "ui/base/l10n/l10n_util.h" | |
| 32 #include "ui/gfx/image/image_skia.h" | |
| 33 | |
| 34 using content::RenderFrameHost; | |
| 35 using content::RenderProcessHost; | |
| 36 using content::RenderViewHost; | |
| 37 using content::SiteInstance; | |
| 38 using content::WebContents; | |
| 39 | |
| 40 namespace task_manager { | |
| 41 | |
| 42 // A resource for a process hosting out-of-process iframes. | |
| 43 class SubframeResource : public RendererResource { | |
| 44 public: | |
| 45 explicit SubframeResource(WebContents* web_contents, | |
| 46 SiteInstance* site_instance, | |
| 47 RenderFrameHost* example_rfh); | |
| 48 ~SubframeResource() override {} | |
| 49 | |
| 50 // Resource methods: | |
| 51 Type GetType() const override; | |
| 52 base::string16 GetTitle() const override; | |
| 53 gfx::ImageSkia GetIcon() const override; | |
| 54 WebContents* GetWebContents() const override; | |
| 55 | |
| 56 private: | |
| 57 WebContents* web_contents_; | |
| 58 base::string16 title_; | |
| 59 DISALLOW_COPY_AND_ASSIGN(SubframeResource); | |
| 60 }; | |
| 61 | |
| 62 SubframeResource::SubframeResource(WebContents* web_contents, | |
| 63 SiteInstance* subframe_site_instance, | |
| 64 RenderFrameHost* example_rfh) | |
| 65 : RendererResource(subframe_site_instance->GetProcess()->GetHandle(), | |
| 66 example_rfh->GetRenderViewHost()), | |
| 67 web_contents_(web_contents) { | |
| 68 int message_id = subframe_site_instance->GetBrowserContext()->IsOffTheRecord() | |
| 69 ? IDS_TASK_MANAGER_SUBFRAME_INCOGNITO_PREFIX | |
| 70 : IDS_TASK_MANAGER_SUBFRAME_PREFIX; | |
| 71 title_ = l10n_util::GetStringFUTF16( | |
| 72 message_id, | |
| 73 base::UTF8ToUTF16(subframe_site_instance->GetSiteURL().spec())); | |
| 74 } | |
| 75 | |
| 76 Resource::Type SubframeResource::GetType() const { | |
| 77 return RENDERER; | |
| 78 } | |
| 79 | |
| 80 base::string16 SubframeResource::GetTitle() const { | |
| 81 return title_; | |
| 82 } | |
| 83 | |
| 84 gfx::ImageSkia SubframeResource::GetIcon() const { | |
| 85 return gfx::ImageSkia(); | |
| 86 } | |
| 87 | |
| 88 WebContents* SubframeResource::GetWebContents() const { | |
| 89 return web_contents_; | |
| 90 } | |
| 91 | |
| 92 // Tracks changes to one WebContents, and manages task manager resources for | |
| 93 // that WebContents, on behalf of a WebContentsResourceProvider. | |
| 94 class TaskManagerWebContentsEntry : public content::WebContentsObserver, | |
| 95 public content::RenderProcessHostObserver { | |
| 96 public: | |
| 97 typedef std::multimap<SiteInstance*, RendererResource*> ResourceMap; | |
| 98 typedef std::pair<ResourceMap::iterator, ResourceMap::iterator> ResourceRange; | |
| 99 | |
| 100 TaskManagerWebContentsEntry(WebContents* web_contents, | |
| 101 WebContentsResourceProvider* provider) | |
| 102 : content::WebContentsObserver(web_contents), | |
| 103 provider_(provider), | |
| 104 main_frame_site_instance_(NULL) {} | |
| 105 | |
| 106 ~TaskManagerWebContentsEntry() override { ClearAllResources(false); } | |
| 107 | |
| 108 // content::WebContentsObserver implementation. | |
| 109 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override { | |
| 110 ClearResourceForFrame(render_frame_host); | |
| 111 } | |
| 112 | |
| 113 void RenderFrameHostChanged(RenderFrameHost* old_host, | |
| 114 RenderFrameHost* new_host) override { | |
| 115 if (old_host) | |
| 116 ClearResourceForFrame(old_host); | |
| 117 CreateResourceForFrame(new_host); | |
| 118 } | |
| 119 | |
| 120 void RenderViewReady() override { | |
| 121 ClearAllResources(true); | |
| 122 CreateAllResources(); | |
| 123 } | |
| 124 | |
| 125 void WebContentsDestroyed() override { | |
| 126 ClearAllResources(true); | |
| 127 provider_->DeleteEntry(web_contents(), this); // Deletes |this|. | |
| 128 } | |
| 129 | |
| 130 // content::RenderProcessHostObserver implementation. | |
| 131 void RenderProcessExited(RenderProcessHost* process_host, | |
| 132 base::TerminationStatus status, | |
| 133 int exit_code) override { | |
| 134 ClearResourcesForProcess(process_host); | |
| 135 } | |
| 136 | |
| 137 void RenderProcessHostDestroyed(RenderProcessHost* process_host) override { | |
| 138 tracked_process_hosts_.erase(process_host); | |
| 139 } | |
| 140 | |
| 141 // Called by WebContentsResourceProvider. | |
| 142 RendererResource* GetResourceForSiteInstance(SiteInstance* site_instance) { | |
| 143 ResourceMap::iterator i = resources_by_site_instance_.find(site_instance); | |
| 144 if (i == resources_by_site_instance_.end()) | |
| 145 return NULL; | |
| 146 return i->second; | |
| 147 } | |
| 148 | |
| 149 void CreateAllResources() { | |
| 150 // We'll show one row per SiteInstance in the task manager. | |
| 151 DCHECK(web_contents()->GetMainFrame() != NULL); | |
| 152 web_contents()->ForEachFrame( | |
| 153 base::Bind(&TaskManagerWebContentsEntry::CreateResourceForFrame, | |
| 154 base::Unretained(this))); | |
| 155 } | |
| 156 | |
| 157 void ClearAllResources(bool update_task_manager) { | |
| 158 RendererResource* last_resource = NULL; | |
| 159 for (auto& x : resources_by_site_instance_) { | |
| 160 RendererResource* resource = x.second; | |
| 161 if (resource == last_resource) | |
| 162 continue; // Skip multiset duplicates. | |
| 163 if (update_task_manager) | |
| 164 task_manager()->RemoveResource(resource); | |
| 165 delete resource; | |
| 166 last_resource = resource; | |
| 167 } | |
| 168 resources_by_site_instance_.clear(); | |
| 169 | |
| 170 RenderProcessHost* last_process_host = NULL; | |
| 171 for (RenderProcessHost* process_host : tracked_process_hosts_) { | |
| 172 if (last_process_host == process_host) | |
| 173 continue; // Skip multiset duplicates. | |
| 174 process_host->RemoveObserver(this); | |
| 175 last_process_host = process_host; | |
| 176 } | |
| 177 tracked_process_hosts_.clear(); | |
| 178 tracked_frame_hosts_.clear(); | |
| 179 } | |
| 180 | |
| 181 void ClearResourceForFrame(RenderFrameHost* render_frame_host) { | |
| 182 SiteInstance* site_instance = render_frame_host->GetSiteInstance(); | |
| 183 std::set<RenderFrameHost*>::iterator frame_set_iterator = | |
| 184 tracked_frame_hosts_.find(render_frame_host); | |
| 185 if (frame_set_iterator == tracked_frame_hosts_.end()) { | |
| 186 // We weren't tracking this RenderFrameHost. | |
| 187 return; | |
| 188 } | |
| 189 tracked_frame_hosts_.erase(frame_set_iterator); | |
| 190 ResourceRange resource_range = | |
| 191 resources_by_site_instance_.equal_range(site_instance); | |
| 192 if (resource_range.first == resource_range.second) { | |
| 193 NOTREACHED(); | |
| 194 return; | |
| 195 } | |
| 196 RendererResource* resource = resource_range.first->second; | |
| 197 resources_by_site_instance_.erase(resource_range.first++); | |
| 198 if (resource_range.first == resource_range.second) { | |
| 199 // The removed entry was the sole remaining reference to that resource, so | |
| 200 // actually destroy it. | |
| 201 task_manager()->RemoveResource(resource); | |
| 202 delete resource; | |
| 203 DecrementProcessWatch(site_instance->GetProcess()); | |
| 204 if (site_instance == main_frame_site_instance_) { | |
| 205 main_frame_site_instance_ = NULL; | |
| 206 } | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 void ClearResourcesForProcess(RenderProcessHost* crashed_process) { | |
| 211 std::vector<RenderFrameHost*> frame_hosts_to_delete; | |
| 212 for (RenderFrameHost* frame_host : tracked_frame_hosts_) { | |
| 213 if (frame_host->GetProcess() == crashed_process) { | |
| 214 frame_hosts_to_delete.push_back(frame_host); | |
| 215 } | |
| 216 } | |
| 217 for (RenderFrameHost* frame_host : frame_hosts_to_delete) { | |
| 218 ClearResourceForFrame(frame_host); | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 void CreateResourceForFrame(RenderFrameHost* render_frame_host) { | |
| 223 SiteInstance* site_instance = render_frame_host->GetSiteInstance(); | |
| 224 | |
| 225 DCHECK_EQ(0u, tracked_frame_hosts_.count(render_frame_host)); | |
| 226 | |
| 227 if (!site_instance->GetProcess()->HasConnection()) | |
| 228 return; | |
| 229 | |
| 230 tracked_frame_hosts_.insert(render_frame_host); | |
| 231 | |
| 232 ResourceRange existing_resource_range = | |
| 233 resources_by_site_instance_.equal_range(site_instance); | |
| 234 bool existing_resource = | |
| 235 (existing_resource_range.first != existing_resource_range.second); | |
| 236 bool is_main_frame = (render_frame_host == web_contents()->GetMainFrame()); | |
| 237 bool site_instance_is_main = (site_instance == main_frame_site_instance_); | |
| 238 std::unique_ptr<RendererResource> new_resource; | |
| 239 if (!existing_resource || (is_main_frame && !site_instance_is_main)) { | |
| 240 if (is_main_frame) { | |
| 241 new_resource = info()->MakeResource(web_contents()); | |
| 242 main_frame_site_instance_ = site_instance; | |
| 243 } else { | |
| 244 new_resource.reset(new SubframeResource( | |
| 245 web_contents(), site_instance, render_frame_host)); | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 if (existing_resource) { | |
| 250 RendererResource* old_resource = existing_resource_range.first->second; | |
| 251 if (!new_resource) { | |
| 252 resources_by_site_instance_.insert( | |
| 253 std::make_pair(site_instance, old_resource)); | |
| 254 } else { | |
| 255 for (ResourceMap::iterator it = existing_resource_range.first; | |
| 256 it != existing_resource_range.second; | |
| 257 ++it) { | |
| 258 it->second = new_resource.get(); | |
| 259 } | |
| 260 task_manager()->RemoveResource(old_resource); | |
| 261 delete old_resource; | |
| 262 DecrementProcessWatch(site_instance->GetProcess()); | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 if (new_resource) { | |
| 267 task_manager()->AddResource(new_resource.get()); | |
| 268 resources_by_site_instance_.insert( | |
| 269 std::make_pair(site_instance, new_resource.release())); | |
| 270 IncrementProcessWatch(site_instance->GetProcess()); | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 // Add ourself as an observer of |process|, if we aren't already. Must be | |
| 275 // balanced by a call to DecrementProcessWatch(). | |
| 276 // TODO(nick): Move away from RenderProcessHostObserver once | |
| 277 // WebContentsObserver supports per-frame process death notices. | |
| 278 void IncrementProcessWatch(RenderProcessHost* process) { | |
| 279 auto range = tracked_process_hosts_.equal_range(process); | |
| 280 if (range.first == range.second) { | |
| 281 process->AddObserver(this); | |
| 282 } | |
| 283 tracked_process_hosts_.insert(range.first, process); | |
| 284 } | |
| 285 | |
| 286 void DecrementProcessWatch(RenderProcessHost* process) { | |
| 287 auto range = tracked_process_hosts_.equal_range(process); | |
| 288 if (range.first == range.second) { | |
| 289 NOTREACHED(); | |
| 290 return; | |
| 291 } | |
| 292 | |
| 293 auto element = range.first++; | |
| 294 if (range.first == range.second) { | |
| 295 process->RemoveObserver(this); | |
| 296 } | |
| 297 tracked_process_hosts_.erase(element, range.first); | |
| 298 } | |
| 299 | |
| 300 private: | |
| 301 TaskManager* task_manager() { return provider_->task_manager(); } | |
| 302 | |
| 303 WebContentsInformation* info() { return provider_->info(); } | |
| 304 | |
| 305 WebContentsResourceProvider* const provider_; | |
| 306 | |
| 307 // Every RenderFrameHost that we're watching. | |
| 308 std::set<RenderFrameHost*> tracked_frame_hosts_; | |
| 309 | |
| 310 // The set of processes we're currently observing. There is one entry here per | |
| 311 // RendererResource we create. A multimap because we may request observation | |
| 312 // more than once, say if two resources happen to share a process. | |
| 313 std::multiset<RenderProcessHost*> tracked_process_hosts_; | |
| 314 | |
| 315 // Maps SiteInstances to the RendererResources. A multimap, this contains one | |
| 316 // entry per tracked RenderFrameHost, so we can tell when we're done reusing. | |
| 317 ResourceMap resources_by_site_instance_; | |
| 318 | |
| 319 // The site instance of the main frame. | |
| 320 SiteInstance* main_frame_site_instance_; | |
| 321 }; | |
| 322 | |
| 323 //////////////////////////////////////////////////////////////////////////////// | |
| 324 // WebContentsResourceProvider class | |
| 325 //////////////////////////////////////////////////////////////////////////////// | |
| 326 | |
| 327 WebContentsResourceProvider::WebContentsResourceProvider( | |
| 328 TaskManager* task_manager, | |
| 329 std::unique_ptr<WebContentsInformation> info) | |
| 330 : task_manager_(task_manager), info_(std::move(info)) {} | |
| 331 | |
| 332 WebContentsResourceProvider::~WebContentsResourceProvider() {} | |
| 333 | |
| 334 RendererResource* WebContentsResourceProvider::GetResource(int origin_pid, | |
| 335 int child_id, | |
| 336 int route_id) { | |
| 337 RenderFrameHost* rfh = RenderFrameHost::FromID(child_id, route_id); | |
| 338 WebContents* web_contents = WebContents::FromRenderFrameHost(rfh); | |
| 339 | |
| 340 // If an origin PID was specified then the request originated in a plugin | |
| 341 // working on the WebContents's behalf, so ignore it. | |
| 342 if (origin_pid) | |
| 343 return NULL; | |
| 344 | |
| 345 EntryMap::const_iterator web_contents_it = entries_.find(web_contents); | |
| 346 | |
| 347 if (web_contents_it == entries_.end()) { | |
| 348 // Can happen if the tab was closed while a network request was being | |
| 349 // performed. | |
| 350 return NULL; | |
| 351 } | |
| 352 | |
| 353 return web_contents_it->second->GetResourceForSiteInstance( | |
| 354 rfh->GetSiteInstance()); | |
| 355 } | |
| 356 | |
| 357 void WebContentsResourceProvider::StartUpdating() { | |
| 358 WebContentsInformation::NewWebContentsCallback new_web_contents_callback = | |
| 359 base::Bind(&WebContentsResourceProvider::OnWebContentsCreated, this); | |
| 360 info_->GetAll(new_web_contents_callback); | |
| 361 info_->StartObservingCreation(new_web_contents_callback); | |
| 362 } | |
| 363 | |
| 364 void WebContentsResourceProvider::StopUpdating() { | |
| 365 info_->StopObservingCreation(); | |
| 366 | |
| 367 // Delete all entries; this dissassociates them from the WebContents too. | |
| 368 STLDeleteValues(&entries_); | |
| 369 } | |
| 370 | |
| 371 void WebContentsResourceProvider::OnWebContentsCreated( | |
| 372 WebContents* web_contents) { | |
| 373 // Don't add dead tabs or tabs that haven't yet connected. | |
| 374 if (!web_contents->GetRenderProcessHost()->GetHandle() || | |
| 375 !web_contents->WillNotifyDisconnection()) { | |
| 376 return; | |
| 377 } | |
| 378 | |
| 379 DCHECK(info_->CheckOwnership(web_contents)); | |
| 380 if (entries_.count(web_contents)) { | |
| 381 // The case may happen that we have added a WebContents as part of the | |
| 382 // iteration performed during StartUpdating() call but the notification that | |
| 383 // it has connected was not fired yet. So when the notification happens, we | |
| 384 // are already observing this WebContents and just ignore it. | |
| 385 return; | |
| 386 } | |
| 387 std::unique_ptr<TaskManagerWebContentsEntry> entry( | |
| 388 new TaskManagerWebContentsEntry(web_contents, this)); | |
| 389 entry->CreateAllResources(); | |
| 390 entries_[web_contents] = entry.release(); | |
| 391 } | |
| 392 | |
| 393 void WebContentsResourceProvider::DeleteEntry( | |
| 394 WebContents* web_contents, | |
| 395 TaskManagerWebContentsEntry* entry) { | |
| 396 if (!entries_.erase(web_contents)) { | |
| 397 NOTREACHED(); | |
| 398 return; | |
| 399 } | |
| 400 delete entry; // Typically, this is our caller. Deletion is okay. | |
| 401 } | |
| 402 | |
| 403 } // namespace task_manager | |
| OLD | NEW |