New Task Manager - Phase 1.3.2: Implement Tab Contents Task Providing

Part 2: Implementing a task-manager representation of WebContents owned
by the prerender manager.

[email protected]
BUG=470990
TEST=browser_tests --gtest_filter=PrerenderBrowserTest.TaskManagement*

Review URL: https://codereview.chromium.org/1185183008

Cr-Commit-Position: refs/heads/master@{#335788}
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 7b742f0..c08cfaa2 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -16,6 +16,7 @@
 #include "base/prefs/pref_service.h"
 #include "base/run_loop.h"
 #include "base/scoped_observer.h"
+#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -48,6 +49,9 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/safe_browsing_util.h"
 #include "chrome/browser/safe_browsing/test_database_manager.h"
+#include "chrome/browser/task_management/providers/task_provider_observer.h"
+#include "chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.h"
+#include "chrome/browser/task_management/providers/web_contents/web_contents_task_provider.h"
 #include "chrome/browser/task_manager/task_manager.h"
 #include "chrome/browser/task_manager/task_manager_browsertest_util.h"
 #include "chrome/browser/ui/browser.h"
@@ -4080,4 +4084,100 @@
 }
 #endif  // !defined(DISABLE_NACL)
 
+#if defined(ENABLE_TASK_MANAGER)
+
+namespace {
+
+// Defines a test class for testing that will act as a mock task manager.
+class MockTaskManager : public task_management::TaskProviderObserver {
+ public:
+  MockTaskManager() {}
+  ~MockTaskManager() override {}
+
+  // task_management::Task_providerObserver:
+  void TaskAdded(task_management::Task* task) override {
+    EXPECT_FALSE(ContainsKey(provided_tasks_, task));
+    provided_tasks_.insert(task);
+  }
+
+  void TaskRemoved(task_management::Task* task) override {
+    EXPECT_TRUE(ContainsKey(provided_tasks_, task));
+    provided_tasks_.erase(task);
+  }
+
+  base::string16 GetPrerenderTitlePrefix() const {
+    return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PRERENDER_PREFIX,
+                                      base::string16());
+  }
+
+  const std::set<task_management::WebContentsTag*>& tracked_tags() const {
+    return task_management::WebContentsTagsManager::GetInstance()->
+        tracked_tags();
+  }
+
+  const std::set<task_management::Task*>& provided_tasks() const {
+    return provided_tasks_;
+  }
+
+ private:
+  std::set<task_management::Task*> provided_tasks_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockTaskManager);
+};
+
+// Tests the correct recording of tags for the prerender WebContents.
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, TaskManagementTagsBasic) {
+  MockTaskManager task_manager;
+  EXPECT_TRUE(task_manager.tracked_tags().empty());
+
+  // Start prerendering a page and make sure it's correctly tagged.
+  PrerenderTestURL("files/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
+  EXPECT_FALSE(task_manager.tracked_tags().empty());
+
+  // TODO(afakhry): Once we start tagging the tab contents the below tests
+  // must be changed.
+  EXPECT_EQ(1U, task_manager.tracked_tags().size());
+
+  // Swap in the prerendered content and make sure its tag is removed.
+  NavigateToDestURL();
+  EXPECT_TRUE(task_manager.tracked_tags().empty());
+}
+
+// Tests that the task manager will be provided by tasks that correspond to
+// prerendered WebContents.
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, TaskManagementTasksProvided) {
+  MockTaskManager task_manager;
+  EXPECT_TRUE(task_manager.tracked_tags().empty());
+
+  task_management::WebContentsTaskProvider provider;
+  provider.SetObserver(&task_manager);
+
+  // Still empty, no pre-existing tasks.
+  EXPECT_TRUE(task_manager.provided_tasks().empty());
+
+  // Start prerendering a page.
+  PrerenderTestURL("files/prerender/prerender_page.html", FINAL_STATUS_USED, 1);
+  EXPECT_FALSE(task_manager.tracked_tags().empty());
+
+  // TODO(afakhry): The below may not be true after we support more tags.
+  EXPECT_EQ(1U, task_manager.tracked_tags().size());
+  ASSERT_EQ(1U, task_manager.provided_tasks().size());
+
+  const task_management::Task* task = *task_manager.provided_tasks().begin();
+  EXPECT_EQ(task_management::Task::RENDERER, task->GetType());
+  const base::string16 title = task->title();
+  const base::string16 expected_prefix = task_manager.GetPrerenderTitlePrefix();
+  EXPECT_TRUE(base::StartsWith(title,
+                               expected_prefix,
+                               base::CompareCase::INSENSITIVE_ASCII));
+
+  NavigateToDestURL();
+  // TODO(afakhry): The below may not be true after we support more tags.
+  EXPECT_TRUE(task_manager.provided_tasks().empty());
+}
+
+}  // namespace
+
+#endif  // defined(ENABLE_TASK_MANAGER)
+
 }  // namespace prerender
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index fb7daa9..d9da339 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/prerender/prerender_resource_throttle.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/task_management/web_contents_tags.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tab_helpers.h"
 #include "chrome/browser/ui/web_contents_sizer.h"
@@ -279,6 +280,11 @@
   TabHelpers::AttachTabHelpers(prerender_contents_.get());
   content::WebContentsObserver::Observe(prerender_contents_.get());
 
+  // Tag the prerender contents with the task manager specific prerender tag, so
+  // that it shows up in the task manager.
+  task_management::WebContentsTags::CreateForPrerenderContents(
+      prerender_contents_.get());
+
   web_contents_delegate_.reset(new WebContentsDelegateImpl(this));
   prerender_contents_.get()->SetDelegate(web_contents_delegate_.get());
   // Set the size of the prerender WebContents.
@@ -671,6 +677,11 @@
 WebContents* PrerenderContents::ReleasePrerenderContents() {
   prerender_contents_->SetDelegate(NULL);
   content::WebContentsObserver::Observe(NULL);
+
+  // Clear the task manager tag we added earlier to our
+  // WebContents since it's no longer a prerender contents.
+  task_management::WebContentsTags::ClearTag(prerender_contents_.get());
+
   return prerender_contents_.release();
 }
 
diff --git a/chrome/browser/task_management/providers/web_contents/prerender_tag.cc b/chrome/browser/task_management/providers/web_contents/prerender_tag.cc
new file mode 100644
index 0000000..6e6ae3d2
--- /dev/null
+++ b/chrome/browser/task_management/providers/web_contents/prerender_tag.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/task_management/providers/web_contents/prerender_tag.h"
+#include "chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.h"
+
+namespace task_management {
+
+PrerenderTask* PrerenderTag::CreateTask() const {
+  return new PrerenderTask(web_contents());
+}
+
+PrerenderTag::PrerenderTag(content::WebContents* web_contents)
+    : WebContentsTag(web_contents) {
+}
+
+PrerenderTag::~PrerenderTag() {
+}
+
+}  // namespace task_management
diff --git a/chrome/browser/task_management/providers/web_contents/prerender_tag.h b/chrome/browser/task_management/providers/web_contents/prerender_tag.h
new file mode 100644
index 0000000..6c6f9593
--- /dev/null
+++ b/chrome/browser/task_management/providers/web_contents/prerender_tag.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_TASK_MANAGEMENT_PROVIDERS_WEB_CONTENTS_PRERENDER_TAG_H_
+#define CHROME_BROWSER_TASK_MANAGEMENT_PROVIDERS_WEB_CONTENTS_PRERENDER_TAG_H_
+
+#include "chrome/browser/task_management/providers/web_contents/prerender_task.h"
+#include "chrome/browser/task_management/providers/web_contents/web_contents_tag.h"
+
+namespace task_management {
+
+// Defines a concrete UserData type for WebContents owned by the
+// PrerenderManager.
+class PrerenderTag : public WebContentsTag {
+ public:
+  // task_management::WebContentsTag:
+  PrerenderTask* CreateTask() const override;
+
+ private:
+  friend class WebContentsTags;
+
+  explicit PrerenderTag(content::WebContents* web_contents);
+  ~PrerenderTag() override;
+
+  DISALLOW_COPY_AND_ASSIGN(PrerenderTag);
+};
+
+}  // namespace task_management
+
+
+#endif  // CHROME_BROWSER_TASK_MANAGEMENT_PROVIDERS_WEB_CONTENTS_PRERENDER_TAG_H_
diff --git a/chrome/browser/task_management/providers/web_contents/prerender_task.cc b/chrome/browser/task_management/providers/web_contents/prerender_task.cc
new file mode 100644
index 0000000..dc039b4f
--- /dev/null
+++ b/chrome/browser/task_management/providers/web_contents/prerender_task.cc
@@ -0,0 +1,59 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/task_management/providers/web_contents/prerender_task.h"
+
+#include "chrome/grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace task_management {
+
+namespace {
+
+gfx::ImageSkia* g_prerender_icon = nullptr;
+
+// Returns the prerender icon or |nullptr| if the |ResourceBundle| is not ready
+// yet.
+gfx::ImageSkia* GetPrerenderIcon() {
+  if (g_prerender_icon)
+    return g_prerender_icon;
+
+  if (!ResourceBundle::HasSharedInstance())
+    return nullptr;
+
+  g_prerender_icon =
+      ResourceBundle::GetSharedInstance().GetImageSkiaNamed(IDR_PRERENDER);
+  return g_prerender_icon;
+}
+
+base::string16 PrefixTitle(const base::string16& title) {
+  return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PRERENDER_PREFIX, title);
+}
+
+}  // namespace
+
+PrerenderTask::PrerenderTask(content::WebContents* web_contents)
+    : RendererTask(
+          PrefixTitle(RendererTask::GetTitleFromWebContents(web_contents)),
+          GetPrerenderIcon(),
+          web_contents) {
+}
+
+PrerenderTask::~PrerenderTask() {
+}
+
+void PrerenderTask::OnTitleChanged(content::NavigationEntry* entry) {
+  // As long as this task lives we keep prefixing its title with "Prerender:".
+  set_title(PrefixTitle(RendererTask::GetTitleFromWebContents(web_contents())));
+}
+
+void PrerenderTask::OnFaviconChanged() {
+  // As long as this task lives we keep using the prerender icon, so we ignore
+  // this event.
+}
+
+}  // namespace task_management
diff --git a/chrome/browser/task_management/providers/web_contents/prerender_task.h b/chrome/browser/task_management/providers/web_contents/prerender_task.h
new file mode 100644
index 0000000..7d4d1d0
--- /dev/null
+++ b/chrome/browser/task_management/providers/web_contents/prerender_task.h
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_TASK_MANAGEMENT_PROVIDERS_WEB_CONTENTS_PRERENDER_TASK_H_
+#define CHROME_BROWSER_TASK_MANAGEMENT_PROVIDERS_WEB_CONTENTS_PRERENDER_TASK_H_
+
+#include "chrome/browser/task_management/providers/web_contents/renderer_task.h"
+
+namespace task_management {
+
+// Defines a task manager representation of WebContents owned by the
+// PrerenderManager.
+class PrerenderTask : public RendererTask {
+ public:
+  explicit PrerenderTask(content::WebContents* web_contents);
+  ~PrerenderTask() override;
+
+  // task_management::RendererTask:
+  void OnTitleChanged(content::NavigationEntry* entry) override;
+  void OnFaviconChanged() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PrerenderTask);
+};
+
+}  // namespace task_management
+
+#endif  // CHROME_BROWSER_TASK_MANAGEMENT_PROVIDERS_WEB_CONTENTS_PRERENDER_TASK_H_
diff --git a/chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.cc b/chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.cc
index b61ef1e..44d0c64 100644
--- a/chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.cc
+++ b/chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.cc
@@ -47,6 +47,11 @@
   provider_ = nullptr;
 }
 
+void WebContentsTagsManager::ClearFromProvider(const WebContentsTag* tag) {
+  if (provider_)
+    provider_->OnWebContentsTagRemoved(tag);
+}
+
 WebContentsTagsManager::WebContentsTagsManager()
     : provider_(nullptr) {
 }
diff --git a/chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.h b/chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.h
index 62ac3cf..da4a315 100644
--- a/chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.h
+++ b/chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.h
@@ -33,6 +33,12 @@
   void SetProvider(WebContentsTaskProvider* provider);
   void ClearProvider();
 
+  // This is called by WebContentsTags::ClearTag(). This is needed for Tags
+  // whose destruction does not correspond to the destruction of their
+  // WebContents. In this case the provider (if any) must be manually cleared,
+  // or else the corresponding task for the |tag| will continue to exist.
+  void ClearFromProvider(const WebContentsTag* tag);
+
   const std::set<WebContentsTag*>& tracked_tags() const {
     return tracked_tags_;
   }
diff --git a/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.cc b/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.cc
index 7cd7c0d6..4c24931 100644
--- a/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.cc
+++ b/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.cc
@@ -35,6 +35,10 @@
   // entry's WebContents.
   void CreateAllTasks();
 
+  // Clears all the tasks in this entry. The provider's observer will be
+  // notified if |notify_observer| is true.
+  void ClearAllTasks(bool notify_observer);
+
   // Returns the |RendererTask| that corresponds to the given
   // |render_frame_host| or |nullptr| if the given frame is not tracked by this
   // entry.
@@ -61,10 +65,6 @@
   // notifies the provider's observer of the tasks removal.
   void ClearTaskForFrame(RenderFrameHost* render_frame_host);
 
-  // Clears all the tasks in this entry. The provider's observer will be
-  // notified if |notify_observer| is true.
-  void ClearAllTasks(bool notify_observer);
-
   // The provider that owns this entry.
   WebContentsTaskProvider* provider_;
 
@@ -106,6 +106,21 @@
                                           base::Unretained(this)));
 }
 
+void WebContentsEntry::ClearAllTasks(bool notify_observer) {
+  for (auto& pair : frames_by_site_instance_) {
+    FramesList& frames_list = pair.second;
+    DCHECK(!frames_list.empty());
+    RendererTask* task = tasks_by_frames_[frames_list[0]];
+    if (notify_observer)
+      provider_->NotifyObserverTaskRemoved(task);
+    delete task;
+  }
+
+  frames_by_site_instance_.clear();
+  tasks_by_frames_.clear();
+  main_frame_site_instance_ = nullptr;
+}
+
 RendererTask* WebContentsEntry::GetTaskForFrame(
     RenderFrameHost* render_frame_host) const {
   auto itr = tasks_by_frames_.find(render_frame_host);
@@ -238,21 +253,6 @@
   }
 }
 
-void WebContentsEntry::ClearAllTasks(bool notify_observer) {
-  for (auto& pair : frames_by_site_instance_) {
-    FramesList& frames_list = pair.second;
-    DCHECK(!frames_list.empty());
-    RendererTask* task = tasks_by_frames_[frames_list[0]];
-    if (notify_observer)
-      provider_->NotifyObserverTaskRemoved(task);
-    delete task;
-  }
-
-  frames_by_site_instance_.clear();
-  tasks_by_frames_.clear();
-  main_frame_site_instance_ = nullptr;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 
 WebContentsTaskProvider::WebContentsTaskProvider() : entries_map_() {
@@ -262,7 +262,8 @@
   STLDeleteValues(&entries_map_);
 }
 
-void WebContentsTaskProvider::OnWebContentsTagCreated(WebContentsTag* tag) {
+void WebContentsTaskProvider::OnWebContentsTagCreated(
+    const WebContentsTag* tag) {
   DCHECK(tag);
   content::WebContents* web_contents = tag->web_contents();
   DCHECK(web_contents);
@@ -282,6 +283,22 @@
   entry->CreateAllTasks();
 }
 
+void WebContentsTaskProvider::OnWebContentsTagRemoved(
+    const WebContentsTag* tag) {
+  DCHECK(tag);
+  content::WebContents* web_contents = tag->web_contents();
+  DCHECK(web_contents);
+
+  auto itr = entries_map_.find(web_contents);
+  DCHECK(itr != entries_map_.end());
+  WebContentsEntry* entry = itr->second;
+
+  // Must manually clear the tasks and notify the observer.
+  entry->ClearAllTasks(true);
+  entries_map_.erase(itr);
+  delete entry;
+}
+
 Task* WebContentsTaskProvider::GetTaskOfUrlRequest(int origin_pid,
                                                    int child_id,
                                                    int route_id) {
diff --git a/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.h b/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.h
index 0104cac..cc9c7f3 100644
--- a/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.h
+++ b/chrome/browser/task_management/providers/web_contents/web_contents_task_provider.h
@@ -29,7 +29,10 @@
 
   // This will be called every time we're notified that a new |WebContentsTag|
   // has been created.
-  void OnWebContentsTagCreated(WebContentsTag* tag);
+  void OnWebContentsTagCreated(const WebContentsTag* tag);
+
+  // Manually remove |tag|'s corresponding Task.
+  void OnWebContentsTagRemoved(const WebContentsTag* tag);
 
   // task_management::TaskProvider:
   Task* GetTaskOfUrlRequest(int origin_pid,
diff --git a/chrome/browser/task_management/web_contents_tags.cc b/chrome/browser/task_management/web_contents_tags.cc
index 72adce1..9371a0d 100644
--- a/chrome/browser/task_management/web_contents_tags.cc
+++ b/chrome/browser/task_management/web_contents_tags.cc
@@ -6,11 +6,13 @@
 
 #include "chrome/browser/task_management/providers/web_contents/background_contents_tag.h"
 #include "chrome/browser/task_management/providers/web_contents/devtools_tag.h"
+#include "chrome/browser/task_management/providers/web_contents/prerender_tag.h"
 #include "chrome/browser/task_management/providers/web_contents/web_contents_tags_manager.h"
 #include "content/public/browser/web_contents.h"
 
 namespace task_management {
 
+#if defined(ENABLE_TASK_MANAGER)
 namespace {
 
 // Adds the |tag| to |contents|. It also adds the |tag| to the
@@ -27,27 +29,53 @@
 }
 
 }  // namespace
+#endif  // defined(ENABLE_TASK_MANAGER)
 
 // static
 void WebContentsTags::CreateForBackgroundContents(
     content::WebContents* web_contents,
     BackgroundContents* background_contents) {
+#if defined(ENABLE_TASK_MANAGER)
   if (!WebContentsTag::FromWebContents(web_contents)) {
     TagWebContents(
         web_contents,
         new BackgroundContentsTag(web_contents, background_contents),
         WebContentsTag::kTagKey);
   }
+#endif  // defined(ENABLE_TASK_MANAGER)
 }
 
 // static
 void WebContentsTags::CreateForDevToolsContents(
     content::WebContents* web_contents) {
+#if defined(ENABLE_TASK_MANAGER)
   if (!WebContentsTag::FromWebContents(web_contents)) {
     TagWebContents(web_contents,
                    new DevToolsTag(web_contents),
                    WebContentsTag::kTagKey);
   }
+#endif  // defined(ENABLE_TASK_MANAGER)
+}
+
+// static
+void WebContentsTags::CreateForPrerenderContents(
+    content::WebContents* web_contents) {
+#if defined(ENABLE_TASK_MANAGER)
+  if (!WebContentsTag::FromWebContents(web_contents)) {
+    TagWebContents(web_contents,
+                   new PrerenderTag(web_contents),
+                   WebContentsTag::kTagKey);
+  }
+#endif  // defined(ENABLE_TASK_MANAGER)
+}
+
+// static
+void WebContentsTags::ClearTag(content::WebContents* web_contents) {
+#if defined(ENABLE_TASK_MANAGER)
+  const WebContentsTag* tag = WebContentsTag::FromWebContents(web_contents);
+  WebContentsTagsManager::GetInstance()->ClearFromProvider(tag);
+  web_contents->RemoveUserData(WebContentsTag::kTagKey);
+#endif  // defined(ENABLE_TASK_MANAGER)
 }
 
 }  // namespace task_management
diff --git a/chrome/browser/task_management/web_contents_tags.h b/chrome/browser/task_management/web_contents_tags.h
index d81b88e..3c929393 100644
--- a/chrome/browser/task_management/web_contents_tags.h
+++ b/chrome/browser/task_management/web_contents_tags.h
@@ -39,6 +39,19 @@
   // not have to be cleaned up by the caller, as it is owned by |web_contents|.
   static void CreateForDevToolsContents(content::WebContents* web_contents);
 
+  // Tag a WebContents owned by the PrerenderManager so that it shows up in the
+  // task manager. Calling this function creates a PrerenderTag, and attaches it
+  // to |web_contents|. If an instance is already attached, this does nothing.
+  // The resulting tag does not have to be cleaned up by the caller, as it is
+  // owned by |web_contents|.
+  static void CreateForPrerenderContents(content::WebContents* web_contents);
+
+  // Clears the task-manager tag, created by any of the above functions, from
+  // the given |web_contents| if any.
+  // Clearing the tag is necessary only when you need to re-tag an existing
+  // WebContents, to indicate a change in ownership.
+  static void ClearTag(content::WebContents* web_contents);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(WebContentsTags);
 };