// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/task_manager/web_contents_resource_provider.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/prerender/prerender_manager_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/task_manager/renderer_resource.h" #include "chrome/browser/task_manager/task_manager.h" #include "chrome/browser/task_manager/task_manager_util.h" #include "chrome/browser/task_manager/web_contents_information.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" using content::WebContents; using content::RenderViewHost; namespace task_manager { // A WebContentsObserver that tracks changes to a WebContents on behalf of // a WebContentsResourceProvider. class TaskManagerWebContentsObserver : public content::WebContentsObserver { public: TaskManagerWebContentsObserver(WebContents* web_contents, WebContentsResourceProvider* provider) : content::WebContentsObserver(web_contents), provider_(provider) {} // content::WebContentsObserver implementation. virtual void RenderViewHostChanged(RenderViewHost* old_host, RenderViewHost* new_host) OVERRIDE { provider_->RemoveFromTaskManager(web_contents()); provider_->AddToTaskManager(web_contents()); } virtual void RenderViewReady() OVERRIDE { provider_->RemoveFromTaskManager(web_contents()); provider_->AddToTaskManager(web_contents()); } virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE { provider_->RemoveFromTaskManager(web_contents()); } virtual void WebContentsDestroyed() OVERRIDE { provider_->RemoveFromTaskManager(web_contents()); provider_->DeleteObserver(this); // Deletes |this|. } private: WebContentsResourceProvider* provider_; }; //////////////////////////////////////////////////////////////////////////////// // WebContentsResourceProvider class //////////////////////////////////////////////////////////////////////////////// WebContentsResourceProvider::WebContentsResourceProvider( TaskManager* task_manager, scoped_ptr info) : updating_(false), task_manager_(task_manager), info_(info.Pass()) {} WebContentsResourceProvider::~WebContentsResourceProvider() {} RendererResource* WebContentsResourceProvider::GetResource(int origin_pid, int child_id, int route_id) { content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(child_id, route_id); content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(rfh); // If an origin PID was specified then the request originated in a plugin // working on the WebContents's behalf, so ignore it. if (origin_pid) return NULL; std::map::iterator res_iter = resources_.find(web_contents); if (res_iter == resources_.end()) { // Can happen if the tab was closed while a network request was being // performed. return NULL; } return res_iter->second; } void WebContentsResourceProvider::StartUpdating() { DCHECK(!updating_); updating_ = true; WebContentsInformation::NewWebContentsCallback new_web_contents_callback = base::Bind(&WebContentsResourceProvider::OnWebContentsCreated, this); info_->GetAll(new_web_contents_callback); info_->StartObservingCreation(new_web_contents_callback); } void WebContentsResourceProvider::StopUpdating() { DCHECK(updating_); updating_ = false; info_->StopObservingCreation(); // Delete all observers; this dissassociates them from the WebContents too. STLDeleteElements(&web_contents_observers_); web_contents_observers_.clear(); // Delete all resources. We don't need to remove them from the TaskManager, // because it's the TaskManager that's asking us to StopUpdating(). STLDeleteValues(&resources_); resources_.clear(); } void WebContentsResourceProvider::OnWebContentsCreated( WebContents* web_contents) { // Don't add dead tabs or tabs that haven't yet connected. if (!web_contents->GetRenderProcessHost()->GetHandle() || !web_contents->WillNotifyDisconnection()) { return; } DCHECK(info_->CheckOwnership(web_contents)); if (AddToTaskManager(web_contents)) { web_contents_observers_.insert( new TaskManagerWebContentsObserver(web_contents, this)); } } bool WebContentsResourceProvider::AddToTaskManager(WebContents* web_contents) { if (!updating_) return false; if (resources_.count(web_contents)) { // The case may happen that we have added a WebContents as part of the // iteration performed during StartUpdating() call but the notification that // it has connected was not fired yet. So when the notification happens, we // are already observing this WebContents and just ignore it. return false; } // TODO(nick): If the RenderView is not live, then do we still want to install // the WebContentsObserver? Only some of the original ResourceProviders // had that check. scoped_ptr resource = info_->MakeResource(web_contents); if (!resource) return false; task_manager_->AddResource(resource.get()); resources_[web_contents] = resource.release(); return true; } void WebContentsResourceProvider::RemoveFromTaskManager( WebContents* web_contents) { if (!updating_) return; std::map::iterator resource_iter = resources_.find(web_contents); if (resource_iter == resources_.end()) { return; } RendererResource* resource = resource_iter->second; task_manager_->RemoveResource(resource); // Remove the resource from the Task Manager. // And from the provider. resources_.erase(resource_iter); // Finally, delete the resource. delete resource; } void WebContentsResourceProvider::DeleteObserver( TaskManagerWebContentsObserver* observer) { if (!web_contents_observers_.erase(observer)) { NOTREACHED(); return; } delete observer; // Typically, this is our caller. Deletion is okay. } } // namespace task_manager