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"
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
{
40 explicit SubframeResource(WebContents
* web_contents
,
41 SiteInstance
* site_instance
,
42 RenderFrameHost
* example_rfh
);
43 ~SubframeResource() override
{}
46 Type
GetType() const override
;
47 base::string16
GetTitle() const override
;
48 gfx::ImageSkia
GetIcon() const override
;
49 WebContents
* GetWebContents() const override
;
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(
68 base::UTF8ToUTF16(subframe_site_instance
->GetSiteURL().spec()));
71 Resource::Type
SubframeResource::GetType() const {
75 base::string16
SubframeResource::GetTitle() const {
79 gfx::ImageSkia
SubframeResource::GetIcon() const {
80 return gfx::ImageSkia();
83 WebContents
* SubframeResource::GetWebContents() const {
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
{
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
),
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.
108 } while (j
!= resources_by_site_instance_
.end() && resource
== j
->second
);
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
{
122 ClearResourceForFrame(old_host
);
123 CreateResourceForFrame(new_host
);
126 void RenderViewReady() override
{
128 CreateAllResources();
131 void RenderProcessGone(base::TerminationStatus status
) override
{
135 void WebContentsDestroyed() override
{
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())
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.
164 } while (j
!= resources_by_site_instance_
.end() && resource
== j
->second
);
166 // Remove the resource from the Task Manager.
167 task_manager()->RemoveResource(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.
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
) {
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
);
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
)) {
217 new_resource
= info()->MakeResource(web_contents());
218 main_frame_site_instance_
= site_instance
;
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
;
228 resources_by_site_instance_
.insert(
229 std::make_pair(site_instance
, old_resource
));
231 for (ResourceMap::iterator it
= existing_resource_range
.first
;
232 it
!= existing_resource_range
.second
;
234 it
->second
= new_resource
.get();
236 task_manager()->RemoveResource(old_resource
);
242 task_manager()->AddResource(new_resource
.get());
243 resources_by_site_instance_
.insert(
244 std::make_pair(site_instance
, new_resource
.release()));
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
,
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.
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
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()) {
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.
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
)) {
337 delete entry
; // Typically, this is our caller. Deletion is okay.
340 } // namespace task_manager