Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / task_management / providers / web_contents / web_contents_task_provider.cc
blob9255ccb965a9f226c432f2a384eab7b0f181574e
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 DidNavigateMainFrame(
55 const content::LoadCommittedDetails& details,
56 const content::FrameNavigateParams& params) override;
57 void TitleWasSet(content::NavigationEntry* entry, bool explicit_set) override;
59 private:
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
79 // RenderFrameHosts.
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),
94 provider_(provider),
95 frames_by_site_instance_(),
96 tasks_by_frames_(),
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]];
115 if (notify_observer)
116 provider_->NotifyObserverTaskRemoved(task);
117 delete 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())
129 return nullptr;
131 return itr->second;
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() {
145 ClearAllTasks(true);
146 CreateAllTasks();
149 void WebContentsEntry::WebContentsDestroyed() {
150 ClearAllTasks(true);
151 provider_->DeleteEntry(web_contents());
154 void WebContentsEntry::RenderProcessGone(base::TerminationStatus status) {
155 ClearAllTasks(true);
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.
167 NOTREACHED();
168 return;
171 itr->second->UpdateTitle();
174 void WebContentsEntry::TitleWasSet(content::NavigationEntry* entry,
175 bool explicit_set) {
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.
179 NOTREACHED();
180 return;
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())
191 return;
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)) {
202 if (is_main_frame) {
203 const WebContentsTag* tag =
204 WebContentsTag::FromWebContents(web_contents());
205 CHECK(tag);
206 new_task = tag->CreateTask();
207 main_frame_site_instance_ = site_instance;
208 } else {
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];
220 if (!new_task) {
221 // We didn't create any new task, so we keep appending the old one.
222 tasks_by_frames_[render_frame_host] = old_task;
223 } else {
224 // Overwrite all the existing old tasks with the new one, and delete the
225 // old one.
226 for (RenderFrameHost* frame : existing_frames_for_site_instance)
227 tasks_by_frames_[frame] = new_task;
229 provider_->NotifyObserverTaskRemoved(old_task);
230 delete old_task;
234 frames_by_site_instance_[site_instance].push_back(render_frame_host);
236 if (new_task) {
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())
245 return;
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);
256 delete task;
258 if (site_instance == main_frame_site_instance_)
259 main_frame_site_instance_ = nullptr;
263 ////////////////////////////////////////////////////////////////////////////////
265 WebContentsTaskProvider::WebContentsTaskProvider()
266 : entries_map_(),
267 is_updating_(false) {
270 WebContentsTaskProvider::~WebContentsTaskProvider() {
271 if (is_updating_) {
272 StopUpdating();
276 void WebContentsTaskProvider::OnWebContentsTagCreated(
277 const WebContentsTag* tag) {
278 DCHECK(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.
289 return;
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) {
299 DCHECK(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);
310 delete entry;
313 Task* WebContentsTaskProvider::GetTaskOfUrlRequest(int origin_pid,
314 int child_id,
315 int route_id) {
316 // If an origin PID was specified then the URL request originated in a plugin
317 // working on the WebContents' behalf, so ignore it.
318 if (origin_pid)
319 return nullptr;
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
329 // performed.
330 return nullptr;
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() {
342 is_updating_ = true;
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();
359 // 2- Clear storage.
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()) {
366 NOTREACHED();
367 return;
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
374 // delete it.
375 delete entry;
378 } // namespace task_management