Introduce content::GetUIThreadTaskRunner() and its IO counterpart

As agreed upon in design doc @
https://docs.google.com/document/d/1tssusPykvx3g0gvbvU4HxGyn3MjJlIylnsH13-Tv6s4/edit?ts=5e1c66d5&pli=1#bookmark=id.ll79iqi5rlpp

base::ThreadPool counterpart to move away from post_task.h is @
https://chromium-review.googlesource.com/c/chromium/src/+/1977964

API usage migration will be done in a follow-up mega CL.
This CL at least uses it in its own tests.

See https://chromium-review.googlesource.com/c/chromium/src/+/2015655
for how this will look in practice for a few callsites.

Bug: 1026641
Change-Id: I98771fd68a513bf0a4776672a8d75fcbbb200bea
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2014055
Commit-Queue: Gabriel Charette <[email protected]>
Reviewed-by: Alexander Timin <[email protected]>
Reviewed-by: François Doray <[email protected]>
Reviewed-by: Darin Fisher <[email protected]>
Cr-Commit-Position: refs/heads/master@{#735734}
diff --git a/base/task/post_task.cc b/base/task/post_task.cc
index 195a29a42..95d4302 100644
--- a/base/task/post_task.cc
+++ b/base/task/post_task.cc
@@ -45,7 +45,8 @@
       traits.extension_id() != TaskTraitsExtensionStorage::kInvalidExtensionId;
   DCHECK(has_extension ^ traits.use_thread_pool())
       << "A destination (e.g. ThreadPool or BrowserThread) must be specified "
-         "to use the post_task.h API.";
+         "to use the post_task.h API. However, you should prefer the direct "
+         "thread_pool.h or browser_thread.h APIs in new code.";
 
   if (traits.use_thread_pool()) {
     DCHECK(ThreadPoolInstance::Get())
diff --git a/content/browser/browser_thread_impl.cc b/content/browser/browser_thread_impl.cc
index b3eedf8..87535e6 100644
--- a/content/browser/browser_thread_impl.cc
+++ b/content/browser/browser_thread_impl.cc
@@ -84,6 +84,16 @@
 
 }  // namespace
 
+scoped_refptr<base::SingleThreadTaskRunner> GetUIThreadTaskRunner(
+    const BrowserTaskTraits& traits) {
+  return BrowserTaskExecutor::GetUIThreadTaskRunner(traits);
+}
+
+scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner(
+    const BrowserTaskTraits& traits) {
+  return BrowserTaskExecutor::GetIOThreadTaskRunner(traits);
+}
+
 BrowserThreadImpl::BrowserThreadImpl(
     ID identifier,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
diff --git a/content/browser/browser_thread_unittest.cc b/content/browser/browser_thread_unittest.cc
index 2fa2e583..8456adf 100644
--- a/content/browser/browser_thread_unittest.cc
+++ b/content/browser/browser_thread_unittest.cc
@@ -165,8 +165,7 @@
   explicit UIThreadDestructionObserver(bool* did_shutdown,
                                        base::OnceClosure callback)
       : callback_task_runner_(base::ThreadTaskRunnerHandle::Get()),
-        ui_task_runner_(
-            base::CreateSingleThreadTaskRunner({BrowserThread::UI})),
+        ui_task_runner_(GetUIThreadTaskRunner({})),
         callback_(std::move(callback)),
         did_shutdown_(did_shutdown) {
     ui_task_runner_->PostTask(FROM_HERE, base::BindOnce(&Watch, this));
@@ -223,8 +222,7 @@
 }
 
 TEST_F(BrowserThreadTest, PostTaskViaTaskRunner) {
-  scoped_refptr<base::TaskRunner> task_runner =
-      base::CreateTaskRunner({BrowserThread::IO});
+  scoped_refptr<base::TaskRunner> task_runner = GetIOThreadTaskRunner({});
   base::RunLoop run_loop;
   EXPECT_TRUE(task_runner->PostTask(
       FROM_HERE, base::BindOnce(&BasicFunction, run_loop.QuitWhenIdleClosure(),
@@ -234,7 +232,7 @@
 
 TEST_F(BrowserThreadTest, PostTaskViaSequencedTaskRunner) {
   scoped_refptr<base::SequencedTaskRunner> task_runner =
-      base::CreateSequencedTaskRunner({BrowserThread::IO});
+      GetIOThreadTaskRunner({});
   base::RunLoop run_loop;
   EXPECT_TRUE(task_runner->PostTask(
       FROM_HERE, base::BindOnce(&BasicFunction, run_loop.QuitWhenIdleClosure(),
@@ -244,7 +242,7 @@
 
 TEST_F(BrowserThreadTest, PostTaskViaSingleThreadTaskRunner) {
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      base::CreateSingleThreadTaskRunner({BrowserThread::IO});
+      GetIOThreadTaskRunner({});
   base::RunLoop run_loop;
   EXPECT_TRUE(task_runner->PostTask(
       FROM_HERE, base::BindOnce(&BasicFunction, run_loop.QuitWhenIdleClosure(),
@@ -255,7 +253,7 @@
 #if defined(OS_WIN)
 TEST_F(BrowserThreadTest, PostTaskViaCOMSTATaskRunner) {
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      base::CreateCOMSTATaskRunner({BrowserThread::UI});
+      GetUIThreadTaskRunner({});
   base::RunLoop run_loop;
   EXPECT_TRUE(task_runner->PostTask(
       FROM_HERE, base::BindOnce(&BasicFunction, run_loop.QuitWhenIdleClosure(),
@@ -266,7 +264,7 @@
 
 TEST_F(BrowserThreadTest, ReleaseViaTaskRunner) {
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      base::CreateSingleThreadTaskRunner({BrowserThread::UI});
+      GetUIThreadTaskRunner({});
   base::RunLoop run_loop;
   ExpectRelease(run_loop.QuitWhenIdleClosure());
   task_runner->ReleaseSoon(FROM_HERE, base::WrapRefCounted(this));
@@ -277,9 +275,8 @@
   // Most of the heavy testing for PostTaskAndReply() is done inside the
   // task runner test.  This just makes sure we get piped through at all.
   base::RunLoop run_loop;
-  ASSERT_TRUE(base::PostTaskAndReply(FROM_HERE, {BrowserThread::IO},
-                                     base::DoNothing(),
-                                     run_loop.QuitWhenIdleClosure()));
+  ASSERT_TRUE(GetIOThreadTaskRunner({})->PostTaskAndReply(
+      FROM_HERE, base::DoNothing(), run_loop.QuitWhenIdleClosure()));
   run_loop.Run();
 }
 
diff --git a/content/browser/scheduler/browser_task_executor.cc b/content/browser/scheduler/browser_task_executor.cc
index 02a59ec..eb9f505 100644
--- a/content/browser/scheduler/browser_task_executor.cc
+++ b/content/browser/scheduler/browser_task_executor.cc
@@ -12,6 +12,7 @@
 #include "base/no_destructor.h"
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
+#include "base/task/task_traits_extension.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -24,47 +25,27 @@
 #include "base/android/task_scheduler/post_task_android.h"
 #endif
 
+using QueueType = content::BrowserTaskQueues::QueueType;
+
 namespace content {
 namespace {
 
-using QueueType = ::content::BrowserTaskQueues::QueueType;
+// Returns the BrowserThread::ID stored in |traits| which must be coming from a
+// call through BaseBrowserTaskExecutor and hence have the
+// BrowserTaskTraitsExtension.
+BrowserThread::ID ExtractBrowserThreadId(const base::TaskTraits& traits) {
+  DCHECK_EQ(BrowserTaskTraitsExtension::kExtensionId, traits.extension_id());
+  const BrowserTaskTraitsExtension extension =
+      traits.GetExtension<BrowserTaskTraitsExtension>();
+
+  const BrowserThread::ID thread_id = extension.browser_thread();
+  DCHECK_GE(thread_id, 0);
+  return thread_id;
+}
 
 // |g_browser_task_executor| is intentionally leaked on shutdown.
 BrowserTaskExecutor* g_browser_task_executor = nullptr;
 
-QueueType GetQueueType(const base::TaskTraits& traits,
-                       BrowserTaskType task_type) {
-  switch (task_type) {
-    case BrowserTaskType::kBootstrap:
-      // Note we currently ignore the priority for bootstrap tasks.
-      return QueueType::kBootstrap;
-
-    case BrowserTaskType::kNavigation:
-    case BrowserTaskType::kPreconnect:
-      // Note we currently ignore the priority for navigation and preconnection
-      // tasks.
-      return QueueType::kNavigationAndPreconnection;
-
-    case BrowserTaskType::kDefault:
-      // Defer to traits.priority() below.
-      break;
-
-    case BrowserTaskType::kBrowserTaskType_Last:
-      NOTREACHED();
-  }
-
-  switch (traits.priority()) {
-    case base::TaskPriority::BEST_EFFORT:
-      return QueueType::kBestEffort;
-
-    case base::TaskPriority::USER_VISIBLE:
-      return QueueType::kUserVisible;
-
-    case base::TaskPriority::USER_BLOCKING:
-      return QueueType::kUserBlocking;
-  }
-}
-
 }  // namespace
 
 BaseBrowserTaskExecutor::BaseBrowserTaskExecutor() = default;
@@ -77,30 +58,30 @@
                                               base::TimeDelta delay) {
   if (traits.extension_id() != BrowserTaskTraitsExtension::kExtensionId ||
       traits.GetExtension<BrowserTaskTraitsExtension>().nestable()) {
-    return GetTaskRunner(traits)->PostDelayedTask(from_here, std::move(task),
-                                                  delay);
+    return GetTaskRunner(ExtractBrowserThreadId(traits), traits)
+        ->PostDelayedTask(from_here, std::move(task), delay);
   } else {
-    return GetTaskRunner(traits)->PostNonNestableDelayedTask(
-        from_here, std::move(task), delay);
+    return GetTaskRunner(ExtractBrowserThreadId(traits), traits)
+        ->PostNonNestableDelayedTask(from_here, std::move(task), delay);
   }
 }
 
 scoped_refptr<base::TaskRunner> BaseBrowserTaskExecutor::CreateTaskRunner(
     const base::TaskTraits& traits) {
-  return GetTaskRunner(traits);
+  return GetTaskRunner(ExtractBrowserThreadId(traits), traits);
 }
 
 scoped_refptr<base::SequencedTaskRunner>
 BaseBrowserTaskExecutor::CreateSequencedTaskRunner(
     const base::TaskTraits& traits) {
-  return GetTaskRunner(traits);
+  return GetTaskRunner(ExtractBrowserThreadId(traits), traits);
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
 BaseBrowserTaskExecutor::CreateSingleThreadTaskRunner(
     const base::TaskTraits& traits,
     base::SingleThreadTaskRunnerThreadMode thread_mode) {
-  return GetTaskRunner(traits);
+  return GetTaskRunner(ExtractBrowserThreadId(traits), traits);
 }
 
 #if defined(OS_WIN)
@@ -108,42 +89,71 @@
 BaseBrowserTaskExecutor::CreateCOMSTATaskRunner(
     const base::TaskTraits& traits,
     base::SingleThreadTaskRunnerThreadMode thread_mode) {
-  return GetTaskRunner(traits);
+  return GetTaskRunner(ExtractBrowserThreadId(traits), traits);
 }
 #endif  // defined(OS_WIN)
 
 scoped_refptr<base::SingleThreadTaskRunner>
-BaseBrowserTaskExecutor::GetTaskRunner(const base::TaskTraits& traits) const {
-  auto id_and_queue = GetThreadIdAndQueueType(traits);
+BaseBrowserTaskExecutor::GetTaskRunner(BrowserThread::ID identifier,
+                                       const base::TaskTraits& traits) const {
+  DCHECK(traits.extension_id() ==
+             base::TaskTraitsExtensionStorage::kInvalidExtensionId ||
+         ExtractBrowserThreadId(traits) == identifier);
 
-  switch (id_and_queue.thread_id) {
+  const QueueType queue_type = GetQueueType(traits);
+
+  switch (identifier) {
     case BrowserThread::UI: {
-      return browser_ui_thread_handle_->GetBrowserTaskRunner(
-          id_and_queue.queue_type);
+      return browser_ui_thread_handle_->GetBrowserTaskRunner(queue_type);
     }
     case BrowserThread::IO:
-      return browser_io_thread_handle_->GetBrowserTaskRunner(
-          id_and_queue.queue_type);
+      return browser_io_thread_handle_->GetBrowserTaskRunner(queue_type);
     case BrowserThread::ID_COUNT:
       NOTREACHED();
   }
   return nullptr;
 }
 
-BaseBrowserTaskExecutor::ThreadIdAndQueueType
-BaseBrowserTaskExecutor::GetThreadIdAndQueueType(
-    const base::TaskTraits& traits) const {
-  DCHECK_EQ(BrowserTaskTraitsExtension::kExtensionId, traits.extension_id());
-  const BrowserTaskTraitsExtension extension =
-      traits.GetExtension<BrowserTaskTraitsExtension>();
+// static
+QueueType BaseBrowserTaskExecutor::GetQueueType(
+    const base::TaskTraits& traits) {
+  if (traits.extension_id() == BrowserTaskTraitsExtension::kExtensionId) {
+    const BrowserTaskTraitsExtension extension =
+        traits.GetExtension<BrowserTaskTraitsExtension>();
 
-  const BrowserThread::ID thread_id = extension.browser_thread();
-  DCHECK_GE(thread_id, 0);
+    const BrowserTaskType task_type = extension.task_type();
+    DCHECK_LT(task_type, BrowserTaskType::kBrowserTaskType_Last);
 
-  const BrowserTaskType task_type = extension.task_type();
-  DCHECK_LT(task_type, BrowserTaskType::kBrowserTaskType_Last);
+    switch (task_type) {
+      case BrowserTaskType::kBootstrap:
+        // Note we currently ignore the priority for bootstrap tasks.
+        return QueueType::kBootstrap;
 
-  return {thread_id, GetQueueType(traits, task_type)};
+      case BrowserTaskType::kNavigation:
+      case BrowserTaskType::kPreconnect:
+        // Note we currently ignore the priority for navigation and
+        // preconnection tasks.
+        return QueueType::kNavigationAndPreconnection;
+
+      case BrowserTaskType::kDefault:
+        // Defer to traits.priority() below.
+        break;
+
+      case BrowserTaskType::kBrowserTaskType_Last:
+        NOTREACHED();
+    }
+  }
+
+  switch (traits.priority()) {
+    case base::TaskPriority::BEST_EFFORT:
+      return QueueType::kBestEffort;
+
+    case base::TaskPriority::USER_VISIBLE:
+      return QueueType::kUserVisible;
+
+    case base::TaskPriority::USER_BLOCKING:
+      return QueueType::kUserBlocking;
+  }
 }
 
 BrowserTaskExecutor::BrowserTaskExecutor(
@@ -283,6 +293,20 @@
 }
 
 // static
+scoped_refptr<base::SingleThreadTaskRunner>
+BrowserTaskExecutor::GetUIThreadTaskRunner(const BrowserTaskTraits& traits) {
+  DCHECK(g_browser_task_executor);
+  return g_browser_task_executor->GetTaskRunner(BrowserThread::UI, traits);
+}
+
+// static
+scoped_refptr<base::SingleThreadTaskRunner>
+BrowserTaskExecutor::GetIOThreadTaskRunner(const BrowserTaskTraits& traits) {
+  DCHECK(g_browser_task_executor);
+  return g_browser_task_executor->GetTaskRunner(BrowserThread::IO, traits);
+}
+
+// static
 void BrowserTaskExecutor::InitializeIOThread() {
   DCHECK(g_browser_task_executor);
   g_browser_task_executor->browser_io_thread_handle_
diff --git a/content/browser/scheduler/browser_task_executor.h b/content/browser/scheduler/browser_task_executor.h
index 9973575f..e80275d6 100644
--- a/content/browser/scheduler/browser_task_executor.h
+++ b/content/browser/scheduler/browser_task_executor.h
@@ -58,20 +58,25 @@
       base::SingleThreadTaskRunnerThreadMode thread_mode) override;
 #endif  // defined(OS_WIN)
 
-  struct ThreadIdAndQueueType {
-    BrowserThread::ID thread_id;
-    BrowserTaskQueues::QueueType queue_type;
-  };
-
-  ThreadIdAndQueueType GetThreadIdAndQueueType(
+  // Returns the task runner for |traits| under |identifier|. Note: during the
+  // migration away from task traits extension, |traits| may also contain a
+  // browser thread id, if so, it should match |identifier| (|identifier| has to
+  // be provided explicitly because in the new source of tasks it's not part of
+  // |traits|) -- ref. crbug.com/1026641.
+  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
+      BrowserThread::ID identifier,
       const base::TaskTraits& traits) const;
 
+  // Helper to match a QueueType from TaskTraits.
+  // TODO(1026641): Take BrowserTaskTraits as a parameter when getting off the
+  // need to support base::TaskTraits currently passed to this class in its role
+  // as a base::TaskExecutor.
+  static content::BrowserTaskQueues::QueueType GetQueueType(
+      const base::TaskTraits& traits);
+
  protected:
   virtual BrowserThread::ID GetCurrentThreadID() const = 0;
 
-  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
-      const base::TaskTraits& traits) const;
-
   scoped_refptr<BrowserUIThreadScheduler::Handle> browser_ui_thread_handle_;
   scoped_refptr<BrowserIOThreadDelegate::Handle> browser_io_thread_handle_;
 };
@@ -126,6 +131,16 @@
   // Can be called multiple times.
   static void EnableAllQueues();
 
+  // Helpers to statically call into BaseBrowserTaskExecutor::GetTaskRunner()
+  // from browser_thread_impl.cc. Callers should use browser_thread.h's
+  // GetUIThreadTaskRunner over this.
+  // TODO(1026641): Clean up this indirection after the migration (once
+  // registering a base::BrowserTaskExecutor is no longer necessary).
+  static scoped_refptr<base::SingleThreadTaskRunner> GetUIThreadTaskRunner(
+      const BrowserTaskTraits& traits);
+  static scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner(
+      const BrowserTaskTraits& traits);
+
   // As Create but with the user provided objects. Must call
   // BindToUIThreadForTesting before tasks can be run on the UI thread.
   static void CreateForTesting(
diff --git a/content/browser/scheduler/browser_task_executor_unittest.cc b/content/browser/scheduler/browser_task_executor_unittest.cc
index 97a1340..660ae58a 100644
--- a/content/browser/scheduler/browser_task_executor_unittest.cc
+++ b/content/browser/scheduler/browser_task_executor_unittest.cc
@@ -11,7 +11,6 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/test/bind_test_util.h"
@@ -49,15 +48,15 @@
     testing::StrictMock<base::MockCallback<base::RepeatingCallback<void()>>>;
 
 TEST_F(BrowserTaskExecutorTest, RegisterExecutorForBothThreads) {
-  base::PostTask(FROM_HERE, {BrowserThread::UI}, base::BindOnce([]() {
-                   EXPECT_THAT(base::GetTaskExecutorForCurrentThread(),
-                               NotNull());
-                 }));
+  GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce([]() {
+        EXPECT_THAT(base::GetTaskExecutorForCurrentThread(), NotNull());
+      }));
 
-  base::PostTask(FROM_HERE, {BrowserThread::IO}, base::BindOnce([]() {
-                   EXPECT_THAT(base::GetTaskExecutorForCurrentThread(),
-                               NotNull());
-                 }));
+  GetIOThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce([]() {
+        EXPECT_THAT(base::GetTaskExecutorForCurrentThread(), NotNull());
+      }));
 
   BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::UI);
   BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
@@ -70,7 +69,7 @@
     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, task_2.Get());
   }));
 
-  base::PostTask(FROM_HERE, {BrowserThread::UI}, task_1.Get());
+  GetUIThreadTaskRunner({})->PostTask(FROM_HERE, task_1.Get());
 
   BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::UI);
 
@@ -84,10 +83,10 @@
   StrictMockTask task_1;
   StrictMockTask task_2;
   EXPECT_CALL(task_1, Run).WillOnce(testing::Invoke([&]() {
-    base::PostTask(FROM_HERE, {BrowserThread::IO}, task_2.Get());
+    GetIOThreadTaskRunner({})->PostTask(FROM_HERE, task_2.Get());
   }));
 
-  base::PostTask(FROM_HERE, {BrowserThread::IO}, task_1.Get());
+  GetIOThreadTaskRunner({})->PostTask(FROM_HERE, task_1.Get());
 
   BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
 
@@ -103,15 +102,15 @@
   StrictMockTask task_3;
 
   EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() {
-    base::PostTask(FROM_HERE, {BrowserThread::IO}, task_2.Get());
+    GetIOThreadTaskRunner({})->PostTask(FROM_HERE, task_2.Get());
     BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(
         BrowserThread::IO);
   }));
   EXPECT_CALL(task_2, Run).WillOnce(Invoke([&]() {
-    base::PostTask(FROM_HERE, {BrowserThread::IO}, task_3.Get());
+    GetIOThreadTaskRunner({})->PostTask(FROM_HERE, task_3.Get());
   }));
 
-  base::PostTask(FROM_HERE, {BrowserThread::IO}, task_1.Get());
+  GetIOThreadTaskRunner({})->PostTask(FROM_HERE, task_1.Get());
   BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
 
   // Cleanup pending tasks, as BrowserTaskEnvironment will run them.
@@ -134,44 +133,33 @@
     }
   };
 
-  template <BrowserThread::ID ID>
-  void CheckExpectations() {
-    EXPECT_EQ(GetQueueType({ID, TaskPriority::BEST_EFFORT}),
-              QueueType::kBestEffort);
-    EXPECT_EQ(GetQueueType({ID, TaskPriority::USER_VISIBLE}),
-              QueueType::kUserVisible);
-    EXPECT_EQ(GetQueueType({ID, TaskPriority::USER_BLOCKING}),
-              QueueType::kUserBlocking);
-
-    EXPECT_EQ(GetQueueType({ID, BrowserTaskType::kBootstrap}),
-              QueueType::kBootstrap);
-    EXPECT_EQ(GetQueueType({ID, BrowserTaskType::kDefault}),
-              QueueType::kUserBlocking);
-    EXPECT_EQ(GetQueueType({ID, BrowserTaskType::kNavigation}),
-              QueueType::kNavigationAndPreconnection);
-    EXPECT_EQ(GetQueueType({ID, BrowserTaskType::kPreconnect}),
-              QueueType::kNavigationAndPreconnection);
-
-    EXPECT_EQ(GetQueueType({ID}), QueueType::kUserBlocking);
-  }
-
  private:
-  QueueType GetQueueType(const base::TaskTraits& traits) {
-    return test_executor_.GetThreadIdAndQueueType(traits).queue_type;
-  }
-
   TestExecutor test_executor_;
 };
 
 TEST_F(BrowserTaskTraitsMappingTest, BrowserTaskTraitsMapToProperPriorities) {
-  CheckExpectations<BrowserThread::UI>();
-  CheckExpectations<BrowserThread::IO>();
+  EXPECT_EQ(BrowserTaskExecutor::GetQueueType({TaskPriority::BEST_EFFORT}),
+            QueueType::kBestEffort);
+  EXPECT_EQ(BrowserTaskExecutor::GetQueueType({TaskPriority::USER_VISIBLE}),
+            QueueType::kUserVisible);
+  EXPECT_EQ(BrowserTaskExecutor::GetQueueType({TaskPriority::USER_BLOCKING}),
+            QueueType::kUserBlocking);
+
+  EXPECT_EQ(BrowserTaskExecutor::GetQueueType({BrowserTaskType::kBootstrap}),
+            QueueType::kBootstrap);
+  EXPECT_EQ(BrowserTaskExecutor::GetQueueType({BrowserTaskType::kDefault}),
+            QueueType::kUserBlocking);
+  EXPECT_EQ(BrowserTaskExecutor::GetQueueType({BrowserTaskType::kNavigation}),
+            QueueType::kNavigationAndPreconnection);
+  EXPECT_EQ(BrowserTaskExecutor::GetQueueType({BrowserTaskType::kPreconnect}),
+            QueueType::kNavigationAndPreconnection);
+
+  EXPECT_EQ(BrowserTaskExecutor::GetQueueType({}), QueueType::kUserBlocking);
 }
 
 TEST_F(BrowserTaskTraitsMappingTest,
        UIThreadTaskRunnerHasSamePriorityAsUIBlocking) {
-  auto ui_blocking = base::CreateSingleThreadTaskRunner(
-      {BrowserThread::UI, TaskPriority::USER_BLOCKING});
+  auto ui_blocking = GetUIThreadTaskRunner({TaskPriority::USER_BLOCKING});
   auto thread_task_runner = base::ThreadTaskRunnerHandle::Get();
 
   std::vector<int> order;
@@ -213,8 +201,6 @@
   };
 
  public:
-  using QueueType = BrowserTaskQueues::QueueType;
-
   ~BrowserTaskExecutorWithCustomSchedulerTest() override {
     BrowserTaskExecutor::ResetForTesting();
   }
@@ -229,14 +215,17 @@
   StrictMockTask user_visible;
   StrictMockTask user_blocking;
 
-  base::PostTask(FROM_HERE,
-                 {BrowserThread::UI, base::TaskPriority::BEST_EFFORT},
+  GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
+      ->PostTask(FROM_HERE,
+
                  best_effort.Get());
-  base::PostTask(FROM_HERE,
-                 {BrowserThread::UI, base::TaskPriority::USER_VISIBLE},
+  GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE})
+      ->PostTask(FROM_HERE,
+
                  user_visible.Get());
-  base::PostTask(FROM_HERE,
-                 {BrowserThread::UI, base::TaskPriority::USER_BLOCKING},
+  GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
+      ->PostTask(FROM_HERE,
+
                  user_blocking.Get());
 
   EXPECT_CALL(user_visible, Run);
@@ -247,19 +236,20 @@
 
 TEST_F(BrowserTaskExecutorWithCustomSchedulerTest,
        BestEffortTasksRunAfterStartup) {
-  auto ui_best_effort_runner = base::CreateSingleThreadTaskRunner(
-      {BrowserThread::UI, base::TaskPriority::BEST_EFFORT});
+  auto ui_best_effort_runner =
+      GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT});
 
   StrictMockTask best_effort;
 
   ui_best_effort_runner->PostTask(FROM_HERE, best_effort.Get());
   ui_best_effort_runner->PostDelayedTask(
       FROM_HERE, best_effort.Get(), base::TimeDelta::FromMilliseconds(100));
-  base::PostDelayedTask(
-      FROM_HERE, {BrowserThread::UI, base::TaskPriority::BEST_EFFORT},
-      best_effort.Get(), base::TimeDelta::FromMilliseconds(100));
-  base::PostTask(FROM_HERE,
-                 {BrowserThread::UI, base::TaskPriority::BEST_EFFORT},
+  GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
+      ->PostDelayedTask(FROM_HERE, best_effort.Get(),
+                        base::TimeDelta::FromMilliseconds(100));
+  GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
+      ->PostTask(FROM_HERE,
+
                  best_effort.Get());
   task_environment_.RunUntilIdle();
 
diff --git a/content/public/browser/browser_task_traits.h b/content/public/browser/browser_task_traits.h
index 060ba21..cb0637e 100644
--- a/content/public/browser/browser_task_traits.h
+++ b/content/public/browser/browser_task_traits.h
@@ -80,7 +80,8 @@
           base::trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
   constexpr BrowserTaskTraitsExtension(ArgTypes... args)
       : browser_thread_(
-            base::trait_helpers::GetEnum<BrowserThread::ID>(args...)),
+            base::trait_helpers::GetEnum<BrowserThread::ID,
+                                         BrowserThread::ID_COUNT>(args...)),
         task_type_(
             base::trait_helpers::GetEnum<BrowserTaskType,
                                          BrowserTaskType::kDefault>(args...)),
@@ -105,7 +106,13 @@
         static_cast<bool>(extension.data[2]));
   }
 
-  constexpr BrowserThread::ID browser_thread() const { return browser_thread_; }
+  constexpr BrowserThread::ID browser_thread() const {
+    // TODO(1026641): Migrate to BrowserTaskTraits under which BrowserThread is
+    // not a trait. Until then, only code that knows traits have explicitly set
+    // the BrowserThread trait should check this field.
+    DCHECK_NE(browser_thread_, BrowserThread::ID_COUNT);
+    return browser_thread_;
+  }
 
   constexpr BrowserTaskType task_type() const { return task_type_; }
 
@@ -135,6 +142,40 @@
       .Serialize();
 }
 
+class CONTENT_EXPORT BrowserTaskTraits : public base::TaskTraits {
+ public:
+  struct ValidTrait : public base::TaskTraits::ValidTrait {
+    ValidTrait(BrowserTaskType);
+    ValidTrait(NonNestable);
+
+    // TODO(1026641): Reconsider whether BrowserTaskTraits should really be
+    // supporting base::TaskPriority.
+    ValidTrait(base::TaskPriority);
+  };
+
+  // TODO(1026641): Get rid of BrowserTaskTraitsExtension and store its members
+  // (|task_type_| & |nestable_|) directly in BrowserTaskTraits.
+  template <
+      class... ArgTypes,
+      class CheckArgumentsAreValid = std::enable_if_t<
+          base::trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>>
+  constexpr BrowserTaskTraits(ArgTypes... args) : base::TaskTraits(args...) {}
+
+  BrowserTaskType task_type() {
+    return GetExtension<BrowserTaskTraitsExtension>().task_type();
+  }
+
+  // Returns true if tasks with these traits may run in a nested RunLoop.
+  bool nestable() const {
+    return GetExtension<BrowserTaskTraitsExtension>().nestable();
+  }
+};
+
+static_assert(sizeof(BrowserTaskTraits) == sizeof(base::TaskTraits),
+              "During the migration away from BrowserTasktraitsExtension, "
+              "BrowserTaskTraits must only use base::TaskTraits for storage "
+              "to prevent slicing.");
+
 }  // namespace content
 
 #endif  // CONTENT_PUBLIC_BROWSER_BROWSER_TASK_TRAITS_H_
diff --git a/content/public/browser/browser_task_traits_unittest.nc b/content/public/browser/browser_task_traits_unittest.nc
index f38fb703..e92fba5 100644
--- a/content/public/browser/browser_task_traits_unittest.nc
+++ b/content/public/browser/browser_task_traits_unittest.nc
@@ -14,11 +14,9 @@
 #if defined(NCTEST_BROWSER_TASK_TRAITS_MULTIPLE_THREADS)  // [r"The traits bag contains multiple traits of the same type."]
 constexpr base::TaskTraits traits = {BrowserThread::UI,
                                      BrowserThread::IO};
-#elif defined(NCTEST_BROWSER_TASK_TRAITS_MULTIPLE_TASK_TYPES)  // [r"The traits bag is missing a required trait."]
+#elif defined(NCTEST_BROWSER_TASK_TRAITS_MULTIPLE_TASK_TYPES)  // [r"The traits bag contains multiple traits of the same type."]
 constexpr base::TaskTraits traits = {BrowserTaskType::kNavigation,
                                      BrowserTaskType::kBootstrap};
-#elif defined(NCTEST_BROWSER_TASK_TRAITS_NO_THREAD_ID)  // [r"The traits bag is missing a required trait."]
-constexpr base::TaskTraits traits = {BrowserTaskType::kNavigation};
 #endif
 
 
diff --git a/content/public/browser/browser_thread.h b/content/public/browser/browser_thread.h
index af24784..3afe558 100644
--- a/content/public/browser/browser_thread.h
+++ b/content/public/browser/browser_thread.h
@@ -21,6 +21,11 @@
 
 namespace content {
 
+// TODO(1026641): Include browser_task_traits.h directly when the migration to
+// Get(UI|IO)ThreadTaskrunner() is complete and the cyclic dependency of
+// browser_task_traits.h on BrowserThread::ID is broken.
+class BrowserTaskTraits;
+
 class BrowserThreadImpl;
 
 // Use DCHECK_CURRENTLY_ON(BrowserThread::ID) to assert that a function can only
@@ -30,34 +35,45 @@
    << ::content::BrowserThread::GetDCheckCurrentlyOnErrorMessage(   \
           thread_identifier))
 
+// The main entry point to post tasks to the UI thread. Tasks posted with the
+// same |traits| will run in posting order (i.e. according to the
+// SequencedTaskRunner contract). Tasks posted with different |traits| can be
+// re-ordered. You may keep a reference to this task runner, it's always
+// thread-safe to post to it though it may start returning false at some point
+// during shutdown when it definitely is no longer accepting tasks.
+//
+// In unit tests, there must be a content::BrowserTaskEnvironment in scope for
+// this API to be available.
+//
+// TODO(1026641): Make default traits |{}| the default param when it's possible
+// to include browser_task_traits.h in this file (see note above on the
+// BrowserTaskTraits fwd-decl).
+CONTENT_EXPORT scoped_refptr<base::SingleThreadTaskRunner>
+GetUIThreadTaskRunner(const BrowserTaskTraits& traits);
+
+// The BrowserThread::IO counterpart to GetUIThreadTaskRunner().
+CONTENT_EXPORT scoped_refptr<base::SingleThreadTaskRunner>
+GetIOThreadTaskRunner(const BrowserTaskTraits& traits);
+
 ///////////////////////////////////////////////////////////////////////////////
 // BrowserThread
 //
-// Utility functions for threads that are known by a browser-wide name.  For
-// example, there is one IO thread for the entire browser process, and various
-// pieces of code find it useful to retrieve a pointer to the IO thread's
-// message loop.
-//
-// See browser_task_traits.h for posting Tasks to a BrowserThread.
-//
-// This class automatically handles the lifetime of different threads. You
-// should never need to cache pointers to MessageLoops, since they're not thread
-// safe.
+// Utility functions for threads that are known by a browser-wide name.
 class CONTENT_EXPORT BrowserThread {
  public:
   // An enumeration of the well-known threads.
-  // NOTE: threads must be listed in the order of their life-time, with each
-  // thread outliving every other thread below it.
   enum ID {
-    // The main thread in the browser.
+    // The main thread in the browser. It stops running tasks during shutdown
+    // and is never joined.
     UI,
 
-    // This is the thread that processes non-blocking IO, i.e. IPC and network.
-    // Blocking I/O should happen in ThreadPool.
+    // This is the thread that processes non-blocking I/O, i.e. IPC and network.
+    // Blocking I/O should happen in base::ThreadPool. It is joined on shutdown
+    // (and thus any task posted to it may block shutdown).
     IO,
 
     // NOTE: do not add new threads here. Instead you should just use
-    // base::Create*TaskRunner to run tasks on the ThreadPool.
+    // base::ThreadPool::Create*TaskRunner to run tasks on the base::ThreadPool.
 
     // This identifier does not represent a thread.  Instead it counts the
     // number of well-known threads.  Insert new well-known threads before this
@@ -65,15 +81,12 @@
     ID_COUNT
   };
 
-  // NOTE: Task posting APIs have moved to post_task.h. See
-  // browser_task_traits.h.
-
   // Delete/ReleaseSoon() helpers allow future deletion of an owned object on
   // its associated thread. If you already have a task runner bound to a
   // BrowserThread you should use its SequencedTaskRunner::DeleteSoon() member
-  // method. If you don't, the helpers below avoid having to do
-  // base::CreateSingleThreadTaskRunner({BrowserThread::ID})->DeleteSoon(...)
-  // which is equivalent.
+  // method.
+  // TODO(1026641): Get rid of the last few callers to these in favor of an
+  // explicit call to content::GetUIThreadTaskRunner({})->DeleteSoon(...).
 
   template <class T>
   static bool DeleteSoon(ID identifier,
@@ -98,7 +111,7 @@
   }
 
   // Posts a |task| to run at BEST_EFFORT priority using an arbitrary
-  // |task_runner| for which we do not control the priority
+  // |task_runner| for which we do not control the priority.
   //
   // This is useful when a task needs to run on |task_runner| (for thread-safety
   // reasons) but should be delayed until after critical phases (e.g. startup).
@@ -196,8 +209,8 @@
 
  private:
   friend class BrowserThreadImpl;
+  BrowserThread() = default;
 
-  BrowserThread() {}
   DISALLOW_COPY_AND_ASSIGN(BrowserThread);
 };
 
diff --git a/docs/threading_and_tasks.md b/docs/threading_and_tasks.md
index 030c8908..998e5251 100644
--- a/docs/threading_and_tasks.md
+++ b/docs/threading_and_tasks.md
@@ -109,7 +109,8 @@
    * in the browser process (BrowserThread::UI): updates the UI
    * in renderer processes (Blink main thread): runs most of Blink
 * an IO thread
-   * in the browser process (BrowserThread::IO): handles IPCs and network requests
+   * in the browser process (BrowserThread::IO): handles IPCs and network
+     requests
    * in renderer processes: handles IPCs
 * a few more special-purpose threads
 * and a pool of general-purpose threads
@@ -222,8 +223,8 @@
 ```
 
 Unless a test needs to control precisely how tasks are executed, it is preferred
-to call `base::ThreadPool::PostTask*()` directly (ref. [Testing](#Testing) for less invasive
-ways of controlling tasks in tests).
+to call `base::ThreadPool::PostTask*()` directly (ref. [Testing](#Testing) for
+less invasive ways of controlling tasks in tests).
 
 ## Posting a Sequenced Task
 
@@ -336,10 +337,17 @@
 ### Posting to the Main Thread or to the IO Thread in the Browser Process
 
 To post tasks to the main thread or to the IO thread, use
-`base::PostTask()` or get the appropriate SingleThreadTaskRunner using
-`base::CreateSingleThreadTaskRunner`, supplying a `BrowserThread::ID`
-as trait. For this, you'll also need to include
-[`content/public/browser/browser_task_traits.h`](https://cs.chromium.org/chromium/src/content/public/browser/browser_task_traits.h).
+`content::GetUIThreadTaskRunner({})` or `content::GetUIThreadTaskRunner({})`
+from
+[`content/public/browser/browser_thread.h`](https://cs.chromium.org/chromium/src/content/public/browser/browser_thread.h)
+
+You may provide additional BrowserTaskTraits as a parameter to those methods
+though this is generally still uncommon in BrowserThreads and should be reserved
+for advanced use cases.
+
+There's an ongoing migration ([task APIs v3]) away from the previous
+base-API-with-traits which you may still find throughout the codebase (it's
+equivalent):
 
 ```cpp
 base::PostTask(FROM_HERE, {content::BrowserThread::UI}, ...);
@@ -348,8 +356,10 @@
     ->PostTask(FROM_HERE, ...);
 ```
 
-Note: This API will soon be updated to follow the API-as-a-destination design
-for [task APIs v3], stay tuned!
+Note: For the duration of the migration, you'll unfortunately need to continue
+manually including
+[`content/public/browser/browser_task_traits.h`](https://cs.chromium.org/chromium/src/content/public/browser/browser_task_traits.h).
+to use the browser_thread.h API.
 
 The main thread and the IO thread are already super busy. Therefore, prefer
 posting to a general purpose thread when possible (ref.
@@ -362,13 +372,14 @@
 send/receive an IPC or send/receive data on the network.
 
 ### Posting to the Main Thread in a Renderer Process
-TODO
+TODO(blink-dev)
 
 ### Posting to a Custom SingleThreadTaskRunner
 
 If multiple tasks need to run on the same thread and that thread doesn’t have to
 be the main thread or the IO thread, post them to a
-`base::SingleThreadTaskRunner` created by `base::Threadpool::CreateSingleThreadTaskRunner`.
+`base::SingleThreadTaskRunner` created by
+`base::Threadpool::CreateSingleThreadTaskRunner`.
 
 ```cpp
 scoped_refptr<SingleThreadTaskRunner> single_thread_task_runner =
@@ -387,13 +398,14 @@
 
 *** note
 **IMPORTANT:** To post a task that needs mutual exclusion with the current
-sequence of tasks but doesn’t absolutely need to run on the current physical thread,
-use `base::SequencedTaskRunnerHandle::Get()` instead of
+sequence of tasks but doesn’t absolutely need to run on the current physical
+thread, use `base::SequencedTaskRunnerHandle::Get()` instead of
 `base::ThreadTaskRunnerHandle::Get()` (ref. [Posting to the Current
-Sequence](#Posting-to-the-Current-Virtual_Thread)). That will better document the
-requirements of the posted task and will avoid unnecessarily making your API
-physical thread-affine. In a single-thread task, `base::SequencedTaskRunnerHandle::Get()`
-is equivalent to `base::ThreadTaskRunnerHandle::Get()`.
+Sequence](#Posting-to-the-Current-Virtual_Thread)). That will better document
+the requirements of the posted task and will avoid unnecessarily making your API
+physical thread-affine. In a single-thread task,
+`base::SequencedTaskRunnerHandle::Get()` is equivalent to
+`base::ThreadTaskRunnerHandle::Get()`.
 ***
 
 If you must post a task to the current physical thread nonetheless, use
@@ -481,11 +493,6 @@
 base::ThreadPool::PostTask(
     FROM_HERE, {base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
     base::BindOnce(...));
-
-// This task will run on the Browser UI thread.
-base::ThreadPool::PostTask(
-    FROM_HERE, {content::BrowserThread::UI},
-    base::BindOnce(...));
 ```
 
 ## Keeping the Browser Responsive