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 DidNavigateMainFrame(
55 const content::LoadCommittedDetails
& details
,
56 const content::FrameNavigateParams
& params
) override
;
57 void TitleWasSet(content::NavigationEntry
* entry
, bool explicit_set
) override
;
60 // Defines a callback for WebContents::ForEachFrame() to create a
61 // corresponding task for the given |render_frame_host| and notifying the
62 // provider's observer of the new task.
63 void CreateTaskForFrame(RenderFrameHost
* render_frame_host
);
65 // Clears the task that corresponds to the given |render_frame_host| and
66 // notifies the provider's observer of the tasks removal.
67 void ClearTaskForFrame(RenderFrameHost
* render_frame_host
);
69 // The provider that owns this entry.
70 WebContentsTaskProvider
* provider_
;
72 // The RenderFrameHosts associated with this entry's WebContents that we're
73 // tracking mapped by their SiteInstances.
74 typedef std::vector
<RenderFrameHost
*> FramesList
;
75 typedef std::map
<SiteInstance
*, FramesList
> SiteInstanceToFramesMap
;
76 SiteInstanceToFramesMap frames_by_site_instance_
;
78 // The RendererTasks that we create for the task manager, mapped by their
80 typedef std::map
<RenderFrameHost
*, RendererTask
*> FramesToTasksMap
;
81 FramesToTasksMap tasks_by_frames_
;
83 // States whether we did record a main frame for this entry.
84 SiteInstance
* main_frame_site_instance_
;
86 DISALLOW_COPY_AND_ASSIGN(WebContentsEntry
);
89 ////////////////////////////////////////////////////////////////////////////////
91 WebContentsEntry::WebContentsEntry(content::WebContents
* web_contents
,
92 WebContentsTaskProvider
* provider
)
93 : WebContentsObserver(web_contents
),
95 frames_by_site_instance_(),
97 main_frame_site_instance_(nullptr) {
100 WebContentsEntry::~WebContentsEntry() {
101 ClearAllTasks(false);
104 void WebContentsEntry::CreateAllTasks() {
105 DCHECK(web_contents()->GetMainFrame());
106 web_contents()->ForEachFrame(base::Bind(&WebContentsEntry::CreateTaskForFrame
,
107 base::Unretained(this)));
110 void WebContentsEntry::ClearAllTasks(bool notify_observer
) {
111 for (auto& pair
: frames_by_site_instance_
) {
112 FramesList
& frames_list
= pair
.second
;
113 DCHECK(!frames_list
.empty());
114 RendererTask
* task
= tasks_by_frames_
[frames_list
[0]];
116 provider_
->NotifyObserverTaskRemoved(task
);
120 frames_by_site_instance_
.clear();
121 tasks_by_frames_
.clear();
122 main_frame_site_instance_
= nullptr;
125 RendererTask
* WebContentsEntry::GetTaskForFrame(
126 RenderFrameHost
* render_frame_host
) const {
127 auto itr
= tasks_by_frames_
.find(render_frame_host
);
128 if (itr
== tasks_by_frames_
.end())
134 void WebContentsEntry::RenderFrameDeleted(RenderFrameHost
* render_frame_host
) {
135 ClearTaskForFrame(render_frame_host
);
138 void WebContentsEntry::RenderFrameHostChanged(RenderFrameHost
* old_host
,
139 RenderFrameHost
* new_host
) {
140 ClearTaskForFrame(old_host
);
141 CreateTaskForFrame(new_host
);
144 void WebContentsEntry::RenderViewReady() {
149 void WebContentsEntry::WebContentsDestroyed() {
151 provider_
->DeleteEntry(web_contents());
154 void WebContentsEntry::RenderProcessGone(base::TerminationStatus status
) {
158 void WebContentsEntry::DidNavigateMainFrame(
159 const content::LoadCommittedDetails
& details
,
160 const content::FrameNavigateParams
& params
) {
161 // Note: Listening to WebContentsObserver::TitleWasSet() only is not enough in
162 // some cases when the the webpage doesn't have a title. That's why we update
163 // the title here as well.
164 auto itr
= tasks_by_frames_
.find(web_contents()->GetMainFrame());
165 if (itr
== tasks_by_frames_
.end()) {
166 // TODO(afakhry): Validate whether this actually happens in practice.
171 itr
->second
->UpdateTitle();
174 void WebContentsEntry::TitleWasSet(content::NavigationEntry
* entry
,
176 auto itr
= tasks_by_frames_
.find(web_contents()->GetMainFrame());
177 if (itr
== tasks_by_frames_
.end()) {
178 // TODO(afakhry): Validate whether this actually happens in practice.
183 itr
->second
->UpdateTitle();
186 void WebContentsEntry::CreateTaskForFrame(RenderFrameHost
* render_frame_host
) {
187 DCHECK_EQ(0U, tasks_by_frames_
.count(render_frame_host
));
189 content::SiteInstance
* site_instance
= render_frame_host
->GetSiteInstance();
190 if (!site_instance
->GetProcess()->HasConnection())
193 bool site_instance_exists
=
194 frames_by_site_instance_
.count(site_instance
) != 0U;
195 bool is_main_frame
= (render_frame_host
== web_contents()->GetMainFrame());
196 bool site_instance_is_main
= (site_instance
== main_frame_site_instance_
);
198 RendererTask
* new_task
= nullptr;
199 // We don't create a task if there's one for this site_instance AND
200 // if this is not the main frame or we did record a main frame for the entry.
201 if (!site_instance_exists
|| (is_main_frame
&& !site_instance_is_main
)) {
203 const WebContentsTag
* tag
=
204 WebContentsTag::FromWebContents(web_contents());
206 new_task
= tag
->CreateTask();
207 main_frame_site_instance_
= site_instance
;
209 new_task
= new SubframeTask(render_frame_host
, web_contents());
213 if (site_instance_exists
) {
214 // One of the existing frame hosts for this site instance.
215 FramesList
& existing_frames_for_site_instance
=
216 frames_by_site_instance_
[site_instance
];
217 RenderFrameHost
* existing_rfh
= existing_frames_for_site_instance
[0];
218 RendererTask
* old_task
= tasks_by_frames_
[existing_rfh
];
221 // We didn't create any new task, so we keep appending the old one.
222 tasks_by_frames_
[render_frame_host
] = old_task
;
224 // Overwrite all the existing old tasks with the new one, and delete the
226 for (RenderFrameHost
* frame
: existing_frames_for_site_instance
)
227 tasks_by_frames_
[frame
] = new_task
;
229 provider_
->NotifyObserverTaskRemoved(old_task
);
234 frames_by_site_instance_
[site_instance
].push_back(render_frame_host
);
237 tasks_by_frames_
[render_frame_host
] = new_task
;
238 provider_
->NotifyObserverTaskAdded(new_task
);
242 void WebContentsEntry::ClearTaskForFrame(RenderFrameHost
* render_frame_host
) {
243 auto itr
= tasks_by_frames_
.find(render_frame_host
);
244 if (itr
== tasks_by_frames_
.end())
247 RendererTask
* task
= itr
->second
;
248 tasks_by_frames_
.erase(itr
);
249 content::SiteInstance
* site_instance
= render_frame_host
->GetSiteInstance();
250 FramesList
& frames
= frames_by_site_instance_
[site_instance
];
251 frames
.erase(std::find(frames
.begin(), frames
.end(), render_frame_host
));
253 if (frames
.empty()) {
254 frames_by_site_instance_
.erase(site_instance
);
255 provider_
->NotifyObserverTaskRemoved(task
);
258 if (site_instance
== main_frame_site_instance_
)
259 main_frame_site_instance_
= nullptr;
263 ////////////////////////////////////////////////////////////////////////////////
265 WebContentsTaskProvider::WebContentsTaskProvider()
267 is_updating_(false) {
270 WebContentsTaskProvider::~WebContentsTaskProvider() {
276 void WebContentsTaskProvider::OnWebContentsTagCreated(
277 const WebContentsTag
* tag
) {
279 content::WebContents
* web_contents
= tag
->web_contents();
280 DCHECK(web_contents
);
282 // TODO(afakhry): Check if we need this check. It seems that we no longer
283 // need it in the new implementation.
284 if (entries_map_
.count(web_contents
)) {
285 // This case may happen if we added a WebContents while collecting all the
286 // pre-existing ones at the time |StartUpdating()| was called, but the
287 // notification of its connection hasn't been fired yet. In this case we
288 // ignore it since we're already tracking it.
292 WebContentsEntry
* entry
= new WebContentsEntry(web_contents
, this);
293 entries_map_
[web_contents
] = entry
;
294 entry
->CreateAllTasks();
297 void WebContentsTaskProvider::OnWebContentsTagRemoved(
298 const WebContentsTag
* tag
) {
300 content::WebContents
* web_contents
= tag
->web_contents();
301 DCHECK(web_contents
);
303 auto itr
= entries_map_
.find(web_contents
);
304 DCHECK(itr
!= entries_map_
.end());
305 WebContentsEntry
* entry
= itr
->second
;
307 // Must manually clear the tasks and notify the observer.
308 entry
->ClearAllTasks(true);
309 entries_map_
.erase(itr
);
313 Task
* WebContentsTaskProvider::GetTaskOfUrlRequest(int origin_pid
,
316 // If an origin PID was specified then the URL request originated in a plugin
317 // working on the WebContents' behalf, so ignore it.
321 content::RenderFrameHost
* rfh
=
322 content::RenderFrameHost::FromID(child_id
, route_id
);
323 content::WebContents
* web_contents
=
324 content::WebContents::FromRenderFrameHost(rfh
);
326 auto itr
= entries_map_
.find(web_contents
);
327 if (itr
== entries_map_
.end()) {
328 // Can happen if the tab was closed while a network request was being
333 return itr
->second
->GetTaskForFrame(rfh
);
336 bool WebContentsTaskProvider::HasWebContents(
337 content::WebContents
* web_contents
) const {
338 return entries_map_
.count(web_contents
) != 0;
341 void WebContentsTaskProvider::StartUpdating() {
344 // 1- Collect all pre-existing WebContents from the WebContentsTagsManager.
345 WebContentsTagsManager
* tags_manager
= WebContentsTagsManager::GetInstance();
346 for (auto& tag
: tags_manager
->tracked_tags())
347 OnWebContentsTagCreated(tag
);
349 // 2- Start observing newly connected ones.
350 tags_manager
->SetProvider(this);
353 void WebContentsTaskProvider::StopUpdating() {
354 is_updating_
= false;
356 // 1- Stop observing.
357 WebContentsTagsManager::GetInstance()->ClearProvider();
360 STLDeleteValues(&entries_map_
);
363 void WebContentsTaskProvider::DeleteEntry(content::WebContents
* web_contents
) {
364 auto itr
= entries_map_
.find(web_contents
);
365 if (itr
== entries_map_
.end()) {
370 WebContentsEntry
* entry
= itr
->second
;
371 entries_map_
.erase(itr
);
373 // The entry we're about to delete is our caller, however its' still fine to
378 } // namespace task_management