1 // Copyright 2015 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_management/providers/web_contents/web_contents_task_provider.h"
8 #include "base/stl_util.h"
9 #include "chrome/browser/task_management/providers/web_contents/subframe_task.h"
10 #include "chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.h"
11 #include "content/public/browser/render_frame_host.h"
12 #include "content/public/browser/render_process_host.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/public/browser/web_contents_observer.h"
16 using content::RenderFrameHost
;
17 using content::SiteInstance
;
18 using content::WebContents
;
20 namespace task_management
{
22 // Defines an entry for each WebContents that will be tracked by the provider.
23 // The entry is used to observe certain events in its corresponding WebContents
24 // and then it notifies the provider or the render task (representing the
25 // WebContents) of these events.
26 // The entry owns the created tasks representing the WebContents, and it is
27 // itself owned by the provider.
28 class WebContentsEntry
: public content::WebContentsObserver
{
30 WebContentsEntry(content::WebContents
* web_contents
,
31 WebContentsTaskProvider
* provider
);
32 ~WebContentsEntry() override
;
34 // Creates all the tasks associated with each |RenderFrameHost| in this
35 // entry's WebContents.
36 void CreateAllTasks();
38 // Clears all the tasks in this entry. The provider's observer will be
39 // notified if |notify_observer| is true.
40 void ClearAllTasks(bool notify_observer
);
42 // Returns the |RendererTask| that corresponds to the given
43 // |render_frame_host| or |nullptr| if the given frame is not tracked by this
45 RendererTask
* GetTaskForFrame(RenderFrameHost
* render_frame_host
) const;
47 // content::WebContentsObserver:
48 void RenderFrameDeleted(RenderFrameHost
* render_frame_host
) override
;
49 void RenderFrameHostChanged(RenderFrameHost
* old_host
,
50 RenderFrameHost
* new_host
) override
;
51 void RenderViewReady() override
;
52 void WebContentsDestroyed() override
;
53 void RenderProcessGone(base::TerminationStatus status
) override
;
54 void TitleWasSet(content::NavigationEntry
* entry
, bool explicit_set
) override
;
55 void DidUpdateFaviconURL(
56 const std::vector
<content::FaviconURL
>& candidates
) override
;
59 // Defines a callback for WebContents::ForEachFrame() to create a
60 // corresponding task for the given |render_frame_host| and notifying the
61 // provider's observer of the new task.
62 void CreateTaskForFrame(RenderFrameHost
* render_frame_host
);
64 // Clears the task that corresponds to the given |render_frame_host| and
65 // notifies the provider's observer of the tasks removal.
66 void ClearTaskForFrame(RenderFrameHost
* render_frame_host
);
68 // The provider that owns this entry.
69 WebContentsTaskProvider
* provider_
;
71 // The RenderFrameHosts associated with this entry's WebContents that we're
72 // tracking mapped by their SiteInstances.
73 typedef std::vector
<RenderFrameHost
*> FramesList
;
74 typedef std::map
<SiteInstance
*, FramesList
> SiteInstanceToFramesMap
;
75 SiteInstanceToFramesMap frames_by_site_instance_
;
77 // The RendererTasks that we create for the task manager, mapped by their
79 typedef std::map
<RenderFrameHost
*, RendererTask
*> FramesToTasksMap
;
80 FramesToTasksMap tasks_by_frames_
;
82 // States whether we did record a main frame for this entry.
83 SiteInstance
* main_frame_site_instance_
;
85 DISALLOW_COPY_AND_ASSIGN(WebContentsEntry
);
88 ////////////////////////////////////////////////////////////////////////////////
90 WebContentsEntry::WebContentsEntry(content::WebContents
* web_contents
,
91 WebContentsTaskProvider
* provider
)
92 : WebContentsObserver(web_contents
),
94 frames_by_site_instance_(),
96 main_frame_site_instance_(nullptr) {
99 WebContentsEntry::~WebContentsEntry() {
100 ClearAllTasks(false);
103 void WebContentsEntry::CreateAllTasks() {
104 DCHECK(web_contents()->GetMainFrame());
105 web_contents()->ForEachFrame(base::Bind(&WebContentsEntry::CreateTaskForFrame
,
106 base::Unretained(this)));
109 void WebContentsEntry::ClearAllTasks(bool notify_observer
) {
110 for (auto& pair
: frames_by_site_instance_
) {
111 FramesList
& frames_list
= pair
.second
;
112 DCHECK(!frames_list
.empty());
113 RendererTask
* task
= tasks_by_frames_
[frames_list
[0]];
115 provider_
->NotifyObserverTaskRemoved(task
);
119 frames_by_site_instance_
.clear();
120 tasks_by_frames_
.clear();
121 main_frame_site_instance_
= nullptr;
124 RendererTask
* WebContentsEntry::GetTaskForFrame(
125 RenderFrameHost
* render_frame_host
) const {
126 auto itr
= tasks_by_frames_
.find(render_frame_host
);
127 if (itr
== tasks_by_frames_
.end())
133 void WebContentsEntry::RenderFrameDeleted(RenderFrameHost
* render_frame_host
) {
134 ClearTaskForFrame(render_frame_host
);
137 void WebContentsEntry::RenderFrameHostChanged(RenderFrameHost
* old_host
,
138 RenderFrameHost
* new_host
) {
139 ClearTaskForFrame(old_host
);
140 CreateTaskForFrame(new_host
);
143 void WebContentsEntry::RenderViewReady() {
148 void WebContentsEntry::WebContentsDestroyed() {
150 provider_
->DeleteEntry(web_contents());
153 void WebContentsEntry::RenderProcessGone(base::TerminationStatus status
) {
157 void WebContentsEntry::TitleWasSet(content::NavigationEntry
* entry
,
159 if (!tasks_by_frames_
.count(web_contents()->GetMainFrame())) {
160 // TODO(afakhry): Validate whether this actually happens in practice.
165 tasks_by_frames_
[web_contents()->GetMainFrame()]->OnTitleChanged(entry
);
168 void WebContentsEntry::DidUpdateFaviconURL(
169 const std::vector
<content::FaviconURL
>& candidates
) {
170 if (!tasks_by_frames_
.count(web_contents()->GetMainFrame())) {
171 // TODO(afakhry): Validate whether this actually happens in practice.
176 tasks_by_frames_
[web_contents()->GetMainFrame()]->OnFaviconChanged();
179 void WebContentsEntry::CreateTaskForFrame(RenderFrameHost
* render_frame_host
) {
180 DCHECK_EQ(0U, tasks_by_frames_
.count(render_frame_host
));
182 content::SiteInstance
* site_instance
= render_frame_host
->GetSiteInstance();
183 if (!site_instance
->GetProcess()->HasConnection())
186 bool site_instance_exists
=
187 frames_by_site_instance_
.count(site_instance
) != 0U;
188 bool is_main_frame
= (render_frame_host
== web_contents()->GetMainFrame());
189 bool site_instance_is_main
= (site_instance
== main_frame_site_instance_
);
191 RendererTask
* new_task
= nullptr;
192 // We don't create a task if there's one for this site_instance AND
193 // if this is not the main frame or we did record a main frame for the entry.
194 if (!site_instance_exists
|| (is_main_frame
&& !site_instance_is_main
)) {
196 const WebContentsTag
* tag
=
197 WebContentsTag::FromWebContents(web_contents());
199 new_task
= tag
->CreateTask();
200 main_frame_site_instance_
= site_instance
;
202 new_task
= new SubframeTask(render_frame_host
, web_contents());
206 if (site_instance_exists
) {
207 // One of the existing frame hosts for this site instance.
208 FramesList
& existing_frames_for_site_instance
=
209 frames_by_site_instance_
[site_instance
];
210 RenderFrameHost
* existing_rfh
= existing_frames_for_site_instance
[0];
211 RendererTask
* old_task
= tasks_by_frames_
[existing_rfh
];
214 // We didn't create any new task, so we keep appending the old one.
215 tasks_by_frames_
[render_frame_host
] = old_task
;
217 // Overwrite all the existing old tasks with the new one, and delete the
219 for (RenderFrameHost
* frame
: existing_frames_for_site_instance
)
220 tasks_by_frames_
[frame
] = new_task
;
222 provider_
->NotifyObserverTaskRemoved(old_task
);
227 frames_by_site_instance_
[site_instance
].push_back(render_frame_host
);
230 tasks_by_frames_
[render_frame_host
] = new_task
;
231 provider_
->NotifyObserverTaskAdded(new_task
);
235 void WebContentsEntry::ClearTaskForFrame(RenderFrameHost
* render_frame_host
) {
236 auto itr
= tasks_by_frames_
.find(render_frame_host
);
237 if (itr
== tasks_by_frames_
.end())
240 RendererTask
* task
= itr
->second
;
241 tasks_by_frames_
.erase(itr
);
242 content::SiteInstance
* site_instance
= render_frame_host
->GetSiteInstance();
243 FramesList
& frames
= frames_by_site_instance_
[site_instance
];
244 frames
.erase(std::find(frames
.begin(), frames
.end(), render_frame_host
));
246 if (frames
.empty()) {
247 frames_by_site_instance_
.erase(site_instance
);
248 provider_
->NotifyObserverTaskRemoved(task
);
251 if (site_instance
== main_frame_site_instance_
)
252 main_frame_site_instance_
= nullptr;
256 ////////////////////////////////////////////////////////////////////////////////
258 WebContentsTaskProvider::WebContentsTaskProvider()
260 is_updating_(false) {
263 WebContentsTaskProvider::~WebContentsTaskProvider() {
269 void WebContentsTaskProvider::OnWebContentsTagCreated(
270 const WebContentsTag
* tag
) {
272 content::WebContents
* web_contents
= tag
->web_contents();
273 DCHECK(web_contents
);
275 // TODO(afakhry): Check if we need this check. It seems that we no longer
276 // need it in the new implementation.
277 if (entries_map_
.count(web_contents
)) {
278 // This case may happen if we added a WebContents while collecting all the
279 // pre-existing ones at the time |StartUpdating()| was called, but the
280 // notification of its connection hasn't been fired yet. In this case we
281 // ignore it since we're already tracking it.
285 WebContentsEntry
* entry
= new WebContentsEntry(web_contents
, this);
286 entries_map_
[web_contents
] = entry
;
287 entry
->CreateAllTasks();
290 void WebContentsTaskProvider::OnWebContentsTagRemoved(
291 const WebContentsTag
* tag
) {
293 content::WebContents
* web_contents
= tag
->web_contents();
294 DCHECK(web_contents
);
296 auto itr
= entries_map_
.find(web_contents
);
297 DCHECK(itr
!= entries_map_
.end());
298 WebContentsEntry
* entry
= itr
->second
;
300 // Must manually clear the tasks and notify the observer.
301 entry
->ClearAllTasks(true);
302 entries_map_
.erase(itr
);
306 Task
* WebContentsTaskProvider::GetTaskOfUrlRequest(int origin_pid
,
309 // If an origin PID was specified then the URL request originated in a plugin
310 // working on the WebContents' behalf, so ignore it.
314 content::RenderFrameHost
* rfh
=
315 content::RenderFrameHost::FromID(child_id
, route_id
);
316 content::WebContents
* web_contents
=
317 content::WebContents::FromRenderFrameHost(rfh
);
319 auto itr
= entries_map_
.find(web_contents
);
320 if (itr
== entries_map_
.end()) {
321 // Can happen if the tab was closed while a network request was being
326 return itr
->second
->GetTaskForFrame(rfh
);
329 bool WebContentsTaskProvider::HasWebContents(
330 content::WebContents
* web_contents
) const {
331 return entries_map_
.count(web_contents
) != 0;
334 void WebContentsTaskProvider::StartUpdating() {
337 // 1- Collect all pre-existing WebContents from the WebContentsTagsManager.
338 WebContentsTagsManager
* tags_manager
= WebContentsTagsManager::GetInstance();
339 for (auto& tag
: tags_manager
->tracked_tags())
340 OnWebContentsTagCreated(tag
);
342 // 2- Start observing newly connected ones.
343 tags_manager
->SetProvider(this);
346 void WebContentsTaskProvider::StopUpdating() {
347 is_updating_
= false;
349 // 1- Stop observing.
350 WebContentsTagsManager::GetInstance()->ClearProvider();
353 STLDeleteValues(&entries_map_
);
356 void WebContentsTaskProvider::DeleteEntry(content::WebContents
* web_contents
) {
357 auto itr
= entries_map_
.find(web_contents
);
358 if (itr
== entries_map_
.end()) {
363 WebContentsEntry
* entry
= itr
->second
;
364 entries_map_
.erase(itr
);
366 // The entry we're about to delete is our caller, however its' still fine to
371 } // namespace task_management