Roll src/third_party/WebKit bf18a82:a9cee16 (svn 185297:185304)
[chromium-blink-merge.git] / chrome / browser / task_manager / web_contents_resource_provider.cc
blobb3cdf672b7607000d847f86b8a564caf9896c5b9
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.
5 #include "chrome/browser/task_manager/web_contents_resource_provider.h"
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"
30 using content::RenderViewHost;
31 using content::RenderFrameHost;
32 using content::SiteInstance;
33 using content::WebContents;
35 namespace task_manager {
37 // A resource for a process hosting out-of-process iframes.
38 class SubframeResource : public RendererResource {
39 public:
40 explicit SubframeResource(WebContents* web_contents,
41 SiteInstance* site_instance,
42 RenderFrameHost* example_rfh);
43 ~SubframeResource() override {}
45 // Resource methods:
46 Type GetType() const override;
47 base::string16 GetTitle() const override;
48 gfx::ImageSkia GetIcon() const override;
49 WebContents* GetWebContents() const override;
51 private:
52 WebContents* web_contents_;
53 base::string16 title_;
54 DISALLOW_COPY_AND_ASSIGN(SubframeResource);
57 SubframeResource::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()));
71 Resource::Type SubframeResource::GetType() const {
72 return RENDERER;
75 base::string16 SubframeResource::GetTitle() const {
76 return title_;
79 gfx::ImageSkia SubframeResource::GetIcon() const {
80 return gfx::ImageSkia();
83 WebContents* SubframeResource::GetWebContents() const {
84 return web_contents_;
87 // Tracks changes to one WebContents, and manages task manager resources for
88 // that WebContents, on behalf of a WebContentsResourceProvider.
89 class TaskManagerWebContentsEntry : public content::WebContentsObserver {
90 public:
91 typedef std::multimap<SiteInstance*, RendererResource*> ResourceMap;
92 typedef std::pair<ResourceMap::iterator, ResourceMap::iterator> ResourceRange;
94 TaskManagerWebContentsEntry(WebContents* web_contents,
95 WebContentsResourceProvider* provider)
96 : content::WebContentsObserver(web_contents),
97 provider_(provider),
98 main_frame_site_instance_(NULL) {}
100 ~TaskManagerWebContentsEntry() override {
101 for (ResourceMap::iterator j = resources_by_site_instance_.begin();
102 j != resources_by_site_instance_.end();) {
103 RendererResource* resource = j->second;
105 // Advance to next non-duplicate entry.
106 do {
107 ++j;
108 } while (j != resources_by_site_instance_.end() && resource == j->second);
110 delete resource;
114 // content::WebContentsObserver implementation.
115 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
116 ClearResourceForFrame(render_frame_host);
119 void RenderFrameHostChanged(RenderFrameHost* old_host,
120 RenderFrameHost* new_host) override {
121 if (old_host)
122 ClearResourceForFrame(old_host);
123 CreateResourceForFrame(new_host);
126 void RenderViewReady() override {
127 ClearAllResources();
128 CreateAllResources();
131 void RenderProcessGone(base::TerminationStatus status) override {
132 ClearAllResources();
135 void WebContentsDestroyed() override {
136 ClearAllResources();
137 provider_->DeleteEntry(web_contents(), this); // Deletes |this|.
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;
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)));
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;
161 // Advance to next non-duplicate entry.
162 do {
163 ++j;
164 } while (j != resources_by_site_instance_.end() && resource == j->second);
166 // Remove the resource from the Task Manager.
167 task_manager()->RemoveResource(resource);
168 delete resource;
170 resources_by_site_instance_.clear();
171 tracked_frame_hosts_.clear();
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;
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;
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;
202 void CreateResourceForFrame(RenderFrameHost* render_frame_host) {
203 SiteInstance* site_instance = render_frame_host->GetSiteInstance();
205 DCHECK_EQ(0u, tracked_frame_hosts_.count(render_frame_host));
206 tracked_frame_hosts_.insert(render_frame_host);
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));
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();
236 task_manager()->RemoveResource(old_resource);
237 delete old_resource;
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()));
248 private:
249 TaskManager* task_manager() { return provider_->task_manager(); }
251 WebContentsInformation* info() { return provider_->info(); }
253 WebContentsResourceProvider* const provider_;
254 std::set<RenderFrameHost*> tracked_frame_hosts_;
255 ResourceMap resources_by_site_instance_;
256 SiteInstance* main_frame_site_instance_;
259 ////////////////////////////////////////////////////////////////////////////////
260 // WebContentsResourceProvider class
261 ////////////////////////////////////////////////////////////////////////////////
263 WebContentsResourceProvider::WebContentsResourceProvider(
264 TaskManager* task_manager,
265 scoped_ptr<WebContentsInformation> info)
266 : task_manager_(task_manager), info_(info.Pass()) {
269 WebContentsResourceProvider::~WebContentsResourceProvider() {}
271 RendererResource* 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);
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;
282 EntryMap::const_iterator web_contents_it = entries_.find(web_contents);
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;
290 return web_contents_it->second->GetResourceForSiteInstance(
291 rfh->GetSiteInstance());
294 void 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);
301 void WebContentsResourceProvider::StopUpdating() {
302 info_->StopObservingCreation();
304 // Delete all entries; this dissassociates them from the WebContents too.
305 STLDeleteValues(&entries_);
308 void 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;
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;
324 scoped_ptr<TaskManagerWebContentsEntry> entry(
325 new TaskManagerWebContentsEntry(web_contents, this));
326 entry->CreateAllResources();
327 entries_[web_contents] = entry.release();
330 void WebContentsResourceProvider::DeleteEntry(
331 WebContents* web_contents,
332 TaskManagerWebContentsEntry* entry) {
333 if (!entries_.erase(web_contents)) {
334 NOTREACHED();
335 return;
337 delete entry; // Typically, this is our caller. Deletion is okay.
340 } // namespace task_manager