Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chrome / browser / task_management / providers / web_contents / web_contents_task_provider.cc
blobfa93a16be65983c4225aec8eb673ab450353feb4
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"
7 #include "base/bind.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 {
29 public:
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
44 // entry.
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;
58 private:
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
78 // RenderFrameHosts.
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),
93 provider_(provider),
94 frames_by_site_instance_(),
95 tasks_by_frames_(),
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]];
114 if (notify_observer)
115 provider_->NotifyObserverTaskRemoved(task);
116 delete 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())
128 return nullptr;
130 return itr->second;
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() {
144 ClearAllTasks(true);
145 CreateAllTasks();
148 void WebContentsEntry::WebContentsDestroyed() {
149 ClearAllTasks(true);
150 provider_->DeleteEntry(web_contents());
153 void WebContentsEntry::RenderProcessGone(base::TerminationStatus status) {
154 ClearAllTasks(true);
157 void WebContentsEntry::TitleWasSet(content::NavigationEntry* entry,
158 bool explicit_set) {
159 if (!tasks_by_frames_.count(web_contents()->GetMainFrame())) {
160 // TODO(afakhry): Validate whether this actually happens in practice.
161 NOTREACHED();
162 return;
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.
172 NOTREACHED();
173 return;
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())
184 return;
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)) {
195 if (is_main_frame) {
196 const WebContentsTag* tag =
197 WebContentsTag::FromWebContents(web_contents());
198 CHECK(tag);
199 new_task = tag->CreateTask();
200 main_frame_site_instance_ = site_instance;
201 } else {
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];
213 if (!new_task) {
214 // We didn't create any new task, so we keep appending the old one.
215 tasks_by_frames_[render_frame_host] = old_task;
216 } else {
217 // Overwrite all the existing old tasks with the new one, and delete the
218 // old one.
219 for (RenderFrameHost* frame : existing_frames_for_site_instance)
220 tasks_by_frames_[frame] = new_task;
222 provider_->NotifyObserverTaskRemoved(old_task);
223 delete old_task;
227 frames_by_site_instance_[site_instance].push_back(render_frame_host);
229 if (new_task) {
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())
238 return;
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);
249 delete task;
251 if (site_instance == main_frame_site_instance_)
252 main_frame_site_instance_ = nullptr;
256 ////////////////////////////////////////////////////////////////////////////////
258 WebContentsTaskProvider::WebContentsTaskProvider()
259 : entries_map_(),
260 is_updating_(false) {
263 WebContentsTaskProvider::~WebContentsTaskProvider() {
264 if (is_updating_) {
265 StopUpdating();
269 void WebContentsTaskProvider::OnWebContentsTagCreated(
270 const WebContentsTag* tag) {
271 DCHECK(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.
282 return;
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) {
292 DCHECK(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);
303 delete entry;
306 Task* WebContentsTaskProvider::GetTaskOfUrlRequest(int origin_pid,
307 int child_id,
308 int route_id) {
309 // If an origin PID was specified then the URL request originated in a plugin
310 // working on the WebContents' behalf, so ignore it.
311 if (origin_pid)
312 return nullptr;
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
322 // performed.
323 return nullptr;
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() {
335 is_updating_ = true;
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();
352 // 2- Clear storage.
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()) {
359 NOTREACHED();
360 return;
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
367 // delete it.
368 delete entry;
371 } // namespace task_management