diff --git a/AUTHORS b/AUTHORS
index b9e09ac..32fc92e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -601,6 +601,7 @@
 Max Vujovic <mvujovic@adobe.com>
 Mayank Gupta <mayank.g1@samsung.com>
 Mayur Kankanwadi <mayurk.vk@samsung.com>
+Md Jobed Hossain <jrony15@gmail.com>
 Md Sami Uddin <md.sami@samsung.com>
 Michael Cirone <mikecirone@gmail.com>
 Michael Gilbert <floppymaster@gmail.com>
diff --git a/DEPS b/DEPS
index 23f42ba46..3260f26 100644
--- a/DEPS
+++ b/DEPS
@@ -138,11 +138,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'dbddfff933522762ea164372b7fbe3d760eb78d4',
+  'skia_revision': '213aa46af167261b39803290294a53a77cbbd21d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'c620a2fe71d3b4d8cfa9e7198bad122d72990e6c',
+  'v8_revision': 'b42d4c7a9ade171bbb1bb8b093f3121e661c4eaf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -150,11 +150,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '3f3a05d4307bad7c3b81cc25a092d69b8ecd826d',
+  'angle_revision': '1b6aded661def7ecea1bc6c6f1695d41af981e9e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '42232ab59a188c814fa55315aa2d26d9674bc068',
+  'swiftshader_revision': '71c012b1b9565d1bb0617f180bb2163b45c6d4d5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -257,7 +257,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '37ae8671a50003d289f96fa0a31e5d06f2e9a74f',
+  'spv_tools_revision': '6c7db9c63098b02739ca585c82e22b58a80a8cba',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -273,7 +273,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '3dd6153eb73e967ee99917879bf4aba237a98f0b',
+  'dawn_revision': '8b5fc36191d7bbe8da7af1b19d1cd509deda3880',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -807,7 +807,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '6fb0eb821ce64267ab5c052e7f002eeeea297062',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '6c8f0250717b4859e25f9b72092a6e805c57b7f4',
       'condition': 'checkout_linux',
   },
 
@@ -1350,7 +1350,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '2370242acf4ccacccf554137bd735d21aae3e53f',
+    Var('webrtc_git') + '/src.git' + '@' + 'f2e9cab3835cd40009a6c01184e432eb3a81c0e2',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1391,7 +1391,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@523eaa8e9a1ff944b4d9f9cebf7bb0cea9fbc1aa',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1ee3ebdb611f5195c921bf4e04ec923b1d897773',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/base/task/promise/promise.h b/base/task/promise/promise.h
index b5dd9b8..1529bbe 100644
--- a/base/task/promise/promise.h
+++ b/base/task/promise/promise.h
@@ -146,8 +146,7 @@
   }
 
   template <typename RejectCb>
-  auto CatchOnCurrent(const Location& from_here,
-                      RejectCb&& on_reject) noexcept {
+  auto CatchHere(const Location& from_here, RejectCb&& on_reject) noexcept {
     return CatchOn(SequencedTaskRunnerHandle::Get(), from_here,
                    std::forward<RejectCb>(on_reject));
   }
@@ -221,8 +220,7 @@
   }
 
   template <typename ResolveCb>
-  auto ThenOnCurrent(const Location& from_here,
-                     ResolveCb&& on_resolve) noexcept {
+  auto ThenHere(const Location& from_here, ResolveCb&& on_resolve) noexcept {
     return ThenOn(SequencedTaskRunnerHandle::Get(), from_here,
                   std::forward<ResolveCb>(on_resolve));
   }
@@ -243,7 +241,7 @@
   //
   // Note if either |on_resolve| or |on_reject| are canceled (due to weak
   // pointer invalidation), then the other must be canceled at the same time as
-  // well. This restriction only applies to this form of ThenOn/ThenOnCurrent.
+  // well. This restriction only applies to this form of ThenOn/ThenHere.
   template <typename ResolveCb, typename RejectCb>
   NOINLINE auto ThenOn(scoped_refptr<TaskRunner> task_runner,
                        const Location& from_here,
@@ -325,9 +323,9 @@
   }
 
   template <typename ResolveCb, typename RejectCb>
-  auto ThenOnCurrent(const Location& from_here,
-                     ResolveCb&& on_resolve,
-                     RejectCb&& on_reject) noexcept {
+  auto ThenHere(const Location& from_here,
+                ResolveCb&& on_resolve,
+                RejectCb&& on_reject) noexcept {
     return ThenOn(SequencedTaskRunnerHandle::Get(), from_here,
                   std::forward<ResolveCb>(on_resolve),
                   std::forward<RejectCb>(on_reject));
@@ -378,8 +376,8 @@
   }
 
   template <typename FinallyCb>
-  auto FinallyOnCurrent(const Location& from_here,
-                        FinallyCb&& finally_callback) noexcept {
+  auto FinallyHere(const Location& from_here,
+                   FinallyCb&& finally_callback) noexcept {
     return FinallyOn(SequencedTaskRunnerHandle::Get(), from_here,
                      std::move(finally_callback));
   }
diff --git a/base/task/promise/promise_unittest.cc b/base/task/promise/promise_unittest.cc
index a12fac2..a87cdb81 100644
--- a/base/task/promise/promise_unittest.cc
+++ b/base/task/promise/promise_unittest.cc
@@ -89,10 +89,9 @@
       .ThenOn(post_runner, FROM_HERE,
               BindOnce(&MockObject::Task, Unretained(&mock_object),
                        MakeRefCounted<ObjectToDelete>(&delete_task_flag)))
-      .ThenOnCurrent(
-          FROM_HERE,
-          BindOnce(&MockObject::Reply, Unretained(&mock_object),
-                   MakeRefCounted<ObjectToDelete>(&delete_reply_flag)));
+      .ThenHere(FROM_HERE,
+                BindOnce(&MockObject::Reply, Unretained(&mock_object),
+                         MakeRefCounted<ObjectToDelete>(&delete_reply_flag)));
 
   post_runner->ClearPendingTasks();
 
@@ -108,10 +107,10 @@
   p.GetResolveCallback().Run(123);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
-                              EXPECT_EQ(123, result);
-                              run_loop.Quit();
-                            }));
+  p.promise().ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                         EXPECT_EQ(123, result);
+                         run_loop.Quit();
+                       }));
 
   run_loop.Run();
 }
@@ -121,11 +120,10 @@
   p.GetResolveCallback().Run(123);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(FROM_HERE,
-                            BindLambdaForTesting([&](const int result) {
-                              EXPECT_EQ(123, result);
-                              run_loop.Quit();
-                            }));
+  p.promise().ThenHere(FROM_HERE, BindLambdaForTesting([&](const int result) {
+                         EXPECT_EQ(123, result);
+                         run_loop.Quit();
+                       }));
 
   run_loop.Run();
 }
@@ -135,13 +133,13 @@
   p.GetResolveCallback<int, bool, float>().Run(123, true, 1.5f);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(FROM_HERE,
-                            BindLambdaForTesting([&](int a, bool b, float c) {
-                              EXPECT_EQ(123, a);
-                              EXPECT_TRUE(b);
-                              EXPECT_EQ(1.5f, c);
-                              run_loop.Quit();
-                            }));
+  p.promise().ThenHere(FROM_HERE,
+                       BindLambdaForTesting([&](int a, bool b, float c) {
+                         EXPECT_EQ(123, a);
+                         EXPECT_TRUE(b);
+                         EXPECT_EQ(1.5f, c);
+                         run_loop.Quit();
+                       }));
 
   run_loop.Run();
 }
@@ -152,15 +150,14 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([]() {
-                       return std::tuple<int, bool>(123, false);
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting(
-                                    [&](const std::tuple<int, bool>& tuple) {
-                                      EXPECT_EQ(123, std::get<0>(tuple));
-                                      EXPECT_FALSE(std::get<1>(tuple));
-                                      run_loop.Quit();
-                                    }));
+      .ThenHere(FROM_HERE,
+                BindOnce([]() { return std::tuple<int, bool>(123, false); }))
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&](const std::tuple<int, bool>& tuple) {
+                  EXPECT_EQ(123, std::get<0>(tuple));
+                  EXPECT_FALSE(std::get<1>(tuple));
+                  run_loop.Quit();
+                }));
 
   run_loop.Run();
 }
@@ -171,14 +168,13 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([]() {
-                       return std::tuple<int, bool>(123, false);
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int a, bool b) {
-                       EXPECT_EQ(123, a);
-                       EXPECT_FALSE(b);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE,
+                BindOnce([]() { return std::tuple<int, bool>(123, false); }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int a, bool b) {
+                  EXPECT_EQ(123, a);
+                  EXPECT_FALSE(b);
+                  run_loop.Quit();
+                }));
 
   run_loop.Run();
 }
@@ -189,17 +185,16 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([]() {
-                       return std::make_tuple(std::make_unique<int>(42),
-                                              std::make_unique<float>(4.2f));
-                     }))
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting(
-                         [&](std::unique_ptr<int> a, std::unique_ptr<float> b) {
-                           EXPECT_EQ(42, *a);
-                           EXPECT_EQ(4.2f, *b);
-                           run_loop.Quit();
-                         }));
+      .ThenHere(FROM_HERE, BindOnce([]() {
+                  return std::make_tuple(std::make_unique<int>(42),
+                                         std::make_unique<float>(4.2f));
+                }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::unique_ptr<int> a,
+                                                    std::unique_ptr<float> b) {
+                  EXPECT_EQ(42, *a);
+                  EXPECT_EQ(4.2f, *b);
+                  run_loop.Quit();
+                }));
 
   run_loop.Run();
 }
@@ -208,7 +203,7 @@
   ManualPromiseResolver<int, std::string> p(FROM_HERE);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(
+  p.promise().ThenHere(
       FROM_HERE, BindLambdaForTesting([&](int result) {
         run_loop.Quit();
         FAIL() << "We shouldn't get here, the promise was rejected!";
@@ -227,10 +222,10 @@
   p.GetRepeatingResolveCallback().Run(123);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
-                              EXPECT_EQ(123, result);
-                              run_loop.Quit();
-                            }));
+  p.promise().ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                         EXPECT_EQ(123, result);
+                         run_loop.Quit();
+                       }));
 
   run_loop.Run();
 }
@@ -239,7 +234,7 @@
   ManualPromiseResolver<int, std::string> p(FROM_HERE);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(
+  p.promise().ThenHere(
       FROM_HERE, BindLambdaForTesting([&](int result) {
         run_loop.Quit();
         FAIL() << "We shouldn't get here, the promise was rejected!";
@@ -256,10 +251,10 @@
 TEST_F(PromiseTest, CreateResolvedThen) {
   RunLoop run_loop;
   Promise<int>::CreateResolved(FROM_HERE, 123)
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
-                       EXPECT_EQ(123, result);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  EXPECT_EQ(123, result);
+                  run_loop.Quit();
+                }));
 
   run_loop.Run();
 }
@@ -270,15 +265,15 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([]() {
-                       return Rejected<std::tuple<int, bool>>{123, false};
-                     }))
-      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting(
-                                     [&](const std::tuple<int, bool>& tuple) {
-                                       EXPECT_EQ(123, std::get<0>(tuple));
-                                       EXPECT_FALSE(std::get<1>(tuple));
-                                       run_loop.Quit();
-                                     }));
+      .ThenHere(FROM_HERE, BindOnce([]() {
+                  return Rejected<std::tuple<int, bool>>{123, false};
+                }))
+      .CatchHere(FROM_HERE,
+                 BindLambdaForTesting([&](const std::tuple<int, bool>& tuple) {
+                   EXPECT_EQ(123, std::get<0>(tuple));
+                   EXPECT_FALSE(std::get<1>(tuple));
+                   run_loop.Quit();
+                 }));
 
   run_loop.Run();
 }
@@ -287,7 +282,7 @@
   ManualPromiseResolver<int, std::tuple<bool, std::string>> p(FROM_HERE);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(
+  p.promise().ThenHere(
       FROM_HERE, BindLambdaForTesting([&](int result) {
         run_loop.Quit();
         FAIL() << "We shouldn't get here, the promise was rejected!";
@@ -304,163 +299,154 @@
   run_loop.Run();
 }
 
-TEST_F(PromiseTest, CatchOnCurrentReturnTypes) {
+TEST_F(PromiseTest, CatchHereReturnTypes) {
   ManualPromiseResolver<int, void> p1(FROM_HERE);
 
-  // Check CatchOnCurrent returns the expected return types for various
+  // Check CatchHere returns the expected return types for various
   // return types.
   Promise<int> r1 =
-      p1.promise().CatchOnCurrent(FROM_HERE, BindOnce([]() { return 123; }));
-  Promise<int> r2 = p1.promise().CatchOnCurrent(
+      p1.promise().CatchHere(FROM_HERE, BindOnce([]() { return 123; }));
+  Promise<int> r2 = p1.promise().CatchHere(
       FROM_HERE, BindOnce([]() { return Resolved<int>(123); }));
-  Promise<int, int> r3 = p1.promise().CatchOnCurrent(
+  Promise<int, int> r3 = p1.promise().CatchHere(
       FROM_HERE, BindOnce([]() { return Rejected<int>(123); }));
 
-  Promise<int, void> r4 = p1.promise().CatchOnCurrent(
+  Promise<int, void> r4 = p1.promise().CatchHere(
       FROM_HERE, BindOnce([]() { return PromiseResult<int, void>(123.0); }));
-  Promise<int> r5 = p1.promise().CatchOnCurrent(
+  Promise<int> r5 = p1.promise().CatchHere(
       FROM_HERE,
       BindOnce([]() { return PromiseResult<int, NoReject>(123.0); }));
-  Promise<int, int> r6 = p1.promise().CatchOnCurrent(
+  Promise<int, int> r6 = p1.promise().CatchHere(
       FROM_HERE,
       BindOnce([]() { return PromiseResult<NoResolve, int>(123.0); }));
 
-  Promise<int, void> r7 = p1.promise().CatchOnCurrent(
+  Promise<int, void> r7 = p1.promise().CatchHere(
       FROM_HERE, BindOnce([]() { return Promise<int, void>(); }));
-  Promise<int> r8 = p1.promise().CatchOnCurrent(
+  Promise<int> r8 = p1.promise().CatchHere(
       FROM_HERE, BindOnce([]() { return Promise<int, NoReject>(); }));
-  Promise<int, int> r9 = p1.promise().CatchOnCurrent(
+  Promise<int, int> r9 = p1.promise().CatchHere(
       FROM_HERE, BindOnce([]() { return Promise<NoResolve, int>(); }));
 
   ManualPromiseResolver<NoResolve, void> p2(FROM_HERE);
   Promise<int> r10 =
-      p2.promise().CatchOnCurrent(FROM_HERE, BindOnce([]() { return 123; }));
-  Promise<int> r11 = p2.promise().CatchOnCurrent(
+      p2.promise().CatchHere(FROM_HERE, BindOnce([]() { return 123; }));
+  Promise<int> r11 = p2.promise().CatchHere(
       FROM_HERE, BindOnce([]() { return Resolved<int>(123); }));
-  Promise<NoResolve, int> r12 = p2.promise().CatchOnCurrent(
+  Promise<NoResolve, int> r12 = p2.promise().CatchHere(
       FROM_HERE, BindOnce([]() { return Rejected<int>(123); }));
 }
 
-TEST_F(PromiseTest, ThenOnCurrentReturnTypes) {
+TEST_F(PromiseTest, ThenHereReturnTypes) {
   ManualPromiseResolver<std::string, void> p1(FROM_HERE);
 
-  // Check ThenOnCurrent returns the expected return types for various
+  // Check ThenHere returns the expected return types for various
   // return types.
   Promise<int, void> r1 =
-      p1.promise().ThenOnCurrent(FROM_HERE, BindOnce([]() { return 123; }));
-  Promise<int, void> r2 = p1.promise().ThenOnCurrent(
+      p1.promise().ThenHere(FROM_HERE, BindOnce([]() { return 123; }));
+  Promise<int, void> r2 = p1.promise().ThenHere(
       FROM_HERE, BindOnce([]() { return Resolved<int>(123); }));
-  Promise<NoResolve, void> r3 = p1.promise().ThenOnCurrent(
+  Promise<NoResolve, void> r3 = p1.promise().ThenHere(
       FROM_HERE, BindOnce([]() { return Rejected<void>(); }));
 
-  Promise<int, void> r4 = p1.promise().ThenOnCurrent(
+  Promise<int, void> r4 = p1.promise().ThenHere(
       FROM_HERE, BindOnce([]() { return PromiseResult<int, void>(123.0); }));
-  Promise<int, void> r5 = p1.promise().ThenOnCurrent(
+  Promise<int, void> r5 = p1.promise().ThenHere(
       FROM_HERE,
       BindOnce([]() { return PromiseResult<int, NoReject>(123.0); }));
-  Promise<NoResolve, void> r6 = p1.promise().ThenOnCurrent(
+  Promise<NoResolve, void> r6 = p1.promise().ThenHere(
       FROM_HERE, BindOnce([]() { return PromiseResult<NoResolve, void>(); }));
 
-  Promise<int, void> r7 = p1.promise().ThenOnCurrent(
+  Promise<int, void> r7 = p1.promise().ThenHere(
       FROM_HERE, BindOnce([]() { return Promise<int, void>(); }));
-  Promise<int, void> r8 = p1.promise().ThenOnCurrent(
+  Promise<int, void> r8 = p1.promise().ThenHere(
       FROM_HERE, BindOnce([]() { return Promise<int, NoReject>(); }));
-  Promise<NoResolve, void> r9 = p1.promise().ThenOnCurrent(
+  Promise<NoResolve, void> r9 = p1.promise().ThenHere(
       FROM_HERE, BindOnce([]() { return Promise<NoResolve, void>(); }));
 
   ManualPromiseResolver<std::string> p2(FROM_HERE);
   Promise<int> r10 =
-      p2.promise().ThenOnCurrent(FROM_HERE, BindOnce([]() { return 123; }));
-  Promise<int> r11 = p2.promise().ThenOnCurrent(
+      p2.promise().ThenHere(FROM_HERE, BindOnce([]() { return 123; }));
+  Promise<int> r11 = p2.promise().ThenHere(
       FROM_HERE, BindOnce([]() { return Resolved<int>(123); }));
-  Promise<NoResolve, int> r12 = p2.promise().ThenOnCurrent(
+  Promise<NoResolve, int> r12 = p2.promise().ThenHere(
       FROM_HERE, BindOnce([]() { return Rejected<int>(123); }));
 }
 
-TEST_F(PromiseTest, ThenAndCatchOnCurrentReturnTypes) {
+TEST_F(PromiseTest, ThenAndCatchHereReturnTypes) {
   struct A {};
   struct B {};
   struct C {};
   struct D {};
 
   Promise<B, NoReject> p1 =
-      ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenOnCurrent(
+      ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenHere(
           FROM_HERE, BindOnce([]() { return Resolved<B>(); }));
   Promise<NoResolve, B> p2 =
-      ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenOnCurrent(
+      ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenHere(
           FROM_HERE, BindOnce([]() { return Rejected<B>(); }));
   Promise<B, C> p3 =
-      ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenOnCurrent(
+      ManualPromiseResolver<A, NoReject>(FROM_HERE).promise().ThenHere(
           FROM_HERE, BindOnce([]() -> PromiseResult<B, C> { return B{}; }));
 
   Promise<B, NoReject> p4 =
-      ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchOnCurrent(
+      ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchHere(
           FROM_HERE, BindOnce([]() { return Resolved<B>(); }));
   Promise<NoResolve, B> p5 =
-      ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchOnCurrent(
+      ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchHere(
           FROM_HERE, BindOnce([]() { return Rejected<B>(); }));
   Promise<B, C> p6 =
-      ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchOnCurrent(
+      ManualPromiseResolver<NoResolve, A>(FROM_HERE).promise().CatchHere(
           FROM_HERE, BindOnce([]() -> PromiseResult<B, C> { return B{}; }));
 
-  Promise<B, C> p7 =
-      ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenOnCurrent(
-          FROM_HERE, BindOnce([]() { return Resolved<B>(); }));
+  Promise<B, C> p7 = ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenHere(
+      FROM_HERE, BindOnce([]() { return Resolved<B>(); }));
   Promise<NoResolve, C> p8 =
-      ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenOnCurrent(
+      ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenHere(
           FROM_HERE, BindOnce([]() { return Rejected<C>(); }));
-  Promise<B, C> p9 =
-      ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenOnCurrent(
-          FROM_HERE, BindOnce([]() -> PromiseResult<B, C> { return B{}; }));
+  Promise<B, C> p9 = ManualPromiseResolver<A, C>(FROM_HERE).promise().ThenHere(
+      FROM_HERE, BindOnce([]() -> PromiseResult<B, C> { return B{}; }));
 
   Promise<A, NoReject> p10 =
-      ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchOnCurrent(
+      ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchHere(
           FROM_HERE, BindOnce([]() { return Resolved<A>(); }));
   Promise<A, B> p11 =
-      ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchOnCurrent(
+      ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchHere(
           FROM_HERE, BindOnce([]() { return Rejected<B>(); }));
   Promise<A, B> p12 =
-      ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchOnCurrent(
+      ManualPromiseResolver<A, C>(FROM_HERE).promise().CatchHere(
           FROM_HERE, BindOnce([]() -> PromiseResult<A, B> { return B{}; }));
 
   Promise<C, NoReject> p13 =
-      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere(
           FROM_HERE, BindOnce([]() { return Resolved<C>(); }),
           BindOnce([]() { return Resolved<C>(); }));
   Promise<NoResolve, D> p14 =
-      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
+      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere(
           FROM_HERE, BindOnce([]() { return Rejected<D>(); }),
           BindOnce([]() { return Rejected<D>(); }));
-  Promise<C, D> p15 =
-      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
-          FROM_HERE, BindOnce([]() { return Resolved<C>(); }),
-          BindOnce([]() { return Rejected<D>(); }));
-  Promise<C, D> p16 =
-      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
-          FROM_HERE, BindOnce([]() { return Rejected<D>(); }),
-          BindOnce([]() { return Resolved<C>(); }));
+  Promise<C, D> p15 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere(
+      FROM_HERE, BindOnce([]() { return Resolved<C>(); }),
+      BindOnce([]() { return Rejected<D>(); }));
+  Promise<C, D> p16 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere(
+      FROM_HERE, BindOnce([]() { return Rejected<D>(); }),
+      BindOnce([]() { return Resolved<C>(); }));
 
-  Promise<C, D> p17 =
-      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
-          FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }),
-          BindOnce([]() { return Resolved<C>(); }));
-  Promise<C, D> p18 =
-      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
-          FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }),
-          BindOnce([]() { return Rejected<D>(); }));
-  Promise<C, D> p19 =
-      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
-          FROM_HERE, BindOnce([]() { return Resolved<C>(); }),
-          BindOnce([]() -> PromiseResult<C, D> { return C{}; }));
-  Promise<C, D> p20 =
-      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
-          FROM_HERE, BindOnce([]() { return Rejected<D>(); }),
-          BindOnce([]() -> PromiseResult<C, D> { return C{}; }));
+  Promise<C, D> p17 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere(
+      FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }),
+      BindOnce([]() { return Resolved<C>(); }));
+  Promise<C, D> p18 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere(
+      FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }),
+      BindOnce([]() { return Rejected<D>(); }));
+  Promise<C, D> p19 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere(
+      FROM_HERE, BindOnce([]() { return Resolved<C>(); }),
+      BindOnce([]() -> PromiseResult<C, D> { return C{}; }));
+  Promise<C, D> p20 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere(
+      FROM_HERE, BindOnce([]() { return Rejected<D>(); }),
+      BindOnce([]() -> PromiseResult<C, D> { return C{}; }));
 
-  Promise<C, D> p21 =
-      ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenOnCurrent(
-          FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }),
-          BindOnce([]() -> PromiseResult<C, D> { return C{}; }));
+  Promise<C, D> p21 = ManualPromiseResolver<A, B>(FROM_HERE).promise().ThenHere(
+      FROM_HERE, BindOnce([]() -> PromiseResult<C, D> { return C{}; }),
+      BindOnce([]() -> PromiseResult<C, D> { return C{}; }));
 }
 
 TEST_F(PromiseTest, UnsettledManualPromiseResolverCancelsChain) {
@@ -469,7 +455,7 @@
 
   {
     ManualPromiseResolver<int> p1(FROM_HERE);
-    p2 = p1.promise().ThenOnCurrent(
+    p2 = p1.promise().ThenHere(
         FROM_HERE, BindOnce([](scoped_refptr<ObjectToDelete> v) {},
                             MakeRefCounted<ObjectToDelete>(&delete_flag)));
   }
@@ -485,16 +471,16 @@
   {
     Cancelable cancelable;
     ManualPromiseResolver<void> p1(FROM_HERE);
-    Promise<void> p2 = p1.promise().ThenOnCurrent(
+    Promise<void> p2 = p1.promise().ThenHere(
         FROM_HERE, BindOnce(&Cancelable::NopTask,
                             cancelable.weak_ptr_factory.GetWeakPtr()));
 
     p1.Resolve();
     cancelable.weak_ptr_factory.InvalidateWeakPtrs();
 
-    p3 = p2.ThenOnCurrent(
-        FROM_HERE, BindOnce([](scoped_refptr<ObjectToDelete> v) {},
-                            MakeRefCounted<ObjectToDelete>(&delete_flag)));
+    p3 = p2.ThenHere(FROM_HERE,
+                     BindOnce([](scoped_refptr<ObjectToDelete> v) {},
+                              MakeRefCounted<ObjectToDelete>(&delete_flag)));
   }
 
   RunLoop().RunUntilIdle();
@@ -507,18 +493,18 @@
   RunLoop run_loop;
 
   p.promise()
-      .CatchOnCurrent(
+      .CatchHere(
           FROM_HERE,
           BindOnce([](const std::string& err) -> PromiseResult<int, int> {
             EXPECT_EQ("Oh no!", err);
             // Re-Reject with -1 this time.
             return Rejected<int>(-1);
           }))
-      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([&](int err) {
-                        EXPECT_EQ(-1, err);
-                        run_loop.Quit();
-                        return -1;
-                      }));
+      .CatchHere(FROM_HERE, BindLambdaForTesting([&](int err) {
+                   EXPECT_EQ(-1, err);
+                   run_loop.Quit();
+                   return -1;
+                 }));
 
   p.GetRejectCallback().Run("Oh no!");
   run_loop.Run();
@@ -529,17 +515,16 @@
   RunLoop run_loop;
 
   p.promise()
-      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([](std::string) {
-                        return Rejected<int>(-1);
-                      }))
-      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting(
-                                     [&](int) { return Resolved<int>(1000); }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int value) {
-                       EXPECT_EQ(1000, value);
-                       return Rejected<DummyError>();
-                     }))
-      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting(
-                                     [&](DummyError) { run_loop.Quit(); }));
+      .CatchHere(FROM_HERE, BindLambdaForTesting(
+                                [](std::string) { return Rejected<int>(-1); }))
+      .CatchHere(FROM_HERE,
+                 BindLambdaForTesting([&](int) { return Resolved<int>(1000); }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int value) {
+                  EXPECT_EQ(1000, value);
+                  return Rejected<DummyError>();
+                }))
+      .CatchHere(FROM_HERE,
+                 BindLambdaForTesting([&](DummyError) { run_loop.Quit(); }));
 
   p.GetRejectCallback().Run("Oh no!");
   run_loop.Run();
@@ -550,14 +535,14 @@
   RunLoop run_loop;
 
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([]() -> Resolved<int> {
-                       // Resolve
-                       return 123;
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int value) {
-                       EXPECT_EQ(123, value);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindOnce([]() -> Resolved<int> {
+                  // Resolve
+                  return 123;
+                }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int value) {
+                  EXPECT_EQ(123, value);
+                  run_loop.Quit();
+                }));
 
   p.GetResolveCallback().Run();
   run_loop.Run();
@@ -568,14 +553,14 @@
   RunLoop run_loop;
 
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([]() -> Rejected<int> {
-                       // Reject
-                       return -1;
-                     }))
-      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([&](int err) {
-                        EXPECT_EQ(-1, err);
-                        run_loop.Quit();
-                      }));
+      .ThenHere(FROM_HERE, BindOnce([]() -> Rejected<int> {
+                  // Reject
+                  return -1;
+                }))
+      .CatchHere(FROM_HERE, BindLambdaForTesting([&](int err) {
+                   EXPECT_EQ(-1, err);
+                   run_loop.Quit();
+                 }));
 
   p.GetResolveCallback().Run();
   run_loop.Run();
@@ -586,14 +571,14 @@
   RunLoop run_loop;
 
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([]() -> Rejected<int> {
-                       // Reject
-                       return -1;
-                     }))
-      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([&](int err) {
-                        EXPECT_EQ(-1, err);
-                        run_loop.Quit();
-                      }));
+      .ThenHere(FROM_HERE, BindOnce([]() -> Rejected<int> {
+                  // Reject
+                  return -1;
+                }))
+      .CatchHere(FROM_HERE, BindLambdaForTesting([&](int err) {
+                   EXPECT_EQ(-1, err);
+                   run_loop.Quit();
+                 }));
 
   p.GetResolveCallback().Run();
   run_loop.Run();
@@ -605,12 +590,11 @@
   base::RunLoop run_loop;
 
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
-                       return Rejected<std::string>(std::string("reject"));
-                     }))
-      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([&](std::string result) {
-                        run_loop.Quit();
-                      }));
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  return Rejected<std::string>(std::string("reject"));
+                }))
+      .CatchHere(FROM_HERE, BindLambdaForTesting(
+                                [&](std::string result) { run_loop.Quit(); }));
 
   p.GetResolveCallback().Run(123);
 
@@ -624,15 +608,15 @@
   RunLoop run_loop;
 
   p.promise()
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](const int& value) -> const int& {
-                       EXPECT_EQ(123, value);
-                       return b;
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](const int& value) {
-                       EXPECT_EQ(456, value);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&](const int& value) -> const int& {
+                  EXPECT_EQ(123, value);
+                  return b;
+                }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](const int& value) {
+                  EXPECT_EQ(456, value);
+                  run_loop.Quit();
+                }));
 
   p.GetResolveCallback().Run(a);
   run_loop.Run();
@@ -643,10 +627,9 @@
   RunLoop run_loop;
 
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting(
-                                    [&]() { return PromiseResult<void>(); }))
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&]() { run_loop.Quit(); }));
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&]() { return PromiseResult<void>(); }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&]() { run_loop.Quit(); }));
 
   p.Resolve();
   run_loop.Run();
@@ -661,18 +644,18 @@
   RunLoop run_loop;
 
   p.promise()
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting(
-                         [&](scoped_refptr<internal::AbstractPromise> value) {
-                           EXPECT_EQ(a, value);
-                           return b;
-                         }))
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting(
-                         [&](scoped_refptr<internal::AbstractPromise> value) {
-                           EXPECT_EQ(b, value);
-                           run_loop.Quit();
-                         }));
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting(
+                    [&](scoped_refptr<internal::AbstractPromise> value) {
+                      EXPECT_EQ(a, value);
+                      return b;
+                    }))
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting(
+                    [&](scoped_refptr<internal::AbstractPromise> value) {
+                      EXPECT_EQ(b, value);
+                      run_loop.Quit();
+                    }));
 
   p.Resolve(a);
   run_loop.Run();
@@ -685,8 +668,8 @@
   // You don't have to use the resolve (or reject) arguments from the
   // previous promise.
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(FROM_HERE,
-                            BindLambdaForTesting([&]() { run_loop.Quit(); }));
+  p.promise().ThenHere(FROM_HERE,
+                       BindLambdaForTesting([&]() { run_loop.Quit(); }));
 
   run_loop.Run();
 }
@@ -695,11 +678,11 @@
   RunLoop run_loop;
   Promise<std::tuple<int, std::string>>::CreateResolved(FROM_HERE, 10,
                                                         std::string("Hi"))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int a, std::string b) {
-                       EXPECT_EQ(10, a);
-                       EXPECT_EQ("Hi", b);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int a, std::string b) {
+                  EXPECT_EQ(10, a);
+                  EXPECT_EQ("Hi", b);
+                  run_loop.Quit();
+                }));
   run_loop.Run();
 }
 
@@ -707,10 +690,10 @@
   ManualPromiseResolver<int> p(FROM_HERE);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
-                              EXPECT_EQ(123, result);
-                              run_loop.Quit();
-                            }));
+  p.promise().ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                         EXPECT_EQ(123, result);
+                         run_loop.Quit();
+                       }));
 
   p.Resolve(123);
   run_loop.Run();
@@ -720,7 +703,7 @@
   ManualPromiseResolver<int, void> p(FROM_HERE);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(
+  p.promise().ThenHere(
       FROM_HERE, BindLambdaForTesting([&](int result) {
         run_loop.Quit();
         FAIL() << "We shouldn't get here, the promise was rejected!";
@@ -736,17 +719,15 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([](std::unique_ptr<int> result) {
-                       return result;
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](std::unique_ptr<int> result) {
-                       return result;
-                     }))
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](std::unique_ptr<int> result) {
-                       EXPECT_THAT(123, *result);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE,
+                BindOnce([](std::unique_ptr<int> result) { return result; }))
+      .ThenHere(FROM_HERE,
+                BindOnce([](std::unique_ptr<int> result) { return result; }))
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&](std::unique_ptr<int> result) {
+                  EXPECT_THAT(123, *result);
+                  run_loop.Quit();
+                }));
 
   p.Resolve(std::make_unique<int>(123));
   run_loop.Run();
@@ -756,13 +737,12 @@
   ManualPromiseResolver<std::unique_ptr<int>> p(FROM_HERE);
 
   // The executor argument will be called with move semantics.
-  p.promise().ThenOnCurrent(FROM_HERE,
-                            BindOnce([](std::unique_ptr<int> result) {}));
+  p.promise().ThenHere(FROM_HERE, BindOnce([](std::unique_ptr<int> result) {}));
 
   // It's an error to do that twice.
   EXPECT_DCHECK_DEATH({
-    p.promise().ThenOnCurrent(FROM_HERE,
-                              BindOnce([](std::unique_ptr<int> result) {}));
+    p.promise().ThenHere(FROM_HERE,
+                         BindOnce([](std::unique_ptr<int> result) {}));
   });
 }
 
@@ -771,23 +751,23 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([](std::vector<size_t> result) {
-                       result.push_back(1);
-                       return result;
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](std::vector<size_t> result) {
-                       result.push_back(2);
-                       return result;
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](std::vector<size_t> result) {
-                       result.push_back(3);
-                       return result;
-                     }))
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](std::vector<size_t> result) {
-                       EXPECT_THAT(result, ElementsAre(0u, 1u, 2u, 3u));
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindOnce([](std::vector<size_t> result) {
+                  result.push_back(1);
+                  return result;
+                }))
+      .ThenHere(FROM_HERE, BindOnce([](std::vector<size_t> result) {
+                  result.push_back(2);
+                  return result;
+                }))
+      .ThenHere(FROM_HERE, BindOnce([](std::vector<size_t> result) {
+                  result.push_back(3);
+                  return result;
+                }))
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&](std::vector<size_t> result) {
+                  EXPECT_THAT(result, ElementsAre(0u, 1u, 2u, 3u));
+                  run_loop.Quit();
+                }));
 
   p.Resolve(std::vector<size_t>{0});
   run_loop.Run();
@@ -798,24 +778,24 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([](std::vector<size_t> result) {
-                       result.push_back(result.size());
-                       return result;
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](std::vector<size_t> result) {
-                       result.push_back(result.size());
-                       return result;
-                     }))
-      .ThenOnCurrent(FROM_HERE,
-                     BindOnce([](std::vector<size_t> result)
-                                  -> PromiseResult<std::vector<size_t>, void> {
-                       return Rejected<void>();
-                     }))
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&](std::vector<size_t> result) {
-            FAIL() << "We shouldn't get here, the promise was rejected!";
-          }),
-          BindLambdaForTesting([&]() { run_loop.Quit(); }));
+      .ThenHere(FROM_HERE, BindOnce([](std::vector<size_t> result) {
+                  result.push_back(result.size());
+                  return result;
+                }))
+      .ThenHere(FROM_HERE, BindOnce([](std::vector<size_t> result) {
+                  result.push_back(result.size());
+                  return result;
+                }))
+      .ThenHere(FROM_HERE,
+                BindOnce([](std::vector<size_t> result)
+                             -> PromiseResult<std::vector<size_t>, void> {
+                  return Rejected<void>();
+                }))
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&](std::vector<size_t> result) {
+                  FAIL() << "We shouldn't get here, the promise was rejected!";
+                }),
+                BindLambdaForTesting([&]() { run_loop.Quit(); }));
 
   p.Resolve(std::vector<size_t>{0});
   run_loop.Run();
@@ -826,26 +806,24 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result + 1; }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result + 1; }))
-      .ThenOnCurrent(
-          FROM_HERE,
-          BindOnce([](int result) -> PromiseResult<int, std::string> {
-            return std::string("Fail shouldn't get here");
-          }),
-          BindOnce([](bool value) -> PromiseResult<int, std::string> {
-            EXPECT_FALSE(value);
-            return std::string("Oh no!");
-          }))
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&](int result) {
-            FAIL() << "We shouldn't get here, the promise was rejected!";
-            run_loop.Quit();
-          }),
-          BindLambdaForTesting([&](const std::string& err) {
-            EXPECT_EQ("Oh no!", err);
-            run_loop.Quit();
-          }));
+      .ThenHere(FROM_HERE, BindOnce([](int result) { return result + 1; }))
+      .ThenHere(FROM_HERE, BindOnce([](int result) { return result + 1; }))
+      .ThenHere(FROM_HERE,
+                BindOnce([](int result) -> PromiseResult<int, std::string> {
+                  return std::string("Fail shouldn't get here");
+                }),
+                BindOnce([](bool value) -> PromiseResult<int, std::string> {
+                  EXPECT_FALSE(value);
+                  return std::string("Oh no!");
+                }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  FAIL() << "We shouldn't get here, the promise was rejected!";
+                  run_loop.Quit();
+                }),
+                BindLambdaForTesting([&](const std::string& err) {
+                  EXPECT_EQ("Oh no!", err);
+                  run_loop.Quit();
+                }));
 
   p.Reject(false);
   run_loop.Run();
@@ -856,34 +834,32 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result + 1; }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result + 1; }))
-      .ThenOnCurrent(
-          FROM_HERE,
-          BindOnce([](int result) -> PromiseResult<int, std::string> {
-            return std::string("Fail shouldn't get here");
-          }),
-          BindOnce([](bool value) -> PromiseResult<int, std::string> {
-            EXPECT_FALSE(value);
-            return std::string("Oh no!");  // Reject
-          }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
-                       CHECK(false) << "Shouldn't get here";
-                       return result + 1;
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
-                       CHECK(false) << "Shouldn't get here";
-                       return result + 1;
-                     }))
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&](int result) {
-            FAIL() << "We shouldn't get here, the promise was rejected!";
-            run_loop.Quit();
-          }),
-          BindLambdaForTesting([&](const std::string& err) {
-            EXPECT_EQ("Oh no!", err);
-            run_loop.Quit();
-          }));
+      .ThenHere(FROM_HERE, BindOnce([](int result) { return result + 1; }))
+      .ThenHere(FROM_HERE, BindOnce([](int result) { return result + 1; }))
+      .ThenHere(FROM_HERE,
+                BindOnce([](int result) -> PromiseResult<int, std::string> {
+                  return std::string("Fail shouldn't get here");
+                }),
+                BindOnce([](bool value) -> PromiseResult<int, std::string> {
+                  EXPECT_FALSE(value);
+                  return std::string("Oh no!");  // Reject
+                }))
+      .ThenHere(FROM_HERE, BindOnce([](int result) {
+                  CHECK(false) << "Shouldn't get here";
+                  return result + 1;
+                }))
+      .ThenHere(FROM_HERE, BindOnce([](int result) {
+                  CHECK(false) << "Shouldn't get here";
+                  return result + 1;
+                }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  FAIL() << "We shouldn't get here, the promise was rejected!";
+                  run_loop.Quit();
+                }),
+                BindLambdaForTesting([&](const std::string& err) {
+                  EXPECT_EQ("Oh no!", err);
+                  run_loop.Quit();
+                }));
 
   p.Reject(false);
   run_loop.Run();
@@ -892,8 +868,8 @@
 TEST_F(PromiseTest, ThenOnWithHetrogenousButCompatibleReturnTypes) {
   ManualPromiseResolver<void, int> p(FROM_HERE);
 
-  // Make sure ThenOnCurrent returns the expected type.
-  Promise<int, std::string> p2 = p.promise().ThenOnCurrent(
+  // Make sure ThenHere returns the expected type.
+  Promise<int, std::string> p2 = p.promise().ThenHere(
       FROM_HERE,
       BindOnce([]() -> PromiseResult<int, std::string> { return 123; }),
       BindOnce([](int err) -> Resolved<int> { return 123; }));
@@ -902,8 +878,8 @@
 TEST_F(PromiseTest, ThenOnWithHetrogenousButCompatibleReturnTypes2) {
   ManualPromiseResolver<void, int> p(FROM_HERE);
 
-  // Make sure ThenOnCurrent returns the expected type.
-  Promise<int, std::string> p2 = p.promise().ThenOnCurrent(
+  // Make sure ThenHere returns the expected type.
+  Promise<int, std::string> p2 = p.promise().ThenHere(
       FROM_HERE,
       BindOnce([]() -> PromiseResult<int, std::string> { return 123; }),
       BindOnce([](int err) -> Rejected<std::string> { return "123"; }));
@@ -912,8 +888,8 @@
 TEST_F(PromiseTest, ThenOnWithHetrogenousButCompatibleReturnTypes3) {
   ManualPromiseResolver<int, std::string> p(FROM_HERE);
 
-  // Make sure ThenOnCurrent returns the expected type.
-  Promise<void, bool> p2 = p.promise().ThenOnCurrent(
+  // Make sure ThenHere returns the expected type.
+  Promise<void, bool> p2 = p.promise().ThenHere(
       FROM_HERE, BindOnce([](int value) -> PromiseResult<void, bool> {
         if (value % 2) {
           return Resolved<void>();
@@ -929,18 +905,18 @@
 
   RunLoop run_loop;
   p1.promise()
-      .CatchOnCurrent(FROM_HERE, BindLambdaForTesting(
-                                     [&](int) -> PromiseResult<NoResolve, int> {
-                                       return Rejected<int>();
-                                     }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](std::unique_ptr<int>) {
-                       run_loop.Quit();
-                       return std::make_unique<int>(42);
-                     }),
-                     BindLambdaForTesting([&](int err) {
-                       CHECK(false) << "Shouldn't get here";
-                       return std::make_unique<int>(42);
-                     }));
+      .CatchHere(FROM_HERE, BindLambdaForTesting(
+                                [&](int) -> PromiseResult<NoResolve, int> {
+                                  return Rejected<int>();
+                                }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::unique_ptr<int>) {
+                  run_loop.Quit();
+                  return std::make_unique<int>(42);
+                }),
+                BindLambdaForTesting([&](int err) {
+                  CHECK(false) << "Shouldn't get here";
+                  return std::make_unique<int>(42);
+                }));
 
   p1.GetResolveCallback().Run(std::make_unique<int>(42));
 
@@ -950,15 +926,15 @@
 TEST_F(PromiseTest, CatchCreatesNoRejectPromise) {
   ManualPromiseResolver<int> p(FROM_HERE);
 
-  // Make sure CatchOnCurrent returns the expected type.
+  // Make sure CatchHere returns the expected type.
   Promise<int> p2 =
       p.promise()
-          .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int) {
-                           return Rejected<std::string>();
-                         }))
-          .CatchOnCurrent(FROM_HERE, BindLambdaForTesting([&](std::string) {
-                            return Resolved<int>();
-                          }));
+          .ThenHere(FROM_HERE, BindLambdaForTesting([&](int) {
+                      return Rejected<std::string>();
+                    }))
+          .CatchHere(FROM_HERE, BindLambdaForTesting([&](std::string) {
+                       return Resolved<int>();
+                     }));
 }
 
 TEST_F(PromiseTest, ResolveSkipsCatches) {
@@ -966,28 +942,27 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result + 1; }))
-      .CatchOnCurrent(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> {
-                        CHECK(false) << "Shouldn't get here";
-                        return -1;
-                      }))
-      .CatchOnCurrent(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> {
-                        CHECK(false) << "Shouldn't get here";
-                        return -1;
-                      }))
-      .CatchOnCurrent(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> {
-                        CHECK(false) << "Shouldn't get here";
-                        return -1;
-                      }))
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&](int result) {
-            EXPECT_EQ(2, result);
-            run_loop.Quit();
-          }),
-          BindLambdaForTesting([&]() {
-            FAIL() << "We shouldn't get here, the promise was resolved!";
-            run_loop.Quit();
-          }));
+      .ThenHere(FROM_HERE, BindOnce([](int result) { return result + 1; }))
+      .CatchHere(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> {
+                   CHECK(false) << "Shouldn't get here";
+                   return -1;
+                 }))
+      .CatchHere(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> {
+                   CHECK(false) << "Shouldn't get here";
+                   return -1;
+                 }))
+      .CatchHere(FROM_HERE, BindOnce([]() -> PromiseResult<int, void> {
+                   CHECK(false) << "Shouldn't get here";
+                   return -1;
+                 }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  EXPECT_EQ(2, result);
+                  run_loop.Quit();
+                }),
+                BindLambdaForTesting([&]() {
+                  FAIL() << "We shouldn't get here, the promise was resolved!";
+                  run_loop.Quit();
+                }));
 
   p.Resolve(1);
   run_loop.Run();
@@ -998,19 +973,19 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([]() { return 5; }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
-                       EXPECT_EQ(5, result);
-                       return std::string("Hello");
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](std::string result) {
-                       EXPECT_EQ("Hello", result);
-                       return true;
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](bool result) {
-                       EXPECT_TRUE(result);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindOnce([]() { return 5; }))
+      .ThenHere(FROM_HERE, BindOnce([](int result) {
+                  EXPECT_EQ(5, result);
+                  return std::string("Hello");
+                }))
+      .ThenHere(FROM_HERE, BindOnce([](std::string result) {
+                  EXPECT_EQ("Hello", result);
+                  return true;
+                }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](bool result) {
+                  EXPECT_TRUE(result);
+                  run_loop.Quit();
+                }));
 
   p.GetResolveCallback().Run();
   run_loop.Run();
@@ -1021,13 +996,12 @@
   ManualPromiseResolver<void> promise_resolver(FROM_HERE);
 
   RunLoop run_loop;
-  p.ThenOnCurrent(FROM_HERE,
-                  BindOnce(
-                      [](ManualPromiseResolver<void>* promise_resolver) {
-                        return promise_resolver->promise();
-                      },
-                      &promise_resolver))
-      .ThenOnCurrent(FROM_HERE, run_loop.QuitClosure());
+  p.ThenHere(FROM_HERE, BindOnce(
+                            [](ManualPromiseResolver<void>* promise_resolver) {
+                              return promise_resolver->promise();
+                            },
+                            &promise_resolver))
+      .ThenHere(FROM_HERE, run_loop.QuitClosure());
   RunLoop().RunUntilIdle();
 
   promise_resolver.Resolve();
@@ -1039,18 +1013,17 @@
   ManualPromiseResolver<int> promise_resolver(FROM_HERE);
 
   RunLoop run_loop;
-  p.ThenOnCurrent(
-       FROM_HERE,
-       BindOnce(
-           [](ManualPromiseResolver<int>* promise_resolver, int result) {
-             EXPECT_EQ(1000, result);
-             return promise_resolver->promise();
-           },
-           &promise_resolver))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
-                       EXPECT_EQ(123, result);
-                       run_loop.Quit();
-                     }));
+  p.ThenHere(FROM_HERE,
+             BindOnce(
+                 [](ManualPromiseResolver<int>* promise_resolver, int result) {
+                   EXPECT_EQ(1000, result);
+                   return promise_resolver->promise();
+                 },
+                 &promise_resolver))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  EXPECT_EQ(123, result);
+                  run_loop.Quit();
+                }));
   RunLoop().RunUntilIdle();
 
   promise_resolver.Resolve(123);
@@ -1062,15 +1035,15 @@
   ManualPromiseResolver<int> promise_resolver(FROM_HERE);
 
   RunLoop run_loop;
-  p.ThenOnCurrent(FROM_HERE,
-                  BindLambdaForTesting([&](int result) -> PromiseResult<int> {
-                    EXPECT_EQ(1000, result);
-                    return promise_resolver.promise();
-                  }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
-                       EXPECT_EQ(123, result);
-                       run_loop.Quit();
-                     }));
+  p.ThenHere(FROM_HERE,
+             BindLambdaForTesting([&](int result) -> PromiseResult<int> {
+               EXPECT_EQ(1000, result);
+               return promise_resolver.promise();
+             }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  EXPECT_EQ(123, result);
+                  run_loop.Quit();
+                }));
   RunLoop().RunUntilIdle();
 
   promise_resolver.Resolve(123);
@@ -1082,21 +1055,19 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE,
-                     BindOnce([](int i) -> PromiseResult<Value, Value> {
-                       if ((i % 2) == 1)
-                         return Resolved<Value>("Success it was odd.");
-                       return Rejected<Value>("Failure it was even.");
-                     }))
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&](Value result) {
-            EXPECT_EQ("Success it was odd.", result.GetString());
-            run_loop.Quit();
-          }),
-          BindLambdaForTesting([&](Value err) {
-            run_loop.Quit();
-            FAIL() << "We shouldn't get here, the promise was resolved!";
-          }));
+      .ThenHere(FROM_HERE, BindOnce([](int i) -> PromiseResult<Value, Value> {
+                  if ((i % 2) == 1)
+                    return Resolved<Value>("Success it was odd.");
+                  return Rejected<Value>("Failure it was even.");
+                }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](Value result) {
+                  EXPECT_EQ("Success it was odd.", result.GetString());
+                  run_loop.Quit();
+                }),
+                BindLambdaForTesting([&](Value err) {
+                  run_loop.Quit();
+                  FAIL() << "We shouldn't get here, the promise was resolved!";
+                }));
 
   p.Resolve(1);
   run_loop.Run();
@@ -1107,18 +1078,17 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([]() -> PromiseResult<int, int> {
-                       return Rejected<int>(123);
-                     }))
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&](int result) {
-            run_loop.Quit();
-            FAIL() << "We shouldn't get here, the promise was rejected!";
-          }),
-          BindLambdaForTesting([&](int err) {
-            run_loop.Quit();
-            EXPECT_EQ(123, err);
-          }));
+      .ThenHere(FROM_HERE, BindOnce([]() -> PromiseResult<int, int> {
+                  return Rejected<int>(123);
+                }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  run_loop.Quit();
+                  FAIL() << "We shouldn't get here, the promise was rejected!";
+                }),
+                BindLambdaForTesting([&](int err) {
+                  run_loop.Quit();
+                  EXPECT_EQ(123, err);
+                }));
 
   p.Resolve();
   run_loop.Run();
@@ -1130,22 +1100,22 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
-                       ManualPromiseResolver<int> p2(FROM_HERE);
-                       p2.Resolve(200);
-                       return p2.promise().ThenOnCurrent(
-                           FROM_HERE, BindOnce([](int result) {
-                             ManualPromiseResolver<int> p3(FROM_HERE);
-                             p3.Resolve(300);
-                             return p3.promise().ThenOnCurrent(
-                                 FROM_HERE,
-                                 BindOnce([](int result) { return result; }));
-                           }));
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
-                       EXPECT_EQ(300, result);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindOnce([](int result) {
+                  ManualPromiseResolver<int> p2(FROM_HERE);
+                  p2.Resolve(200);
+                  return p2.promise().ThenHere(
+                      FROM_HERE, BindOnce([](int result) {
+                        ManualPromiseResolver<int> p3(FROM_HERE);
+                        p3.Resolve(300);
+                        return p3.promise().ThenHere(
+                            FROM_HERE,
+                            BindOnce([](int result) { return result; }));
+                      }));
+                }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  EXPECT_EQ(300, result);
+                  run_loop.Quit();
+                }));
 
   run_loop.Run();
 }
@@ -1155,15 +1125,14 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result; }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result; }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) { return result; }))
-      .CatchOnCurrent(FROM_HERE,
-                      BindLambdaForTesting([&](const std::string& err) {
-                        EXPECT_EQ("Whoops!", err);
-                        run_loop.Quit();
-                        return -1;
-                      }));
+      .ThenHere(FROM_HERE, BindOnce([](int result) { return result; }))
+      .ThenHere(FROM_HERE, BindOnce([](int result) { return result; }))
+      .ThenHere(FROM_HERE, BindOnce([](int result) { return result; }))
+      .CatchHere(FROM_HERE, BindLambdaForTesting([&](const std::string& err) {
+                   EXPECT_EQ("Whoops!", err);
+                   run_loop.Quit();
+                   return -1;
+                 }));
 
   p.Reject("Whoops!");
   run_loop.Run();
@@ -1175,18 +1144,18 @@
   ManualPromiseResolver<void> promise_a(FROM_HERE);
   Promise<void> promise_b =
       promise_a.promise()
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 0))
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 1));
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 0))
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 1));
 
   Promise<void> promise_c =
       promise_a.promise()
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 2))
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 3));
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 2))
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 3));
 
   Promise<void> promise_d =
       promise_a.promise()
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 4))
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 5));
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 4))
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 5));
 
   promise_a.Resolve();
   RunLoop().RunUntilIdle();
@@ -1200,21 +1169,21 @@
   ManualPromiseResolver<void, void> promise_a(FROM_HERE);
   Promise<void> promise_b =
       promise_a.promise()
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 0))
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 1))
-          .CatchOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 2));
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 0))
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 1))
+          .CatchHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 2));
 
   Promise<void> promise_c =
       promise_a.promise()
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 3))
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 4))
-          .CatchOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 5));
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 3))
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 4))
+          .CatchHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 5));
 
   Promise<void> promise_d =
       promise_a.promise()
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 6))
-          .ThenOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 7))
-          .CatchOnCurrent(FROM_HERE, BindOnce(&RecordOrder, &run_order, 8));
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 6))
+          .ThenHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 7))
+          .CatchHere(FROM_HERE, BindOnce(&RecordOrder, &run_order, 8));
 
   promise_a.Reject();
   RunLoop().RunUntilIdle();
@@ -1227,25 +1196,23 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(
-          FROM_HERE,
-          BindOnce([](int result) -> PromiseResult<int, std::string> {
-            return std::string("Whoops!");
-          }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
-                       CHECK(false) << "Shouldn't get here";
-                       return result;
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindOnce([](int result) {
-                       CHECK(false) << "Shouldn't get here";
-                       return result;
-                     }))
-      .CatchOnCurrent(FROM_HERE,
-                      BindLambdaForTesting([&](const std::string& err) {
-                        EXPECT_EQ("Whoops!", err);
-                        run_loop.Quit();
-                        return -1;
-                      }));
+      .ThenHere(FROM_HERE,
+                BindOnce([](int result) -> PromiseResult<int, std::string> {
+                  return std::string("Whoops!");
+                }))
+      .ThenHere(FROM_HERE, BindOnce([](int result) {
+                  CHECK(false) << "Shouldn't get here";
+                  return result;
+                }))
+      .ThenHere(FROM_HERE, BindOnce([](int result) {
+                  CHECK(false) << "Shouldn't get here";
+                  return result;
+                }))
+      .CatchHere(FROM_HERE, BindLambdaForTesting([&](const std::string& err) {
+                   EXPECT_EQ("Whoops!", err);
+                   run_loop.Quit();
+                   return -1;
+                 }));
 
   p.Resolve(123);
   run_loop.Run();
@@ -1256,11 +1223,11 @@
 
   RunLoop run_loop;
   p.promise()
-      .CatchOnCurrent(FROM_HERE, BindOnce([]() { return 123; }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
-                       EXPECT_EQ(123, result);
-                       run_loop.Quit();
-                     }));
+      .CatchHere(FROM_HERE, BindOnce([]() { return 123; }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  EXPECT_EQ(123, result);
+                  run_loop.Quit();
+                }));
 
   p.Reject();
   run_loop.Run();
@@ -1271,11 +1238,11 @@
 
   RunLoop run_loop;
   p.promise()
-      .CatchOnCurrent(FROM_HERE, BindOnce([](int err) { return err + 1; }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int result) {
-                       EXPECT_EQ(124, result);
-                       run_loop.Quit();
-                     }));
+      .CatchHere(FROM_HERE, BindOnce([](int err) { return err + 1; }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                  EXPECT_EQ(124, result);
+                  run_loop.Quit();
+                }));
 
   p.Reject(123);
   run_loop.Run();
@@ -1288,12 +1255,12 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](int value) { result = value; }))
-      .FinallyOnCurrent(FROM_HERE, BindLambdaForTesting([&]() {
-                          EXPECT_EQ(123, result);
-                          run_loop.Quit();
-                        }));
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&](int value) { result = value; }))
+      .FinallyHere(FROM_HERE, BindLambdaForTesting([&]() {
+                     EXPECT_EQ(123, result);
+                     run_loop.Quit();
+                   }));
 
   run_loop.Run();
 }
@@ -1305,17 +1272,16 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](int value) { result = value; }))
-      .FinallyOnCurrent(FROM_HERE, BindLambdaForTesting([&]() {
-                          EXPECT_EQ(123, result);
-                          return std::string("hi");
-                        }))
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](const std::string& value) {
-                       EXPECT_EQ("hi", value);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&](int value) { result = value; }))
+      .FinallyHere(FROM_HERE, BindLambdaForTesting([&]() {
+                     EXPECT_EQ(123, result);
+                     return std::string("hi");
+                   }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](const std::string& value) {
+                  EXPECT_EQ("hi", value);
+                  run_loop.Quit();
+                }));
 
   run_loop.Run();
 }
@@ -1327,19 +1293,17 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](int value) { result = value; }))
-      .FinallyOnCurrent(
-          FROM_HERE,
-          BindLambdaForTesting([&]() -> PromiseResult<void, std::string> {
-            EXPECT_EQ(123, result);
-            return std::string("Oh no");
-          }))
-      .CatchOnCurrent(FROM_HERE,
-                      BindLambdaForTesting([&](const std::string& value) {
-                        EXPECT_EQ("Oh no", value);
-                        run_loop.Quit();
-                      }));
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&](int value) { result = value; }))
+      .FinallyHere(FROM_HERE, BindLambdaForTesting(
+                                  [&]() -> PromiseResult<void, std::string> {
+                                    EXPECT_EQ(123, result);
+                                    return std::string("Oh no");
+                                  }))
+      .CatchHere(FROM_HERE, BindLambdaForTesting([&](const std::string& value) {
+                   EXPECT_EQ("Oh no", value);
+                   run_loop.Quit();
+                 }));
 
   run_loop.Run();
 }
@@ -1349,12 +1313,12 @@
   ManualPromiseResolver<int> p(FROM_HERE);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(
+  p.promise().ThenHere(
       FROM_HERE, BindLambdaForTesting([&](int value) { result = value; }));
-  p.promise().FinallyOnCurrent(FROM_HERE, BindLambdaForTesting([&]() {
-                                 EXPECT_EQ(123, result);
-                                 run_loop.Quit();
-                               }));
+  p.promise().FinallyHere(FROM_HERE, BindLambdaForTesting([&]() {
+                            EXPECT_EQ(123, result);
+                            run_loop.Quit();
+                          }));
   p.Resolve(123);
   run_loop.Run();
 }
@@ -1364,13 +1328,13 @@
   ManualPromiseResolver<int, void> p(FROM_HERE);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(
-      FROM_HERE, BindLambdaForTesting([&](int value) { result = value; }),
-      BindLambdaForTesting([&]() { result = -1; }));
-  p.promise().FinallyOnCurrent(FROM_HERE, BindLambdaForTesting([&]() {
-                                 EXPECT_EQ(-1, result);
-                                 run_loop.Quit();
-                               }));
+  p.promise().ThenHere(FROM_HERE,
+                       BindLambdaForTesting([&](int value) { result = value; }),
+                       BindLambdaForTesting([&]() { result = -1; }));
+  p.promise().FinallyHere(FROM_HERE, BindLambdaForTesting([&]() {
+                            EXPECT_EQ(-1, result);
+                            run_loop.Quit();
+                          }));
   p.Reject();
   run_loop.Run();
 }
@@ -1380,15 +1344,13 @@
 
   RunLoop run_loop;
   p.promise()
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&]() { return Rejected<int>(123); }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&]() {
-                       FAIL() << "Promise was rejected";
-                     }))
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&]() {
-                       FAIL() << "Promise was rejected";
-                     }))
-      .FinallyOnCurrent(FROM_HERE, run_loop.QuitClosure());
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&]() { return Rejected<int>(123); }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting(
+                               [&]() { FAIL() << "Promise was rejected"; }))
+      .ThenHere(FROM_HERE, BindLambdaForTesting(
+                               [&]() { FAIL() << "Promise was rejected"; }))
+      .FinallyHere(FROM_HERE, run_loop.QuitClosure());
   p.Resolve();
   run_loop.Run();
 }
@@ -1400,30 +1362,29 @@
   Promise<void, std::string> p1 = mpr.promise();
   {
     Cancelable cancelable;
-    Promise<void, std::string> p2 = p1.ThenOnCurrent(
+    Promise<void, std::string> p2 = p1.ThenHere(
         FROM_HERE,
         BindOnce(&Cancelable::LogTask, cancelable.weak_ptr_factory.GetWeakPtr(),
                  &log, "Then #1"));
-    p2.ThenOnCurrent(FROM_HERE, BindLambdaForTesting(
-                                    [&]() -> PromiseResult<void, std::string> {
-                                      log.push_back("Then #2 (reject)");
-                                      return std::string("Whoops!");
-                                    }))
-        .ThenOnCurrent(FROM_HERE, BindLambdaForTesting(
-                                      [&]() { log.push_back("Then #3"); }))
-        .ThenOnCurrent(FROM_HERE, BindLambdaForTesting(
-                                      [&]() { log.push_back("Then #4"); }))
-        .CatchOnCurrent(FROM_HERE,
-                        BindLambdaForTesting([&](const std::string& err) {
-                          log.push_back("Caught " + err);
-                        }));
+    p2.ThenHere(FROM_HERE,
+                BindLambdaForTesting([&]() -> PromiseResult<void, std::string> {
+                  log.push_back("Then #2 (reject)");
+                  return std::string("Whoops!");
+                }))
+        .ThenHere(FROM_HERE,
+                  BindLambdaForTesting([&]() { log.push_back("Then #3"); }))
+        .ThenHere(FROM_HERE,
+                  BindLambdaForTesting([&]() { log.push_back("Then #4"); }))
+        .CatchHere(FROM_HERE, BindLambdaForTesting([&](const std::string& err) {
+                     log.push_back("Caught " + err);
+                   }));
 
-    p2.FinallyOnCurrent(
-        FROM_HERE, BindLambdaForTesting([&]() { log.push_back("Finally"); }));
-    p2.ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&]() { log.push_back("Then #5"); }));
-    p2.ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&]() { log.push_back("Then #6"); }));
+    p2.FinallyHere(FROM_HERE,
+                   BindLambdaForTesting([&]() { log.push_back("Finally"); }));
+    p2.ThenHere(FROM_HERE,
+                BindLambdaForTesting([&]() { log.push_back("Then #5"); }));
+    p2.ThenHere(FROM_HERE,
+                BindLambdaForTesting([&]() { log.push_back("Then #6"); }));
   }
 
   mpr.Resolve();
@@ -1442,7 +1403,7 @@
   {
     Cancelable cancelable;
 
-    p3 = p2.promise().ThenOnCurrent(
+    p3 = p2.promise().ThenHere(
         FROM_HERE, BindOnce(&Cancelable::NopTask,
                             cancelable.weak_ptr_factory.GetWeakPtr()));
 
@@ -1468,11 +1429,11 @@
     Cancelable cancelable;
 
     p3 = p2.promise()
-             .ThenOnCurrent(FROM_HERE,
-                            BindOnce(&Cancelable::NopTask,
-                                     cancelable.weak_ptr_factory.GetWeakPtr()))
-             .ThenOnCurrent(FROM_HERE, BindOnce([]() {}))
-             .ThenOnCurrent(FROM_HERE, BindOnce([]() {}));
+             .ThenHere(FROM_HERE,
+                       BindOnce(&Cancelable::NopTask,
+                                cancelable.weak_ptr_factory.GetWeakPtr()))
+             .ThenHere(FROM_HERE, BindOnce([]() {}))
+             .ThenHere(FROM_HERE, BindOnce([]() {}));
 
     pAll = Promises::All(FROM_HERE, p1.promise(), p3);
 
@@ -1490,7 +1451,7 @@
                                      RejectPolicy::kCatchNotRequired);
 
   RunLoop run_loop;
-  p.promise().ThenOnCurrent(FROM_HERE, run_loop.QuitClosure());
+  p.promise().ThenHere(FROM_HERE, run_loop.QuitClosure());
 
   // Note this doesn't DCHECK even though we haven't specified a Catch.
   p.Resolve();
@@ -1503,13 +1464,12 @@
       Promise<std::unique_ptr<int>>::CreateResolved(FROM_HERE,
                                                     std::make_unique<int>(123));
 
-  p.ThenOnCurrent(FROM_HERE,
-                  BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
+  p.ThenHere(FROM_HERE,
+             BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
 
   EXPECT_DCHECK_DEATH({
-    p.ThenOnCurrent(FROM_HERE, BindOnce([](std::unique_ptr<int> i) {
-                      EXPECT_EQ(123, *i);
-                    }));
+    p.ThenHere(FROM_HERE,
+               BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
   });
 #endif
 }
@@ -1519,22 +1479,20 @@
   auto p = Promise<void, std::unique_ptr<int>>::CreateRejected(
       FROM_HERE, std::make_unique<int>(123));
 
-  p.CatchOnCurrent(
-      FROM_HERE, BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
+  p.CatchHere(FROM_HERE,
+              BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
 
   EXPECT_DCHECK_DEATH({
-    p.CatchOnCurrent(FROM_HERE, BindOnce([](std::unique_ptr<int> i) {
-                       EXPECT_EQ(123, *i);
-                     }));
+    p.CatchHere(FROM_HERE,
+                BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
   });
 #endif
 }
 
 TEST_F(PromiseTest, UnhandledRejection) {
 #if DCHECK_IS_ON()
-  Promise<void, int> p =
-      Promise<void, int>::CreateRejected(FROM_HERE).ThenOnCurrent(
-          FROM_HERE, BindOnce([]() {}));
+  Promise<void, int> p = Promise<void, int>::CreateRejected(FROM_HERE).ThenHere(
+      FROM_HERE, BindOnce([]() {}));
 
   RunLoop().RunUntilIdle();
 
@@ -1552,7 +1510,7 @@
 
   // |promise_resolver| could reject but there's no catch.
   Promise<void, void> p =
-      promise_resolver.promise().ThenOnCurrent(FROM_HERE, BindOnce([]() {}));
+      promise_resolver.promise().ThenHere(FROM_HERE, BindOnce([]() {}));
 
   promise_resolver.Resolve();
   RunLoop().RunUntilIdle();
@@ -1662,7 +1620,7 @@
           thread_c_->task_runner(), FROM_HERE, BindLambdaForTesting([&]() {
             EXPECT_TRUE(thread_c_->task_runner()->RunsTasksInCurrentSequence());
           }))
-      .ThenOnCurrent(
+      .ThenHere(
           FROM_HERE, BindLambdaForTesting([&]() {
             EXPECT_FALSE(
                 thread_a_->task_runner()->RunsTasksInCurrentSequence());
@@ -1740,12 +1698,12 @@
                 result.push_back(3);
                 return result;
               }))
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](std::vector<size_t> result) {
-                       EXPECT_TRUE(main_sequence->RunsTasksInCurrentSequence());
-                       EXPECT_THAT(result, ElementsAre(0u, 1u, 2u, 3u));
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&](std::vector<size_t> result) {
+                  EXPECT_TRUE(main_sequence->RunsTasksInCurrentSequence());
+                  EXPECT_THAT(result, ElementsAre(0u, 1u, 2u, 3u));
+                  run_loop.Quit();
+                }));
 
   p.Resolve(std::vector<size_t>{0});
   run_loop.Run();
@@ -1759,14 +1717,13 @@
       Promises::All(FROM_HERE, p1.promise(), p2.promise(), p3.promise());
 
   RunLoop run_loop;
-  p.ThenOnCurrent(
-      FROM_HERE,
-      BindLambdaForTesting([&](const std::tuple<float, int, bool>& result) {
-        EXPECT_EQ(1.234f, std::get<0>(result));
-        EXPECT_EQ(1234, std::get<1>(result));
-        EXPECT_TRUE(std::get<2>(result));
-        run_loop.Quit();
-      }));
+  p.ThenHere(FROM_HERE, BindLambdaForTesting(
+                            [&](const std::tuple<float, int, bool>& result) {
+                              EXPECT_EQ(1.234f, std::get<0>(result));
+                              EXPECT_EQ(1234, std::get<1>(result));
+                              EXPECT_TRUE(std::get<2>(result));
+                              run_loop.Quit();
+                            }));
 
   p1.Resolve(1.234f);
   p2.Resolve(1234);
@@ -1781,13 +1738,12 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, p1.promise(), p2.promise(), p3.promise())
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](float a, int b, bool c) {
-                       EXPECT_EQ(1.234f, a);
-                       EXPECT_EQ(1234, b);
-                       EXPECT_TRUE(c);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](float a, int b, bool c) {
+                  EXPECT_EQ(1.234f, a);
+                  EXPECT_EQ(1234, b);
+                  EXPECT_TRUE(c);
+                  run_loop.Quit();
+                }));
 
   p1.Resolve(1.234f);
   p2.Resolve(1234);
@@ -1802,7 +1758,7 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, p1.promise(), p2.promise(), p3.promise())
-      .ThenOnCurrent(
+      .ThenHere(
           FROM_HERE,
           BindLambdaForTesting([&](const std::tuple<float, int, bool>& result) {
             FAIL() << "We shouldn't get here, the promise was rejected!";
@@ -1822,10 +1778,10 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, p1.promise())
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting([&](int value1) {
-                       EXPECT_EQ(value1, 1);
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](int value1) {
+                  EXPECT_EQ(value1, 1);
+                  run_loop.Quit();
+                }));
 
   p1.Resolve(1);
   run_loop.Run();
@@ -1837,11 +1793,11 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, p1.promise(), p2.promise())
-      .ThenOnCurrent(FROM_HERE, BindLambdaForTesting(
-                                    [&](const std::tuple<int, Void>& result) {
-                                      EXPECT_EQ(1234, std::get<0>(result));
-                                      run_loop.Quit();
-                                    }));
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting([&](const std::tuple<int, Void>& result) {
+                  EXPECT_EQ(1234, std::get<0>(result));
+                  run_loop.Quit();
+                }));
 
   p1.Resolve(1234);
   p2.Resolve();
@@ -1855,16 +1811,15 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, p1.promise(), p2.promise(), p3.promise())
-      .ThenOnCurrent(
-          FROM_HERE,
-          BindLambdaForTesting(
-              [&](std::tuple<std::unique_ptr<float>, std::unique_ptr<int>,
-                             std::unique_ptr<bool>> result) {
-                EXPECT_EQ(1.234f, *std::get<0>(result));
-                EXPECT_EQ(1234, *std::get<1>(result));
-                EXPECT_TRUE(*std::get<2>(result));
-                run_loop.Quit();
-              }));
+      .ThenHere(FROM_HERE,
+                BindLambdaForTesting(
+                    [&](std::tuple<std::unique_ptr<float>, std::unique_ptr<int>,
+                                   std::unique_ptr<bool>> result) {
+                      EXPECT_EQ(1.234f, *std::get<0>(result));
+                      EXPECT_EQ(1234, *std::get<1>(result));
+                      EXPECT_TRUE(*std::get<2>(result));
+                      run_loop.Quit();
+                    }));
 
   p1.Resolve(std::make_unique<float>(1.234f));
   p2.Resolve(std::make_unique<int>(1234));
@@ -1880,7 +1835,7 @@
   // You can choose to ignore the result.
   RunLoop run_loop;
   Promises::All(FROM_HERE, p1.promise(), p2.promise(), p3.promise())
-      .ThenOnCurrent(FROM_HERE, run_loop.QuitClosure());
+      .ThenHere(FROM_HERE, run_loop.QuitClosure());
 
   p1.Resolve(1);
   p2.Resolve(2);
@@ -1902,11 +1857,10 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, promises)
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](std::vector<int> result) {
-                       EXPECT_THAT(result, ElementsAre(10, 20, 30, 40));
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::vector<int> result) {
+                  EXPECT_THAT(result, ElementsAre(10, 20, 30, 40));
+                  run_loop.Quit();
+                }));
 
   mpr1.Resolve(10);
   mpr2.Resolve(20);
@@ -1920,11 +1874,10 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, promises)
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](std::vector<int> result) {
-                       EXPECT_TRUE(result.empty());
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::vector<int> result) {
+                  EXPECT_TRUE(result.empty());
+                  run_loop.Quit();
+                }));
 
   run_loop.Run();
 }
@@ -1943,15 +1896,14 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, promises)
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&](std::vector<int> result) {
-            FAIL() << "We shouldn't get here, the promise was rejected!";
-            run_loop.Quit();
-          }),
-          BindLambdaForTesting([&](const std::string& err) {
-            EXPECT_EQ("Oh dear", err);
-            run_loop.Quit();
-          }));
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::vector<int> result) {
+                  FAIL() << "We shouldn't get here, the promise was rejected!";
+                  run_loop.Quit();
+                }),
+                BindLambdaForTesting([&](const std::string& err) {
+                  EXPECT_EQ("Oh dear", err);
+                  run_loop.Quit();
+                }));
 
   mpr2.Reject("Oh dear");
   run_loop.Run();
@@ -1971,11 +1923,10 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, promises)
-      .ThenOnCurrent(FROM_HERE,
-                     BindLambdaForTesting([&](std::vector<Void> result) {
-                       EXPECT_EQ(4u, result.size());
-                       run_loop.Quit();
-                     }));
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::vector<Void> result) {
+                  EXPECT_EQ(4u, result.size());
+                  run_loop.Quit();
+                }));
 
   mpr1.Resolve();
   mpr2.Resolve();
@@ -1998,15 +1949,14 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, promises)
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&](std::vector<Void> result) {
-            FAIL() << "We shouldn't get here, the promise was rejected!";
-            run_loop.Quit();
-          }),
-          BindLambdaForTesting([&](int err) {
-            EXPECT_EQ(-1, err);
-            run_loop.Quit();
-          }));
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&](std::vector<Void> result) {
+                  FAIL() << "We shouldn't get here, the promise was rejected!";
+                  run_loop.Quit();
+                }),
+                BindLambdaForTesting([&](int err) {
+                  EXPECT_EQ(-1, err);
+                  run_loop.Quit();
+                }));
 
   mpr1.Reject(-1);
   run_loop.Run();
@@ -2026,12 +1976,11 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, promises)
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&]() {
-            FAIL() << "We shouldn't get here, the promise was rejected!";
-            run_loop.Quit();
-          }),
-          run_loop.QuitClosure());
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&]() {
+                  FAIL() << "We shouldn't get here, the promise was rejected!";
+                  run_loop.Quit();
+                }),
+                run_loop.QuitClosure());
 
   mpr4.Reject();
   run_loop.Run();
@@ -2051,12 +2000,11 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, promises)
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&]() {
-            FAIL() << "We shouldn't get here, the promise was rejected!";
-            run_loop.Quit();
-          }),
-          run_loop.QuitClosure());
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&]() {
+                  FAIL() << "We shouldn't get here, the promise was rejected!";
+                  run_loop.Quit();
+                }),
+                run_loop.QuitClosure());
 
   mpr1.Reject();
   mpr2.Reject();
@@ -2078,12 +2026,11 @@
 
   RunLoop run_loop;
   Promises::All(FROM_HERE, promises)
-      .ThenOnCurrent(
-          FROM_HERE, BindLambdaForTesting([&]() {
-            FAIL() << "We shouldn't get here, the promise was rejected!";
-            run_loop.Quit();
-          }),
-          run_loop.QuitClosure());
+      .ThenHere(FROM_HERE, BindLambdaForTesting([&]() {
+                  FAIL() << "We shouldn't get here, the promise was rejected!";
+                  run_loop.Quit();
+                }),
+                run_loop.QuitClosure());
 
   mpr1.Reject();
   run_loop.Run();
diff --git a/base/task/promise/promise_unittest.nc b/base/task/promise/promise_unittest.nc
index 191a583a..261a87f2 100644
--- a/base/task/promise/promise_unittest.nc
+++ b/base/task/promise/promise_unittest.nc
@@ -13,32 +13,32 @@
 #if defined(NCTEST_METHOD_CANT_CATCH_NOREJECT_PROMISE) // [r"fatal error: static_assert failed .*\"Can't catch a NoReject promise\."]
 void WontCompile() {
   Promise<int> p;
-  p.CatchOnCurrent(FROM_HERE, BindOnce([]() {}));
+  p.CatchHere(FROM_HERE, BindOnce([]() {}));
 }
 #elif defined(NCTEST_METHOD_CANT_CATCH_NOREJECT_PROMISE_TYPE_TWO) // [r"fatal error: static_assert failed .*\"Can't catch a NoReject promise\."]
 void WontCompile() {
   Promise<int> p;
-  p.ThenOnCurrent(FROM_HERE, BindOnce([](int) {}), BindOnce([]() {}));
+  p.ThenHere(FROM_HERE, BindOnce([](int) {}), BindOnce([]() {}));
 }
 #elif defined(NCTEST_METHOD_RESOLVE_CALLBACK_TYPE_MISSMATCH) // [r"fatal error: static_assert failed .*\"|on_resolve| callback must accept Promise::ResolveType or void\."]
 void WontCompile() {
   Promise<int, void> p;
-  p.ThenOnCurrent(FROM_HERE, BindOnce([](bool) { }));
+  p.ThenHere(FROM_HERE, BindOnce([](bool) { }));
 }
 #elif defined(NCTEST_METHOD_REJECT_CALLBACK_TYPE_MISSMATCH) // [r"fatal error: static_assert failed .*\"|on_reject| callback must accept Promise::ResolveType or void\."]
 void WontCompile() {
   Promise<int, void> p;
-  p.CatchOnCurrent(FROM_HERE, BindOnce([](bool) { }));
+  p.CatchHere(FROM_HERE, BindOnce([](bool) { }));
 }
 #elif defined(NCTEST_METHOD_REJECT_CALLBACK_TYPE_MISSMATCH_TYPE_TWO) // [r"fatal error: static_assert failed .*\"|on_reject| callback must accept Promise::ResolveType or void\."]
 void WontCompile() {
   Promise<int, void> p;
-  p.ThenOnCurrent(FROM_HERE, BindOnce([](int) { }), BindOnce([](bool) { }));
+  p.ThenHere(FROM_HERE, BindOnce([](int) { }), BindOnce([](bool) { }));
 }
 #elif defined(NCTEST_METHOD_INCOMPATIBLE_RETURN_TYPES) // [r"fatal error: static_assert failed .*\"|on_resolve| callback and |on_resolve| callback must return compatible types\."]
 void WontCompile() {
   Promise<void> p;
-  p.ThenOnCurrent(
+  p.ThenHere(
       FROM_HERE,
       BindOnce([]() -> PromiseResult<int, std::string> { return 123; }),
       BindOnce([](int err) -> Rejected<bool> { return "123"; }));
@@ -46,7 +46,7 @@
 #elif defined(NCTEST_METHOD_INCOMPATIBLE_RETURN_TYPES2) // [r"fatal error: static_assert failed .*\"|on_resolve| callback and |on_resolve| callback must return compatible types\."]
 void WontCompile() {
   Promise<void> p;
-  p.ThenOnCurrent(
+  p.ThenHere(
       FROM_HERE,
       BindOnce([]() -> PromiseResult<int, std::string> { return 123; }),
       BindOnce([](int err) -> Resolved<std::string> { return "123"; }));
@@ -54,7 +54,7 @@
 #elif defined(NCTEST_METHOD_INCOMPATIBLE_RETURN_TYPES3) // [r"fatal error: static_assert failed .*\"|on_resolve| callback and |on_resolve| callback must return compatible types\."]
 void WontCompile() {
   Promise<int, void> p;
-  p.ThenOnCurrent(FROM_HERE, BindOnce([](int) { return true; }),
+  p.ThenHere(FROM_HERE, BindOnce([](int) { return true; }),
                              BindOnce([](int) { return 123.0; }));
 }
 #elif defined(NCTEST_METHOD_AMBIGUOUS_CONSTRUCTOR) // [r"fatal error: static_assert failed .*\"Ambiguous because ResolveType and RejectType are the same"]
@@ -92,18 +92,18 @@
 void WontCompile() {
   Promise<int, void> p1;
   // If supported |p2| would have type Promise<NoReject, variant<void, bool>>.
-  auto p2 = p1.ThenOnCurrent(FROM_HERE, BindOnce([]() { return Rejected<bool>(true); }));
+  auto p2 = p1.ThenHere(FROM_HERE, BindOnce([]() { return Rejected<bool>(true); }));
 }
 #elif defined(NCTEST_METHOD_AMBIGUOUS_RESOLVE_TYPE) // [r"fatal error: static_assert failed .*\"Ambiguous promise resolve type"]
 void WontCompile() {
   Promise<int, void> p1;
   // If supported |p2| would have type Promise<variant<int, bool>, NoReject>.
-  auto p2 = p1.CatchOnCurrent(FROM_HERE, BindOnce([](int) { return Resolved<bool>(true); }));
+  auto p2 = p1.CatchHere(FROM_HERE, BindOnce([](int) { return Resolved<bool>(true); }));
 }
 #elif defined(NCTEST_METHOD_NON_CONST_REFERENCE) // [r"fatal error: static_assert failed .*\"Google C.. Style: References in function parameters must be const\."]
 void WontCompile() {
   Promise<std::unique_ptr<int>> p;
-  p.ThenOnCurrent(FROM_HERE, BindOnce([](std::unique_ptr<int>& result) {}));
+  p.ThenHere(FROM_HERE, BindOnce([](std::unique_ptr<int>& result) {}));
 }
 #elif defined(NCTEST_METHOD_NON_CONST_REFERENCE2) // [r"fatal error: static_assert failed .*\"Google C.. Style: References in function parameters must be const\."]
 void WontCompile() {
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 6c58e69..f83a7b1 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8912282021335029072
\ No newline at end of file
+8912228414512885520
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index cf8ccbbb..7945ee6 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8912281389906622656
\ No newline at end of file
+8912230475324815216
\ No newline at end of file
diff --git a/cc/layers/surface_layer.cc b/cc/layers/surface_layer.cc
index f2750e4..ec1c8646 100644
--- a/cc/layers/surface_layer.cc
+++ b/cc/layers/surface_layer.cc
@@ -113,6 +113,10 @@
   SetNeedsCommit();
 }
 
+void SurfaceLayer::SetIsReflection(bool is_reflection) {
+  is_reflection_ = true;
+}
+
 void SurfaceLayer::SetMayContainVideo(bool may_contain_video) {
   may_contain_video_ = may_contain_video;
 }
@@ -150,6 +154,7 @@
   // Unless the client explicitly calls SetSurfaceId again after this
   // commit, don't block on |surface_range_| again.
   deadline_in_frames_ = 0u;
+  layer_impl->SetIsReflection(is_reflection_);
   layer_impl->SetStretchContentToFillBounds(stretch_content_to_fill_bounds_);
   layer_impl->SetSurfaceHitTestable(surface_hit_testable_);
   layer_impl->SetHasPointerEventsNone(has_pointer_events_none_);
diff --git a/cc/layers/surface_layer.h b/cc/layers/surface_layer.h
index 9d15812..30e329a 100644
--- a/cc/layers/surface_layer.h
+++ b/cc/layers/surface_layer.h
@@ -44,7 +44,9 @@
 
   void SetHasPointerEventsNone(bool has_pointer_events_none);
 
-  void SetMayContainVideo(bool);
+  void SetIsReflection(bool is_reflection);
+
+  void SetMayContainVideo(bool may_contain_video);
 
   // Layer overrides.
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
@@ -90,6 +92,9 @@
   // TODO(sunxd): consider renaming it to oopif_has_pointer_events_none_ for
   // disambiguation.
   bool has_pointer_events_none_ = false;
+
+  // This surface layer is reflecting the root surface of another display.
+  bool is_reflection_ = false;
 };
 
 }  // namespace cc
diff --git a/cc/layers/surface_layer_impl.cc b/cc/layers/surface_layer_impl.cc
index 3c12862..9d4a37a 100644
--- a/cc/layers/surface_layer_impl.cc
+++ b/cc/layers/surface_layer_impl.cc
@@ -83,6 +83,14 @@
   NoteLayerPropertyChanged();
 }
 
+void SurfaceLayerImpl::SetIsReflection(bool is_reflection) {
+  if (is_reflection_ == is_reflection)
+    return;
+
+  is_reflection_ = is_reflection;
+  NoteLayerPropertyChanged();
+}
+
 void SurfaceLayerImpl::PushPropertiesTo(LayerImpl* layer) {
   LayerImpl::PushPropertiesTo(layer);
   SurfaceLayerImpl* layer_impl = static_cast<SurfaceLayerImpl*>(layer);
@@ -93,6 +101,7 @@
   layer_impl->SetStretchContentToFillBounds(stretch_content_to_fill_bounds_);
   layer_impl->SetSurfaceHitTestable(surface_hit_testable_);
   layer_impl->SetHasPointerEventsNone(has_pointer_events_none_);
+  layer_impl->SetIsReflection(is_reflection_);
 }
 
 bool SurfaceLayerImpl::WillDraw(
@@ -141,6 +150,7 @@
     quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect,
                  surface_range_, background_color(),
                  stretch_content_to_fill_bounds_, has_pointer_events_none_);
+    quad->is_reflection = is_reflection_;
     // Add the primary surface ID as a dependency.
     append_quads_data->activation_dependencies.push_back(surface_range_.end());
     if (deadline_in_frames_) {
diff --git a/cc/layers/surface_layer_impl.h b/cc/layers/surface_layer_impl.h
index 933664a..97b1f6c 100644
--- a/cc/layers/surface_layer_impl.h
+++ b/cc/layers/surface_layer_impl.h
@@ -61,6 +61,9 @@
   void SetHasPointerEventsNone(bool has_pointer_events_none);
   bool has_pointer_events_none() const { return has_pointer_events_none_; }
 
+  void SetIsReflection(bool is_reflection);
+  bool is_reflection() const { return is_reflection_; }
+
   // LayerImpl overrides.
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
   void PushPropertiesTo(LayerImpl* layer) override;
@@ -87,6 +90,7 @@
   bool stretch_content_to_fill_bounds_ = false;
   bool surface_hit_testable_ = false;
   bool has_pointer_events_none_ = false;
+  bool is_reflection_ = false;
   bool will_draw_ = false;
 };
 
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index c268cce1..27fe4aa 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -484,6 +484,10 @@
     "//chrome/android/features/keyboard_accessory:internal_java",
     "//chrome/android/features/media_router:java",
   ]
+
+  if (disable_autofill_assistant_dfm) {
+    deps += [ "//chrome/android/features/autofill_assistant:java" ]
+  }
 }
 
 # This is a list of all base module jni headers. New features should add their
@@ -2320,12 +2324,6 @@
         },
       ]
     }
-    extra_modules += [
-      {
-        name = "autofill_assistant"
-        module_target = ":${target_name}__autofill_assistant_bundle_module"
-      },
-    ]
     if (dfmify_devtools) {
       extra_modules += [
         {
@@ -2334,6 +2332,14 @@
         },
       ]
     }
+    if (!disable_autofill_assistant_dfm) {
+      extra_modules += [
+        {
+          name = "autofill_assistant"
+          module_target = ":${target_name}__autofill_assistant_bundle_module"
+        },
+      ]
+    }
   }
 }
 
diff --git a/chrome/android/features/autofill_assistant/autofill_assistant_module_tmpl.gni b/chrome/android/features/autofill_assistant/autofill_assistant_module_tmpl.gni
index be350a2..d43b17f5 100644
--- a/chrome/android/features/autofill_assistant/autofill_assistant_module_tmpl.gni
+++ b/chrome/android/features/autofill_assistant/autofill_assistant_module_tmpl.gni
@@ -2,6 +2,15 @@
 import("//build/config/locales.gni")
 import("//chrome/android/features/dynamic_feature_modules.gni")
 
+declare_args() {
+  # This is a developer flag to be able to use the incremental build/install workflow for autofill
+  # assistant. When set to true, autofill_assistant is built as part of the base apk and not a
+  # separate feature module which currently doesn't support incremental builds.
+  #
+  # TODO(http://crbug/864142): Remove once incremental bundle install is available.
+  disable_autofill_assistant_dfm = false
+}
+
 template("autofill_assistant_module_tmpl") {
   _manifest = "$target_gen_dir/$target_name/AndroidManifest.xml"
   _manifest_target = "${target_name}__manifest"
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index 3c67e9db..5d16fe0 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -134,6 +134,8 @@
                     // to show parts of the content view.
                     bottomSheetContainer.setVisibility(View.VISIBLE);
                 }
+
+                maybeShowHeaderChip();
             }
 
             @Override
@@ -169,6 +171,14 @@
                 });
     }
 
+    private void maybeShowHeaderChip() {
+        boolean showChip = mBottomSheetController.getBottomSheet().getSheetState()
+                        == BottomSheet.SheetState.PEEK
+                && mPeekHeightCoordinator.getPeekMode()
+                        == AssistantPeekHeightCoordinator.PeekMode.HANDLE_HEADER;
+        mModel.getHeaderModel().set(AssistantHeaderModel.CHIP_VISIBLE, showChip);
+    }
+
     /**
      * Cleanup resources when this goes out of scope.
      */
@@ -229,6 +239,7 @@
     /** Set the peek mode. */
     void setPeekMode(@AssistantPeekHeightCoordinator.PeekMode int peekMode) {
         mPeekHeightCoordinator.setPeekMode(peekMode);
+        maybeShowHeaderChip();
     }
 
     @Override
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java
index 710e21f..71819605 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantPeekHeightCoordinator.java
@@ -159,6 +159,11 @@
         return mPeekHeight;
     }
 
+    /** Return the current peek mode. */
+    int getPeekMode() {
+        return mPeekMode;
+    }
+
     /**
      * Adapt the padding top of the toolbar such that header and carousel are visible if desired.
      */
@@ -172,16 +177,12 @@
                 break;
             case PeekMode.HANDLE_HEADER_CAROUSELS:
                 mToolbarPaddingBottom = mHeaderHeight + mProgressBarHeight;
-                if (mActionsHeight > 0 || mSuggestionsHeight > 0) {
-                    mToolbarPaddingBottom += mChildrenVerticalSpacing;
-                }
-
                 if (mSuggestionsHeight > 0) {
                     mToolbarPaddingBottom += mSuggestionsHeight + mChildrenVerticalSpacing;
                 }
 
                 if (mActionsHeight > 0) {
-                    mToolbarPaddingBottom += mActionsHeight + mChildrenVerticalSpacing;
+                    mToolbarPaddingBottom += mActionsHeight;
                 }
 
                 // We decrease the artificial padding we add to the toolbar by 1 pixel to make sure
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index ef44af0..416693d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -15,6 +15,7 @@
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantCarouselModel;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip.Type;
+import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
@@ -229,7 +230,8 @@
         for (int i = 0; i < texts.length; i++) {
             final int suggestionIndex = i;
             chips.add(new AssistantChip(AssistantChip.Type.CHIP_ASSISTIVE, icons[i], texts[i],
-                    disabled[i], () -> safeNativeOnSuggestionSelected(suggestionIndex)));
+                    disabled[i], /* sticky= */ false,
+                    () -> safeNativeOnSuggestionSelected(suggestionIndex)));
         }
         AssistantCarouselModel model = getModel().getSuggestionsModel();
         setChips(model, chips);
@@ -245,10 +247,10 @@
      * Adds an action button to the chip list, which executes the action {@code actionIndex}.
      */
     @CalledByNative
-    private void addActionButton(
-            List<AssistantChip> chips, int icon, String text, int actionIndex, boolean disabled) {
+    private void addActionButton(List<AssistantChip> chips, int icon, String text, int actionIndex,
+            boolean disabled, boolean sticky) {
         chips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, icon, text, disabled,
-                () -> safeNativeOnActionSelected(actionIndex)));
+                sticky, () -> safeNativeOnActionSelected(actionIndex)));
     }
 
     /**
@@ -256,9 +258,9 @@
      * actionIndex}.
      */
     @CalledByNative
-    private void addHighlightedActionButton(
-            List<AssistantChip> chips, int icon, String text, int actionIndex, boolean disabled) {
-        chips.add(new AssistantChip(Type.BUTTON_FILLED_BLUE, icon, text, disabled,
+    private void addHighlightedActionButton(List<AssistantChip> chips, int icon, String text,
+            int actionIndex, boolean disabled, boolean sticky) {
+        chips.add(new AssistantChip(Type.BUTTON_FILLED_BLUE, icon, text, disabled, sticky,
                 () -> safeNativeOnActionSelected(actionIndex)));
     }
 
@@ -267,10 +269,10 @@
      * {@code actionIndex}, or shuts down Autofill Assistant if {@code actionIndex} is {@code -1}.
      */
     @CalledByNative
-    private void addCancelButton(
-            List<AssistantChip> chips, int icon, String text, int actionIndex, boolean disabled) {
+    private void addCancelButton(List<AssistantChip> chips, int icon, String text, int actionIndex,
+            boolean disabled, boolean sticky) {
         chips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, icon, text, disabled,
-                () -> safeNativeOnCancelButtonClicked(actionIndex)));
+                sticky, () -> safeNativeOnCancelButtonClicked(actionIndex)));
     }
 
     /**
@@ -278,15 +280,29 @@
      */
     @CalledByNative
     private void addCloseButton(
-            List<AssistantChip> chips, int icon, String text, boolean disabled) {
+            List<AssistantChip> chips, int icon, String text, boolean disabled, boolean sticky) {
         chips.add(new AssistantChip(AssistantChip.Type.BUTTON_HAIRLINE, icon, text, disabled,
-                this::safeNativeOnCloseButtonClicked));
+                sticky, this::safeNativeOnCloseButtonClicked));
     }
 
     @CalledByNative
     private void setActions(List<AssistantChip> chips) {
         AssistantCarouselModel model = getModel().getActionsModel();
         setChips(model, chips);
+        setHeaderChip(chips);
+    }
+
+    private void setHeaderChip(List<AssistantChip> chips) {
+        // The header chip is the first sticky chip found in the actions.
+        AssistantChip headerChip = null;
+        for (AssistantChip chip : chips) {
+            if (chip.isSticky()) {
+                headerChip = chip;
+                break;
+            }
+        }
+
+        getModel().getHeaderModel().set(AssistantHeaderModel.CHIP, headerChip);
     }
 
     private void setChips(AssistantCarouselModel model, List<AssistantChip> chips) {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChip.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChip.java
index 54c1946..61cc3f4 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChip.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChip.java
@@ -44,19 +44,37 @@
         int REFRESH = 3;
     }
 
+    /**
+     * The type of the chip. This will impact the background, border and text colors of the chip.
+     */
     private final @Type int mType;
+
+    /** The icon, shown next to the text.*/
     private final @Icon int mIcon;
+
+    /** The text displayed on the chip. */
     private final String mText;
+
+    /** Whether this chip is enabled or not. */
     private final boolean mDisabled;
+
+    /**
+     * Whether this chip is sticky. A sticky chip will be a candidate to be displayed in the header
+     * if the peek mode of the sheet is HANDLE_HEADER.
+     */
+    private final boolean mSticky;
+
+    /** The callback that will be triggered when this chip is clicked. */
     private final Runnable mSelectedListener;
 
     public AssistantChip(@Type int type, @Icon int icon, String text, boolean disabled,
-            Runnable selectedListener) {
+            boolean sticky, Runnable selectedListener) {
         mType = type;
         mIcon = icon;
         mText = text;
-        mSelectedListener = selectedListener;
         mDisabled = disabled;
+        mSticky = sticky;
+        mSelectedListener = selectedListener;
     }
 
     public int getType() {
@@ -75,6 +93,10 @@
         return mDisabled;
     }
 
+    public boolean isSticky() {
+        return mSticky;
+    }
+
     public Runnable getSelectedListener() {
         return mSelectedListener;
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
index dcee81e..84c98bea 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
@@ -15,15 +15,19 @@
  * The {@link ViewHolder} responsible for reflecting an {@link AssistantChip} to a {@link
  * ButtonView}.
  */
-class AssistantChipViewHolder extends ViewHolder {
-    final ButtonView mView;
+public class AssistantChipViewHolder extends ViewHolder {
+    private final ButtonView mView;
 
-    private AssistantChipViewHolder(ButtonView view) {
+    /** The type of this ViewHolder, as returned by {@link #getViewType(AssistantChip)}. */
+    private final int mType;
+
+    private AssistantChipViewHolder(ButtonView view, int type) {
         super(view);
         mView = view;
+        mType = type;
     }
 
-    static AssistantChipViewHolder create(ViewGroup parent, int viewType) {
+    public static AssistantChipViewHolder create(ViewGroup parent, int viewType) {
         LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
         ButtonView view = null;
         switch (viewType % AssistantChip.Type.NUM_ENTRIES) {
@@ -49,10 +53,10 @@
             view.setEnabled(false);
         }
 
-        return new AssistantChipViewHolder(view);
+        return new AssistantChipViewHolder(view, viewType);
     }
 
-    static int getViewType(AssistantChip chip) {
+    public static int getViewType(AssistantChip chip) {
         // We add AssistantChip.Type.CHIP_TYPE_NUMBER to differentiate between enabled and disabled
         // chips of the same type. Ideally, we should return a (type, disabled) tuple but
         // RecyclerView does not allow that.
@@ -63,6 +67,14 @@
         return chip.getType();
     }
 
+    public ButtonView getView() {
+        return mView;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
     public void bind(AssistantChip chip) {
         String text = chip.getText();
         if (text.isEmpty()) {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
index f5771cf..e5022a80 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
@@ -7,6 +7,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /**
@@ -31,9 +32,14 @@
     static final WritableObjectPropertyKey<Runnable> FEEDBACK_BUTTON_CALLBACK =
             new WritableObjectPropertyKey<>();
 
+    public static final WritableObjectPropertyKey<AssistantChip> CHIP =
+            new WritableObjectPropertyKey<>();
+
+    public static final WritableBooleanPropertyKey CHIP_VISIBLE = new WritableBooleanPropertyKey();
+
     public AssistantHeaderModel() {
         super(VISIBLE, STATUS_MESSAGE, PROGRESS, PROGRESS_VISIBLE, SPIN_POODLE,
-                FEEDBACK_BUTTON_CALLBACK);
+                FEEDBACK_BUTTON_CALLBACK, CHIP, CHIP_VISIBLE);
     }
 
     @CalledByNative
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
index bc6b3f1..df74cef 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
@@ -6,11 +6,18 @@
 
 import android.content.Context;
 import android.support.annotation.Nullable;
+import android.transition.ChangeBounds;
+import android.transition.Fade;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.PopupMenu;
 import android.widget.TextView;
 
 import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
+import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChipViewHolder;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.autofill_assistant.AutofillAssistantPreferences;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -24,16 +31,23 @@
 class AssistantHeaderViewBinder
         implements PropertyModelChangeProcessor.ViewBinder<AssistantHeaderModel,
                 AssistantHeaderViewBinder.ViewHolder, PropertyKey> {
+    private final TransitionSet mTransition = new TransitionSet()
+                                                      .addTransition(new Fade(Fade.OUT))
+                                                      .addTransition(new ChangeBounds())
+                                                      .addTransition(new Fade(Fade.IN));
+
     /**
      * A wrapper class that holds the different views of the header.
      */
     static class ViewHolder {
         final AnimatedPoodle mPoodle;
-        final View mHeader;
+        final ViewGroup mHeader;
         final TextView mStatusMessage;
         final AnimatedProgressBar mProgressBar;
         final View mProfileIconView;
         final PopupMenu mProfileIconMenu;
+        @Nullable
+        AssistantChipViewHolder mChip;
 
         public ViewHolder(Context context, View bottomBarView, AnimatedPoodle poodle) {
             mPoodle = poodle;
@@ -65,11 +79,57 @@
             view.mPoodle.setSpinEnabled(model.get(AssistantHeaderModel.SPIN_POODLE));
         } else if (AssistantHeaderModel.FEEDBACK_BUTTON_CALLBACK == propertyKey) {
             setProfileMenuListener(view, model.get(AssistantHeaderModel.FEEDBACK_BUTTON_CALLBACK));
+        } else if (AssistantHeaderModel.CHIP == propertyKey) {
+            bindChip(view, model.get(AssistantHeaderModel.CHIP));
+            maybeShowChip(model, view);
+        } else if (AssistantHeaderModel.CHIP_VISIBLE == propertyKey) {
+            maybeShowChip(model, view);
         } else {
             assert false : "Unhandled property detected in AssistantHeaderViewBinder!";
         }
     }
 
+    private void maybeShowChip(AssistantHeaderModel model, ViewHolder view) {
+        TransitionManager.beginDelayedTransition(view.mHeader, mTransition);
+        if (model.get(AssistantHeaderModel.CHIP_VISIBLE)
+                && model.get(AssistantHeaderModel.CHIP) != null) {
+            view.mChip.getView().setVisibility(View.VISIBLE);
+            view.mProfileIconView.setVisibility(View.GONE);
+        } else {
+            if (view.mChip != null) {
+                view.mChip.getView().setVisibility(View.GONE);
+            }
+
+            view.mProfileIconView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private void bindChip(ViewHolder view, @Nullable AssistantChip chip) {
+        if (chip == null) {
+            return;
+        }
+
+        TransitionManager.beginDelayedTransition(view.mHeader, mTransition);
+
+        int viewType = AssistantChipViewHolder.getViewType(chip);
+
+        // If there is already a chip in the header but with incompatible type, remove it.
+        if (view.mChip != null && view.mChip.getType() != viewType) {
+            view.mHeader.removeView(view.mChip.getView());
+            view.mChip = null;
+        }
+
+        // If there is no chip already in the header, create one and add it at the end of the
+        // header.
+        if (view.mChip == null) {
+            view.mChip = AssistantChipViewHolder.create(view.mHeader, viewType);
+            view.mHeader.addView(view.mChip.getView());
+        }
+
+        // Bind the chip to the view.
+        view.mChip.bind(chip);
+    }
+
     private void setProgressBarVisibility(ViewHolder view, AssistantHeaderModel model) {
         if (model.get(AssistantHeaderModel.VISIBLE)
                 && model.get(AssistantHeaderModel.PROGRESS_VISIBLE)) {
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
index cb3dfbe..21c574c 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
@@ -239,13 +239,13 @@
 
     private void testChips(InOrder inOrder, AssistantCarouselModel carouselModel,
             AssistantCarouselCoordinator carouselCoordinator) {
-        List<AssistantChip> chips =
-                Arrays.asList(new AssistantChip(AssistantChip.Type.CHIP_ASSISTIVE,
-                                      AssistantChip.Icon.NONE, "chip 0",
-                                      /* disabled= */ false, () -> {/* do nothing */}),
-                        new AssistantChip(AssistantChip.Type.CHIP_ASSISTIVE,
-                                AssistantChip.Icon.NONE, "chip 1",
-                                /* disabled= */ false, mRunnableMock));
+        List<AssistantChip> chips = Arrays.asList(
+                new AssistantChip(AssistantChip.Type.CHIP_ASSISTIVE, AssistantChip.Icon.NONE,
+                        "chip 0",
+                        /* disabled= */ false, /* sticky= */ false, () -> {/* do nothing */}),
+                new AssistantChip(AssistantChip.Type.CHIP_ASSISTIVE, AssistantChip.Icon.NONE,
+                        "chip 1",
+                        /* disabled= */ false, /* sticky= */ false, mRunnableMock));
         ThreadUtils.runOnUiThreadBlocking(() -> carouselModel.getChipsModel().set(chips));
         RecyclerView chipsViewContainer = carouselCoordinator.getView();
         Assert.assertEquals(2, chipsViewContainer.getAdapter().getItemCount());
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java
index df458d8..90461bd 100644
--- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java
+++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java
@@ -4,8 +4,11 @@
 
 package org.chromium.chrome.browser.touchless.ui.iph;
 
+import android.support.annotation.IntDef;
+
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.dom_distiller.TabDistillabilityProvider;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.native_page.NativePageFactory;
 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
@@ -17,6 +20,8 @@
 import org.chromium.ui.touchless.CursorObserver;
 import org.chromium.ui.touchless.TouchlessEventHandler;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.FutureTask;
 
 /**
@@ -28,6 +33,8 @@
     private FutureTask mHideTask;
     private int mPageLoadCount;
     private DisplayLockHandle mDisplayLockHandle;
+    private boolean mIsFallbackCursorModeOn;
+    private boolean mShowedWhenPageLoadStarted;
 
     private static final long DISPLAY_DURATION_MS = 3000;
 
@@ -36,6 +43,15 @@
     private static final int INTRODUCTORY_SESSIONS = 6;
     private static final int INTRODUCTORY_PAGE_LOAD_CYCLE = 3;
 
+    @IntDef({DisplayCause.PAGE_LOAD_STARTED, DisplayCause.PAGE_LOAD_FINISHED,
+            DisplayCause.FALLBACK_CURSOR_TOGGLED})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface DisplayCause {
+        int PAGE_LOAD_STARTED = 0;
+        int PAGE_LOAD_FINISHED = 1;
+        int FALLBACK_CURSOR_TOGGLED = 2;
+    }
+
     KeyFunctionsIPHMediator(PropertyModel model, ActivityTabProvider activityTabProvider) {
         mModel = model;
         mKeyFunctionsIPHTabObserver = new KeyFunctionsIPHTabObserver(activityTabProvider);
@@ -47,13 +63,13 @@
 
     @Override
     public void onFallbackCursorModeToggled(boolean isOn) {
-        show(isOn, false);
+        mIsFallbackCursorModeOn = isOn;
+        show(DisplayCause.FALLBACK_CURSOR_TOGGLED);
     }
 
-    private void show(boolean isFallbackCursorModeOn, boolean fromPageLoadStarted) {
-        // TODO(crbug.com/942665): Populate this.
-        boolean pageOptimizedForMobile = true;
-        if (fromPageLoadStarted && pageOptimizedForMobile) {
+    private void show(@DisplayCause int displayCause) {
+        if (displayCause == DisplayCause.PAGE_LOAD_STARTED) {
+            mShowedWhenPageLoadStarted = false;
             int totalSessionCount = ChromePreferenceManager.getInstance().readInt(
                     ChromePreferenceManager.TOUCHLESS_BROWSING_SESSION_COUNT);
             if (totalSessionCount <= INTRODUCTORY_SESSIONS
@@ -61,6 +77,11 @@
                 return;
             }
             if (totalSessionCount > INTRODUCTORY_SESSIONS && mPageLoadCount > 1) return;
+            mShowedWhenPageLoadStarted = true;
+        } else if (mShowedWhenPageLoadStarted && displayCause == DisplayCause.PAGE_LOAD_FINISHED) {
+            // If we have already shown the IPH when page load started, we should avoid showing it
+            // again when page load is finished.
+            return;
         }
 
         // If we are already showing this IPH, we should release the lock.
@@ -78,7 +99,7 @@
             return null;
         });
         PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, mHideTask, DISPLAY_DURATION_MS);
-        mModel.set(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE, isFallbackCursorModeOn);
+        mModel.set(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE, mIsFallbackCursorModeOn);
         mModel.set(KeyFunctionsIPHProperties.IS_VISIBLE, true);
     }
 
@@ -98,7 +119,19 @@
             if (NativePageFactory.isNativePageUrl(url, tab.isIncognito())) return;
 
             mPageLoadCount++;
-            show(false, true);
+            show(DisplayCause.PAGE_LOAD_STARTED);
+        }
+
+        @Override
+        public void onPageLoadFinished(Tab tab, String url) {
+            if (NativePageFactory.isNativePageUrl(url, tab.isIncognito())) return;
+
+            TabDistillabilityProvider distillabilityProvider = TabDistillabilityProvider.get(tab);
+            if (distillabilityProvider != null
+                    && distillabilityProvider.isDistillabilityDetermined()
+                    && !distillabilityProvider.isMobileOptimized()) {
+                show(DisplayCause.PAGE_LOAD_FINISHED);
+            }
         }
     }
 }
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index 41c5299e..24e8a86 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -341,7 +341,10 @@
 }
 
 junit_binary("webapk_shell_apk_h2o_junit_tests") {
-  java_files = [ "junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java" ]
+  java_files = [
+    "junit/src/org/chromium/webapk/shell_apk/CustomAndroidOsShadowAsyncTask.java",
+    "junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java",
+  ]
   deps = [
     ":${h2o_junit_manifest_target_name}",
     ":h2o_j_unit_webapk_generated_webapk_with_service_java",
diff --git a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/CustomAndroidOsShadowAsyncTask.java b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/CustomAndroidOsShadowAsyncTask.java
new file mode 100644
index 0000000..f2d960d
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/CustomAndroidOsShadowAsyncTask.java
@@ -0,0 +1,31 @@
+// Copyright 2019 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.
+
+package org.chromium.webapk.shell_apk;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowAsyncTask;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Forces async tasks to execute with the default executor.
+ * This works around Robolectric not working out of the box with custom executors.
+ *
+ * @param <Params>
+ * @param <Progress>
+ * @param <Result>
+ */
+@SuppressWarnings("NoAndroidAsyncTaskCheck")
+@Implements(android.os.AsyncTask.class)
+public class CustomAndroidOsShadowAsyncTask<Params, Progress, Result>
+        extends ShadowAsyncTask<Params, Progress, Result> {
+    @Override
+    @Implementation
+    public final android.os.AsyncTask<Params, Progress, Result> executeOnExecutor(
+            Executor executor, Params... params) {
+        return super.execute(params);
+    }
+}
diff --git a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java
index cc42488..4df2885 100644
--- a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java
+++ b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/h2o/LaunchTest.java
@@ -19,7 +19,6 @@
 
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
@@ -31,26 +30,28 @@
 
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 import org.chromium.webapk.lib.common.WebApkConstants;
+import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
+import org.chromium.webapk.shell_apk.CustomAndroidOsShadowAsyncTask;
 import org.chromium.webapk.shell_apk.HostBrowserLauncher;
 import org.chromium.webapk.shell_apk.WebApkSharedPreferences;
 import org.chromium.webapk.shell_apk.WebApkUtils;
+import org.chromium.webapk.test.WebApkTestHelper;
 
 import java.util.ArrayList;
 
 /** Tests launching WebAPK. */
 @RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, packageName = LaunchTest.WEBAPK_PACKAGE_NAME)
+@Config(manifest = Config.NONE, shadows = {CustomAndroidOsShadowAsyncTask.class})
 public final class LaunchTest {
     /** Values based on manifest specified in GN file. */
-    public static final String WEBAPK_PACKAGE_NAME = "org.chromium.webapk.h2o.junit_webapk";
     private static final String BROWSER_PACKAGE_NAME = "com.google.android.apps.chrome";
-    private static final String SHARE_ACTIVITY1_CLASS_NAME =
-            "org.chromium.webapk.shell_apk.ShareActivity1";
     private static final String DEFAULT_START_URL = "https://pwa.rocks/";
 
     /** Chromium version which does not support showing the splash screen within WebAPK. */
     private static final int BROWSER_H2O_INCOMPATIBLE_VERSION = 57;
 
+    private static String sWebApkPackageName;
+
     private Context mAppContext;
     private ShadowApplication mShadowApplication;
     private PackageManager mPackageManager;
@@ -58,10 +59,16 @@
 
     @Before
     public void setUp() {
+        sWebApkPackageName = RuntimeEnvironment.application.getPackageName();
+
         mShadowApplication = ShadowApplication.getInstance();
         mAppContext = RuntimeEnvironment.application;
         mPackageManager = mAppContext.getPackageManager();
         mShadowPackageManager = Shadows.shadowOf(mPackageManager);
+
+        Bundle metadata = new Bundle();
+        metadata.putString(WebApkMetaDataKeys.START_URL, "https://pwa.rocks/");
+        WebApkTestHelper.registerWebApkWithMetaData(sWebApkPackageName, metadata, null);
     }
 
     /**
@@ -72,12 +79,11 @@
      * the intent and the host browser getting launched.
      */
     @Test
-    @Ignore
     public void testDeepLink() {
         final String deepLinkUrl = "https://pwa.rocks/deep.html";
 
         Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(deepLinkUrl));
-        launchIntent.setPackage(WEBAPK_PACKAGE_NAME);
+        launchIntent.setPackage(sWebApkPackageName);
 
         ArrayList<Intent> launchedIntents;
         launchedIntents =
@@ -118,10 +124,9 @@
 
     /** Test that the host browser is launched as a result of a main launch intent. */
     @Test
-    @Ignore
     public void testMainIntent() {
         Intent launchIntent = new Intent(Intent.ACTION_MAIN);
-        launchIntent.setPackage(WEBAPK_PACKAGE_NAME);
+        launchIntent.setPackage(sWebApkPackageName);
 
         ArrayList<Intent> launchedIntents;
         launchedIntents =
@@ -165,11 +170,20 @@
      * browser getting launched.
      */
     @Test
-    @Ignore
     public void testTargetShareActivityPreserved() {
+        Bundle metadata = new Bundle();
+        metadata.putString(WebApkMetaDataKeys.START_URL, "https://pwa.rocks/");
+        Bundle[] shareMetadata = new Bundle[2];
+        for (int i = 0; i < shareMetadata.length; ++i) {
+            shareMetadata[i] = new Bundle();
+            shareMetadata[i].putString(WebApkMetaDataKeys.SHARE_ACTION, "https://pwa.rocks/share");
+        }
+        WebApkTestHelper.registerWebApkWithMetaData(sWebApkPackageName, metadata, shareMetadata);
+
+        final String shareActivityClassName =
+                WebApkTestHelper.getGeneratedShareTargetActivityClassName(1);
         Intent launchIntent = new Intent(Intent.ACTION_SEND);
-        launchIntent.setComponent(
-                new ComponentName(WEBAPK_PACKAGE_NAME, SHARE_ACTIVITY1_CLASS_NAME));
+        launchIntent.setComponent(new ComponentName(sWebApkPackageName, shareActivityClassName));
         launchIntent.putExtra(Intent.EXTRA_TEXT, "subject_value");
 
         ArrayList<Intent> launchedIntents =
@@ -179,7 +193,7 @@
         Assert.assertTrue(launchedIntents.size() > 1);
 
         Intent browserLaunchIntent = launchedIntents.get(launchedIntents.size() - 1);
-        Assert.assertEquals(SHARE_ACTIVITY1_CLASS_NAME,
+        Assert.assertEquals(shareActivityClassName,
                 browserLaunchIntent.getStringExtra(
                         WebApkConstants.EXTRA_WEBAPK_SELECTED_SHARE_TARGET_ACTIVITY_CLASS_NAME));
     }
@@ -190,13 +204,12 @@
      * the deep link getting handled and the host browser getting launched.
      */
     @Test
-    @Ignore
     public void testSourcePropagated() {
         final String deepLinkUrl = "https://pwa.rocks/deep_link.html";
         final int source = 2;
 
         Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(deepLinkUrl));
-        launchIntent.setPackage(WEBAPK_PACKAGE_NAME);
+        launchIntent.setPackage(sWebApkPackageName);
         launchIntent.putExtra(WebApkConstants.EXTRA_SOURCE, source);
 
         ArrayList<Intent> launchedIntents =
@@ -217,12 +230,11 @@
      * yield an infinite loop.
      */
     @Test
-    @Ignore
     public void testDoesNotPropagateRelaunchDirective() throws Exception {
         final String deepLinkUrl = "https://pwa.rocks/deep_link.html";
 
         Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(deepLinkUrl));
-        launchIntent.setPackage(WEBAPK_PACKAGE_NAME);
+        launchIntent.setPackage(sWebApkPackageName);
         launchIntent.putExtra(WebApkConstants.EXTRA_RELAUNCH, true);
 
         ArrayList<Intent> launchedIntents =
@@ -240,7 +252,6 @@
      * enabled component is slow.
      */
     @Test
-    @Ignore
     public void testDoesNotLoopIfEnablingInitialSplashActivityIsSlow() {
         // InitialSplashActivity is disabled. Host browser is compatible with SplashActivity.
         changeWebApkActivityEnabledSetting(mPackageManager, H2OOpaqueMainActivity.class,
@@ -251,7 +262,7 @@
                 BROWSER_PACKAGE_NAME, H2OLauncher.MINIMUM_REQUIRED_CHROMIUM_VERSION_NEW_SPLASH);
 
         Intent launchIntent = new Intent(Intent.ACTION_MAIN);
-        launchIntent.setPackage(WEBAPK_PACKAGE_NAME);
+        launchIntent.setPackage(sWebApkPackageName);
 
         // WebAPK requested host browser to relaunch WebAPK recently. The WebAPK should not ask
         // the host browser to relaunch it again.
@@ -261,7 +272,7 @@
                     System.currentTimeMillis() - 1);
             editor.apply();
 
-            Robolectric.buildActivity(H2OMainActivity.class, launchIntent).create();
+            buildActivityFully(H2OMainActivity.class, launchIntent);
             Intent startedActivityIntent = mShadowApplication.getNextStartedActivity();
             Assert.assertEquals(BROWSER_PACKAGE_NAME, startedActivityIntent.getPackage());
             Assert.assertFalse(startedActivityIntent.hasExtra(WebApkConstants.EXTRA_RELAUNCH));
@@ -274,7 +285,7 @@
             editor.putLong(WebApkSharedPreferences.PREF_REQUEST_HOST_BROWSER_RELAUNCH_TIMESTAMP, 1);
             editor.apply();
 
-            Robolectric.buildActivity(H2OMainActivity.class, launchIntent).create();
+            buildActivityFully(H2OMainActivity.class, launchIntent);
             Intent startedActivityIntent = mShadowApplication.getNextStartedActivity();
             Assert.assertEquals(BROWSER_PACKAGE_NAME, startedActivityIntent.getPackage());
             Assert.assertTrue(startedActivityIntent.hasExtra(WebApkConstants.EXTRA_RELAUNCH));
@@ -338,7 +349,7 @@
     /** Changes whether the passed in WebAPK activity is enabled. */
     private static void changeWebApkActivityEnabledSetting(
             PackageManager packageManager, Class<? extends Activity> activity, int enabledSetting) {
-        ComponentName component = new ComponentName(WEBAPK_PACKAGE_NAME, activity.getName());
+        ComponentName component = new ComponentName(sWebApkPackageName, activity.getName());
         packageManager.setComponentEnabledSetting(
                 component, enabledSetting, PackageManager.DONT_KILL_APP);
     }
@@ -346,7 +357,7 @@
     /** Returns whether the passed in WebAPK activity is enabled. */
     private static boolean isWebApkActivityEnabled(
             PackageManager packageManager, Class<? extends Activity> activity) {
-        ComponentName component = new ComponentName(WEBAPK_PACKAGE_NAME, activity.getName());
+        ComponentName component = new ComponentName(sWebApkPackageName, activity.getName());
         int enabledSetting = packageManager.getComponentEnabledSetting(component);
         return (enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
     }
@@ -360,7 +371,7 @@
             Intent launchIntent, Class<? extends Activity> launchActivity, String browserPackage) {
         ArrayList<Intent> activityIntentChain = new ArrayList<Intent>();
 
-        Robolectric.buildActivity(launchActivity, launchIntent).create();
+        buildActivityFully(launchActivity, launchIntent);
         for (;;) {
             Intent startedActivityIntent = mShadowApplication.getNextStartedActivity();
             if (startedActivityIntent == null) break;
@@ -374,7 +385,7 @@
                 String startUrl = startedActivityIntent.getStringExtra(WebApkConstants.EXTRA_URL);
                 Intent relaunchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(startUrl));
                 relaunchIntent.setComponent(new ComponentName(
-                        WEBAPK_PACKAGE_NAME, H2OTransparentLauncherActivity.class.getName()));
+                        sWebApkPackageName, H2OTransparentLauncherActivity.class.getName()));
                 Bundle startedActivityExtras = startedActivityIntent.getExtras();
                 if (startedActivityExtras != null) {
                     relaunchIntent.putExtras(startedActivityExtras);
@@ -390,11 +401,15 @@
             } catch (ClassNotFoundException e) {
                 Assert.fail();
             }
-            Robolectric.buildActivity(startedActivityClass, startedActivityIntent).create();
+            buildActivityFully(startedActivityClass, startedActivityIntent);
         }
         return activityIntentChain;
     }
 
+    private static void buildActivityFully(Class<? extends Activity> activityClass, Intent intent) {
+        Robolectric.buildActivity(activityClass, intent).create().start().resume().visible();
+    }
+
     /** Installs browser with the given package name and version. */
     private void installBrowser(String browserPackageName, int version) {
         Intent intent = WebApkUtils.getQueryInstalledBrowsersIntent();
@@ -415,7 +430,9 @@
         packageInfo.packageName = packageName;
         packageInfo.versionName = version + ".";
         packageInfo.applicationInfo = new ApplicationInfo();
+        packageInfo.applicationInfo.packageName = packageName;
         packageInfo.applicationInfo.enabled = true;
+        packageInfo.applicationInfo.metaData = new Bundle();
         return packageInfo;
     }
 }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0f84b7f..d96ae27 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3821,9 +3821,9 @@
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
 
 #if defined(OS_CHROMEOS)
-    {"smart-dim-20190221", flag_descriptions::kSmartDim20190221Name,
-     flag_descriptions::kSmartDim20190221Description, kOsCrOS,
-     FEATURE_VALUE_TYPE(chromeos::features::kSmartDim20190221)},
+    {"smart-dim-model-v3", flag_descriptions::kSmartDimModelV3Name,
+     flag_descriptions::kSmartDimModelV3Description, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kSmartDimModelV3)},
 #endif  // defined(OS_CHROMEOS)
 
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 21084fa..adf9f60 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -404,31 +404,36 @@
     switch (action.type) {
       case HIGHLIGHTED_ACTION:
         Java_AutofillAssistantUiController_addHighlightedActionButton(
-            env, java_object_, chips, icon, text, i, action.disabled);
+            env, java_object_, chips, icon, text, i, action.disabled,
+            action.sticky);
         break;
 
       case NORMAL_ACTION:
         Java_AutofillAssistantUiController_addActionButton(
-            env, java_object_, chips, icon, text, i, action.disabled);
+            env, java_object_, chips, icon, text, i, action.disabled,
+            action.sticky);
         break;
 
       case CANCEL_ACTION:
         // A Cancel button sneaks in an UNDO snackbar before executing the
         // action, while a close button behaves like a normal button.
         Java_AutofillAssistantUiController_addCancelButton(
-            env, java_object_, chips, icon, text, i, action.disabled);
+            env, java_object_, chips, icon, text, i, action.disabled,
+            action.sticky);
         has_close_or_cancel = true;
         break;
 
       case CLOSE_ACTION:
         Java_AutofillAssistantUiController_addActionButton(
-            env, java_object_, chips, icon, text, i, action.disabled);
+            env, java_object_, chips, icon, text, i, action.disabled,
+            action.sticky);
         has_close_or_cancel = true;
         break;
 
       case DONE_ACTION:
         Java_AutofillAssistantUiController_addHighlightedActionButton(
-            env, java_object_, chips, icon, text, i, action.disabled);
+            env, java_object_, chips, icon, text, i, action.disabled,
+            action.sticky);
         has_close_or_cancel = true;
         break;
 
@@ -446,12 +451,12 @@
       Java_AutofillAssistantUiController_addCloseButton(
           env, java_object_, chips, ICON_CLEAR,
           base::android::ConvertUTF8ToJavaString(env, ""),
-          /* disabled= */ false);
+          /* disabled= */ false, /* sticky= */ true);
     } else if (ui_delegate_->GetState() != AutofillAssistantState::INACTIVE) {
       Java_AutofillAssistantUiController_addCancelButton(
           env, java_object_, chips, ICON_CLEAR,
           base::android::ConvertUTF8ToJavaString(env, ""), -1,
-          /* disabled= */ false);
+          /* disabled= */ false, /* sticky= */ true);
     }
   }
 
diff --git a/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.h b/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.h
index 5383da5..0f829266 100644
--- a/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.h
+++ b/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl.h
@@ -35,7 +35,7 @@
       policy::CloudPolicyClient* policy_client);
 
   // A constructor which allows custom CryptohomeClient and AttestationFlow
-  // implementations.  Useful for testing.
+  // implementations. Useful for testing.
   EnrollmentCertificateUploaderImpl(policy::CloudPolicyClient* policy_client,
                                     CryptohomeClient* cryptohome_client,
                                     AttestationFlow* attestation_flow);
diff --git a/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl_unittest.cc b/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl_unittest.cc
index a2613e0..b56723a9 100644
--- a/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl_unittest.cc
+++ b/chrome/browser/chromeos/attestation/enrollment_certificate_uploader_impl_unittest.cc
@@ -69,16 +69,15 @@
 
  protected:
   void SetupMocks() {
-    // Setup expected key uploads.  Use WillOnce() so StrictMock will trigger an
-    // error if our expectations are not met exactly.  We want to verify that
+    // Setup expected cert uploads. Use WillOnce() so StrictMock will trigger an
+    // error if our expectations are not met exactly. We want to verify that
     // during a single run through the uploader only one upload operation occurs
-    // (because it is costly) and similarly, that the writing of the uploaded
-    // status in the key payload matches the upload operation.
+    // (because it is costly).
     EXPECT_CALL(policy_client_,
                 UploadEnterpriseEnrollmentCertificate("fake_cert", _))
         .WillOnce(WithArgs<1>(Invoke(StatusCallbackSuccess)));
 
-    // Setup expected cert generations.  Again use WillOnce().  Cert generation
+    // Setup expected cert generations. Again use WillOnce(). Cert generation
     // is another costly operation and if it gets triggered more than once
     // during a single pass this indicates a logical problem in the uploader.
     EXPECT_CALL(attestation_flow_, GetCertificate(_, _, _, _, _))
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index 7bcc8a8..594e2380 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -225,31 +225,15 @@
       return;
     }
 
-    crostini_manager_->ListVmDisks(
-        base::BindOnce(&CrostiniRestarter::ListVmDisksFinished, this));
-  }
-
-  void ListVmDisksFinished(CrostiniResult result, int64_t disk_space_taken) {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    if (is_aborted_) {
-      std::move(abort_callback_).Run();
-      return;
-    }
-    if (result != CrostiniResult::SUCCESS) {
-      LOG(ERROR) << "Failed to list disk images.";
-      FinishRestart(result);
-      return;
-    }
     base::PostTaskWithTraitsAndReplyWithResult(
         FROM_HERE, {base::MayBlock()},
         base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace,
                        base::FilePath(kHomeDirectory)),
-        base::BindOnce(&CrostiniRestarter::CreateDiskImageAfterSizeCheck, this,
-                       disk_space_taken));
+        base::BindOnce(&CrostiniRestarter::CreateDiskImageAfterSizeCheck,
+                       this));
   }
 
-  void CreateDiskImageAfterSizeCheck(int64_t disk_space_taken,
-                                     int64_t free_disk_bytes) {
+  void CreateDiskImageAfterSizeCheck(int64_t free_disk_bytes) {
     // Unlike other functions, this isn't called from a crostini_manager_
     // function, so crostini_manager_ could have been deleted.
     if (!crostini_manager_) {
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 303ea85..d534259 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -340,9 +340,7 @@
     OpenSniffedFiles, /* open_sniffed_files.js */
     FilesAppBrowserTest,
     ::testing::Values(TestCase("pdfOpenDownloads"),
-                      TestCase("pdfOpenDrive").EnableDriveFs(),
-                      TestCase("textOpenDownloads"),
-                      TestCase("textOpenDrive").EnableDriveFs()));
+                      TestCase("pdfOpenDrive").EnableDriveFs()));
 
 // NaCl fails to compile zip plugin.pexe too often on ASAN, crbug.com/867738
 // The tests are flaky on the debug bot and always time out first and then pass
@@ -525,7 +523,6 @@
 #endif
         TestCase("openQuickViewKeyboardUpDownChangesView"),
         TestCase("openQuickViewKeyboardLeftRightChangesView"),
-        TestCase("openQuickViewSniffedText"),
         TestCase("openQuickViewScrollText"),
         TestCase("openQuickViewScrollHtml"),
         TestCase("openQuickViewBackgroundColorText"),
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
index 7d9ba5bd..70c10e68d 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
@@ -210,3 +210,7 @@
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, FileTableList) {
   RunGeneratedTest("/foreground/js/ui/file_table_list_unittest.html");
 }
+
+IN_PROC_BROWSER_TEST_F(FileManagerJsTest, FileTransferController) {
+  RunGeneratedTest("/foreground/js/file_transfer_controller_unittest.html");
+}
diff --git a/chrome/browser/chromeos/input_method/input_method_engine.cc b/chrome/browser/chromeos/input_method/input_method_engine.cc
index aa2698a..011d92b 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine.cc
+++ b/chrome/browser/chromeos/input_method/input_method_engine.cc
@@ -315,7 +315,10 @@
 
   // Marks the simulated key event is from the Virtual Keyboard.
   ui::Event::Properties properties;
-  properties[ui::kPropertyFromVK] = std::vector<uint8_t>();
+  properties[ui::kPropertyFromVK] =
+      std::vector<uint8_t>(ui::kPropertyFromVKSize);
+  properties[ui::kPropertyFromVK][ui::kPropertyFromVKIsMirroringIndex] =
+      (uint8_t)is_mirroring_;
   event->SetProperties(properties);
 
   ui::IMEInputContextHandlerInterface* input_context =
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_files.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_files.cc
index a82a899..2e53a046 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_files.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_files.cc
@@ -10,6 +10,7 @@
 #include "base/files/file_util.h"
 #include "base/location.h"
 #include "base/task/post_task.h"
+#include "base/task/task_traits.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -18,13 +19,17 @@
 
 namespace {
 
-void DirExistsResult(bool result, base::OnceCallback<void(bool)> callback) {
-  std::move(callback).Run(result);
+void DirExistsResult(
+    const base::FilePath& dir,
+    bool result,
+    base::OnceCallback<void(const base::FilePath&, bool)> callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  std::move(callback).Run(dir, result);
 }
 
-void EnsureDirExistsOnIOThread(base::FilePath dir,
-                               base::OnceCallback<void(bool)> callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+void EnsureDirExists(
+    base::FilePath dir,
+    base::OnceCallback<void(const base::FilePath&, bool)> callback) {
   base::File::Error error = base::File::FILE_OK;
   bool result = base::CreateDirectoryAndGetError(dir, &error);
   if (!result) {
@@ -33,21 +38,22 @@
   }
   base::PostTaskWithTraits(
       FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&DirExistsResult, result, std::move(callback)));
+      base::BindOnce(&DirExistsResult, dir, result, std::move(callback)));
 }
 
 }  // namespace
 
 namespace plugin_vm {
 
-void EnsureDefaultSharedDirExists(Profile* profile,
-                                  base::OnceCallback<void(bool)> callback) {
+void EnsureDefaultSharedDirExists(
+    Profile* profile,
+    base::OnceCallback<void(const base::FilePath&, bool)> callback) {
   base::FilePath dir =
       file_manager::util::GetMyFilesFolderForProfile(profile).Append(
           kPluginVmName);
   base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(&EnsureDirExistsOnIOThread, dir, std::move(callback)));
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&EnsureDirExists, dir, std::move(callback)));
 }
 
 }  // namespace plugin_vm
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_files.h b/chrome/browser/chromeos/plugin_vm/plugin_vm_files.h
index bc796c6..cecbbca 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_files.h
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_files.h
@@ -6,15 +6,18 @@
 #define CHROME_BROWSER_CHROMEOS_PLUGIN_VM_PLUGIN_VM_FILES_H_
 
 #include "base/callback.h"
+#include "base/files/file_path.h"
 
 class Profile;
 
 namespace plugin_vm {
 
 // Ensure default shared dir <cryptohome>/MyFiles/PluginVm exists. Invokes
-// |callback| with true if dir is successfully created or already exists.
-void EnsureDefaultSharedDirExists(Profile* profile,
-                                  base::OnceCallback<void(bool)> callback);
+// |callback| with dir and true if dir is successfully created or already
+// exists.
+void EnsureDefaultSharedDirExists(
+    Profile* profile,
+    base::OnceCallback<void(const base::FilePath&, bool)> callback);
 
 }  // namespace plugin_vm
 
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc
index 318fb25..355bb40d 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc
@@ -24,13 +24,17 @@
 
 class PluginVmFilesTest : public testing::Test {
  public:
-  void Callback(bool expected, bool result) { EXPECT_EQ(result, expected); }
+  void Callback(bool expected, const base::FilePath& dir, bool result) {
+    EXPECT_EQ(dir, my_files_.Append("PluginVm"));
+    EXPECT_EQ(result, expected);
+  }
 
   void SetUp() override {
     profile_ = std::make_unique<TestingProfile>();
     fake_release_ =
         std::make_unique<chromeos::ScopedSetRunningOnChromeOSForTesting>(
             kLsbRelease, base::Time());
+    my_files_ = file_manager::util::GetMyFilesFolderForProfile(profile_.get());
   }
 
   void TearDown() override {
@@ -42,11 +46,10 @@
   content::TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<TestingProfile> profile_;
   std::unique_ptr<chromeos::ScopedSetRunningOnChromeOSForTesting> fake_release_;
+  base::FilePath my_files_;
 };
 
 TEST_F(PluginVmFilesTest, DirNotExists) {
-  base::FilePath my_files =
-      file_manager::util::GetMyFilesFolderForProfile(profile_.get());
   EnsureDefaultSharedDirExists(profile_.get(),
                                base::BindOnce(&PluginVmFilesTest::Callback,
                                               base::Unretained(this), true));
@@ -54,9 +57,7 @@
 }
 
 TEST_F(PluginVmFilesTest, DirAlreadyExists) {
-  base::FilePath my_files =
-      file_manager::util::GetMyFilesFolderForProfile(profile_.get());
-  base::CreateDirectory(my_files.Append("PluginVm"));
+  base::CreateDirectory(my_files_.Append("PluginVm"));
   EnsureDefaultSharedDirExists(profile_.get(),
                                base::BindOnce(&PluginVmFilesTest::Callback,
                                               base::Unretained(this), true));
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc
index a149c4b..78325d1d7 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.cc
@@ -192,12 +192,16 @@
 }
 
 base::Optional<base::ScopedFD> PluginVmImageManager::PrepareFD() {
+  // In case import has been cancelled meantime.
+  if (state_ == State::IMPORT_CANCELLED || state_ == State::NOT_STARTED)
+    return base::nullopt;
+
   base::File file(downloaded_plugin_vm_image_archive_,
                   base::File::FLAG_OPEN | base::File::FLAG_READ);
   if (!file.IsValid()) {
     LOG(ERROR) << "Failed to open "
                << downloaded_plugin_vm_image_archive_.value();
-    return {};
+    return base::nullopt;
   }
   base::ScopedFD fd(file.TakePlatformFile());
   return fd;
@@ -205,6 +209,10 @@
 
 void PluginVmImageManager::OnFDPrepared(
     base::Optional<base::ScopedFD> maybeFd) {
+  // In case import has been cancelled meantime.
+  if (state_ == State::IMPORT_CANCELLED || state_ == State::NOT_STARTED)
+    return;
+
   if (!maybeFd.has_value()) {
     LOG(ERROR) << "Could not open downloaded image archive";
     OnImported(false);
@@ -231,7 +239,7 @@
     base::Optional<vm_tools::concierge::ImportDiskImageResponse> reply) {
   if (!reply.has_value()) {
     LOG(ERROR) << "Could not retrieve response from ImportDiskImage call to "
-                  "concierge";
+               << "concierge";
     OnImported(false);
     return;
   }
@@ -240,10 +248,12 @@
 
   // TODO(https://crbug.com/966397): handle cases where this jumps straight to
   // completed?
+  // TODO(https://crbug.com/966396): Handle error case when image already
+  // exists.
   if (response.status() !=
       vm_tools::concierge::DiskImageStatus::DISK_STATUS_IN_PROGRESS) {
     LOG(ERROR) << "Disk image is not in progress. Status: " << response.status()
-               << "," << response.failure_reason();
+               << ", " << response.failure_reason();
     OnImported(false);
     return;
   }
@@ -300,7 +310,7 @@
     base::Optional<vm_tools::concierge::DiskImageStatusResponse> reply) {
   if (!reply.has_value()) {
     LOG(ERROR) << "Could not retrieve response from DiskImageStatus call to "
-                  "concierge";
+               << "concierge";
     OnImported(false);
     return;
   }
@@ -325,7 +335,7 @@
 
   if (!success) {
     LOG(ERROR) << "Image import failed";
-    state_ = State::IMPORTING_FAILED;
+    state_ = State::IMPORT_FAILED;
     if (observer_)
       observer_->OnImportFailed();
 
@@ -341,9 +351,42 @@
 }
 
 void PluginVmImageManager::CancelImport() {
-  VLOG(1) << "Cancelling import with command_uuid: "
+  state_ = State::IMPORT_CANCELLED;
+  VLOG(1) << "Cancelling disk image import with command_uuid: "
           << current_import_command_uuid_;
-  // TODO(aoldemeier,okalitova) Make D-Bus call and set/handle state.
+
+  vm_tools::concierge::CancelDiskImageRequest request;
+  request.set_command_uuid(current_import_command_uuid_);
+  GetConciergeClient()->CancelDiskImageOperation(
+      request, base::BindOnce(&PluginVmImageManager::OnImportDiskImageCancelled,
+                              weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PluginVmImageManager::OnImportDiskImageCancelled(
+    base::Optional<vm_tools::concierge::CancelDiskImageResponse> reply) {
+  DCHECK_EQ(state_, State::IMPORT_CANCELLED);
+
+  RemoveTemporaryPluginVmImageArchiveIfExists();
+
+  // TODO(https://crbug.com/966392): Handle unsuccessful PluginVm image
+  // importing cancellation.
+  if (!reply.has_value()) {
+    LOG(ERROR) << "Could not retrieve response from CancelDiskImageOperation "
+               << "call to concierge";
+    return;
+  }
+
+  vm_tools::concierge::CancelDiskImageResponse response = reply.value();
+  if (!response.success()) {
+    LOG(ERROR) << "Import disk image request failed to be cancelled, "
+               << response.failure_reason();
+    return;
+  }
+
+  if (observer_)
+    observer_->OnImportCancelled();
+  state_ = State::NOT_STARTED;
+  VLOG(1) << "Import disk image request has been cancelled successfully";
 }
 
 void PluginVmImageManager::SetObserver(Observer* observer) {
@@ -397,14 +440,14 @@
       return "DOWNLOADED";
     case State::IMPORTING:
       return "IMPORTING";
-    case State::IMPORTING_CANCELLED:
-      return "IMPORTING_CANCELLED";
+    case State::IMPORT_CANCELLED:
+      return "IMPORT_CANCELLED";
     case State::CONFIGURED:
       return "CONFIGURED";
     case State::DOWNLOAD_FAILED:
       return "DOWNLOAD_FAILED";
-    case State::IMPORTING_FAILED:
-      return "IMPORTING_FAILED";
+    case State::IMPORT_FAILED:
+      return "IMPORT_FAILED";
   }
 }
 
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.h b/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.h
index b0fc2bdf..792f00b 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.h
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager.h
@@ -55,6 +55,7 @@
     virtual void OnImportProgressUpdated(uint64_t percent_completed,
                                          int64_t import_percent_per_sec) = 0;
     virtual void OnImported() = 0;
+    virtual void OnImportCancelled() = 0;
     virtual void OnImportFailed() = 0;
   };
 
@@ -109,10 +110,10 @@
     DOWNLOAD_CANCELLED,
     DOWNLOADED,
     IMPORTING,
-    IMPORTING_CANCELLED,
+    IMPORT_CANCELLED,
     CONFIGURED,
     DOWNLOAD_FAILED,
-    IMPORTING_FAILED,
+    IMPORT_FAILED,
   };
 
   Profile* profile_ = nullptr;
@@ -173,6 +174,10 @@
   // Finishes the processing of PluginVm image.
   void OnImported(bool success);
 
+  // Callback for the concierge CancelDiskImageOperation call.
+  void OnImportDiskImageCancelled(
+      base::Optional<vm_tools::concierge::CancelDiskImageResponse> reply);
+
   void RemoveTemporaryPluginVmImageArchiveIfExists();
   void OnTemporaryPluginVmImageArchiveRemoved(bool success);
 
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager_unittest.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager_unittest.cc
index 7ee4a334..7d31e63 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager_unittest.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_image_manager_unittest.cc
@@ -67,6 +67,7 @@
                void(uint64_t percent_completed,
                     int64_t import_percent_per_second));
   MOCK_METHOD0(OnImported, void());
+  MOCK_METHOD0(OnImportCancelled, void());
   MOCK_METHOD0(OnImportFailed, void());
 };
 
@@ -294,6 +295,24 @@
                                         kDownloadedPluginVmImageSizeInMb, 1);
 }
 
+TEST_F(PluginVmImageManagerTest, CancelledImportTest) {
+  SetupConciergeForSuccessfulDiskImageImport(fake_concierge_client_);
+  SetupConciergeForCancelDiskImageOperation(fake_concierge_client_,
+                                            true /* success */);
+
+  EXPECT_CALL(*observer_, OnDownloadCompleted());
+  EXPECT_CALL(*observer_, OnImportCancelled());
+
+  ProcessImageUntilImporting();
+
+  // Faking downloaded file for testing.
+  manager_->SetDownloadedPluginVmImageArchiveForTesting(
+      fake_downloaded_plugin_vm_image_archive_);
+  manager_->StartImport();
+  manager_->CancelImport();
+  test_browser_thread_bundle_.RunUntilIdle();
+}
+
 TEST_F(PluginVmImageManagerTest, EmptyPluginVmImageUrlTest) {
   SetPluginVmImagePref("", kHash);
   EXPECT_CALL(*observer_, OnDownloadFailed());
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.cc
index c8194db5..d19c2419 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.cc
@@ -65,6 +65,15 @@
       disk_image_status_response);
 }
 
+void SetupConciergeForCancelDiskImageOperation(
+    chromeos::FakeConciergeClient* fake_concierge_client_,
+    bool success) {
+  vm_tools::concierge::CancelDiskImageResponse cancel_disk_image_response;
+  cancel_disk_image_response.set_success(success);
+  fake_concierge_client_->set_cancel_disk_image_response(
+      cancel_disk_image_response);
+}
+
 PluginVmTestHelper::PluginVmTestHelper(TestingProfile* testing_profile)
     : testing_profile_(testing_profile) {
   testing_profile_->ScopedCrosSettingsTestHelper()
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.h b/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.h
index 2f93c391..fcea0c6 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.h
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.h
@@ -16,6 +16,10 @@
 void SetupConciergeForSuccessfulDiskImageImport(
     chromeos::FakeConciergeClient* fake_concierge_client_);
 
+void SetupConciergeForCancelDiskImageOperation(
+    chromeos::FakeConciergeClient* fake_concierge_client_,
+    bool success);
+
 // A helper class for enabling Plugin VM in unit tests.
 class PluginVmTestHelper {
  public:
diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
index 134026c..4d5260c 100644
--- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
+++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc
@@ -348,6 +348,13 @@
   return std::string();
 }
 
+std::string BrowserPolicyConnectorChromeOS::GetCustomerLogoURL() const {
+  const em::PolicyData* policy = GetDevicePolicy();
+  if (policy && policy->has_customer_logo())
+    return policy->customer_logo().logo_url();
+  return std::string();
+}
+
 DeviceMode BrowserPolicyConnectorChromeOS::GetDeviceMode() const {
   return chromeos::InstallAttributes::Get()->GetMode();
 }
diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h
index 1fc68a4e..cc4305a 100644
--- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h
+++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h
@@ -112,6 +112,9 @@
   // Returns the cloud directory API ID or an empty string if it is not set.
   std::string GetDirectoryApiID() const;
 
+  // Returns the organization logo URL or an empty string if it is not set.
+  std::string GetCustomerLogoURL() const;
+
   // Returns the device mode. For Chrome OS this function will return the mode
   // stored in the lockbox, or DEVICE_MODE_CONSUMER if the lockbox has been
   // locked empty, or DEVICE_MODE_UNKNOWN if the device has not been owned yet.
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/adapter.cc b/chrome/browser/chromeos/power/auto_screen_brightness/adapter.cc
index 344fd14..e667b8af 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/adapter.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/adapter.cc
@@ -356,14 +356,16 @@
       features::kAutoScreenBrightness, "stabilization_threshold",
       params_.stabilization_threshold);
 
-  const int model_curve = base::GetFieldTrialParamByFeatureAsInt(
-      features::kAutoScreenBrightness, "model_curve", 2);
-  if (model_curve < 0 || model_curve > 2) {
+  const int model_curve_as_int = base::GetFieldTrialParamByFeatureAsInt(
+      features::kAutoScreenBrightness, "model_curve",
+      static_cast<int>(params_.model_curve));
+  if (model_curve_as_int < static_cast<int>(ModelCurve::kGlobal) ||
+      model_curve_as_int > static_cast<int>(ModelCurve::kMaxValue)) {
     enabled_by_model_configs_ = false;
     LogParameterError(ParameterError::kAdapterError);
     return;
   }
-  params_.model_curve = static_cast<ModelCurve>(model_curve);
+  params_.model_curve = static_cast<ModelCurve>(model_curve_as_int);
   params_.auto_brightness_als_horizon = base::TimeDelta::FromSeconds(
       model_config.auto_brightness_als_horizon_seconds);
   log_als_values_ = std::make_unique<AmbientLightSampleBuffer>(
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.cc b/chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.cc
index 40e7f2b..d47a8f4 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.cc
@@ -5,7 +5,9 @@
 #include "chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.h"
 
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
 #include "chrome/browser/chromeos/power/auto_screen_brightness/utils.h"
 #include "chromeos/constants/chromeos_features.h"
 
@@ -21,6 +23,10 @@
 
 namespace {
 
+// If the curve error is greater than |kErrorTol|, then error will be written
+// to logs.
+constexpr double kErrorTol = 5;
+
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
 // Logs whether a new brightness exceeded the reasonable distance from the old
@@ -189,6 +195,18 @@
   return std::exp(-xs * xs);
 }
 
+void LogModelCurveError(double error, bool model_updated) {
+  DCHECK_GE(error, 0.0);
+  const std::string histogram_name =
+      std::string("AutoScreenBrightness.ModelTraining.Inaccuracy.") +
+      (model_updated ? "Update" : "NoUpdate");
+  base::UmaHistogramPercentage(histogram_name, std::round(error));
+  if (error > kErrorTol) {
+    VLOG(1) << "Model error " << (model_updated ? "with " : "without ")
+            << "model updated: " << base::StringPrintf("%.4f", error) << "%";
+  }
+}
+
 }  // namespace
 
 GaussianTrainer::Params::Params() = default;
@@ -391,13 +409,19 @@
     AdjustCurveWithSingleDataPoint(data_point);
   }
 
-  if (!need_to_update_curve_)
+  if (!need_to_update_curve_) {
+    const double error = CalculateCurveError(data);
+    LogModelCurveError(error, false /* model_updated */);
     return *current_curve_;
+  }
 
   current_curve_ = MonotoneCubicSpline::CreateMonotoneCubicSpline(
       ambient_log_lux_, brightness_);
   DCHECK(current_curve_);
   need_to_update_curve_ = false;
+
+  const double error = CalculateCurveError(data);
+  LogModelCurveError(error, true /* model_updated */);
   return *current_curve_;
 }
 
@@ -506,6 +530,17 @@
 #endif
 }
 
+double GaussianTrainer::CalculateCurveError(
+    const std::vector<TrainingDataPoint>& data) const {
+  DCHECK(current_curve_);
+  double error = 0.0;
+  for (const auto& data_point : data) {
+    error += std::abs(data_point.brightness_new -
+                      current_curve_->Interpolate(data_point.ambient_log_lux));
+  }
+  return error / data.size();
+}
+
 }  // namespace auto_screen_brightness
 }  // namespace power
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.h b/chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.h
index aac4d85..7bcf0d0 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.h
@@ -89,6 +89,10 @@
   // of |center_index|.
   void EnforceMonotonicity(size_t center_index);
 
+  // Calculates (possibly) updated curve's MAE error w.r.t. |data|. The error
+  // will be in the range of [0, 100].
+  double CalculateCurveError(const std::vector<TrainingDataPoint>& data) const;
+
   // Default params_ are valid.
   bool valid_params_ = true;
 
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
index 5112339..263e415 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/modeller_impl.cc
@@ -317,9 +317,11 @@
 
   const base::FilePath model_dir = profile_path.Append(kModelDir);
   if (!base::DirectoryExists(model_dir) && !base::CreateDirectory(model_dir)) {
+    VLOG(1) << "Auto screen brightness model dir does not exist.";
     return model_saving_spec;
   }
 
+  VLOG(1) << "Auto screen brightness model dir: " << model_dir.value();
   model_saving_spec.global_curve = model_dir.Append(kGlobalCurveFileName);
   model_saving_spec.personal_curve = model_dir.Append(kPersonalCurveFileName);
   model_saving_spec.iteration_count =
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/ml_service_client.cc b/chrome/browser/chromeos/power/ml/smart_dim/ml_service_client.cc
index 0a548ba..9b4c3fa 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/ml_service_client.cc
+++ b/chrome/browser/chromeos/power/ml/smart_dim/ml_service_client.cc
@@ -133,7 +133,7 @@
   if (!model_) {
     // Load the model.
     ModelSpecPtr spec =
-        ModelSpec::New(base::FeatureList::IsEnabled(features::kSmartDim20190221)
+        ModelSpec::New(base::FeatureList::IsEnabled(features::kSmartDimModelV3)
                            ? ModelId::SMART_DIM_20190221
                            : ModelId::SMART_DIM_20181115);
     chromeos::machine_learning::ServiceConnection::GetInstance()->LoadModel(
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/model_impl.cc b/chrome/browser/chromeos/power/ml/smart_dim/model_impl.cc
index a0cccbc..a6a80bb 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/model_impl.cc
+++ b/chrome/browser/chromeos/power/ml/smart_dim/model_impl.cc
@@ -46,7 +46,7 @@
   auto config = std::make_unique<assist_ranker::ExamplePreprocessorConfig>();
 
   const int res_id =
-      base::FeatureList::IsEnabled(features::kSmartDim20190221)
+      base::FeatureList::IsEnabled(features::kSmartDimModelV3)
           ? IDR_SMART_DIM_20190221_EXAMPLE_PREPROCESSOR_CONFIG_PB
           : IDR_SMART_DIM_20181115_EXAMPLE_PREPROCESSOR_CONFIG_PB;
 
@@ -227,7 +227,7 @@
 // Returns "dim_threshold" from experiment parameter. Also logs status to UMA.
 float GetDimThreshold() {
   const double default_threshold =
-      base::FeatureList::IsEnabled(features::kSmartDim20190221)
+      base::FeatureList::IsEnabled(features::kSmartDimModelV3)
           ? k20190221ModelDefaultDimThreshold
           : k20181115ModelDefaultDimThreshold;
   const double dim_threshold = base::GetFieldTrialParamByFeatureAsDouble(
@@ -316,7 +316,7 @@
   }
 
   const size_t expected_size =
-      base::FeatureList::IsEnabled(features::kSmartDim20190221)
+      base::FeatureList::IsEnabled(features::kSmartDimModelV3)
           ? k20190221ModelInputVectorSize
           : k20181115ModelInputVectorSize;
 
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index 12afd4e..812e63a 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -139,7 +139,8 @@
 
   // Indicate that the simulated key event is from the Virtual Keyboard.
   ui::Event::Properties properties;
-  properties[ui::kPropertyFromVK] = std::vector<uint8_t>();
+  properties[ui::kPropertyFromVK] =
+      std::vector<uint8_t>(ui::kPropertyFromVKSize);
   event.SetProperties(properties);
 
   ui::EventDispatchDetails details = aura::EventInjector().Inject(host, &event);
diff --git a/chrome/browser/extensions/browsertest_util.cc b/chrome/browser/extensions/browsertest_util.cc
index fb3e30a..76ffadd0 100644
--- a/chrome/browser/extensions/browsertest_util.cc
+++ b/chrome/browser/extensions/browsertest_util.cc
@@ -6,8 +6,11 @@
 
 #include <memory>
 
+#include "base/feature_list.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -16,9 +19,11 @@
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/web_applications/components/install_manager.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/browser/web_applications/components/web_app_tab_helper_base.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/web_application_info.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
@@ -51,26 +56,43 @@
   size_t num_extensions =
       ExtensionRegistry::Get(profile)->enabled_extensions().size();
 
+  // TODO(crbug.com/915043): Erase windowed_observer code path.
   content::WindowedNotificationObserver windowed_observer(
       NOTIFICATION_CRX_INSTALLER_DONE,
       content::NotificationService::AllSources());
 
+  base::RunLoop run_loop;
+  web_app::AppId app_id;
   auto* provider = web_app::WebAppProviderBase::GetProviderBase(profile);
   DCHECK(provider);
   provider->install_manager().InstallWebAppForTesting(
-      std::make_unique<WebApplicationInfo>(info), base::DoNothing());
+      std::make_unique<WebApplicationInfo>(info),
+      base::BindLambdaForTesting([&](const web_app::AppId& installed_app_id,
+                                     web_app::InstallResultCode code) {
+        DCHECK_EQ(web_app::InstallResultCode::kSuccess, code);
+        app_id = installed_app_id;
+        run_loop.Quit();
+      }));
 
-  windowed_observer.Wait();
+  const Extension* app = nullptr;
+  // The legacy system doesn't support completion callback in
+  // InstallWebAppForTesting. Use |windowed_observer| if
+  // kDesktopPWAsUnifiedInstall disabled.
+  if (base::FeatureList::IsEnabled(features::kDesktopPWAsUnifiedInstall)) {
+    run_loop.Run();
+    app = ExtensionRegistry::Get(profile)->enabled_extensions().GetByID(app_id);
+  } else {
+    windowed_observer.Wait();
+    app = content::Details<const Extension>(windowed_observer.details()).ptr();
+  }
 
   EXPECT_EQ(++num_extensions,
             ExtensionRegistry::Get(profile)->enabled_extensions().size());
-  const Extension* app =
-      content::Details<const Extension>(windowed_observer.details()).ptr();
+  DCHECK(app);
   extensions::SetLaunchType(profile, app->id(),
                             info.open_as_window
                                 ? extensions::LAUNCH_TYPE_WINDOW
                                 : extensions::LAUNCH_TYPE_REGULAR);
-
   return app;
 }
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 43f95b5..639c9a4 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2733,7 +2733,7 @@
     "expiry_milestone": 78
   },
   {
-    "name": "smart-dim-20190221",
+    "name": "smart-dim-model-v3",
     "owners": [ "amoylan", "jiameng" ],
     "expiry_milestone": 80
   },
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 9e9abb8..777d7e5 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3120,8 +3120,8 @@
     "Ctrl-Alt-I shows a heads-up display view in the top-left corner. Helps "
     "debug hardware issues that generate spurious touch events.";
 
-const char kSmartDim20190221Name[] = "Smart Dim updated model";
-const char kSmartDim20190221Description[] =
+const char kSmartDimModelV3Name[] = "Smart Dim updated model";
+const char kSmartDimModelV3Description[] =
     "Uses an updated model for user activity prediction (Smart Dim).";
 
 const char kSmartTextSelectionName[] = "Smart Text Selection";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index d96bf6c..ccd0a22 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1871,8 +1871,8 @@
 extern const char kShowTouchHudName[];
 extern const char kShowTouchHudDescription[];
 
-extern const char kSmartDim20190221Name[];
-extern const char kSmartDim20190221Description[];
+extern const char kSmartDimModelV3Name[];
+extern const char kSmartDimModelV3Description[];
 
 extern const char kSmartTextSelectionName[];
 extern const char kSmartTextSelectionDescription[];
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 0c73b980..8e5ee217 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -128,6 +128,7 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
 #include "chrome/browser/signin/signin_status_metrics_provider_chromeos.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h"
 #include "chromeos/assistant/buildflags.h"
 
 #if BUILDFLAG(ENABLE_CROS_ASSISTANT)
@@ -679,6 +680,10 @@
 
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<chromeos::PrinterMetricsProvider>());
+
+  // Using WrapUnique to access a private constructor.
+  metrics_service_->RegisterMetricsProvider(
+      base::WrapUnique(new app_list::AppListLaunchMetricsProvider()));
 #endif  // defined(OS_CHROMEOS)
 
 #if !defined(OS_CHROMEOS)
diff --git a/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc b/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
index a729913..d45c6ba 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
@@ -182,9 +182,9 @@
 #if BUILDFLAG(ENABLE_CROS_ASSISTANT)
   expected_providers++;  // AssistantServiceMetricsProvider.
 #endif                   // BUILDFLAG(ENABLE_CROS_ASSISTANT)
-  // ChromeOSMetricsProvider, SigninStatusMetricsProviderChromeOS and
-  // PrinterMetricsProvider.
-  expected_providers += 3;
+  // ChromeOSMetricsProvider, SigninStatusMetricsProviderChromeOS,
+  // PrinterMetricsProvider, and HashedLoggingMetricsProvider.
+  expected_providers += 4;
 #endif  // defined(OS_CHROMEOS)
 
 #if !defined(OS_CHROMEOS)
diff --git a/chrome/browser/password_manager/password_accessory_controller_impl.cc b/chrome/browser/password_manager/password_accessory_controller_impl.cc
index 7961d86..135268f 100644
--- a/chrome/browser/password_manager/password_accessory_controller_impl.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_impl.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
 #include "chrome/browser/vr/vr_tab_helper.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/autofill/core/common/autofill_util.h"
 #include "components/autofill/core/common/password_form.h"
 #include "components/autofill/core/common/password_generation_util.h"
 #include "components/favicon/core/favicon_service.h"
@@ -192,9 +193,7 @@
   const bool is_password_field =
       focused_field_type == FocusedFieldType::kFillablePasswordField;
 
-  if (focused_field_type == FocusedFieldType::kFillableTextField ||
-      focused_field_type == FocusedFieldType::kFillableUsernameField ||
-      focused_field_type == FocusedFieldType::kFillablePasswordField) {
+  if (autofill::IsFillable(focused_field_type)) {
     const std::vector<PasswordAccessorySuggestion> suggestions =
         GetSuggestions();
     info_to_add.resize(suggestions.size());
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit.cc b/chrome/browser/resource_coordinator/lifecycle_unit.cc
index 2de2ff1..8c23f9e 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/lifecycle_unit.cc
@@ -11,14 +11,10 @@
 LifecycleUnit::SortKey::SortKey(base::TimeTicks last_focused_time)
     : last_focused_time(last_focused_time) {}
 
-LifecycleUnit::SortKey::SortKey(float score, base::TimeTicks last_focused_time)
-    : score(score), last_focused_time(last_focused_time) {}
 
 LifecycleUnit::SortKey::SortKey(const SortKey& other) = default;
 
 bool LifecycleUnit::SortKey::operator<(const SortKey& other) const {
-  if (score != other.score)
-    return score < other.score;
   return last_focused_time < other.last_focused_time;
 }
 
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit.h b/chrome/browser/resource_coordinator/lifecycle_unit.h
index 9fb0314..1e2d3b0 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit.h
+++ b/chrome/browser/resource_coordinator/lifecycle_unit.h
@@ -35,37 +35,22 @@
 // use any system resource.
 class LifecycleUnit {
  public:
-  // Used to sort LifecycleUnit by importance using a reactivation score or the
-  // last focused time.
+  // Used to sort LifecycleUnit by importance using the last focused time.
   // The most important LifecycleUnit has the greatest SortKey.
   struct SortKey {
-    // kMaxScore is used when a SortKey should rank ahead of any other SortKey.
-    // Two SortKeys with kMaxScore are compared using |last_focused_time|.
-    static constexpr float kMaxScore = std::numeric_limits<float>::max();
 
     SortKey();
 
     // Creates a SortKey based on the LifecycleUnit's last focused time.
     explicit SortKey(base::TimeTicks last_focused_time);
 
-    // Creates a SortKey based on a score calculated for the LifecycleUnit and
-    // the last focused time. Used when the TabRanker feature is enabled.
-    SortKey(float score, base::TimeTicks last_focused_time);
-
     SortKey(const SortKey& other);
 
     bool operator<(const SortKey& other) const;
     bool operator>(const SortKey& other) const;
 
-    // Abstract importance score calculated by the Tab Ranker where a higher
-    // score suggests the tab is more likely to be reactivated.
-    // kMaxScore if the LifecycleUnit is currently focused.
-    float score = kMaxScore;
-
     // Last time at which the LifecycleUnit was focused. base::TimeTicks::Max()
     // if the LifecycleUnit is currently focused.
-    // Used when the TabRanker feature is disabled. Also used as a tiebreaker
-    // when two scores are the same.
     base::TimeTicks last_focused_time;
   };
 
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/lifecycle_unit_unittest.cc
index fe7a0ef..69aaad4 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/lifecycle_unit_unittest.cc
@@ -9,7 +9,7 @@
 
 namespace resource_coordinator {
 
-TEST(LifecycleUnitTest, SortKeyNoScoresComparison) {
+TEST(LifecycleUnitTest, SortKeyComparison) {
   constexpr base::TimeTicks kBaseTime;
   LifecycleUnit::SortKey a(kBaseTime);
   LifecycleUnit::SortKey b(kBaseTime + base::TimeDelta::FromHours(1));
@@ -40,36 +40,4 @@
   EXPECT_FALSE(c > c);
 }
 
-// Tests that the |score| field is used for sorting when populated.
-TEST(LifecycleUnitTest, SortKeyWithScoresComparison) {
-  constexpr base::TimeTicks kBaseTime;
-  LifecycleUnit::SortKey a(0, kBaseTime);
-  LifecycleUnit::SortKey b(1, kBaseTime);
-  LifecycleUnit::SortKey c(1, kBaseTime + base::TimeDelta::FromHours(1));
-
-  EXPECT_FALSE(a < a);
-  EXPECT_TRUE(a < b);
-  EXPECT_TRUE(a < c);
-
-  EXPECT_FALSE(b < a);
-  EXPECT_FALSE(b < b);
-  EXPECT_TRUE(b < c);
-
-  EXPECT_FALSE(c < a);
-  EXPECT_FALSE(c < b);
-  EXPECT_FALSE(c < c);
-
-  EXPECT_FALSE(a > a);
-  EXPECT_FALSE(a > b);
-  EXPECT_FALSE(a > c);
-
-  EXPECT_TRUE(b > a);
-  EXPECT_FALSE(b > b);
-  EXPECT_FALSE(b > c);
-
-  EXPECT_TRUE(c > a);
-  EXPECT_TRUE(c > b);
-  EXPECT_FALSE(c > c);
-}
-
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher.cc b/chrome/browser/resource_coordinator/tab_activity_watcher.cc
index 72eba02..9036c25 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher.cc
@@ -10,6 +10,8 @@
 #include "base/no_destructor.h"
 #include "base/rand_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/resource_coordinator/lifecycle_unit.h"
+#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
 #include "chrome/browser/resource_coordinator/tab_metrics_logger.h"
 #include "chrome/browser/resource_coordinator/tab_ranker/mru_features.h"
@@ -68,16 +70,7 @@
     if (web_contents()->IsBeingDestroyed() || backgrounded_time_.is_null())
       return base::nullopt;
 
-    // Only Scores Oldest N tabs (based on least recently used index calculated
-    // as mru.total - mru.index - 1).
     const auto mru = GetMRUFeatures();
-    const int lru_index = mru.total - mru.index - 1;
-
-    // If the least recently used index is greater than or equal to N, which
-    // means the tab is not in the oldest N list, we should simply skip it.
-    // The N is defaulted as kMaxInt so that all tabs are scored.
-    if (lru_index >= GetNumOldestTabsToScoreWithTabRanker())
-      return base::nullopt;
 
     base::Optional<tab_ranker::TabFeatures> tab = GetTabFeatures(mru);
     if (!tab.has_value())
@@ -542,6 +535,26 @@
   }
 }
 
+void TabActivityWatcher::SortLifecycleUnitWithTabRanker(
+    std::vector<LifecycleUnit*>* tabs) {
+  std::map<int32_t, float> reactivation_scores;
+
+  for (auto* lifecycle_unit : *tabs) {
+    content::WebContents* web_content =
+        lifecycle_unit->AsTabLifecycleUnitExternal()->GetWebContents();
+    base::Optional<float> score = CalculateReactivationScore(web_content);
+    reactivation_scores[lifecycle_unit->GetID()] =
+        score.has_value() ? score.value() : std::numeric_limits<float>::max();
+  }
+
+  // Sort with larger reactivation_score first (desending importance).
+  std::sort(tabs->begin(), tabs->end(),
+            [&reactivation_scores](LifecycleUnit* a, LifecycleUnit* b) {
+              return reactivation_scores[a->GetID()] >
+                     reactivation_scores[b->GetID()];
+            });
+}
+
 void TabActivityWatcher::OnBrowserSetLastActive(Browser* browser) {
   if (browser->tab_strip_model()->closing_all())
     return;
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher.h b/chrome/browser/resource_coordinator/tab_activity_watcher.h
index 39bffed..7da9140 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher.h
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher.h
@@ -20,6 +20,8 @@
 
 namespace resource_coordinator {
 
+class LifecycleUnit;
+
 // Observes background tab activity in order to log UKMs for tabs and score tabs
 // using the Tab Ranker. Metrics will be compared against tab reactivation/close
 // events to determine the end state of each background tab.
@@ -39,6 +41,10 @@
   // Log TabFeatures for oldest n tabs.
   void LogOldestNTabFeatures();
 
+  // |tabs| are sorted by descending importance, so that the last tab is
+  // the first candidate that will be discarded.
+  void SortLifecycleUnitWithTabRanker(std::vector<LifecycleUnit*>* tabs);
+
   // Returns the single instance, creating it if necessary.
   static TabActivityWatcher* GetInstance();
 
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc b/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc
index cd7d0256..d4ac8eb6 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher_browsertest.cc
@@ -107,40 +107,6 @@
   CloseBrowserSynchronously(browser());
 }
 
-// Tests only oldest N tabs are scored.
-IN_PROC_BROWSER_TEST_F(TabActivityWatcherTest,
-                       OnlyCalculateReactivationScoreForOldestN) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      features::kTabRanker,
-      {{"number_of_oldest_tabs_to_score_with_TabRanker", "1"}});
-  // Use test clock so tabs have non-zero backgrounded times.
-  base::SimpleTestTickClock test_clock;
-  ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing(&test_clock);
-  test_clock.Advance(base::TimeDelta::FromMinutes(1));
-
-  AddTabAtIndex(1, test_urls_[0], ui::PAGE_TRANSITION_LINK);
-  test_clock.Advance(base::TimeDelta::FromMinutes(1));
-  AddTabAtIndex(2, test_urls_[0], ui::PAGE_TRANSITION_LINK);
-  test_clock.Advance(base::TimeDelta::FromMinutes(1));
-  browser()->tab_strip_model()->ActivateTabAt(
-      0, {TabStripModel::GestureType::kOther});
-  test_clock.Advance(base::TimeDelta::FromMinutes(1));
-
-  // tab@1 is scored successfully.
-  base::Optional<float> tab_1 =
-      TabActivityWatcher::GetInstance()->CalculateReactivationScore(
-          browser()->tab_strip_model()->GetWebContentsAt(1));
-  EXPECT_TRUE(tab_1.has_value());
-
-  // tab@2 is not scored successfully since it's not in the OldestN.
-  base::Optional<float> tab_2 =
-      TabActivityWatcher::GetInstance()->CalculateReactivationScore(
-          browser()->tab_strip_model()->GetWebContentsAt(2));
-  EXPECT_FALSE(tab_2.has_value());
-
-  CloseBrowserSynchronously(browser());
-}
 // Tests UKM entries generated by TabActivityWatcher/TabMetricsLogger as tabs
 // are backgrounded and foregrounded.
 // Modeled after the TabActivityWatcherTest unit tests, these browser tests
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc b/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
index 3dc9146..a312a30 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
@@ -7,9 +7,14 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
+#include "chrome/browser/resource_coordinator/lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/tab_activity_watcher.h"
+#include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
+#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
+#include "chrome/browser/resource_coordinator/tab_manager_features.h"
 #include "chrome/browser/resource_coordinator/time.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/recently_audible_helper.h"
@@ -69,6 +74,9 @@
 class TabActivityWatcherTest : public ChromeRenderViewHostTestHarness {
  public:
   TabActivityWatcherTest() {
+    // Use MRUScorer for TabRanker to bypass ML model.
+    feature_list_.InitAndEnableFeatureWithParameters(features::kTabRanker,
+                                                     {{"scorer_type", "0"}});
     TabActivityWatcher::GetInstance()->ResetForTesting();
   }
 
@@ -79,14 +87,55 @@
     ChromeRenderViewHostTestHarness::TearDown();
   }
 
+  LifecycleUnit* AddNewTab(TabStripModel* tab_strip_model, int i) {
+    LifecycleUnit* result = TabLifecycleUnitSource::GetTabLifecycleUnit(
+        tab_activity_simulator_.AddWebContentsAndNavigate(tab_strip_model,
+                                                          GURL(kTestUrls[i])));
+    if (i == 0)
+      tab_strip_model->ActivateTabAt(i);
+    else
+      tab_activity_simulator_.SwitchToTabAt(tab_strip_model, i);
+
+    return result;
+  }
+
  protected:
   UkmEntryChecker ukm_entry_checker_;
   TabActivitySimulator tab_activity_simulator_;
+  base::test::ScopedFeatureList feature_list_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TabActivityWatcherTest);
 };
 
+// Test that lifecycleunits are sorted with high activation score first order.
+TEST_F(TabActivityWatcherTest, SortLifecycleUnitWithTabRanker) {
+  Browser::CreateParams params(profile(), true);
+  std::unique_ptr<Browser> browser =
+      CreateBrowserWithTestWindowForParams(&params);
+  TabStripModel* tab_strip_model = browser->tab_strip_model();
+
+  // Create lifecycleunits.
+  LifecycleUnit* tab0 = AddNewTab(tab_strip_model, 0);
+  LifecycleUnit* tab1 = AddNewTab(tab_strip_model, 1);
+  LifecycleUnit* tab2 = AddNewTab(tab_strip_model, 2);
+  LifecycleUnit* tab3 = AddNewTab(tab_strip_model, 3);
+  std::vector<LifecycleUnit*> lifecycleunits = {tab0, tab2, tab3, tab1};
+
+  // Sort and check the new order.
+  TabActivityWatcher::GetInstance()->SortLifecycleUnitWithTabRanker(
+      &lifecycleunits);
+  EXPECT_EQ(lifecycleunits[0], tab3);
+  EXPECT_EQ(lifecycleunits[1], tab2);
+  EXPECT_EQ(lifecycleunits[2], tab1);
+  EXPECT_EQ(lifecycleunits[3], tab0);
+
+  // Closing the tabs destroys the WebContentses but should not trigger logging.
+  // The TestWebContentsObserver simulates hiding these tabs as they are closed;
+  // we verify in TearDown() that no logging occurred.
+  tab_strip_model->CloseAllTabs();
+}
+
 // Tests TabManager.TabMetrics UKM entries generated when tabs are backgrounded.
 class TabMetricsTest : public TabActivityWatcherTest {
  public:
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index 0928a14..0a3c8084 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -460,15 +460,6 @@
 
 LifecycleUnit::SortKey TabLifecycleUnitSource::TabLifecycleUnit::GetSortKey()
     const {
-  if (base::FeatureList::IsEnabled(features::kTabRanker)) {
-    const base::Optional<float> reactivation_score =
-        resource_coordinator::TabActivityWatcher::GetInstance()
-            ->CalculateReactivationScore(web_contents());
-    if (reactivation_score.has_value())
-      return SortKey(reactivation_score.value(), last_focused_time_);
-    return SortKey(SortKey::kMaxScore, last_focused_time_);
-  }
-
   return SortKey(last_focused_time_);
 }
 
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
index 5c58462..4d368c9 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
@@ -80,6 +80,7 @@
   friend class TabLifecycleStateObserver;
   friend class TabLifecycleUnitTest;
   friend class TabManagerTest;
+  friend class TabActivityWatcherTest;
   FRIEND_TEST_ALL_PREFIXES(TabLifecycleUnitSourceTest,
                            TabProactiveDiscardedByFrozenCallback);
   FRIEND_TEST_ALL_PREFIXES(TabManagerTest, TabManagerWasDiscarded);
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index e672261..052a69d6 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -94,22 +94,6 @@
 // load the next background tab when the loading slots free up.
 constexpr size_t kNumOfLoadingSlots = 1;
 
-struct LifecycleUnitAndSortKey {
-  explicit LifecycleUnitAndSortKey(LifecycleUnit* lifecycle_unit)
-      : lifecycle_unit(lifecycle_unit),
-        sort_key(lifecycle_unit->GetSortKey()) {}
-
-  bool operator<(const LifecycleUnitAndSortKey& other) const {
-    return sort_key < other.sort_key;
-  }
-  bool operator>(const LifecycleUnitAndSortKey& other) const {
-    return sort_key > other.sort_key;
-  }
-
-  LifecycleUnit* lifecycle_unit;
-  LifecycleUnit::SortKey sort_key;
-};
-
 std::unique_ptr<base::trace_event::ConvertableToTraceFormat> DataAsTraceValue(
     TabManager::BackgroundTabLoadingMode mode,
     size_t num_of_pending_navigations,
@@ -242,21 +226,13 @@
 }
 
 LifecycleUnitVector TabManager::GetSortedLifecycleUnits() {
-  std::vector<LifecycleUnitAndSortKey> lifecycle_units_and_sort_keys;
-  lifecycle_units_and_sort_keys.reserve(lifecycle_units_.size());
-  for (auto* lifecycle_unit : lifecycle_units_)
-    lifecycle_units_and_sort_keys.emplace_back(lifecycle_unit);
-
-  std::sort(lifecycle_units_and_sort_keys.begin(),
-            lifecycle_units_and_sort_keys.end());
-
-  LifecycleUnitVector sorted_lifecycle_units;
-  sorted_lifecycle_units.reserve(lifecycle_units_and_sort_keys.size());
-  for (auto& lifecycle_unit_and_sort_key : lifecycle_units_and_sort_keys) {
-    sorted_lifecycle_units.push_back(
-        lifecycle_unit_and_sort_key.lifecycle_unit);
-  }
-
+  LifecycleUnitVector sorted_lifecycle_units(lifecycle_units_.begin(),
+                                             lifecycle_units_.end());
+  // Sort lifecycle_units with ascending importance.
+  std::sort(sorted_lifecycle_units.begin(), sorted_lifecycle_units.end(),
+            [](LifecycleUnit* a, LifecycleUnit* b) {
+              return a->GetSortKey() < b->GetSortKey();
+            });
   return sorted_lifecycle_units;
 }
 
diff --git a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc
index 3ddb35d0..c8454d0 100644
--- a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.cc
@@ -29,6 +29,8 @@
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/memory/memory_kills_monitor.h"
+#include "chrome/browser/resource_coordinator/lifecycle_unit.h"
+#include "chrome/browser/resource_coordinator/tab_activity_watcher.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_manager_stats_collector.h"
 #include "chrome/browser/resource_coordinator/utils.h"
@@ -126,7 +128,7 @@
     const TabManagerDelegate::Candidate& rhs) const {
   if (process_type() != rhs.process_type())
     return process_type() < rhs.process_type();
-  return lifecycle_unit_sort_key_ > rhs.lifecycle_unit_sort_key_;
+  return LastActivityTime() > rhs.LastActivityTime();
 }
 
 ProcessType TabManagerDelegate::Candidate::GetProcessTypeInternal() const {
@@ -140,7 +142,7 @@
     return ProcessType::BACKGROUND;
   }
   if (lifecycle_unit()) {
-    if (lifecycle_unit_sort_key_.last_focused_time == base::TimeTicks::Max())
+    if (LastActivityTime() == base::TimeTicks::Max())
       return ProcessType::FOCUSED_TAB;
     DecisionDetails decision_details;
     if (!lifecycle_unit()->CanDiscard(
@@ -153,6 +155,17 @@
   return ProcessType::UNKNOWN_TYPE;
 }
 
+base::TimeTicks TabManagerDelegate::Candidate::LastActivityTime() const {
+  if (app()) {
+    return base::TimeTicks::FromUptimeMillis(app()->last_activity_time());
+  }
+  if (lifecycle_unit()) {
+    return lifecycle_unit()->GetLastFocusedTime();
+  }
+  NOTREACHED();
+  return base::TimeTicks();
+}
+
 // Holds the info of a newly focused tab or app window. The focused process is
 // set to highest priority (lowest OOM score), but not immediately. To avoid
 // redundant settings the OOM score adjusting only happens after a timeout. If
@@ -534,6 +547,40 @@
   return candidates;
 }
 
+void TabManagerDelegate::SortLifecycleUnitWithTabRanker(
+    std::vector<Candidate>* candidates,
+    LifecycleUnitSorter sorter) {
+  const uint32_t num_of_tab_to_score = GetNumOldestTabsToScoreWithTabRanker();
+  if (num_of_tab_to_score <= 1)
+    return;
+  // Put the oldest num_of_tab_to_score lifecycle units into a vector.
+  LifecycleUnitVector oldest_lifecycle_units;
+  for (auto it = candidates->rbegin(); it != candidates->rend(); ++it) {
+    auto& candidate = *it;
+    if (oldest_lifecycle_units.size() == num_of_tab_to_score ||
+        candidate.process_type() < ProcessType::BACKGROUND)
+      break;
+    if (candidate.lifecycle_unit()) {
+      oldest_lifecycle_units.push_back(candidate.lifecycle_unit());
+    }
+  }
+
+  // Re-sort them with TabRanker.
+  std::move(sorter).Run(&oldest_lifecycle_units);
+
+  // Put the sorted lifecycle units back to their original vacancies.
+  for (auto it = candidates->rbegin(); it != candidates->rend(); ++it) {
+    const auto& candidate = *it;
+    if (oldest_lifecycle_units.empty() ||
+        candidate.process_type() < ProcessType::BACKGROUND)
+      break;
+    if (candidate.lifecycle_unit()) {
+      *it = Candidate(oldest_lifecycle_units.back());
+      oldest_lifecycle_units.pop_back();
+    }
+  }
+}
+
 bool TabManagerDelegate::IsRecentlyKilledArcProcess(
     const std::string& process_name,
     const TimeTicks& now) {
@@ -581,9 +628,16 @@
                   [](auto& proc) { return proc.IsPersistent(); });
   }
 
-  std::vector<TabManagerDelegate::Candidate> candidates =
+  std::vector<Candidate> candidates =
       GetSortedCandidates(GetLifecycleUnits(), arc_processes);
 
+  if (base::FeatureList::IsEnabled(features::kTabRanker)) {
+    SortLifecycleUnitWithTabRanker(
+        &candidates,
+        base::BindOnce(&TabActivityWatcher::SortLifecycleUnitWithTabRanker,
+                       base::Unretained(TabActivityWatcher::GetInstance())));
+  }
+
   // TODO(semenzato): decide if TargetMemoryToFreeKB is doing real
   // I/O and if it is, move to I/O thread (crbug.com/778703).
   int target_memory_to_free_kb = 0;
diff --git a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h
index 47a655f..bd817cd 100644
--- a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h
+++ b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/containers/flat_map.h"
 #include "base/gtest_prod_util.h"
 #include "base/logging.h"
@@ -110,7 +111,7 @@
   FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest,
                            CandidatesSortedWithFocusedAppAndTab);
   FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest,
-                           CandidatesSortedWithTabRanker);
+                           SortLifecycleUnitWithTabRanker);
   FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest,
                            DoNotKillRecentlyKilledArcProcesses);
   FRIEND_TEST_ALL_PREFIXES(TabManagerDelegateTest, IsRecentlyKilledArcProcess);
@@ -139,11 +140,22 @@
   // A map from an ARC process name to a monotonic timestamp when it's killed.
   typedef base::flat_map<std::string, base::TimeTicks> KilledArcProcessesMap;
 
+  typedef base::OnceCallback<void(LifecycleUnitVector*)> LifecycleUnitSorter;
+
   // Get the list of candidates to kill, sorted by descending importance.
   static std::vector<Candidate> GetSortedCandidates(
       const LifecycleUnitVector& lifecycle_units,
       const OptionalArcProcessList& arc_processes);
 
+  // This is only used for TabRanker experiment for now.
+  // If TabRanker is enabled, this will take the last N lifecycle units in the
+  // |candidates|; sort these lifecycle units based on the TabRanker order; and
+  // put these sorted lifecycle unit back to the vacancies of these lifecycle
+  // units in the |candidates|.
+  // All apps in the |candidates| will not be influenced.
+  static void SortLifecycleUnitWithTabRanker(std::vector<Candidate>* candidates,
+                                             LifecycleUnitSorter sorter);
+
   // Returns the LifecycleUnits in TabManager. Virtual for unit tests.
   virtual LifecycleUnitVector GetLifecycleUnits();
 
@@ -227,8 +239,7 @@
 class TabManagerDelegate::Candidate {
  public:
   explicit Candidate(LifecycleUnit* lifecycle_unit)
-      : lifecycle_unit_(lifecycle_unit),
-        lifecycle_unit_sort_key_(lifecycle_unit_->GetSortKey()) {
+      : lifecycle_unit_(lifecycle_unit) {
     DCHECK(lifecycle_unit_);
   }
 
@@ -238,44 +249,29 @@
   // kMaxScore so this has the effect of sorting by last_activity_time only.
   // But if TabRanker is on, kMaxScore guarantees all apps are sorted before
   // tabs.
-  explicit Candidate(const arc::ArcProcess* app)
-      : lifecycle_unit_sort_key_(
-            LifecycleUnit::SortKey::kMaxScore,
-            base::TimeTicks::FromUptimeMillis(app->last_activity_time())),
-        app_(app) {
-    DCHECK(app_);
-  }
+  explicit Candidate(const arc::ArcProcess* app) : app_(app) { DCHECK(app_); }
 
   // Move-only class.
   Candidate(Candidate&&) = default;
   Candidate& operator=(Candidate&& other);
 
-  // Candidates are sorted higher priority first. They are first sorted into
-  // their respective ProcessTypes.
-  // LifecycleUnit::SortKey is used to compare processes within a ProcessType,
-  // using a combination of TabRanker reactivation score (for tabs only) and
-  // last focused time for all processes. When TabRanker is disabled, tabs are
-  // given a default score of kMaxScore. An ArcProcess (app) is always assigned
-  // kMaxScore. This means that when TabRanker is off, all processes have
-  // kMaxScore and are then compared by last focused time.
-  // When TabRanker is on, tabs have their own defined order so we can't compare
-  // apps to tabs, since we wouldn't have a comparator to satisfy transitivity.
-  // In this case, ARC processes are sorted before (higher priority than) tabs
-  // and compared by last focused time, while tabs are compared by their
-  // reactivation score.
+  // Candidates are sorted by descending importance. A candidate is more
+  // important if:
+  // (1) it has lower respective ProcessTypes.
+  // (2) it has the same ProcessTypes, but larger LastActivityTime().
   bool operator<(const Candidate& rhs) const;
+  // Returns the last activity time of this Candidate.
+  base::TimeTicks LastActivityTime() const;
 
   LifecycleUnit* lifecycle_unit() { return lifecycle_unit_; }
   const LifecycleUnit* lifecycle_unit() const { return lifecycle_unit_; }
   const arc::ArcProcess* app() const { return app_; }
   ProcessType process_type() const { return process_type_; }
-
  private:
   // Derive process type for this candidate. Used to initialize |process_type_|.
   ProcessType GetProcessTypeInternal() const;
 
   LifecycleUnit* lifecycle_unit_ = nullptr;
-  LifecycleUnit::SortKey lifecycle_unit_sort_key_;
   const arc::ArcProcess* app_ = nullptr;
   ProcessType process_type_ = GetProcessTypeInternal();
   DISALLOW_COPY_AND_ASSIGN(Candidate);
diff --git a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc
index 290bda2..8488a66d 100644
--- a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos_unittest.cc
@@ -69,17 +69,13 @@
   // focused LifecycleUnit
   EXPECT_EQ(candidates[0].lifecycle_unit(), &focused_lifecycle_unit);
   // focused app.
-  ASSERT_TRUE(candidates[1].app());
   EXPECT_EQ("focused", candidates[1].app()->process_name());
   // visible app 1, last_activity_time larger than visible app 2.
-  ASSERT_TRUE(candidates[2].app());
   EXPECT_EQ("visible1", candidates[2].app()->process_name());
   // visible app 2, last_activity_time less than visible app 1.
-  ASSERT_TRUE(candidates[3].app());
   EXPECT_EQ("visible2", candidates[3].app()->process_name());
   EXPECT_EQ(candidates[4].lifecycle_unit(), &protected_lifecycle_unit);
   // background service.
-  ASSERT_TRUE(candidates[5].app());
   EXPECT_EQ("service", candidates[5].app()->process_name());
   // protected LifecycleUnit
   // non-focused LifecycleUnits, sorted by last focused time.
@@ -105,32 +101,26 @@
   ASSERT_EQ(2U, candidates.size());
   // FOCUSED_TAB should be the first one.
   EXPECT_EQ(&focused_lifecycle_unit, candidates[0].lifecycle_unit());
-  ASSERT_TRUE(candidates[1].app());
   EXPECT_EQ("focused", candidates[1].app()->process_name());
 }
 
 // Test to make sure old process types are active when TabRanker experiment
 // is turned on.
-TEST_F(TabManagerDelegateTest, CandidatesSortedWithTabRanker) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures({features::kTabRanker}, {});
+TEST_F(TabManagerDelegateTest, SortLifecycleUnitWithTabRanker) {
   std::vector<arc::ArcProcess> arc_processes;
   arc_processes.emplace_back(1, 10, "focused", arc::mojom::ProcessState::TOP,
                              kIsFocused, 99);
   arc_processes.emplace_back(2, 20, "visible1", arc::mojom::ProcessState::TOP,
                              kNotFocused, 89);
   arc_processes.emplace_back(
-      3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 79);
+      3, 30, "service", arc::mojom::ProcessState::SERVICE, kNotFocused, 95);
 
   TestLifecycleUnit tab1(
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(100), 4);
-  tab1.SetSortKey(LifecycleUnit::SortKey(10, tab1.GetLastFocusedTime()));
   TestLifecycleUnit tab2(
-      base::TimeTicks() + base::TimeDelta::FromMilliseconds(90), 5);
-  tab2.SetSortKey(LifecycleUnit::SortKey(20, tab2.GetLastFocusedTime()));
+      base::TimeTicks() + base::TimeDelta::FromMilliseconds(90), 5, false);
   TestLifecycleUnit tab3(
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(80), 6);
-  tab3.SetSortKey(LifecycleUnit::SortKey(30, tab3.GetLastFocusedTime()));
   LifecycleUnitVector lifecycle_units{&tab1, &tab2, &tab3};
 
   TabManagerDelegate::OptionalArcProcessList opt_arc_processes(
@@ -138,24 +128,43 @@
   std::vector<TabManagerDelegate::Candidate> candidates;
   candidates = TabManagerDelegate::GetSortedCandidates(lifecycle_units,
                                                        opt_arc_processes);
-
+  // Verify the original order.
   ASSERT_EQ(6U, candidates.size());
-  ASSERT_TRUE(candidates[0].app());
+
   EXPECT_EQ("focused", candidates[0].app()->process_name());
   EXPECT_EQ(ProcessType::FOCUSED_APP, candidates[0].process_type());
-  ASSERT_TRUE(candidates[1].app());
-  EXPECT_EQ("visible1", candidates[1].app()->process_name());
-  EXPECT_EQ(ProcessType::PROTECTED_BACKGROUND, candidates[1].process_type());
-  ASSERT_TRUE(candidates[2].app());
-  EXPECT_EQ("service", candidates[2].app()->process_name());
-  EXPECT_EQ(ProcessType::BACKGROUND, candidates[2].process_type());
 
-  EXPECT_EQ(&tab3, candidates[3].lifecycle_unit());
+  EXPECT_EQ(&tab2, candidates[1].lifecycle_unit());
+  EXPECT_EQ(ProcessType::PROTECTED_BACKGROUND, candidates[1].process_type());
+
+  EXPECT_EQ("visible1", candidates[2].app()->process_name());
+  EXPECT_EQ(ProcessType::PROTECTED_BACKGROUND, candidates[2].process_type());
+
+  EXPECT_EQ(&tab1, candidates[3].lifecycle_unit());
   EXPECT_EQ(ProcessType::BACKGROUND, candidates[3].process_type());
-  EXPECT_EQ(&tab2, candidates[4].lifecycle_unit());
+
+  EXPECT_EQ("service", candidates[4].app()->process_name());
   EXPECT_EQ(ProcessType::BACKGROUND, candidates[4].process_type());
-  EXPECT_EQ(&tab1, candidates[5].lifecycle_unit());
+
+  EXPECT_EQ(&tab3, candidates[5].lifecycle_unit());
   EXPECT_EQ(ProcessType::BACKGROUND, candidates[5].process_type());
+
+  auto oldest_first = [](LifecycleUnitVector* lifecycle_units) {
+    std::sort(lifecycle_units->begin(), lifecycle_units->end(),
+              [](LifecycleUnit* a, LifecycleUnit* b) {
+                return a->GetLastFocusedTime() < b->GetLastFocusedTime();
+              });
+  };
+
+  // Verify the re-ranked order.
+  TabManagerDelegate::SortLifecycleUnitWithTabRanker(
+      &candidates, base::BindOnce(oldest_first));
+  EXPECT_EQ("focused", candidates[0].app()->process_name());
+  EXPECT_EQ(&tab2, candidates[1].lifecycle_unit());
+  EXPECT_EQ("visible1", candidates[2].app()->process_name());
+  EXPECT_EQ(&tab3, candidates[3].lifecycle_unit());
+  EXPECT_EQ("service", candidates[4].app()->process_name());
+  EXPECT_EQ(&tab1, candidates[5].lifecycle_unit());
 }
 
 class MockTabManagerDelegate : public TabManagerDelegate {
diff --git a/chrome/browser/resource_coordinator/tab_manager_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
index 0e76784a..665a63ed 100644
--- a/chrome/browser/resource_coordinator/tab_manager_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
@@ -1050,6 +1050,32 @@
   EXPECT_EQ(tab_manager_->num_loaded_lifecycle_units_, 0);
 }
 
+TEST_F(TabManagerTest, GetSortedLifecycleUnits) {
+  auto window = std::make_unique<TestBrowserWindow>();
+  Browser::CreateParams params(profile(), true);
+  params.type = Browser::TYPE_TABBED;
+  params.window = window.get();
+  auto browser = std::make_unique<Browser>(params);
+  TabStripModel* tab_strip = browser->tab_strip_model();
+
+  const int num_of_tabs_to_test = 20;
+  for (int i = 0; i < num_of_tabs_to_test; ++i) {
+    task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(10));
+    tab_strip->AppendWebContents(CreateWebContents(), /*foreground=*/true);
+  }
+
+  LifecycleUnitVector lifecycle_units = tab_manager_->GetSortedLifecycleUnits();
+  EXPECT_EQ(lifecycle_units.size(), static_cast<size_t>(num_of_tabs_to_test));
+
+  // Check that the lifecycle_units are sorted with ascending importance.
+  for (int i = 0; i < num_of_tabs_to_test - 1; ++i) {
+    EXPECT_TRUE(lifecycle_units[i]->GetSortKey() <
+                lifecycle_units[i + 1]->GetSortKey());
+  }
+
+  tab_strip->CloseAllTabs();
+}
+
 TEST_F(TabManagerWithProactiveDiscardExperimentEnabledTest,
        GetTimeInBackgroundBeforeProactiveDiscardTest) {
   auto window = std::make_unique<TestBrowserWindow>();
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/settings.js b/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
index 98cc3d7..469e9f9 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/views/settings.js
@@ -259,8 +259,8 @@
       s.splice(3, 1, s[3].filter(([w, h]) => w * h >= 100000));
       return s;
     };
-    this.frontPhotoSetting_ = zeroMPFilter(front);
-    this.backPhotoSetting_ = zeroMPFilter(back);
+    this.frontPhotoSetting_ = front && zeroMPFilter(front);
+    this.backPhotoSetting_ = back && zeroMPFilter(back);
     this.externalPhotoSettings_ = externals.map(zeroMPFilter);
     this.updateResolutions_();
   });
@@ -341,7 +341,7 @@
   const compareId = (setting, setting2) =>
       (setting && setting[0]) === (setting2 && setting2[0]);
   if (!compareId(this.frontPhotoSetting_, this.frontVideoSetting_) ||
-      !compareId(this.backPhotoItem_, this.backVideoItem_) ||
+      !compareId(this.backPhotoSetting_, this.backVideoSetting_) ||
       this.externalPhotoSettings_.length !=
           this.externalVideoSettings_.length ||
       this.externalPhotoSettings_.some(
diff --git a/chrome/browser/resources/management/management_browser_proxy.js b/chrome/browser/resources/management/management_browser_proxy.js
index cdbda1f9..b25f115 100644
--- a/chrome/browser/resources/management/management_browser_proxy.js
+++ b/chrome/browser/resources/management/management_browser_proxy.js
@@ -46,6 +46,7 @@
  *   pageSubtitle: string,
  *   managed: boolean,
  *   overview: string,
+ *   customerLogo: string,
  * }}
  */
 management.ManagedDataResponse;
diff --git a/chrome/browser/resources/management/management_ui.html b/chrome/browser/resources/management/management_ui.html
index a46ab223..a3b3522 100644
--- a/chrome/browser/resources/management/management_ui.html
+++ b/chrome/browser/resources/management/management_ui.html
@@ -67,6 +67,19 @@
         margin-top: 16px;
       }
 
+      .overview-container img {
+        float: left;
+        margin-inline-end: 10px;
+        vertical-align: top;
+        width: 20%;
+      }
+
+      .overview-container div {
+        display: block;
+        overflow: hidden;
+        width: auto;
+      }
+
       .overview-messages {
         margin-top: 0;
       }
@@ -186,25 +199,32 @@
             <div inner-h-t-m-l="[[managementNoticeHtml_]]"></div>
 </if>
 <if expr="chromeos">
-            <div>[[managementOverview_]]</div>
-            <div>[[deviceManagedInfo_.overview]]</div>
-            <ul class="overview-messages"
-                hidden="[[!deviceManagedInfo_]]">
-              <li>
-                [[deviceManagedInfo_.setup]]
+            <div class="overview-container">
+              <img src="[[customerLogo_]]" hidden="[[!customerLogo_]]"
+                  alt="" aria-hidden="true">
+              <div>
+                <div>[[managementOverview_]]</div>
+                <div>[[deviceManagedInfo_.overview]]</div>
+                <ul class="overview-messages"
+                    hidden="[[!deviceManagedInfo_]]">
+                  <li>
+                    [[deviceManagedInfo_.setup]]
                     <a href="$i18nRaw{managementDeviceLearnMoreUrl}"
-                      target="_blank">$i18n{learnMore}</a>
-              </li>
-              <li>[[deviceManagedInfo_.data]]</li>
-            </ul>
-            <div>[[accountManagedInfo_.overview]]</div>
-            <ul class="overview-messages" hidden="[[!accountManagedInfo_]]">
-              <li>[[accountManagedInfo_.setup]]
+                        target="_blank">$i18n{learnMore}</a>
+                  </li>
+                  <li>[[deviceManagedInfo_.data]]</li>
+                </ul>
+                <div>[[accountManagedInfo_.overview]]</div>
+                <ul class="overview-messages" hidden="[[!accountManagedInfo_]]">
+                  <li>
+                    [[accountManagedInfo_.setup]]
                     <a href="$i18nRaw{managementAccountLearnMoreUrl}"
-                    target="_blank">$i18n{learnMore}</a>
-              </li>
-              <li>[[accountManagedInfo_.data]]</li>
-            </ul>
+                        target="_blank">$i18n{learnMore}</a>
+                  </li>
+                  <li>[[accountManagedInfo_.data]]</li>
+                </ul>
+              </div>
+            </div>
 </if>
           </section>
 <if expr="chromeos">
diff --git a/chrome/browser/resources/management/management_ui.js b/chrome/browser/resources/management/management_ui.js
index 1df3609..12682482 100644
--- a/chrome/browser/resources/management/management_ui.js
+++ b/chrome/browser/resources/management/management_ui.js
@@ -46,6 +46,9 @@
     localTrustRoots_: String,
 
     /** @private */
+    customerLogo_: String,
+
+    /** @private */
     managementOverview_: String,
 
     /** @private {?management.ManagedInfo} */
@@ -84,7 +87,7 @@
         'browser-reporting-info-updated',
         reportingInfo => this.onBrowserReportingInfoReceived_(reportingInfo));
 
-    this.addWebUIListener('managed_state_changed', () => {
+    this.addWebUIListener('managed_data_changed', () => {
       this.updateManagedFields_();
     });
 
@@ -255,6 +258,7 @@
       this.subtitle_ = data.pageSubtitle;
       this.accountManagedInfo_ = data.accountManagedInfo;
       // <if expr="chromeos">
+      this.customerLogo_ = data.customerLogo;
       this.managementOverview_ = data.overview;
       this.deviceManagedInfo_ = data.deviceManagedInfo;
       // </if>
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index 376f481..37a32295 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -301,10 +301,9 @@
 
 std::unique_ptr<GaiaAuthFetcher> ChromeSigninClient::CreateGaiaAuthFetcher(
     GaiaAuthConsumer* consumer,
-    gaia::GaiaSource source,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+    gaia::GaiaSource source) {
   return std::make_unique<GaiaAuthFetcher>(consumer, source,
-                                           url_loader_factory);
+                                           GetURLLoaderFactory());
 }
 
 void ChromeSigninClient::VerifySyncToken() {
diff --git a/chrome/browser/signin/chrome_signin_client.h b/chrome/browser/signin/chrome_signin_client.h
index f225d5403..93d4ede 100644
--- a/chrome/browser/signin/chrome_signin_client.h
+++ b/chrome/browser/signin/chrome_signin_client.h
@@ -62,9 +62,7 @@
   void DelayNetworkCall(base::OnceClosure callback) override;
   std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
       GaiaAuthConsumer* consumer,
-      gaia::GaiaSource source,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-      override;
+      gaia::GaiaSource source) override;
 
   // Returns a string describing the chrome version environment. Version format:
   // <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel">
diff --git a/chrome/browser/signin/dice_response_handler.cc b/chrome/browser/signin/dice_response_handler.cc
index ea2ebd4b..e127523 100644
--- a/chrome/browser/signin/dice_response_handler.cc
+++ b/chrome/browser/signin/dice_response_handler.cc
@@ -176,8 +176,8 @@
   DCHECK(dice_response_handler_);
   account_reconcilor_lock_ =
       std::make_unique<AccountReconcilor::Lock>(account_reconcilor);
-  gaia_auth_fetcher_ = signin_client->CreateGaiaAuthFetcher(
-      this, gaia::GaiaSource::kChrome, signin_client->GetURLLoaderFactory());
+  gaia_auth_fetcher_ =
+      signin_client->CreateGaiaAuthFetcher(this, gaia::GaiaSource::kChrome);
   VLOG(1) << "Start fetching token for account: " << email;
   gaia_auth_fetcher_->StartAuthCodeForOAuth2TokenExchange(authorization_code_);
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
diff --git a/chrome/browser/signin/dice_response_handler_unittest.cc b/chrome/browser/signin/dice_response_handler_unittest.cc
index 9c03594..47876e3 100644
--- a/chrome/browser/signin/dice_response_handler_unittest.cc
+++ b/chrome/browser/signin/dice_response_handler_unittest.cc
@@ -52,17 +52,14 @@
 
   std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
       GaiaAuthConsumer* consumer,
-      gaia::GaiaSource source,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-      override {
+      gaia::GaiaSource source) override {
     DCHECK(!consumer_ || (consumer_ == consumer));
     consumer_ = consumer;
 
     // Pass |this| as a dummy consumer to CreateGaiaAuthFetcher().
     // Since DiceTestSigninClient does not overrides any consumer method,
     // everything will be dropped on the floor.
-    return TestSigninClient::CreateGaiaAuthFetcher(this, source,
-                                                   url_loader_factory);
+    return TestSigninClient::CreateGaiaAuthFetcher(this, source);
   }
 
   GaiaAuthConsumer* consumer_;
diff --git a/chrome/browser/signin/identity_manager_factory.cc b/chrome/browser/signin/identity_manager_factory.cc
index d405d8d..404fda1 100644
--- a/chrome/browser/signin/identity_manager_factory.cc
+++ b/chrome/browser/signin/identity_manager_factory.cc
@@ -85,7 +85,9 @@
       ChromeSigninClientFactory::GetInstance()->GetForProfile(profile);
 #if defined(OS_CHROMEOS)
   signin_manager = std::make_unique<ConcreteSigninManager>(
-      client, token_service, account_tracker_service);
+      client, token_service, account_tracker_service,
+      gaia_cookie_manager_service,
+      AccountConsistencyModeManager::GetMethodForProfile(profile));
 #else
   signin_manager = std::make_unique<ConcreteSigninManager>(
       client, token_service, account_tracker_service,
diff --git a/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc b/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc
index 14305fb..681fa5a 100644
--- a/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc
+++ b/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc
@@ -43,7 +43,7 @@
     auto* signin_client =
         ChromeSigninClientFactory::GetForProfile(profile_.get());
     return static_cast<TestSigninClient*>(signin_client)
-        ->test_url_loader_factory();
+        ->GetTestURLLoaderFactory();
   }
 
   identity::AccountsCookieMutator* GetAccountsCookieMutator() {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index a8fa147..533f68b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3315,6 +3315,8 @@
       "app_list/search/search_resource_manager.h",
       "app_list/search/search_result_ranker/app_launch_predictor.cc",
       "app_list/search/search_result_ranker/app_launch_predictor.h",
+      "app_list/search/search_result_ranker/app_list_launch_metrics_provider.cc",
+      "app_list/search/search_result_ranker/app_list_launch_metrics_provider.h",
       "app_list/search/search_result_ranker/app_list_launch_recorder.cc",
       "app_list/search/search_result_ranker/app_list_launch_recorder.h",
       "app_list/search/search_result_ranker/app_search_result_ranker.cc",
@@ -3346,6 +3348,7 @@
       "//ash/resources/vector_icons",
       "//chrome/browser/ui:app_launch_event_logger_proto",
       "//chrome/browser/ui/app_list/search/search_result_ranker:app_launch_predictor_proto",
+      "//chrome/browser/ui/app_list/search/search_result_ranker:app_list_launch_recorder_proto",
       "//chrome/browser/ui/app_list/search/search_result_ranker:recurrence_ranker_proto",
     ]
 
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/BUILD.gn b/chrome/browser/ui/app_list/search/search_result_ranker/BUILD.gn
index 1b043c0f3..6e811a3 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/BUILD.gn
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/BUILD.gn
@@ -10,6 +10,12 @@
   ]
 }
 
+proto_library("app_list_launch_recorder_proto") {
+  sources = [
+    "app_list_launch_recorder_state.proto",
+  ]
+}
+
 proto_library("recurrence_ranker_proto") {
   sources = [
     "frecency_store.proto",
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.cc
new file mode 100644
index 0000000..3822eb8
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.cc
@@ -0,0 +1,285 @@
+// Copyright 2019 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/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/important_file_writer.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/time/time.h"
+#include "base/unguessable_token.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder_state.pb.h"
+#include "components/metrics/metrics_log.h"
+#include "crypto/sha2.h"
+#include "third_party/metrics_proto/chrome_os_app_list_launch_event.pb.h"
+#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
+
+namespace app_list {
+namespace {
+
+using LaunchInfo = ::app_list::AppListLaunchRecorder::LaunchInfo;
+
+using ::metrics::ChromeOSAppListLaunchEventProto;
+using ::metrics::ChromeUserMetricsExtension;
+using ::metrics::MetricsLog;
+
+// Length of the user secret string, in bytes.
+constexpr size_t kSecretSize = 32;
+
+// Tries to serialize the given AppListLaunchRecorderStateProto to disk at the
+// given |filepath|.
+void SaveStateToDisk(const base::FilePath& filepath,
+                     const AppListLaunchRecorderStateProto& proto) {
+  std::string proto_str;
+  if (!proto.SerializeToString(&proto_str)) {
+    LOG(DFATAL) << "Error serializing AppListLaunchRecorderStateProto.";
+    return;
+  }
+
+  bool write_result = false;
+  {
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+    write_result = base::ImportantFileWriter::WriteFileAtomically(
+        filepath, proto_str, "AppListLaunchMetricsProvider");
+  }
+
+  if (!write_result) {
+    LOG(DFATAL) << "Error writing AppListLaunchRecorderStateProto.";
+  }
+}
+
+// Tries to load an |AppListLaunchRecorderStateProto| from the given filepath.
+// If it fails, returns nullopt.
+base::Optional<AppListLaunchRecorderStateProto> LoadStateFromDisk(
+    const base::FilePath& filepath) {
+  std::string proto_str;
+  {
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+    // If the state file doesn't exist, it's not necessarily an error: this may
+    // be the first time the provider is being run.
+    if (!base::PathExists(filepath))
+      return base::nullopt;
+
+    if (!base::ReadFileToString(filepath, &proto_str)) {
+      LOG(DFATAL) << "Error reading AppListLaunchRecorderStateProto.";
+      return base::nullopt;
+    }
+  }
+
+  AppListLaunchRecorderStateProto proto;
+  if (!proto.ParseFromString(proto_str)) {
+    LOG(DFATAL) << "Error parsing AppListLaunchRecorderStateProto.";
+    return base::nullopt;
+  }
+
+  return proto;
+}
+
+// Returns the file path of the primary user's profile, if it exists and is not
+// a guest session or system profile. Otherwise, returns base::nullopt.
+base::Optional<base::FilePath> GetProfileDir() {
+  // We do not handle multiprofile, events from all logged-in profiles are
+  // logged under the primary user.
+  Profile* profile = ProfileManager::GetPrimaryUserProfile();
+  // Only enable if the current profile is a regular profile, not a guest
+  // session or the login screen.
+  if (!profile || profile->IsGuestSession() || profile->IsSystemProfile())
+    return base::nullopt;
+  return profile->GetPath();
+}
+
+// Generates a cryptographically secure random secret of size |kSecretSize|.
+Secret GenerateSecret() {
+  const auto secret = base::UnguessableToken::Create().ToString();
+  DCHECK_EQ(secret.size(), kSecretSize);
+  return {secret};
+}
+
+// Generates a random user ID.
+uint64_t GenerateUserID() {
+  // This is analogous to how the UMA client ID is generated in
+  // metrics::MetricsStateManager.
+  return MetricsLog::Hash(base::GenerateGUID());
+}
+
+// Generates a proto containing a new secret and user ID.
+AppListLaunchRecorderStateProto GenerateStateProto() {
+  AppListLaunchRecorderStateProto proto;
+  proto.set_secret(GenerateSecret().value);
+  proto.set_recurrence_ranker_user_id(GenerateUserID());
+  return proto;
+}
+
+// Returns the user secret stored in |proto|.
+Secret GetSecretFromProto(const AppListLaunchRecorderStateProto& proto) {
+  DCHECK(proto.has_secret() && !proto.secret().empty());
+  return {proto.secret()};
+}
+
+// Returns the first 8 bytes of the SHA256 hash of |secret| concatenated with
+// |value|.
+uint64_t HashWithSecret(const std::string& value, const Secret& secret) {
+  DCHECK(!secret.value.empty());
+  uint64_t hash;
+  crypto::SHA256HashString(secret.value + value, &hash, sizeof(uint64_t));
+  return hash;
+}
+
+}  // namespace
+
+int AppListLaunchMetricsProvider::kMaxEventsPerUpload = 100;
+char AppListLaunchMetricsProvider::kStateProtoFilename[] =
+    "app_list_launch_recorder_state.pb";
+
+AppListLaunchMetricsProvider::AppListLaunchMetricsProvider(
+    base::RepeatingCallback<base::Optional<base::FilePath>()>
+        get_profile_dir_callback)
+    : get_profile_dir_callback_(get_profile_dir_callback),
+      init_state_(InitState::DISABLED),
+      secret_(base::nullopt),
+      user_id_(base::nullopt),
+      weak_factory_(this) {}
+
+AppListLaunchMetricsProvider::AppListLaunchMetricsProvider()
+    : AppListLaunchMetricsProvider(base::BindRepeating(GetProfileDir)) {}
+
+AppListLaunchMetricsProvider::~AppListLaunchMetricsProvider() = default;
+
+void AppListLaunchMetricsProvider::OnRecordingEnabled() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // We do not perform actual initialization or set to InitState::ENABLED here.
+  // Initialization must occur after the browser thread has finished
+  // initializing, which may not be the case when OnRecordingEnabled is
+  // called. Instead, initialization happens on the first call to
+  // OnAppListLaunch.
+  init_state_ = InitState::UNINITIALIZED;
+
+  subscription_ = AppListLaunchRecorder::GetInstance()->RegisterCallback(
+      base::BindRepeating(&AppListLaunchMetricsProvider::OnAppListLaunch,
+                          weak_factory_.GetWeakPtr()));
+}
+
+void AppListLaunchMetricsProvider::OnRecordingDisabled() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  init_state_ = InitState::DISABLED;
+  launch_info_cache_.clear();
+  secret_.reset();
+  user_id_.reset();
+  subscription_.reset();
+}
+
+void AppListLaunchMetricsProvider::Initialize() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const auto& profile_dir = get_profile_dir_callback_.Run();
+  if (!profile_dir) {
+    OnRecordingDisabled();
+    return;
+  }
+  const base::FilePath& proto_filepath = profile_dir.value().AppendASCII(
+      AppListLaunchMetricsProvider::kStateProtoFilename);
+
+  PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(&LoadStateFromDisk, proto_filepath),
+      base::BindOnce(&AppListLaunchMetricsProvider::OnStateLoaded,
+                     weak_factory_.GetWeakPtr(), proto_filepath));
+}
+
+void AppListLaunchMetricsProvider::OnStateLoaded(
+    const base::FilePath& proto_filepath,
+    const base::Optional<AppListLaunchRecorderStateProto>& proto) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (proto) {
+    secret_ = GetSecretFromProto(proto.value());
+    user_id_ = proto.value().recurrence_ranker_user_id();
+  } else {
+    // Either there is no state proto saved, or it was corrupt. Generate a new
+    // secret and ID regardless.
+    AppListLaunchRecorderStateProto new_proto = GenerateStateProto();
+    PostTaskWithTraits(
+        FROM_HERE, {base::MayBlock()},
+        base::BindOnce(&SaveStateToDisk, proto_filepath, new_proto));
+
+    secret_ = GetSecretFromProto(new_proto);
+    user_id_ = new_proto.recurrence_ranker_user_id();
+  }
+
+  if (!user_id_) {
+    LOG(ERROR) << "Invalid user ID.";
+    OnRecordingDisabled();
+  } else if (!secret_ || secret_.value().value.size() != kSecretSize) {
+    LOG(ERROR) << "Invalid user secret.";
+    OnRecordingDisabled();
+  } else {
+    init_state_ = InitState::ENABLED;
+  }
+}
+
+void AppListLaunchMetricsProvider::ProvideCurrentSessionData(
+    ChromeUserMetricsExtension* uma_proto) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (init_state_ == InitState::ENABLED) {
+    for (const auto& launch_info : launch_info_cache_) {
+      CreateLaunchEvent(launch_info,
+                        uma_proto->add_chrome_os_app_list_launch_event());
+    }
+    launch_info_cache_.clear();
+  }
+}
+
+void AppListLaunchMetricsProvider::OnAppListLaunch(
+    const LaunchInfo& launch_info) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (init_state_ == InitState::UNINITIALIZED) {
+    init_state_ = InitState::INIT_STARTED;
+    Initialize();
+  }
+
+  if (launch_info_cache_.size() >= static_cast<size_t>(kMaxEventsPerUpload)) {
+    // TODO(951287): Log an error to UMA if this happens.
+  } else if (launch_info.launch_type ==
+             ChromeOSAppListLaunchEventProto::LAUNCH_TYPE_UNSPECIFIED) {
+    // TODO(951287): Log an error to UMA if this happens.
+    LOG(ERROR) << "Invalid launch type.";
+  } else if (init_state_ != InitState::DISABLED) {
+    launch_info_cache_.push_back(launch_info);
+  }
+}
+
+void AppListLaunchMetricsProvider::CreateLaunchEvent(
+    const LaunchInfo& launch_info,
+    ChromeOSAppListLaunchEventProto* event) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(secret_ && user_id_);
+
+  base::Time::Exploded now;
+  base::Time::Now().LocalExplode(&now);
+
+  // Unhashed data.
+  event->set_recurrence_ranker_user_id(user_id_.value());
+  event->set_hour(now.hour);
+  event->set_search_query_length(launch_info.query.size());
+  event->set_launch_type(launch_info.launch_type);
+
+  // Hashed data.
+  event->set_hashed_target(HashWithSecret(launch_info.target, secret_.value()));
+  event->set_hashed_query(HashWithSecret(launch_info.query, secret_.value()));
+  event->set_hashed_domain(HashWithSecret(launch_info.domain, secret_.value()));
+  event->set_hashed_app(HashWithSecret(launch_info.app, secret_.value()));
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h
new file mode 100644
index 0000000..2d5529daa
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h
@@ -0,0 +1,133 @@
+// Copyright 2019 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_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_APP_LIST_LAUNCH_METRICS_PROVIDER_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_APP_LIST_LAUNCH_METRICS_PROVIDER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/callback_list.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.h"
+#include "components/metrics/metrics_provider.h"
+#include "third_party/metrics_proto/chrome_os_app_list_launch_event.pb.h"
+#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
+
+class ChromeMetricsServiceClient;
+
+namespace app_list {
+
+// Stores a user's random secret. This struct exists to make clear at the type
+// system level what is a secret and what isn't.
+struct Secret {
+  std::string value;
+};
+
+class AppListLaunchMetricsProviderTest;
+class AppListLaunchRecorderStateProto;
+
+// AppListLaunchMetricsProvider is responsible for filling out the
+// |app_list_launch_event| section of the UMA proto. This class should not be
+// instantiated directly except by the ChromeMetricsServiceClient. Instead,
+// logging events should be sent to AppListLaunchRecorder.
+class AppListLaunchMetricsProvider : public metrics::MetricsProvider {
+ public:
+  ~AppListLaunchMetricsProvider() override;
+
+  // metrics::MetricsProvider:
+  void OnRecordingEnabled() override;
+  void OnRecordingDisabled() override;
+  void ProvideCurrentSessionData(
+      metrics::ChromeUserMetricsExtension* uma_proto) override;
+
+ private:
+  friend class ::ChromeMetricsServiceClient;
+  friend class ::app_list::AppListLaunchMetricsProviderTest;
+  FRIEND_TEST_ALL_PREFIXES(AppListLaunchMetricsProviderTest, EventsAreCapped);
+
+  // Filename for the the state proto within the user's home directory. This is
+  // just the basename, not the full path.
+  static char kStateProtoFilename[];
+
+  // Beyond this number of OnAppListLaunch calls between successive calls to
+  // ProvideCurrentSessionData, we stop logging.
+  static int kMaxEventsPerUpload;
+
+  enum class InitState { UNINITIALIZED, INIT_STARTED, ENABLED, DISABLED };
+
+  // The constructors are private so that this class can only be instantied by
+  // ChromeMetricsServiceClient and tests. The class has two constructors so
+  // that the tests can override the profile dir.
+  AppListLaunchMetricsProvider();
+
+  AppListLaunchMetricsProvider(
+      base::RepeatingCallback<base::Optional<base::FilePath>()>
+          get_profile_dir_callback);
+
+  // Records the information in |launch_info|, to be converted into a hashed
+  // form and logged to UMA. Actual logging occurs periodically, so it is not
+  // guaranteed that any call to this method will be logged.
+  //
+  // OnAppListLaunch should only be called from the browser thread, after it has
+  // finished initializing.
+  void OnAppListLaunch(const AppListLaunchRecorder::LaunchInfo& launch_info);
+
+  // Starts initialization if needed. This involves reading the secret from
+  // disk.
+  void Initialize();
+
+  // Populates this object's internal state from the given state |proto|, and
+  // finishes initialisation. This is called by Initialize() and passed the
+  // state proto that has been loaded from disk, if it exists. OnStateLoaded may
+  // write a new proto to disk if, for example, the given |proto| is invalid.
+  void OnStateLoaded(
+      const base::FilePath& proto_filepath,
+      const base::Optional<AppListLaunchRecorderStateProto>& proto);
+
+  // Converts |launch_info| into a hashed |event| proto ready for logging.
+  void CreateLaunchEvent(const AppListLaunchRecorder::LaunchInfo& launch_info,
+                         metrics::ChromeOSAppListLaunchEventProto* event);
+
+  // A function that returns the directory of the current profile. This is a
+  // callback so that it can be faked out for testing.
+  const base::RepeatingCallback<base::Optional<base::FilePath>()>
+      get_profile_dir_callback_;
+
+  // Subscription for receiving logging event callbacks from HashedLogger.
+  std::unique_ptr<AppListLaunchRecorder::LaunchEventSubscription> subscription_;
+
+  // Cache of input launch data, to be hashed and returned when
+  // ProvideCurrentSessionData() is called.
+  std::vector<AppListLaunchRecorder::LaunchInfo> launch_info_cache_;
+
+  // The initialization state of the AppListLaunchMetricsProvider. Events are
+  // only provided on a call to ProvideCurrentSessionData() once this is
+  // enabled, and nothing is recorded if this is disabled.
+  InitState init_state_;
+
+  // Secret per-user salt concatenated with values before hashing. Serialized
+  // to disk.
+  base::Optional<Secret> secret_;
+
+  // Per-user per-client ID used only for AppListLaunchMetricsProvider.
+  // Serialized to disk.
+  base::Optional<uint64_t> user_id_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<AppListLaunchMetricsProvider> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppListLaunchMetricsProvider);
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_APP_LIST_LAUNCH_METRICS_PROVIDER_H_
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc
new file mode 100644
index 0000000..1876247
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc
@@ -0,0 +1,310 @@
+// Copyright 2019 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/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h"
+
+#include <string>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/important_file_writer.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.h"
+#include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder_state.pb.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/metrics/client_info.h"
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/metrics_service.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/metrics_proto/chrome_os_app_list_launch_event.pb.h"
+#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
+#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
+
+namespace app_list {
+
+namespace {
+
+using LaunchType = ::metrics::ChromeOSAppListLaunchEventProto::LaunchType;
+
+using ::chromeos::ProfileHelper;
+using ::metrics::ChromeOSAppListLaunchEventProto;
+
+// 32 bytes long, matching the size of a real secret.
+constexpr char kSecret[] = "abcdefghijklmnopqrstuvwxyzabcdef";
+
+constexpr uint64_t kUserId = 1123u;
+
+constexpr char kValue1[] = "value one";
+constexpr char kValue2[] = "value two";
+constexpr char kValue3[] = "value three";
+constexpr char kValue4[] = "value four";
+
+// These are hex encoded values of the first 8 bytes of sha256(kSecret +
+// kValueN), generated with the sha256sum command. These are hex encoded to make
+// debugging incorrect values easier.
+constexpr char kValue1Hash[] = "E79C24CD2117A2BB";
+constexpr char kValue2Hash[] = "506ECDDC0BA3C341";
+constexpr char kValue3Hash[] = "1E0CDC361557A12F";
+constexpr char kValue4Hash[] = "4875030730DE902A";
+
+std::string HashToHex(uint64_t hash) {
+  return base::HexEncode(&hash, sizeof(uint64_t));
+}
+
+void ExpectLoggingEventEquals(ChromeOSAppListLaunchEventProto proto,
+                              const char* target_hash,
+                              const char* query_hash,
+                              const char* domain_hash,
+                              const char* app_hash,
+                              int search_query_length) {
+  EXPECT_EQ(ChromeOSAppListLaunchEventProto::APP_TILES, proto.launch_type());
+  // Hour field is untested.
+  EXPECT_EQ(kUserId, proto.recurrence_ranker_user_id());
+  EXPECT_EQ(search_query_length, proto.search_query_length());
+
+  EXPECT_EQ(target_hash, HashToHex(proto.hashed_target()));
+  EXPECT_EQ(query_hash, HashToHex(proto.hashed_query()));
+  EXPECT_EQ(domain_hash, HashToHex(proto.hashed_domain()));
+  EXPECT_EQ(app_hash, HashToHex(proto.hashed_app()));
+}
+
+}  // namespace
+
+class AppListLaunchMetricsProviderTest : public testing::Test {
+ protected:
+  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+
+  base::Optional<base::FilePath> GetTempDir() { return temp_dir_.GetPath(); }
+
+  void MakeProvider() {
+    // Using WrapUnique to access a private constructor.
+    provider_ = base::WrapUnique(new AppListLaunchMetricsProvider(
+        base::BindRepeating(&AppListLaunchMetricsProviderTest::GetTempDir,
+                            base::Unretained(this))));
+    provider_->OnRecordingEnabled();
+  }
+
+  void InitProvider() {
+    AddLog("", "", "", "");
+    Wait();
+  }
+
+  void ExpectUninitialized() {
+    EXPECT_EQ(AppListLaunchMetricsProvider::InitState::UNINITIALIZED,
+              provider_->init_state_);
+  }
+
+  void ExpectInitStarted() {
+    EXPECT_EQ(AppListLaunchMetricsProvider::InitState::INIT_STARTED,
+              provider_->init_state_);
+  }
+
+  void ExpectEnabled() {
+    EXPECT_EQ(AppListLaunchMetricsProvider::InitState::ENABLED,
+              provider_->init_state_);
+  }
+
+  void ExpectDisabled() {
+    EXPECT_EQ(AppListLaunchMetricsProvider::InitState::DISABLED,
+              provider_->init_state_);
+  }
+
+  base::Optional<Secret> GetSecret() { return provider_->secret_; }
+
+  std::string ReadSecret() {
+    std::string proto_str;
+    {
+      base::ScopedBlockingCall scoped_blocking_call(
+          FROM_HERE, base::BlockingType::MAY_BLOCK);
+
+      base::FilePath path = temp_dir_.GetPath().AppendASCII(
+          AppListLaunchMetricsProvider::kStateProtoFilename);
+      CHECK(base::ReadFileToString(path, &proto_str));
+    }
+
+    auto proto = std::make_unique<AppListLaunchRecorderStateProto>();
+    CHECK(proto->ParseFromString(proto_str));
+    return proto->secret();
+  }
+
+  void WriteStateProto(const std::string& secret) {
+    AppListLaunchRecorderStateProto proto;
+    proto.set_recurrence_ranker_user_id(kUserId);
+    proto.set_secret(secret);
+
+    std::string proto_str;
+    CHECK(proto.SerializeToString(&proto_str));
+    {
+      base::ScopedBlockingCall scoped_blocking_call(
+          FROM_HERE, base::BlockingType::MAY_BLOCK);
+      CHECK(base::ImportantFileWriter::WriteFileAtomically(
+          temp_dir_.GetPath().AppendASCII(
+              AppListLaunchMetricsProvider::kStateProtoFilename),
+          proto_str, "AppListLaunchMetricsProviderTest"));
+    }
+
+    Wait();
+  }
+
+  void AddLog(
+      const std::string& target,
+      const std::string& query,
+      const std::string& domain,
+      const std::string& app,
+      LaunchType launch_type = ChromeOSAppListLaunchEventProto::APP_TILES) {
+    provider_->OnAppListLaunch({launch_type, target, query, domain, app});
+  }
+
+  google::protobuf::RepeatedPtrField<ChromeOSAppListLaunchEventProto>
+  GetLogs() {
+    metrics::ChromeUserMetricsExtension uma_log;
+    provider_->ProvideCurrentSessionData(&uma_log);
+    return uma_log.chrome_os_app_list_launch_event();
+  }
+
+  void Wait() { scoped_task_environment_.RunUntilIdle(); }
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  base::ScopedTempDir temp_dir_;
+
+  std::unique_ptr<AppListLaunchMetricsProvider> provider_;
+};
+
+TEST_F(AppListLaunchMetricsProviderTest, ProvidesNothingWhenUninitialized) {
+  MakeProvider();
+
+  ExpectUninitialized();
+  EXPECT_TRUE(GetLogs().empty());
+}
+
+TEST_F(AppListLaunchMetricsProviderTest, SucceedsGeneratingNewSecret) {
+  MakeProvider();
+  InitProvider();
+
+  ExpectEnabled();
+  // Because the secret is random, we settle for just checking its length.
+  // In the object:
+  EXPECT_EQ(32ul, GetSecret().value().value.size());
+  // On disk:
+  EXPECT_EQ(32ul, ReadSecret().size());
+
+  EXPECT_EQ(GetSecret().value().value, ReadSecret());
+}
+
+TEST_F(AppListLaunchMetricsProviderTest, SucceedsLoadingExistingSecret) {
+  WriteStateProto(kSecret);
+
+  MakeProvider();
+  InitProvider();
+
+  ExpectEnabled();
+  EXPECT_EQ(kSecret, GetSecret().value().value);
+}
+
+// Tests that a call to ProvideCurrentSessionData populates protos for each log,
+// and that those protos contain the right values.
+TEST_F(AppListLaunchMetricsProviderTest, CorrectHashedValues) {
+  WriteStateProto(kSecret);
+  MakeProvider();
+  InitProvider();
+
+  AddLog(kValue1, kValue2, kValue3, kValue4);
+  AddLog(kValue2, kValue3, kValue4, kValue1);
+  AddLog(kValue3, kValue4, kValue1, kValue2);
+
+  const auto& events = GetLogs();
+  // events[0] is a dummy log created in |InitProvider|. We don't test for its
+  // contents below.
+  ASSERT_EQ(events.size(), 4);
+
+  ExpectLoggingEventEquals(events[1], kValue1Hash, kValue2Hash, kValue3Hash,
+                           kValue4Hash, std::string(kValue2).size());
+  ExpectLoggingEventEquals(events[2], kValue2Hash, kValue3Hash, kValue4Hash,
+                           kValue1Hash, std::string(kValue3).size());
+  ExpectLoggingEventEquals(events[3], kValue3Hash, kValue4Hash, kValue1Hash,
+                           kValue2Hash, std::string(kValue4).size());
+}
+
+// Tests that the logs reported in one call to ProvideCurrentSessionData do no
+// appear in the next.
+TEST_F(AppListLaunchMetricsProviderTest, EventsNotDuplicated) {
+  WriteStateProto(kSecret);
+  MakeProvider();
+  InitProvider();
+
+  AddLog(kValue1, kValue2, kValue3, kValue4);
+  auto events = GetLogs();
+  ASSERT_EQ(events.size(), 2);
+  ExpectLoggingEventEquals(events[1], kValue1Hash, kValue2Hash, kValue3Hash,
+                           kValue4Hash, std::string(kValue2).size());
+
+  AddLog(kValue2, kValue3, kValue4, kValue1);
+  events = GetLogs();
+  ASSERT_EQ(events.size(), 1);
+  ExpectLoggingEventEquals(events[0], kValue2Hash, kValue3Hash, kValue4Hash,
+                           kValue1Hash, std::string(kValue3).size());
+
+  EXPECT_TRUE(GetLogs().empty());
+}
+
+// Tests that logging events are dropped after an unreasonably large number of
+// them are made between uploads.
+TEST_F(AppListLaunchMetricsProviderTest, EventsAreCapped) {
+  MakeProvider();
+  InitProvider();
+
+  const int max_events = AppListLaunchMetricsProvider::kMaxEventsPerUpload;
+
+  // Not enough events to hit the cap.
+  for (int i = 0; i < max_events / 2; ++i)
+    AddLog(kValue1, kValue2, kValue3, kValue4);
+  // One event from init, kMaxEventsPerUpload/2 from the loop.
+  EXPECT_EQ(1 + max_events / 2, GetLogs().size());
+
+  // Enough events to hit the cap.
+  for (int i = 0; i < 2 * max_events; ++i)
+    AddLog(kValue1, kValue2, kValue3, kValue4);
+  EXPECT_EQ(max_events, GetLogs().size());
+}
+
+// Tests that logging events that occur before the provider is initialized are
+// still correctly logged after initialization.
+TEST_F(AppListLaunchMetricsProviderTest,
+       LaunchEventsBeforeInitializationAreRecorded) {
+  WriteStateProto(kSecret);
+  MakeProvider();
+
+  // To begin with, the provider is uninitialized and has no logs at all.
+  EXPECT_TRUE(GetLogs().empty());
+  // These logs are added before the provider has finished initialising.
+  AddLog(kValue1, kValue2, kValue3, kValue4);
+  AddLog(kValue2, kValue3, kValue4, kValue1);
+  AddLog(kValue3, kValue4, kValue1, kValue2);
+  // Initialisation is finished here.
+  Wait();
+
+  const auto& events = GetLogs();
+  ASSERT_EQ(events.size(), 3);
+  ExpectLoggingEventEquals(events[0], kValue1Hash, kValue2Hash, kValue3Hash,
+                           kValue4Hash, std::string(kValue2).size());
+  ExpectLoggingEventEquals(events[1], kValue2Hash, kValue3Hash, kValue4Hash,
+                           kValue1Hash, std::string(kValue3).size());
+  ExpectLoggingEventEquals(events[2], kValue3Hash, kValue4Hash, kValue1Hash,
+                           kValue2Hash, std::string(kValue4).size());
+
+  EXPECT_TRUE(GetLogs().empty());
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.cc
index bd756c8..44cc3ca 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.cc
@@ -39,8 +39,6 @@
 
 std::unique_ptr<AppListLaunchRecorder::LaunchEventSubscription>
 AppListLaunchRecorder::RegisterCallback(const LaunchEventCallback& callback) {
-  // TODO(951287): implement a metrics provider that is notified of these
-  // events.
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return callback_list_.Add(callback);
 }
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder_state.proto b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder_state.proto
new file mode 100644
index 0000000..9029b15a
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder_state.proto
@@ -0,0 +1,23 @@
+// Copyright 2019 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package app_list;
+
+// AppListLaunchRecorderStateProto contains all the data related to hashed
+// logging that is serialised to disk. This proto is never logged, but it is
+// serialised and should only be changed with care.
+message AppListLaunchRecorderStateProto {
+  // The per-user per-client id, used only for app list launch recording. This
+  // is generated by taking the first 8 bytes of the MD5 hash of
+  // a GUID from base::GeneratedGUID.
+  optional fixed64 recurrence_ranker_user_id = 1;
+
+  // The user's secret, used for hashing. This should never leave the device.
+  // This is generated by converting a base::UnguessableToken to a string.
+  optional string secret = 2;
+}
diff --git a/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.cc b/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.cc
index aeaa073..a00d1409 100644
--- a/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.cc
+++ b/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.cc
@@ -309,6 +309,10 @@
   time_left_message_label_->SetText(time_left_message);
 }
 
+void PluginVmLauncherView::OnImportCancelled() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
 void PluginVmLauncherView::OnImportFailed() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
diff --git a/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.h b/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.h
index 7a8192ab..eb96e22 100644
--- a/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.h
+++ b/chrome/browser/ui/views/plugin_vm/plugin_vm_launcher_view.h
@@ -45,6 +45,7 @@
   void OnImportProgressUpdated(uint64_t percent_completed,
                                int64_t import_percent_per_sec) override;
   void OnImported() override;
+  void OnImportCancelled() override;
   void OnImportFailed() override;
 
   // Public for testing purposes.
diff --git a/chrome/browser/ui/webui/management_ui_handler.cc b/chrome/browser/ui/webui/management_ui_handler.cc
index 3e284c8..7901fd8e 100644
--- a/chrome/browser/ui/webui/management_ui_handler.cc
+++ b/chrome/browser/ui/webui/management_ui_handler.cc
@@ -26,10 +26,14 @@
 #include "chrome/common/pref_names.h"
 
 #include "components/strings/grit/components_strings.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/buildflags/buildflags.h"
 #include "google_apis/gaia/gaia_auth_util.h"
+#include "net/base/load_flags.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/webui/web_ui_util.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
@@ -122,6 +126,7 @@
 const char kAccountManagedInfo[] = "accountManagedInfo";
 const char kSetup[] = "setup";
 const char kData[] = "data";
+const char kCustomerLogo[] = "customerLogo";
 
 namespace {
 
@@ -483,7 +488,7 @@
 }
 
 base::DictionaryValue ManagementUIHandler::GetContextualManagedData(
-    Profile* profile) const {
+    Profile* profile) {
   base::DictionaryValue response;
 #if defined(OS_CHROMEOS)
   std::string management_domain = GetDeviceDomain();
@@ -549,6 +554,9 @@
   }
   response.SetBoolean("managed", managed_());
   GetManagementStatus(profile, &response);
+  AsyncUpdateLogo();
+  if (!fetched_image_.empty())
+    response.SetKey(kCustomerLogo, base::Value(fetched_image_));
   return response;
 }
 
@@ -566,6 +574,57 @@
 }
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
+void ManagementUIHandler::AsyncUpdateLogo() {
+#if defined(OS_CHROMEOS)
+  policy::BrowserPolicyConnectorChromeOS* connector =
+      g_browser_process->platform_part()->browser_policy_connector_chromeos();
+  const auto url = connector->GetCustomerLogoURL();
+  if (!url.empty() && GURL(url) != logo_url_) {
+    net::NetworkTrafficAnnotationTag traffic_annotation =
+        net::DefineNetworkTrafficAnnotation("management_ui_customer_logo", R"(
+          semantics {
+            sender: "Management UI Handler"
+            description:
+              "Download organization logo for visualization on the "
+              "chrome://management page."
+            trigger:
+              "The user managed by organization that provides a company logo "
+              "in their GSuites account loads the chrome://management page."
+            data:
+              "Organization uploaded image URL."
+            destination: GOOGLE_OWNED_SERVICE
+          }
+          policy {
+            cookies_allowed: NO
+            setting:
+              "This feature cannot be disabled by settings, but it is only "
+              "triggered by a user action."
+            policy_exception_justification: "Not implemented."
+          })");
+    icon_fetcher_ =
+        std::make_unique<BitmapFetcher>(GURL(url), this, traffic_annotation);
+    icon_fetcher_->Init(
+        std::string(), net::URLRequest::NEVER_CLEAR_REFERRER,
+        net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES);
+    auto* profile = Profile::FromWebUI(web_ui());
+    icon_fetcher_->Start(
+        content::BrowserContext::GetDefaultStoragePartition(profile)
+            ->GetURLLoaderFactoryForBrowserProcess()
+            .get());
+  }
+#endif  // defined(OS_CHROMEOS)
+}
+
+void ManagementUIHandler::OnFetchComplete(const GURL& url,
+                                          const SkBitmap* bitmap) {
+  if (!bitmap)
+    return;
+  fetched_image_ = webui::GetBitmapDataUrl(*bitmap);
+  logo_url_ = url;
+  // Fire listener to reload managed data.
+  FireWebUIListener("managed_data_changed");
+}
+
 void AddStatusAccountManagedInfo(base::Value* status,
                                  const std::string& account_domain) {
   base::Value info(base::Value::Type::DICTIONARY);
@@ -668,7 +727,6 @@
                                   IDS_MANAGEMENT_DEVICE_NOT_MANAGED)));
     return;
   }
-
   std::string account_domain = GetAccountDomain(profile);
   auto* primary_user = user_manager::UserManager::Get()->GetPrimaryUser();
   auto* primary_profile =
@@ -799,7 +857,7 @@
 #endif  // defined(OS_CHROMEOS)
 
   if (managed_state_changed)
-    FireWebUIListener("managed_state_changed");
+    FireWebUIListener("managed_data_changed");
 }
 
 void ManagementUIHandler::OnPolicyUpdated(
diff --git a/chrome/browser/ui/webui/management_ui_handler.h b/chrome/browser/ui/webui/management_ui_handler.h
index 27d6edc..2247b4b 100644
--- a/chrome/browser/ui/webui/management_ui_handler.h
+++ b/chrome/browser/ui/webui/management_ui_handler.h
@@ -12,12 +12,14 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
 #include "chrome/common/url_constants.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/browser/web_ui_message_handler.h"
 #include "extensions/buildflags/buildflags.h"
+#include "url/gurl.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "components/policy/core/common/policy_service.h"
@@ -84,9 +86,11 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 class ManagementUIHandler : public content::WebUIMessageHandler,
                             public extensions::ExtensionRegistryObserver,
-                            public policy::PolicyService::Observer {
+                            public policy::PolicyService::Observer,
+                            public BitmapFetcherDelegate {
 #else
-class ManagementUIHandler : public content::WebUIMessageHandler {
+class ManagementUIHandler : public content::WebUIMessageHandler,
+                            public BitmapFetcherDelegate {
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
  public:
   ManagementUIHandler();
@@ -116,7 +120,7 @@
                                  Profile* profile);
   void AddExtensionReportingInfo(base::Value* report_sources);
 
-  base::DictionaryValue GetContextualManagedData(Profile* profile) const;
+  base::DictionaryValue GetContextualManagedData(Profile* profile);
   virtual policy::PolicyService* GetPolicyService() const;
   virtual const extensions::Extension* GetEnabledExtension(
       const std::string& extensionId) const;
@@ -142,6 +146,11 @@
   void HandleGetContextualManagedData(const base::ListValue* args);
   void HandleInitBrowserReportingInfo(const base::ListValue* args);
 
+  void AsyncUpdateLogo();
+
+  // BitmapFetcherDelegate
+  void OnFetchComplete(const GURL& url, const SkBitmap* bitmap) override;
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   void NotifyBrowserReportingInfoUpdated();
 
@@ -174,6 +183,9 @@
 
   std::set<extensions::ExtensionId> reporting_extension_ids_;
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
+  GURL logo_url_;
+  std::string fetched_image_;
+  std::unique_ptr<BitmapFetcher> icon_fetcher_;
 
   DISALLOW_COPY_AND_ASSIGN(ManagementUIHandler);
 };
diff --git a/chrome/browser/ui/webui/management_ui_handler_unittest.cc b/chrome/browser/ui/webui/management_ui_handler_unittest.cc
index ee7a380..83ed53f7 100644
--- a/chrome/browser/ui/webui/management_ui_handler_unittest.cc
+++ b/chrome/browser/ui/webui/management_ui_handler_unittest.cc
@@ -60,8 +60,7 @@
     cloud_reporting_extension_exists_ = enable;
   }
 
-  base::DictionaryValue GetContextualManagedDataForTesting(
-      Profile* profile) const {
+  base::DictionaryValue GetContextualManagedDataForTesting(Profile* profile) {
     return GetContextualManagedData(profile);
   }
 
diff --git a/chrome/common/extensions/image_writer/image_writer_util_mac.cc b/chrome/common/extensions/image_writer/image_writer_util_mac.cc
index 484a29b..62432ee 100644
--- a/chrome/common/extensions/image_writer/image_writer_util_mac.cc
+++ b/chrome/common/extensions/image_writer/image_writer_util_mac.cc
@@ -89,10 +89,9 @@
     CFNumberRef cf_media_size = base::mac::GetValueFromDictionary<CFNumberRef>(
         dict, CFSTR(kIOMediaSizeKey));
     if (cf_media_size)
-      CFNumberGetValue(cf_media_size, kCFNumberLongLongType,
-                       &out_size_in_bytes);
+      CFNumberGetValue(cf_media_size, kCFNumberLongLongType, out_size_in_bytes);
     else
-      out_size_in_bytes = 0;
+      *out_size_in_bytes = 0;
   }
 
   if (out_bsd_name) {
diff --git a/chrome/renderer/autofill/form_autocomplete_browsertest.cc b/chrome/renderer/autofill/form_autocomplete_browsertest.cc
index ae1ebfb..14ae59c 100644
--- a/chrome/renderer/autofill/form_autocomplete_browsertest.cc
+++ b/chrome/renderer/autofill/form_autocomplete_browsertest.cc
@@ -352,7 +352,7 @@
 // compare field data within the forms.
 // TODO(kolos) Re-enable when the implementation of IsFormVisible is on-par
 // for these platforms.
-#if defined(OS_MACOSX) || defined(OS_ANDROID)
+#if defined(OS_MACOSX)
 #define MAYBE_NoLongerVisibleBothNoActions DISABLED_NoLongerVisibleBothNoActions
 #else
 #define MAYBE_NoLongerVisibleBothNoActions NoLongerVisibleBothNoActions
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 2d8e91e..0cdeafd 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4792,6 +4792,7 @@
       "../browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc",
       "../browser/ui/app_list/search/launcher_search/launcher_search_icon_image_loader_unittest.cc",
       "../browser/ui/app_list/search/search_result_ranker/app_launch_predictor_unittest.cc",
+      "../browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc",
       "../browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc",
       "../browser/ui/app_list/search/search_result_ranker/frecency_store_unittest.cc",
       "../browser/ui/app_list/search/search_result_ranker/ranking_item_util_unittest.cc",
diff --git a/chrome/test/data/chromeos/file_manager/plaintext b/chrome/test/data/chromeos/file_manager/plaintext
deleted file mode 100644
index 5368ac1d..0000000
--- a/chrome/test/data/chromeos/file_manager/plaintext
+++ /dev/null
@@ -1 +0,0 @@
-From somewhere a long time ago.
diff --git a/chrome/test/data/webrtc/peerconnection_getstats.js b/chrome/test/data/webrtc/peerconnection_getstats.js
index 881b179..1cd3926b1 100644
--- a/chrome/test/data/webrtc/peerconnection_getstats.js
+++ b/chrome/test/data/webrtc/peerconnection_getstats.js
@@ -145,7 +145,9 @@
   roundTripTime: 'number',
   fractionLost: 'number',
 });
-// TODO(hbos): When remote-inbound-rtp is implemented, make presence MANDATORY.
+// TODO(https://crbug.com/967382): Update the browser_tests to wait for the
+// existence of remote-inbound-rtp as well (these are created later than
+// outbound-rtp). When this is done, change presence to MANDATORY.
 addRTCStatsToWhitelist(
     Presence.OPTIONAL, 'remote-inbound-rtp', kRTCRemoteInboundRtpStreamStats);
 
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index e52e7c14..e9a9e58 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -94,9 +94,10 @@
 const base::Feature kInstantTethering{"InstantTethering",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Uses the new 20190221 Smart Dim model instead of the default 20181115 model.
-const base::Feature kSmartDim20190221{"SmartDim20190221",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
+// Uses the V3 (~2019-05 era) Smart Dim model instead of the default V2
+// (~2018-11) model.
+const base::Feature kSmartDimModelV3{"SmartDimModelV3",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Splits OS settings (display, mouse, keyboard, etc.) out from browser settings
 // into a separate window.
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index a76dc47..6f77a64b 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -50,7 +50,7 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kInstantTethering;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-extern const base::Feature kSmartDim20190221;
+extern const base::Feature kSmartDimModelV3;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kSplitSettings;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
diff --git a/chromeos/dbus/concierge_client.cc b/chromeos/dbus/concierge_client.cc
index d482f2e..e46fc2c 100644
--- a/chromeos/dbus/concierge_client.cc
+++ b/chromeos/dbus/concierge_client.cc
@@ -227,6 +227,27 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
+  void GetVmInfo(const vm_tools::concierge::GetVmInfoRequest& request,
+                 DBusMethodCallback<vm_tools::concierge::GetVmInfoResponse>
+                     callback) override {
+    dbus::MethodCall method_call(vm_tools::concierge::kVmConciergeInterface,
+                                 vm_tools::concierge::kGetVmInfoMethod);
+    dbus::MessageWriter writer(&method_call);
+
+    if (!writer.AppendProtoAsArrayOfBytes(request)) {
+      LOG(ERROR) << "Failed to encode GetVmInfoRequest protobuf";
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+      return;
+    }
+
+    concierge_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&ConciergeClientImpl::OnDBusProtoResponse<
+                           vm_tools::concierge::GetVmInfoResponse>,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void WaitForServiceToBeAvailable(
       dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback)
       override {
diff --git a/chromeos/dbus/concierge_client.h b/chromeos/dbus/concierge_client.h
index f6366f7..9de98e1 100644
--- a/chromeos/dbus/concierge_client.h
+++ b/chromeos/dbus/concierge_client.h
@@ -122,6 +122,12 @@
       const vm_tools::concierge::StopVmRequest& request,
       DBusMethodCallback<vm_tools::concierge::StopVmResponse> callback) = 0;
 
+  // Get VM Info.
+  // |callback| is called after the method call finishes.
+  virtual void GetVmInfo(
+      const vm_tools::concierge::GetVmInfoRequest& request,
+      DBusMethodCallback<vm_tools::concierge::GetVmInfoResponse> callback) = 0;
+
   // Registers |callback| to run when the Concierge service becomes available.
   // If the service is already available, or if connecting to the name-owner-
   // changed signal fails, |callback| will be run once asynchronously.
diff --git a/chromeos/dbus/fake_concierge_client.cc b/chromeos/dbus/fake_concierge_client.cc
index 18517578..4b03e09 100644
--- a/chromeos/dbus/fake_concierge_client.cc
+++ b/chromeos/dbus/fake_concierge_client.cc
@@ -75,6 +75,8 @@
 void FakeConciergeClient::CancelDiskImageOperation(
     const vm_tools::concierge::CancelDiskImageRequest& request,
     DBusMethodCallback<vm_tools::concierge::CancelDiskImageResponse> callback) {
+  // Removes signals sent during disk image import.
+  disk_image_status_signals_.clear();
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(std::move(callback), cancel_disk_image_response_));
@@ -152,6 +154,14 @@
       FROM_HERE, base::BindOnce(std::move(callback), stop_vm_response_));
 }
 
+void FakeConciergeClient::GetVmInfo(
+    const vm_tools::concierge::GetVmInfoRequest& request,
+    DBusMethodCallback<vm_tools::concierge::GetVmInfoResponse> callback) {
+  get_vm_info_called_ = true;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), get_vm_info_response_));
+}
+
 void FakeConciergeClient::WaitForServiceToBeAvailable(
     dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
@@ -218,6 +228,10 @@
   stop_vm_response_.Clear();
   stop_vm_response_.set_success(true);
 
+  get_vm_info_response_.Clear();
+  get_vm_info_response_.set_success(true);
+  get_vm_info_response_.mutable_vm_info()->set_seneschal_server_handle(1);
+
   container_ssh_keys_response_.Clear();
   container_ssh_keys_response_.set_container_public_key("pubkey");
   container_ssh_keys_response_.set_host_private_key("privkey");
diff --git a/chromeos/dbus/fake_concierge_client.h b/chromeos/dbus/fake_concierge_client.h
index cfc0354..71169415 100644
--- a/chromeos/dbus/fake_concierge_client.h
+++ b/chromeos/dbus/fake_concierge_client.h
@@ -96,6 +96,12 @@
               DBusMethodCallback<vm_tools::concierge::StopVmResponse> callback)
       override;
 
+  // Fake version of the method that gets VM info. Sets get_vm_info_called_.
+  // |callback| is called after the method call finishes.
+  void GetVmInfo(const vm_tools::concierge::GetVmInfoRequest& request,
+                 DBusMethodCallback<vm_tools::concierge::GetVmInfoResponse>
+                     callback) override;
+
   // Fake version of the method that waits for the Concierge service to be
   // availble.  |callback| is called after the method call finishes.
   void WaitForServiceToBeAvailable(
@@ -143,6 +149,8 @@
   bool start_termina_vm_called() const { return start_termina_vm_called_; }
   // Indicates whether StopVm has been called
   bool stop_vm_called() const { return stop_vm_called_; }
+  // Indicates whether GetVmInfo has been called
+  bool get_vm_info_called() const { return get_vm_info_called_; }
   // Indicates whether GetContainerSshKeys has been called
   bool get_container_ssh_keys_called() const {
     return get_container_ssh_keys_called_;
@@ -198,6 +206,10 @@
       const vm_tools::concierge::StopVmResponse& stop_vm_response) {
     stop_vm_response_ = stop_vm_response;
   }
+  void set_get_vm_info_response(
+      const vm_tools::concierge::GetVmInfoResponse& get_vm_info_response) {
+    get_vm_info_response_ = get_vm_info_response;
+  }
   void set_container_ssh_keys_response(
       const vm_tools::concierge::ContainerSshKeysResponse&
           container_ssh_keys_response) {
@@ -249,6 +261,7 @@
   bool list_vm_disks_called_ = false;
   bool start_termina_vm_called_ = false;
   bool stop_vm_called_ = false;
+  bool get_vm_info_called_ = false;
   bool get_container_ssh_keys_called_ = false;
   bool attach_usb_device_called_ = false;
   bool detach_usb_device_called_ = false;
@@ -264,6 +277,7 @@
   vm_tools::concierge::ListVmDisksResponse list_vm_disks_response_;
   vm_tools::concierge::StartVmResponse start_vm_response_;
   vm_tools::concierge::StopVmResponse stop_vm_response_;
+  vm_tools::concierge::GetVmInfoResponse get_vm_info_response_;
   vm_tools::concierge::ContainerSshKeysResponse container_ssh_keys_response_;
   vm_tools::concierge::AttachUsbDeviceResponse attach_usb_device_response_;
   vm_tools::concierge::DetachUsbDeviceResponse detach_usb_device_response_;
diff --git a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
index 1cbcbadc..2437df1 100644
--- a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
+++ b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
@@ -443,7 +443,6 @@
           mojom::VideoDecodeAccelerator::Result::INVALID_ARGUMENT);
       return;
     }
-    LOG(ERROR) << handle_size;
     shm_handle = base::SharedMemoryHandle(
         base::FileDescriptor(handle_fd.release(), true), handle_size,
         base::UnguessableToken::Create());
@@ -462,6 +461,12 @@
                                              bitstream_buffer->bytes_used,
                                              bitstream_buffer->offset)),
        PendingCallback()});
+
+  // Close |shm_handle| because it is actually duplicated on the ctor of
+  // media::BitstreamBuffer and it will not close itself on the dtor.
+  if (shm_handle.IsValid()) {
+    shm_handle.Close();
+  }
 }
 
 void GpuArcVideoDecodeAccelerator::AssignPictureBuffers(uint32_t count) {
diff --git a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
index 10cd4e7d..7dfb9b31 100644
--- a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
+++ b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
@@ -233,6 +233,12 @@
       media::BitstreamBuffer(bitstream_buffer_serial_, shm_handle,
                              false /* read_only */, size, offset));
 
+  // Close |shm_handle| because it is actually duplicated on the ctor of
+  // media::BitstreamBuffer and it will not close itself on the dtor.
+  if (shm_handle.IsValid()) {
+    shm_handle.Close();
+  }
+
   // Mask against 30 bits to avoid (undefined) wraparound on signed integer.
   bitstream_buffer_serial_ = (bitstream_buffer_serial_ + 1) & 0x3FFFFFFF;
 }
diff --git a/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc b/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc
index c415ccf..c907e80 100644
--- a/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc
+++ b/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc
@@ -74,11 +74,15 @@
 
 bool AutofillWalletModelTypeController::ReadyForStart() const {
   DCHECK(CalledOnValidThread());
+  // Not being in a persistent error state implies not being in a web signout
+  // state.
+  // TODO(https://crbug.com/819729): Add integration tests for web signout and
+  // other persistent auth errors.
   return pref_service_->GetBoolean(
              autofill::prefs::kAutofillWalletImportEnabled) &&
          pref_service_->GetBoolean(
              autofill::prefs::kAutofillCreditCardEnabled) &&
-         !syncer::IsWebSignout(sync_service_->GetAuthError());
+         !sync_service_->GetAuthError().IsPersistentError();
 }
 
 void AutofillWalletModelTypeController::OnUserPrefChanged() {
diff --git a/components/autofill/ios/browser/BUILD.gn b/components/autofill/ios/browser/BUILD.gn
index 688e561..f6a9a04 100644
--- a/components/autofill/ios/browser/BUILD.gn
+++ b/components/autofill/ios/browser/BUILD.gn
@@ -42,6 +42,8 @@
     "//google_apis",
     "//ios/web/common",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
+    "//ios/web/public/security",
     "//services/network/public/cpp",
     "//ui/gfx/geometry",
   ]
@@ -68,6 +70,7 @@
     "//components/autofill/core/browser",
     "//components/leveldb_proto:leveldb_proto",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
   ]
 }
 
@@ -85,6 +88,7 @@
     "//components/leveldb_proto:leveldb_proto",
     "//components/prefs",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//testing/gmock",
diff --git a/components/autofill/ios/browser/autofill_agent.mm b/components/autofill/ios/browser/autofill_agent.mm
index 6689e89..91e6bb5 100644
--- a/components/autofill/ios/browser/autofill_agent.mm
+++ b/components/autofill/ios/browser/autofill_agent.mm
@@ -45,13 +45,13 @@
 #import "components/prefs/ios/pref_observer_bridge.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #include "ios/web/public/url_scheme_util.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 #import "ios/web/public/web_state/navigation_context.h"
 #include "ios/web/public/web_state/url_verification_constants.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/components/autofill/ios/browser/autofill_agent_unittests.mm b/components/autofill/ios/browser/autofill_agent_unittests.mm
index 30f34de..864c9f4d1 100644
--- a/components/autofill/ios/browser/autofill_agent_unittests.mm
+++ b/components/autofill/ios/browser/autofill_agent_unittests.mm
@@ -17,12 +17,12 @@
 #include "components/autofill/ios/browser/autofill_driver_ios.h"
 #import "components/autofill/ios/browser/js_autofill_manager.h"
 #include "components/prefs/pref_service.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #include "ios/web/public/test/fakes/fake_web_frame.h"
 #include "ios/web/public/test/fakes/test_browser_state.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
-#include "ios/web/public/web_state/web_frame_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index 686120ae..e881464 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -10,7 +10,7 @@
 #include "components/autofill/ios/browser/autofill_driver_ios_webframe.h"
 #import "ios/web/common/origin_util.h"
 #include "ios/web/public/browser_state.h"
-#import "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
diff --git a/components/autofill/ios/browser/autofill_driver_ios_webframe.h b/components/autofill/ios/browser/autofill_driver_ios_webframe.h
index 005468fb..6e1c543 100644
--- a/components/autofill/ios/browser/autofill_driver_ios_webframe.h
+++ b/components/autofill/ios/browser/autofill_driver_ios_webframe.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_WEBFRAME_H_
 
 #include "components/autofill/ios/browser/autofill_driver_ios.h"
-#include "ios/web/public/web_state/web_frame_user_data.h"
+#include "ios/web/public/js_messaging/web_frame_user_data.h"
 #include "ios/web/public/web_state/web_state_user_data.h"
 
 namespace web {
diff --git a/components/autofill/ios/browser/autofill_util.h b/components/autofill/ios/browser/autofill_util.h
index 260aa73..9e427ea 100644
--- a/components/autofill/ios/browser/autofill_util.h
+++ b/components/autofill/ios/browser/autofill_util.h
@@ -7,7 +7,7 @@
 
 #include <vector>
 
-#import "ios/web/public/web_state/web_frame.h"
+#import "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/web_state/web_state.h"
 
 class GURL;
diff --git a/components/autofill/ios/browser/fake_js_autofill_manager.mm b/components/autofill/ios/browser/fake_js_autofill_manager.mm
index 775a5a9..8bfb862 100644
--- a/components/autofill/ios/browser/fake_js_autofill_manager.mm
+++ b/components/autofill/ios/browser/fake_js_autofill_manager.mm
@@ -7,7 +7,7 @@
 #include "base/bind.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/task/post_task.h"
-#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame.h"
 #include "ios/web/public/web_task_traits.h"
 #include "ios/web/public/web_thread.h"
 
diff --git a/components/autofill/ios/browser/js_autofill_manager.mm b/components/autofill/ios/browser/js_autofill_manager.mm
index a75c7ef..c81f80ff9 100644
--- a/components/autofill/ios/browser/js_autofill_manager.mm
+++ b/components/autofill/ios/browser/js_autofill_manager.mm
@@ -20,7 +20,7 @@
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/ios/browser/autofill_switches.h"
 #import "components/autofill/ios/browser/autofill_util.h"
-#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/components/autofill/ios/browser/js_suggestion_manager.mm b/components/autofill/ios/browser/js_suggestion_manager.mm
index b1af40d..40abf79d 100644
--- a/components/autofill/ios/browser/js_suggestion_manager.mm
+++ b/components/autofill/ios/browser/js_suggestion_manager.mm
@@ -14,9 +14,9 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/values.h"
 #import "components/autofill/ios/browser/autofill_util.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frames_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/components/autofill/ios/form_util/BUILD.gn b/components/autofill/ios/form_util/BUILD.gn
index 708b34b..20b699fc 100644
--- a/components/autofill/ios/form_util/BUILD.gn
+++ b/components/autofill/ios/form_util/BUILD.gn
@@ -19,6 +19,7 @@
   deps = [
     "//base",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
   ]
 }
 
@@ -54,6 +55,7 @@
     ":test_support",
     "//base",
     "//base/test:test_support",
+    "//ios/web/public/js_messaging",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//ios/web/web_state/js",
diff --git a/components/autofill/ios/form_util/form_activity_tab_helper.mm b/components/autofill/ios/form_util/form_activity_tab_helper.mm
index bf8bc033..1b2400f5 100644
--- a/components/autofill/ios/form_util/form_activity_tab_helper.mm
+++ b/components/autofill/ios/form_util/form_activity_tab_helper.mm
@@ -10,7 +10,7 @@
 #include "base/values.h"
 #include "components/autofill/ios/form_util/form_activity_observer.h"
 #include "components/autofill/ios/form_util/form_activity_params.h"
-#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm b/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
index e3d8b1d8..aca9c6a4 100644
--- a/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
+++ b/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
@@ -7,14 +7,14 @@
 #import "base/test/ios/wait_util.h"
 #import "components/autofill/ios/form_util/form_activity_observer.h"
 #import "components/autofill/ios/form_util/test_form_activity_observer.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/fakes/test_web_client.h"
 #import "ios/web/public/test/fakes/test_web_state_observer_util.h"
 #import "ios/web/public/test/js_test_util.h"
 #import "ios/web/public/test/web_js_test.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #include "testing/platform_test.h"
 
 class FormTestClient : public web::TestWebClient {
diff --git a/components/autofill_assistant/browser/actions/prompt_action.cc b/components/autofill_assistant/browser/actions/prompt_action.cc
index f79e046..ac5795e 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action.cc
@@ -113,7 +113,8 @@
     auto& choice_proto = proto_.prompt().choices(i);
     // Don't show choices with no names, icon or types; they're likely just
     // there for auto_select_if_element_exists.
-    if (choice_proto.name().empty() && choice_proto.chip_icon() == NO_ICON &&
+    if (!choice_proto.has_chip() && choice_proto.name().empty() &&
+        choice_proto.chip_icon() == NO_ICON &&
         choice_proto.chip_type() == UNKNOWN_CHIP_TYPE)
       continue;
 
@@ -121,12 +122,16 @@
     if (!precondition_results_[i] && !choice_proto.allow_disabling())
       continue;
 
-    chips->emplace_back();
-    Chip& chip = chips->back();
-    chip.text = choice_proto.name();
-    chip.type = choice_proto.chip_type();
-    chip.icon = choice_proto.chip_icon();
-    chip.disabled = !precondition_results_[i];
+    if (choice_proto.has_chip()) {
+      chips->emplace_back(choice_proto.chip());
+    } else {
+      chips->emplace_back();
+      chips->back().text = choice_proto.name();
+      chips->back().type = choice_proto.chip_type();
+      chips->back().icon = choice_proto.chip_icon();
+    }
+
+    chips->back().disabled = !precondition_results_[i];
     chips->back().callback = base::BindOnce(&PromptAction::OnSuggestionChosen,
                                             weak_ptr_factory_.GetWeakPtr(), i);
   }
diff --git a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
index 5f1902ba..2bb8a5e 100644
--- a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
@@ -73,8 +73,9 @@
 
 TEST_F(PromptActionTest, SelectButtons) {
   auto* ok_proto = prompt_proto_->add_choices();
-  ok_proto->set_name("Ok");
-  ok_proto->set_chip_type(HIGHLIGHTED_ACTION);
+  auto* chip = ok_proto->mutable_chip();
+  chip->set_text("Ok");
+  chip->set_type(HIGHLIGHTED_ACTION);
   ok_proto->set_server_payload("ok");
 
   auto* cancel_proto = prompt_proto_->add_choices();
diff --git a/components/autofill_assistant/browser/chip.cc b/components/autofill_assistant/browser/chip.cc
index 038b844..e336fe16 100644
--- a/components/autofill_assistant/browser/chip.cc
+++ b/components/autofill_assistant/browser/chip.cc
@@ -14,7 +14,8 @@
 Chip::Chip(const ChipProto& chip_proto)
     : type(chip_proto.type()),
       icon(chip_proto.icon()),
-      text(chip_proto.text()) {}
+      text(chip_proto.text()),
+      sticky(chip_proto.sticky()) {}
 
 void SetDefaultChipType(std::vector<Chip>* chips) {
   ChipType default_type = SUGGESTION;
diff --git a/components/autofill_assistant/browser/chip.h b/components/autofill_assistant/browser/chip.h
index 66be0ad..9e3352e 100644
--- a/components/autofill_assistant/browser/chip.h
+++ b/components/autofill_assistant/browser/chip.h
@@ -33,6 +33,10 @@
 
   // Whether this chip is disabled.
   bool disabled = false;
+
+  // Whether this chip is sticky. A sticky chip will be a candidate to be
+  // displayed in the header if the peek mode of the sheet is HANDLE_HEADER.
+  bool sticky = false;
 };
 
 // Guarantees that the Chip.type of all chips is set to a sensible value.
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index dd68f79..273dfd8 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -1000,12 +1000,9 @@
   // Update the set of scripts in the UI.
   auto chips = std::make_unique<std::vector<Chip>>();
   for (const auto& script : runnable_scripts) {
-    if (!script.autostart &&
-        (!script.name.empty() || script.chip_icon != ChipIcon::NO_ICON)) {
-      chips->emplace_back();
-      chips->back().text = script.name;
-      chips->back().type = script.chip_type;
-      chips->back().icon = script.chip_icon;
+    if (!script.autostart && (!script.chip.text().empty() ||
+                              script.chip.icon() != ChipIcon::NO_ICON)) {
+      chips->emplace_back(script.chip);
       chips->back().callback =
           base::BindOnce(&Controller::OnScriptSelected,
                          weak_ptr_factory_.GetWeakPtr(), script.path);
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index 6622dc20..16c45690 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -87,19 +87,25 @@
   script->handle.path = script_proto.path();
 
   const auto& presentation = script_proto.presentation();
-  script->handle.name = presentation.name();
   script->handle.autostart = presentation.autostart();
   script->handle.interrupt = presentation.interrupt();
   script->handle.initial_prompt = presentation.initial_prompt();
-  script->handle.chip_type = presentation.chip_type();
-  script->handle.chip_icon = presentation.chip_icon();
+
+  if (presentation.has_chip()) {
+    script->handle.chip = presentation.chip();
+  } else {
+    script->handle.chip.set_text(presentation.name());
+    script->handle.chip.set_type(presentation.chip_type());
+    script->handle.chip.set_icon(presentation.chip_icon());
+  }
+
   script->precondition = ScriptPrecondition::FromProto(
       script_proto.path(), presentation.precondition());
   script->priority = presentation.priority();
 
   if (script->handle.path.empty() || !script->precondition ||
-      (script->handle.name.empty() &&
-       script->handle.chip_icon == ChipIcon::NO_ICON &&
+      (script->handle.chip.text().empty() &&
+       script->handle.chip.icon() == ChipIcon::NO_ICON &&
        !script->handle.interrupt)) {
     return;
   }
diff --git a/components/autofill_assistant/browser/protocol_utils_unittest.cc b/components/autofill_assistant/browser/protocol_utils_unittest.cc
index d81a161a..ff9899fa 100644
--- a/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -55,7 +55,7 @@
 
   ASSERT_THAT(scripts, SizeIs(1));
   EXPECT_EQ("path", scripts[0]->handle.path);
-  EXPECT_EQ("name", scripts[0]->handle.name);
+  EXPECT_EQ("name", scripts[0]->handle.chip.text());
   EXPECT_NE(nullptr, scripts[0]->precondition);
 }
 
@@ -73,7 +73,7 @@
 
   ASSERT_THAT(scripts, SizeIs(1));
   EXPECT_EQ("path", scripts[0]->handle.path);
-  EXPECT_EQ("name", scripts[0]->handle.name);
+  EXPECT_EQ("name", scripts[0]->handle.chip.text());
   EXPECT_EQ("prompt", scripts[0]->handle.initial_prompt);
   EXPECT_TRUE(scripts[0]->handle.autostart);
   EXPECT_NE(nullptr, scripts[0]->precondition);
@@ -92,7 +92,7 @@
   ProtocolUtils::AddScript(script_proto, &scripts);
   ASSERT_THAT(scripts, SizeIs(1));
   EXPECT_EQ("path", scripts[0]->handle.path);
-  EXPECT_EQ("", scripts[0]->handle.name);
+  EXPECT_EQ("", scripts[0]->handle.chip.text());
   EXPECT_TRUE(scripts[0]->handle.interrupt);
 }
 
@@ -186,7 +186,7 @@
 
   EXPECT_NE(nullptr, script);
   EXPECT_EQ("path", script->handle.path);
-  EXPECT_EQ("name", script->handle.name);
+  EXPECT_EQ("name", script->handle.chip.text());
   EXPECT_EQ("prompt", script->handle.initial_prompt);
   EXPECT_TRUE(script->handle.autostart);
   EXPECT_NE(nullptr, script->precondition);
@@ -268,7 +268,7 @@
   EXPECT_TRUE(should_update_scripts);
   EXPECT_THAT(scripts, SizeIs(1));
   EXPECT_THAT("a", Eq(scripts[0]->handle.path));
-  EXPECT_THAT("name", Eq(scripts[0]->handle.name));
+  EXPECT_THAT("name", Eq(scripts[0]->handle.chip.text()));
 }
 
 }  // namespace
diff --git a/components/autofill_assistant/browser/script.h b/components/autofill_assistant/browser/script.h
index 85e14d7..ec76746 100644
--- a/components/autofill_assistant/browser/script.h
+++ b/components/autofill_assistant/browser/script.h
@@ -19,15 +19,13 @@
   ScriptHandle(const ScriptHandle& orig);
   ~ScriptHandle();
 
-  std::string name;
+  ChipProto chip;
   std::string path;
   std::string initial_prompt;
 
   // When set to true this script can be run in 'autostart mode'. Script won't
   // be shown.
   bool autostart;
-  ChipType chip_type;
-  ChipIcon chip_icon;
 
   // If set, the script might be run during WaitForDom actions with
   // allow_interrupt=true.
diff --git a/components/autofill_assistant/browser/script_executor_unittest.cc b/components/autofill_assistant/browser/script_executor_unittest.cc
index 57239a6a..9fae015 100644
--- a/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -846,7 +846,7 @@
   EXPECT_THAT(scripts_update_, SizeIs(1));
   EXPECT_THAT(scripts_update_count_, Eq(1));
   EXPECT_THAT("path", scripts_update_[0]->handle.path);
-  EXPECT_THAT("name", scripts_update_[0]->handle.name);
+  EXPECT_THAT("name", scripts_update_[0]->handle.chip.text());
 }
 
 TEST_F(ScriptExecutorTest, UpdateScriptListShouldNotifyMultipleTimes) {
@@ -921,7 +921,7 @@
   EXPECT_THAT(scripts_update_, SizeIs(1));
   EXPECT_THAT(scripts_update_count_, Eq(1));
   EXPECT_THAT("path", scripts_update_[0]->handle.path);
-  EXPECT_THAT("update_from_interrupt", scripts_update_[0]->handle.name);
+  EXPECT_THAT("update_from_interrupt", scripts_update_[0]->handle.chip.text());
 }
 
 TEST_F(ScriptExecutorTest, RestorePreInterruptStatusMessage) {
diff --git a/components/autofill_assistant/browser/script_tracker.cc b/components/autofill_assistant/browser/script_tracker.cc
index 0253da3..3a62d64 100644
--- a/components/autofill_assistant/browser/script_tracker.cc
+++ b/components/autofill_assistant/browser/script_tracker.cc
@@ -26,8 +26,10 @@
               // Order of scripts with the same priority is arbitrary. Fallback
               // to ordering by name and path, arbitrarily, for the behavior to
               // be consistent across runs.
-              return std::tie(a->priority, a->handle.name, a->handle.path) <
-                     std::tie(b->priority, b->handle.name, a->handle.path);
+              return std::tie(a->priority, a->handle.chip.text(),
+                              a->handle.path) < std::tie(b->priority,
+                                                         b->handle.chip.text(),
+                                                         a->handle.path);
             });
 }
 
@@ -77,8 +79,8 @@
   batch_element_checker_ = std::make_unique<BatchElementChecker>();
   for (const auto& entry : available_scripts_) {
     Script* script = entry.first;
-    if (script->handle.name.empty() &&
-        script->handle.chip_icon == ChipIcon::NO_ICON &&
+    if (script->handle.chip.text().empty() &&
+        script->handle.chip.icon() == ChipIcon::NO_ICON &&
         !script->handle.autostart)
       continue;
 
@@ -156,11 +158,11 @@
   std::vector<base::Value> runnable_scripts_js;
   for (const auto& entry : runnable_scripts_) {
     base::Value script_js = base::Value(base::Value::Type::DICTIONARY);
-    script_js.SetKey("name", base::Value(entry.name));
+    script_js.SetKey("name", base::Value(entry.chip.text()));
     script_js.SetKey("path", base::Value(entry.path));
     script_js.SetKey("initial_prompt", base::Value(entry.initial_prompt));
     script_js.SetKey("autostart", base::Value(entry.autostart));
-    script_js.SetKey("chip_type", base::Value(entry.chip_type));
+    script_js.SetKey("chip_type", base::Value(entry.chip.type()));
     runnable_scripts_js.push_back(std::move(script_js));
   }
   dict.SetKey("runnable-scripts", base::Value(runnable_scripts_js));
diff --git a/components/autofill_assistant/browser/script_tracker_unittest.cc b/components/autofill_assistant/browser/script_tracker_unittest.cc
index 3aae6dfc..1d0d1d5 100644
--- a/components/autofill_assistant/browser/script_tracker_unittest.cc
+++ b/components/autofill_assistant/browser/script_tracker_unittest.cc
@@ -149,7 +149,7 @@
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
-  EXPECT_EQ("runnable name", runnable_scripts()[0].name);
+  EXPECT_EQ("runnable name", runnable_scripts()[0].chip.text());
   EXPECT_EQ("runnable path", runnable_scripts()[0].path);
   EXPECT_EQ(0, no_runnable_scripts_anymore_);
 }
@@ -170,7 +170,7 @@
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
-  EXPECT_EQ("with name", runnable_scripts()[0].name);
+  EXPECT_EQ("with name", runnable_scripts()[0].chip.text());
 }
 
 TEST_F(ScriptTrackerTest, ReportInterruptToAutostart) {
@@ -189,7 +189,7 @@
 TEST_F(ScriptTrackerTest, OrderScriptsByPriority) {
   SupportedScriptProto* a = AddScript();
   a->set_path("a");
-  a->mutable_presentation()->set_name("a");
+  a->mutable_presentation()->mutable_chip()->set_text("a");
   a->mutable_presentation()->set_priority(2);
 
   SupportedScriptProto* b = AddScript();
@@ -303,7 +303,7 @@
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
-  EXPECT_EQ("runnable name", runnable_scripts()[0].name);
+  EXPECT_EQ("runnable name", runnable_scripts()[0].chip.text());
   EXPECT_EQ("runnable path", runnable_scripts()[0].path);
 
   // 2. Run the action and trigger a script list update.
@@ -330,9 +330,9 @@
   // 3. Verify that the runnable scripts have changed to the updated list.
   EXPECT_EQ(2, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(2));
-  EXPECT_EQ("update name", runnable_scripts()[0].name);
+  EXPECT_EQ("update name", runnable_scripts()[0].chip.text());
   EXPECT_EQ("update path", runnable_scripts()[0].path);
-  EXPECT_EQ("update name 2", runnable_scripts()[1].name);
+  EXPECT_EQ("update name 2", runnable_scripts()[1].chip.text());
   EXPECT_EQ("update path 2", runnable_scripts()[1].path);
 }
 
@@ -344,7 +344,7 @@
 
   EXPECT_EQ(1, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(1));
-  EXPECT_EQ("runnable name", runnable_scripts()[0].name);
+  EXPECT_EQ("runnable name", runnable_scripts()[0].chip.text());
   EXPECT_EQ("runnable path", runnable_scripts()[0].path);
 
   // 2. Run the interrupt action and trigger a script list update from an
@@ -372,9 +372,9 @@
   // 3. Verify that the runnable scripts have changed to the updated list.
   EXPECT_EQ(2, runnable_scripts_changed_);
   ASSERT_THAT(runnable_scripts(), SizeIs(2));
-  EXPECT_EQ("update name", runnable_scripts()[0].name);
+  EXPECT_EQ("update name", runnable_scripts()[0].chip.text());
   EXPECT_EQ("update path", runnable_scripts()[0].path);
-  EXPECT_EQ("update name 2", runnable_scripts()[1].name);
+  EXPECT_EQ("update name 2", runnable_scripts()[1].chip.text());
   EXPECT_EQ("update path 2", runnable_scripts()[1].path);
 }
 
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 577e80b..90045b8 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -83,7 +83,8 @@
 
   message PresentationProto {
     // Script name.
-    optional string name = 1;
+    // Deprecated, use chip.text instead.
+    optional string name = 1 [deprecated = true];
 
     // Precondition contains a set of conditions that must hold for a script to
     // be executed. No precondition means that a script can run in any case.
@@ -101,10 +102,16 @@
 
     // Describes the chip to be shown. The name of the chip is set by the
     // name field.
-    optional ChipType chip_type = 10;
+    // Deprecated, use chip.type instead.
+    optional ChipType chip_type = 10 [deprecated = true];
 
     // An icon the should be shown next to the chip text.
-    optional ChipIcon chip_icon = 11;
+    // Deprecated, use chip.icon instead.
+    optional ChipIcon chip_icon = 11 [deprecated = true];
+
+    // The chip to display. This chip has precedence over name, chip_type and
+    // chip_icon. Not required if autostart is true.
+    optional ChipProto chip = 12;
 
     // When set to true this script can be run in 'autostart mode'. Chip won't
     // be shown; name and chip_type are ignored.
@@ -122,11 +129,17 @@
   // The type of the chip.
   optional ChipType type = 1;
 
-  // The icon shown next to the text.
+  // The icon shown on the chip. Not required if text is set.
   optional ChipIcon icon = 2;
 
-  // The text shown on the chip.
+  // The text shown on the chip. Not required if icon is set.
   optional string text = 3;
+
+  // Whether the chip is sticky. When the bottom sheet is configured with the
+  // ConfigureBottomSheetProto::PeekMode::HANDLE_HEADER peek mode and the
+  // sheet is minimized, the first sticky action will be displayed instead of
+  // the profile icon.
+  optional bool sticky = 4;
 }
 
 enum ChipType {
@@ -881,14 +894,21 @@
   message Choice {
     // Localized text message to display. Not required if
     // auto_select_if_element_exists is set.
-    optional string name = 2;
+    // Deprecated, use chip.text instead.
+    optional string name = 2 [deprecated = true];
 
     // Describes the chip to be shown. The name of the chip is set by the
     // name field.
-    optional ChipType chip_type = 7;
+    // Deprecated, use chip.type instead.
+    optional ChipType chip_type = 7 [deprecated = true];
 
     // An icon the should be shown next to the chip text.
-    optional ChipIcon chip_icon = 10;
+    // Deprecated, use chip.icon instead.
+    optional ChipIcon chip_icon = 10 [deprecated = true];
+
+    // The chip to display. This chip has precedence over name, chip_type and
+    // chip_icon. Not required if auto_select_if_element_exists is set.
+    optional ChipProto chip = 11;
 
     // Auto-select this choice if the given element exist.
     optional ElementReferenceProto auto_select_if_element_exists = 4;
diff --git a/components/invalidation/impl/BUILD.gn b/components/invalidation/impl/BUILD.gn
index 821142c4..71acb09 100644
--- a/components/invalidation/impl/BUILD.gn
+++ b/components/invalidation/impl/BUILD.gn
@@ -128,19 +128,6 @@
   }
 }
 
-source_set("json_unsafe_parser") {
-  testonly = !is_ios
-
-  sources = [
-    "json_unsafe_parser.cc",
-    "json_unsafe_parser.h",
-  ]
-
-  public_deps = [
-    "//base",
-  ]
-}
-
 source_set("unit_tests") {
   testonly = true
   sources = [
@@ -155,7 +142,6 @@
   ]
   deps = [
     ":impl",
-    ":json_unsafe_parser",
     ":test_support",
     "//base",
     "//base/test:test_support",
@@ -169,6 +155,7 @@
     "//google_apis/gcm:gcm",
     "//net",
     "//net:test_support",
+    "//services/data_decoder/public/cpp:test_support",
     "//services/identity/public/cpp:test_support",
     "//services/network:test_support",
     "//testing/gmock",
diff --git a/components/invalidation/impl/DEPS b/components/invalidation/impl/DEPS
index dbd6d616..63ce600 100644
--- a/components/invalidation/impl/DEPS
+++ b/components/invalidation/impl/DEPS
@@ -20,6 +20,7 @@
   "+net/url_request",
   "+net/base/load_flags.h",
 
+  "+services/data_decoder/public/cpp/testing_json_parser.h",
   "+services/identity/public",
   "+services/network/public",
   "+services/network/test",
diff --git a/components/invalidation/impl/fcm_invalidation_listener_unittest.cc b/components/invalidation/impl/fcm_invalidation_listener_unittest.cc
index 5defef6..54d1cac4 100644
--- a/components/invalidation/impl/fcm_invalidation_listener_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidation_listener_unittest.cc
@@ -13,7 +13,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/invalidation/impl/fake_invalidation_state_tracker.h"
 #include "components/invalidation/impl/fcm_invalidation_listener.h"
-#include "components/invalidation/impl/json_unsafe_parser.h"
 #include "components/invalidation/impl/per_user_topic_registration_manager.h"
 #include "components/invalidation/impl/push_client_channel.h"
 #include "components/invalidation/impl/unacked_invalidation_set_test_util.h"
@@ -23,6 +22,7 @@
 #include "components/invalidation/public/topic_invalidation_map.h"
 #include "google/cacheinvalidation/include/types.h"
 #include "jingle/notifier/listener/fake_push_client.h"
+#include "services/data_decoder/public/cpp/testing_json_parser.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -164,7 +164,7 @@
             nullptr /* identity_provider */,
             nullptr /* pref_service */,
             nullptr /* loader_factory */,
-            base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
+            base::BindRepeating(&data_decoder::SafeJsonParser::Parse, nullptr),
             "fake_sender_id",
             false) {
     ON_CALL(*this, LookupRegisteredPublicTopicByPrivateTopic)
@@ -285,6 +285,7 @@
 
  private:
   base::test::ScopedTaskEnvironment task_environment_;
+  data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
   FCMSyncNetworkChannel* fcm_sync_network_channel_;
   MockRegistrationManager* registration_manager_;
 
diff --git a/components/invalidation/impl/fcm_invalidation_service_unittest.cc b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
index 106bfd20..b49e4ff 100644
--- a/components/invalidation/impl/fcm_invalidation_service_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
@@ -22,11 +22,11 @@
 #include "components/invalidation/impl/invalidation_service_test_template.h"
 #include "components/invalidation/impl/invalidation_state_tracker.h"
 #include "components/invalidation/impl/invalidator.h"
-#include "components/invalidation/impl/json_unsafe_parser.h"
 #include "components/invalidation/impl/profile_identity_provider.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "services/data_decoder/public/cpp/testing_json_parser.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -133,7 +133,7 @@
     invalidation_service_ = std::make_unique<FCMInvalidationService>(
         identity_provider_.get(), gcm_driver_.get(),
         mock_instance_id_driver_.get(), &pref_service_,
-        base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
+        base::BindRepeating(&data_decoder::SafeJsonParser::Parse, nullptr),
         &url_loader_factory_);
   }
 
@@ -158,6 +158,7 @@
   }
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
   std::unique_ptr<gcm::GCMDriver> gcm_driver_;
   std::unique_ptr<MockInstanceIDDriver> mock_instance_id_driver_;
   std::unique_ptr<MockInstanceID> mock_instance_id_;
diff --git a/components/invalidation/impl/fcm_invalidator_unittest.cc b/components/invalidation/impl/fcm_invalidator_unittest.cc
index 370928aa..1ac9ca92 100644
--- a/components/invalidation/impl/fcm_invalidator_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidator_unittest.cc
@@ -13,13 +13,13 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/invalidation/impl/fake_invalidation_handler.h"
 #include "components/invalidation/impl/invalidator_test_template.h"
-#include "components/invalidation/impl/json_unsafe_parser.h"
 #include "components/invalidation/impl/per_user_topic_registration_manager.h"
 #include "components/invalidation/impl/profile_identity_provider.h"
 #include "components/invalidation/impl/push_client_channel.h"
 #include "components/invalidation/public/topic_invalidation_map.h"
 #include "components/prefs/testing_pref_service.h"
 #include "net/url_request/url_request_test_util.h"
+#include "services/data_decoder/public/cpp/testing_json_parser.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -54,8 +54,8 @@
     invalidator_.reset(new FCMInvalidator(
         std::move(network_channel), identity_provider_.get(), &pref_service_,
         &url_loader_factory_,
-        base::BindRepeating(&syncer::JsonUnsafeParser::Parse), "fake_sender_id",
-        false));
+        base::BindRepeating(&data_decoder::SafeJsonParser::Parse, nullptr),
+        "fake_sender_id", false));
   }
 
   Invalidator* GetInvalidator() { return invalidator_.get(); }
@@ -79,6 +79,7 @@
 
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
   std::unique_ptr<FCMInvalidator> invalidator_;
   identity::IdentityTestEnvironment identity_test_env_;
   std::unique_ptr<invalidation::IdentityProvider> identity_provider_;
diff --git a/components/invalidation/impl/json_unsafe_parser.cc b/components/invalidation/impl/json_unsafe_parser.cc
deleted file mode 100644
index acfa5648..0000000
--- a/components/invalidation/impl/json_unsafe_parser.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2018 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 "components/invalidation/impl/json_unsafe_parser.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/json/json_parser.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/values.h"
-
-namespace syncer {
-
-void JsonUnsafeParser::Parse(const std::string& unsafe_json,
-                             const SuccessCallback& success_callback,
-                             const ErrorCallback& error_callback) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          [](const std::string& unsafe_json,
-             const SuccessCallback& success_callback,
-             const ErrorCallback& error_callback) {
-            base::JSONReader::ValueWithError value_with_error =
-                base::JSONReader::ReadAndReturnValueWithError(
-                    unsafe_json, base::JSON_ALLOW_TRAILING_COMMAS);
-            if (value_with_error.value) {
-              success_callback.Run(std::move(*value_with_error.value));
-            } else {
-              error_callback.Run(base::StringPrintf(
-                  "%s (%d:%d)", value_with_error.error_message.c_str(),
-                  value_with_error.error_line, value_with_error.error_column));
-            }
-          },
-          unsafe_json, success_callback, error_callback));
-}
-
-}  // namespace syncer
diff --git a/components/invalidation/impl/json_unsafe_parser.h b/components/invalidation/impl/json_unsafe_parser.h
deleted file mode 100644
index f7dee9c..0000000
--- a/components/invalidation/impl/json_unsafe_parser.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2018 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 COMPONENTS_INVALIDATION_IMPL_JSON_UNSAFE_PARSER_H_
-#define COMPONENTS_INVALIDATION_IMPL_JSON_UNSAFE_PARSER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/callback_forward.h"
-
-namespace base {
-class Value;
-}
-
-namespace syncer {
-
-// Mimics SafeJsonParser, but parses unsafely.
-//
-// Do not use this class, unless you can't help it. On most platforms,
-// SafeJsonParser is available and is safer. If it is not available (e.g. on
-// iOS), then this class mimics its API without its safety.
-//
-// TODO(https://crbug.com/828833): This code is the duplicate of same code in
-// the ntp component. It should be removed, once appropriate place is found.
-// TODO(https://crbug.com/842655): Flip Callback to OnceCallback once safe
-// parser is refactored.
-class JsonUnsafeParser {
- public:
-  using SuccessCallback = base::RepeatingCallback<void(base::Value)>;
-  using ErrorCallback = base::RepeatingCallback<void(const std::string&)>;
-
-  // As with SafeJsonParser, runs either success_callback or error_callback on
-  // the calling thread, but not before the call returns.
-  static void Parse(const std::string& unsafe_json,
-                    const SuccessCallback& success_callback,
-                    const ErrorCallback& error_callback);
-
-  JsonUnsafeParser() = delete;
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_INVALIDATION_IMPL_JSON_UNSAFE_PARSER_H_
diff --git a/components/invalidation/impl/per_user_topic_registration_manager_unittest.cc b/components/invalidation/impl/per_user_topic_registration_manager_unittest.cc
index e1496ab3..5fe2210 100644
--- a/components/invalidation/impl/per_user_topic_registration_manager_unittest.cc
+++ b/components/invalidation/impl/per_user_topic_registration_manager_unittest.cc
@@ -15,11 +15,11 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/values.h"
 #include "components/invalidation/impl/invalidation_switches.h"
-#include "components/invalidation/impl/json_unsafe_parser.h"
 #include "components/invalidation/impl/profile_identity_provider.h"
 #include "components/invalidation/public/invalidation_util.h"
 #include "components/prefs/testing_pref_service.h"
 #include "net/http/http_status_code.h"
+#include "services/data_decoder/public/cpp/testing_json_parser.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -136,8 +136,8 @@
       bool migrate_prefs = true) {
     auto reg_manager = std::make_unique<PerUserTopicRegistrationManager>(
         identity_provider_.get(), &pref_service_, url_loader_factory(),
-        base::BindRepeating(&syncer::JsonUnsafeParser::Parse), kProjectId,
-        migrate_prefs);
+        base::BindRepeating(&data_decoder::SafeJsonParser::Parse, nullptr),
+        kProjectId, migrate_prefs);
     reg_manager->Init();
     reg_manager->AddObserver(&state_observer_);
     return reg_manager;
@@ -183,6 +183,7 @@
 
  private:
   base::test::ScopedTaskEnvironment task_environment_;
+  data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
   network::TestURLLoaderFactory url_loader_factory_;
   TestingPrefServiceSimple pref_service_;
 
diff --git a/components/invalidation/impl/per_user_topic_registration_request_unittest.cc b/components/invalidation/impl/per_user_topic_registration_request_unittest.cc
index d0973fa..91306258 100644
--- a/components/invalidation/impl/per_user_topic_registration_request_unittest.cc
+++ b/components/invalidation/impl/per_user_topic_registration_request_unittest.cc
@@ -16,9 +16,9 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
-#include "components/invalidation/impl/json_unsafe_parser.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_request_test_util.h"
+#include "services/data_decoder/public/cpp/testing_json_parser.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -75,6 +75,7 @@
 
  private:
   base::test::ScopedTaskEnvironment task_environment_;
+  data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
   network::TestURLLoaderFactory url_loader_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PerUserTopicRegistrationRequestTest);
@@ -101,9 +102,10 @@
           .SetProjectId(project_id)
           .SetType(type)
           .Build();
-  request->Start(callback.Get(),
-                 base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
-                 url_loader_factory());
+  request->Start(
+      callback.Get(),
+      base::BindRepeating(&data_decoder::SafeJsonParser::Parse, nullptr),
+      url_loader_factory());
   base::RunLoop().RunUntilIdle();
 
   // Destroy the request before getting any response.
@@ -145,9 +147,10 @@
   url_loader_factory()->AddResponse(url(request.get()),
                                     CreateHeadersForTest(net::HTTP_OK),
                                     response_body, response_status);
-  request->Start(callback.Get(),
-                 base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
-                 url_loader_factory());
+  request->Start(
+      callback.Get(),
+      base::BindRepeating(&data_decoder::SafeJsonParser::Parse, nullptr),
+      url_loader_factory());
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(status.code, StatusCode::SUCCESS);
@@ -190,9 +193,10 @@
   url_loader_factory()->AddResponse(url(request.get()),
                                     CreateHeadersForTest(net::HTTP_OK),
                                     response_body, response_status);
-  request->Start(callback.Get(),
-                 base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
-                 url_loader_factory());
+  request->Start(
+      callback.Get(),
+      base::BindRepeating(&data_decoder::SafeJsonParser::Parse, nullptr),
+      url_loader_factory());
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(status.code, StatusCode::FAILED);
@@ -233,9 +237,10 @@
   url_loader_factory()->AddResponse(url(request.get()),
                                     CreateHeadersForTest(net::HTTP_OK),
                                     response_body, response_status);
-  request->Start(callback.Get(),
-                 base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
-                 url_loader_factory());
+  request->Start(
+      callback.Get(),
+      base::BindRepeating(&data_decoder::SafeJsonParser::Parse, nullptr),
+      url_loader_factory());
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(status.code, StatusCode::FAILED);
@@ -276,9 +281,10 @@
   url_loader_factory()->AddResponse(url(request.get()),
                                     CreateHeadersForTest(net::HTTP_OK),
                                     response_body, response_status);
-  request->Start(callback.Get(),
-                 base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
-                 url_loader_factory());
+  request->Start(
+      callback.Get(),
+      base::BindRepeating(&data_decoder::SafeJsonParser::Parse, nullptr),
+      url_loader_factory());
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(status.code, StatusCode::SUCCESS);
@@ -325,9 +331,10 @@
   url_loader_factory()->AddResponse(
       url(request.get()), CreateHeadersForTest(GetParam()),
       /* response_body */ std::string(), response_status);
-  request->Start(callback.Get(),
-                 base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
-                 url_loader_factory());
+  request->Start(
+      callback.Get(),
+      base::BindRepeating(&data_decoder::SafeJsonParser::Parse, nullptr),
+      url_loader_factory());
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(status.code, StatusCode::FAILED_NON_RETRIABLE);
diff --git a/components/ntp_tiles/BUILD.gn b/components/ntp_tiles/BUILD.gn
index bda66ad..9ae334e 100644
--- a/components/ntp_tiles/BUILD.gn
+++ b/components/ntp_tiles/BUILD.gn
@@ -70,22 +70,6 @@
   ]
 }
 
-# If you want to use this, let us (ntp-dev@chromium.org) know. In that case, it
-# should be moved to a more common location as it has 2+ callers already.
-# Note that you probably shouldn't be using it outside of ios or tests.
-source_set("json_unsafe_parser") {
-  testonly = !is_ios
-
-  sources = [
-    "json_unsafe_parser.cc",
-    "json_unsafe_parser.h",
-  ]
-
-  public_deps = [
-    "//base",
-  ]
-}
-
 source_set("unit_tests") {
   testonly = true
   sources = [
@@ -98,7 +82,6 @@
   ]
 
   deps = [
-    ":json_unsafe_parser",
     ":ntp_tiles",
     "//base/test:test_support",
     "//components/favicon/core",
@@ -110,6 +93,7 @@
     "//components/rappor:test_support",
     "//components/sync_preferences:test_support",
     "//net:test_support",
+    "//services/data_decoder/public/cpp:test_support",
     "//services/network:test_support",
     "//services/network/public/cpp",
     "//testing/gmock",
diff --git a/components/ntp_tiles/DEPS b/components/ntp_tiles/DEPS
index 3d33b797..00bafbc 100644
--- a/components/ntp_tiles/DEPS
+++ b/components/ntp_tiles/DEPS
@@ -16,6 +16,7 @@
   "+components/variations",
   "+jni",
   "+net",
+  "+services/data_decoder/public/cpp/testing_json_parser.h",
   "+services/network/public/cpp",
   "+services/network/test",
   "+ui/gfx",
diff --git a/components/ntp_tiles/json_unsafe_parser.cc b/components/ntp_tiles/json_unsafe_parser.cc
deleted file mode 100644
index 0399d5a..0000000
--- a/components/ntp_tiles/json_unsafe_parser.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2016 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 "components/ntp_tiles/json_unsafe_parser.h"
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/json/json_parser.h"
-#include "base/strings/stringprintf.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/values.h"
-
-namespace ntp_tiles {
-
-void JsonUnsafeParser::Parse(const std::string& unsafe_json,
-                             const SuccessCallback& success_callback,
-                             const ErrorCallback& error_callback) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          [](const std::string& unsafe_json,
-             const SuccessCallback& success_callback,
-             const ErrorCallback& error_callback) {
-            base::JSONReader::ValueWithError value_with_error =
-                base::JSONReader::ReadAndReturnValueWithError(
-                    unsafe_json, base::JSON_ALLOW_TRAILING_COMMAS);
-            if (value_with_error.value) {
-              success_callback.Run(std::move(*value_with_error.value));
-            } else {
-              error_callback.Run(base::StringPrintf(
-                  "%s (%d:%d)", value_with_error.error_message.c_str(),
-                  value_with_error.error_line, value_with_error.error_column));
-            }
-          },
-          unsafe_json, success_callback, error_callback));
-}
-
-}  // namespace ntp_tiles
diff --git a/components/ntp_tiles/json_unsafe_parser.h b/components/ntp_tiles/json_unsafe_parser.h
deleted file mode 100644
index c985357..0000000
--- a/components/ntp_tiles/json_unsafe_parser.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2016 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 COMPONENTS_NTP_TILES_JSON_UNSAFE_PARSER_H_
-#define COMPONENTS_NTP_TILES_JSON_UNSAFE_PARSER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/callback_forward.h"
-
-namespace base {
-class Value;
-}
-
-namespace ntp_tiles {
-
-// Mimics SafeJsonParser, but parses unsafely.
-//
-// Do not use this class, unless you can't help it. On most platforms,
-// SafeJsonParser is available and is safer. If it is not available (e.g. on
-// iOS), then this class mimics its API without its safety.
-class JsonUnsafeParser {
- public:
-  using SuccessCallback = base::Callback<void(base::Value)>;
-  using ErrorCallback = base::Callback<void(const std::string&)>;
-
-  // As with SafeJsonParser, runs either success_callback or error_callback on
-  // the calling thread, but not before the call returns.
-  static void Parse(const std::string& unsafe_json,
-                    const SuccessCallback& success_callback,
-                    const ErrorCallback& error_callback);
-
-  JsonUnsafeParser() = delete;
-};
-
-}  // namespace ntp_tiles
-
-#endif  // COMPONENTS_NTP_TILES_POPULAR_SITES_H_
diff --git a/components/ntp_tiles/most_visited_sites_unittest.cc b/components/ntp_tiles/most_visited_sites_unittest.cc
index 6ba9ebd3..60e2bc5 100644
--- a/components/ntp_tiles/most_visited_sites_unittest.cc
+++ b/components/ntp_tiles/most_visited_sites_unittest.cc
@@ -30,12 +30,12 @@
 #include "components/ntp_tiles/custom_links_manager.h"
 #include "components/ntp_tiles/features.h"
 #include "components/ntp_tiles/icon_cacher.h"
-#include "components/ntp_tiles/json_unsafe_parser.h"
 #include "components/ntp_tiles/popular_sites_impl.h"
 #include "components/ntp_tiles/pref_names.h"
 #include "components/ntp_tiles/section_type.h"
 #include "components/ntp_tiles/switches.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "services/data_decoder/public/cpp/testing_json_parser.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -293,7 +293,7 @@
               "title": "PopularSite2",
               "url": "http://popularsite2/",
               "favicon_url": "http://popularsite2/favicon.ico"
-            },
+            }
            ])");
 
     test_url_loader_factory_.AddResponse(
@@ -310,7 +310,7 @@
               "title": "Google News",
               "url": "http://news.google.com",
               "favicon_url": "http://news.google.com/favicon.ico"
-            },
+            }
            ])");
 
     test_url_loader_factory_.AddResponse(
@@ -364,7 +364,7 @@
         prefs_,
         /*template_url_service=*/nullptr,
         /*variations_service=*/nullptr, test_shared_loader_factory_,
-        base::Bind(JsonUnsafeParser::Parse));
+        base::Bind(&data_decoder::SafeJsonParser::Parse, nullptr));
   }
 
  private:
@@ -517,6 +517,7 @@
   TopSitesCallbackList top_sites_callbacks_;
 
   base::test::ScopedTaskEnvironment task_environment_;
+  data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
   sync_preferences::TestingPrefServiceSyncable pref_service_;
   PopularSitesFactoryForTest popular_sites_factory_;
   scoped_refptr<StrictMock<MockTopSites>> mock_top_sites_;
diff --git a/components/ntp_tiles/popular_sites_impl_unittest.cc b/components/ntp_tiles/popular_sites_impl_unittest.cc
index feb0f291..b965d6b 100644
--- a/components/ntp_tiles/popular_sites_impl_unittest.cc
+++ b/components/ntp_tiles/popular_sites_impl_unittest.cc
@@ -23,12 +23,12 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "components/ntp_tiles/features.h"
-#include "components/ntp_tiles/json_unsafe_parser.h"
 #include "components/ntp_tiles/pref_names.h"
 #include "components/ntp_tiles/tile_source.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "net/http/http_status_code.h"
+#include "services/data_decoder/public/cpp/testing_json_parser.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -204,7 +204,7 @@
         prefs_.get(),
         /*template_url_service=*/nullptr,
         /*variations_service=*/nullptr, test_shared_loader_factory_,
-        base::Bind(JsonUnsafeParser::Parse));
+        base::Bind(&data_decoder::SafeJsonParser::Parse, nullptr));
   }
 
   const TestPopularSite kWikipedia;
@@ -213,6 +213,7 @@
 
   base::test::ScopedTaskEnvironment task_environment_{
       base::test::ScopedTaskEnvironment::MainThreadType::UI};
+  data_decoder::TestingJsonParser::ScopedFactoryOverride factory_override_;
   std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> prefs_;
   network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
diff --git a/components/password_manager/ios/BUILD.gn b/components/password_manager/ios/BUILD.gn
index 2254462..83e9449 100644
--- a/components/password_manager/ios/BUILD.gn
+++ b/components/password_manager/ios/BUILD.gn
@@ -15,6 +15,7 @@
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser/form_parsing",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
     "//url",
   ]
 
diff --git a/components/password_manager/ios/password_form_helper.mm b/components/password_manager/ios/password_form_helper.mm
index 77a0aef..30f9b05 100644
--- a/components/password_manager/ios/password_form_helper.mm
+++ b/components/password_manager/ios/password_form_helper.mm
@@ -16,7 +16,7 @@
 #include "components/password_manager/core/browser/form_parsing/ios_form_parser.h"
 #include "components/password_manager/ios/account_select_fill_data.h"
 #include "components/password_manager/ios/js_password_manager.h"
-#import "ios/web/public/web_state/web_frame.h"
+#import "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/components/password_manager/ios/password_suggestion_helper.mm b/components/password_manager/ios/password_suggestion_helper.mm
index a8ce9a1..3a313fa 100644
--- a/components/password_manager/ios/password_suggestion_helper.mm
+++ b/components/password_manager/ios/password_suggestion_helper.mm
@@ -8,8 +8,8 @@
 #include "components/autofill/core/common/form_data.h"
 #import "components/autofill/ios/browser/form_suggestion.h"
 #include "components/password_manager/ios/account_select_fill_data.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 7e48b974..eafdef0 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -1682,7 +1682,7 @@
 message DeviceInitialEnrollmentStateResponse {
   // Initial action to take after OOBE.
   enum InitialEnrollmentMode {
-    // No initial enrollment restoration.
+    // No initial enrollment.
     INITIAL_ENROLLMENT_MODE_NONE = 0;
     // Enterprise enrollment is enforced and cannot be skipped.
     INITIAL_ENROLLMENT_MODE_ENROLLMENT_ENFORCED = 1;
@@ -1690,12 +1690,16 @@
     // skipped.
     INITIAL_ENROLLMENT_MODE_ZERO_TOUCH_ENFORCED = 2;
   }
+
   // The server-indicated initial enrollment mode.
   optional InitialEnrollmentMode initial_enrollment_mode = 1
       [default = INITIAL_ENROLLMENT_MODE_NONE];
 
   // The domain the device should be enrolled into.
   optional string management_domain = 2;
+
+  // Whether the device comes packaged with a license or not.
+  optional bool is_license_packaged_with_device = 3;
 }
 
 // Sent by the client to the server to pair the Host device with the Controller
diff --git a/components/security_state/ios/BUILD.gn b/components/security_state/ios/BUILD.gn
index 6f745b8f..2111331 100644
--- a/components/security_state/ios/BUILD.gn
+++ b/components/security_state/ios/BUILD.gn
@@ -14,5 +14,6 @@
     "//base",
     "//components/security_state/core/",
     "//ios/web/public/",
+    "//ios/web/public/security",
   ]
 }
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc
index fd6c3f3a..a1d22a5 100644
--- a/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -328,8 +328,8 @@
 
 AccountReconcilorTest::AccountReconcilorTest()
     : account_consistency_(signin::AccountConsistencyMethod::kDisabled),
-      test_signin_client_(&pref_service_),
-      identity_test_env_(&test_url_loader_factory_,
+      test_signin_client_(&pref_service_, &test_url_loader_factory_),
+      identity_test_env_(/*test_url_loader_factory=*/nullptr,
                          &pref_service_,
                          account_consistency_,
                          &test_signin_client_) {
diff --git a/components/signin/core/browser/account_tracker_service_unittest.cc b/components/signin/core/browser/account_tracker_service_unittest.cc
index 3bb9cdb..46eac21d 100644
--- a/components/signin/core/browser/account_tracker_service_unittest.cc
+++ b/components/signin/core/browser/account_tracker_service_unittest.cc
@@ -339,8 +339,8 @@
   PrefService* prefs() { return &pref_service_; }
   AccountTrackerObserver* observer() { return &observer_; }
 
-  network::TestURLLoaderFactory* test_url_loader_factory() {
-    return signin_client_.test_url_loader_factory();
+  network::TestURLLoaderFactory* GetTestURLLoaderFactory() {
+    return signin_client_.GetTestURLLoaderFactory();
   }
 
   bool* force_account_id_to_email_for_legacy_tests_pointer() {
@@ -406,11 +406,11 @@
     net::HttpStatusCode response_code,
     const std::string& response_string) {
   GURL url = GaiaUrls::GetInstance()->oauth_user_info_url();
-  EXPECT_TRUE(test_url_loader_factory()->IsPending(url.spec()));
+  EXPECT_TRUE(GetTestURLLoaderFactory()->IsPending(url.spec()));
 
   // It's possible for multiple requests to be pending. Respond to all of them.
-  while (test_url_loader_factory()->IsPending(url.spec())) {
-    test_url_loader_factory()->SimulateResponseForPendingRequest(
+  while (GetTestURLLoaderFactory()->IsPending(url.spec())) {
+    GetTestURLLoaderFactory()->SimulateResponseForPendingRequest(
         url, network::URLLoaderCompletionStatus(net::OK),
         network::CreateResourceResponseHead(response_code), response_string,
         network::TestURLLoaderFactory::kMostRecentMatch);
@@ -438,14 +438,14 @@
 
 void AccountTrackerServiceTest::ReturnAccountImageFetchSuccess(
     AccountKey account_key) {
-  test_url_loader_factory()->AddResponse(
+  GetTestURLLoaderFactory()->AddResponse(
       AccountKeyToPictureURLWithSize(account_key), "image data");
   scoped_task_environment_.RunUntilIdle();
 }
 
 void AccountTrackerServiceTest::ReturnAccountImageFetchFailure(
     AccountKey account_key) {
-  test_url_loader_factory()->AddResponse(
+  GetTestURLLoaderFactory()->AddResponse(
       AccountKeyToPictureURLWithSize(account_key), std::string(),
       net::HTTP_BAD_REQUEST);
   scoped_task_environment_.RunUntilIdle();
@@ -637,7 +637,7 @@
   SimulateTokenAvailable(kAccountKeyAlpha);
   IssueAccessToken(kAccountKeyAlpha);
   // No fetcher has been created yet.
-  EXPECT_EQ(0, test_url_loader_factory()->NumPending());
+  EXPECT_EQ(0, GetTestURLLoaderFactory()->NumPending());
 
   // Enable the network to create the fetcher then issue the access token.
   account_fetcher()->EnableNetworkFetchesForTest();
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.cc b/components/signin/core/browser/gaia_cookie_manager_service.cc
index 46bf3592..50df29d 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -259,7 +259,7 @@
   CleanupTransientState();
   results_.clear();
   helper_->gaia_auth_fetcher_ = helper_->signin_client_->CreateGaiaAuthFetcher(
-      this, gaia::GaiaSource::kChrome, helper_->GetURLLoaderFactory());
+      this, gaia::GaiaSource::kChrome);
   helper_->gaia_auth_fetcher_->StartGetCheckConnectionInfo();
 
   // Some fetches may timeout.  Start a timer to decide when the result fetcher
@@ -451,20 +451,8 @@
 GaiaCookieManagerService::GaiaCookieManagerService(
     OAuth2TokenService* token_service,
     SigninClient* signin_client)
-    : GaiaCookieManagerService(
-          token_service,
-          signin_client,
-          base::BindRepeating(&SigninClient::GetURLLoaderFactory,
-                              base::Unretained(signin_client))) {}
-
-GaiaCookieManagerService::GaiaCookieManagerService(
-    OAuth2TokenService* token_service,
-    SigninClient* signin_client,
-    base::RepeatingCallback<scoped_refptr<network::SharedURLLoaderFactory>()>
-        shared_url_loader_factory_getter)
     : token_service_(token_service),
       signin_client_(signin_client),
-      shared_url_loader_factory_getter_(shared_url_loader_factory_getter),
       external_cc_result_fetcher_(this),
       fetcher_backoff_(&kBackoffPolicy),
       fetcher_retries_(0),
@@ -684,7 +672,7 @@
 
 scoped_refptr<network::SharedURLLoaderFactory>
 GaiaCookieManagerService::GetURLLoaderFactory() {
-  return shared_url_loader_factory_getter_.Run();
+  return signin_client_->GetURLLoaderFactory();
 }
 
 void GaiaCookieManagerService::MarkListAccountsStale() {
@@ -939,18 +927,17 @@
                      base::Unretained(this)),
       base::BindRepeating(
           [](SigninClient* client,
-             scoped_refptr<network::SharedURLLoaderFactory> url_loader,
              GaiaAuthConsumer* consumer) -> std::unique_ptr<GaiaAuthFetcher> {
-            return client->CreateGaiaAuthFetcher(
-                consumer, gaia::GaiaSource::kChrome, url_loader);
+            return client->CreateGaiaAuthFetcher(consumer,
+                                                 gaia::GaiaSource::kChrome);
           },
-          base::Unretained(signin_client_), GetURLLoaderFactory()));
+          base::Unretained(signin_client_)));
 }
 
 void GaiaCookieManagerService::StartFetchingMergeSession() {
   DCHECK(!uber_token_.empty());
-  gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
-      this, requests_.front().source(), GetURLLoaderFactory());
+  gaia_auth_fetcher_ =
+      signin_client_->CreateGaiaAuthFetcher(this, requests_.front().source());
 
   gaia_auth_fetcher_->StartMergeSession(
       uber_token_, external_cc_result_fetcher_.GetExternalCcResult());
@@ -967,15 +954,15 @@
 
 void GaiaCookieManagerService::StartFetchingLogOut() {
   RecordLogoutRequestState(LogoutRequestState::kStarted);
-  gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
-      this, requests_.front().source(), GetURLLoaderFactory());
+  gaia_auth_fetcher_ =
+      signin_client_->CreateGaiaAuthFetcher(this, requests_.front().source());
   gaia_auth_fetcher_->StartLogOut();
 }
 
 void GaiaCookieManagerService::StartFetchingListAccounts() {
   VLOG(1) << "GaiaCookieManagerService::ListAccounts";
-  gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
-      this, requests_.front().source(), GetURLLoaderFactory());
+  gaia_auth_fetcher_ =
+      signin_client_->CreateGaiaAuthFetcher(this, requests_.front().source());
   gaia_auth_fetcher_->StartListAccounts();
 }
 
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.h b/components/signin/core/browser/gaia_cookie_manager_service.h
index 6c361383..13aeee1 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -228,19 +228,6 @@
   GaiaCookieManagerService(OAuth2TokenService* token_service,
                            SigninClient* signin_client);
 
-  // Creates a GaiaCookieManagerService that uses the provided
-  // |shared_url_loader_factory_getter| to determine the SharedUrlLoaderFactory
-  // used for cookie-related requests.
-  // Note: SharedUrlLoaderFactory is passed via callback, so that if the
-  // callback has side-effects (e.g. network initialization), they do not occur
-  // until the first time GaiaCookieManagerService::GetSharedUrlLoaderFactory is
-  // called.
-  GaiaCookieManagerService(
-      OAuth2TokenService* token_service,
-      SigninClient* signin_client,
-      base::RepeatingCallback<scoped_refptr<network::SharedURLLoaderFactory>()>
-          shared_url_loader_factory_getter);
-
   ~GaiaCookieManagerService() override;
 
   void InitCookieListener();
@@ -373,8 +360,6 @@
   OAuth2TokenService* token_service_;
   SigninClient* signin_client_;
 
-  base::RepeatingCallback<scoped_refptr<network::SharedURLLoaderFactory>()>
-      shared_url_loader_factory_getter_;
   std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_;
   std::unique_ptr<signin::UbertokenFetcherImpl> uber_token_fetcher_;
   ExternalCcResultFetcher external_cc_result_fetcher_;
diff --git a/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc b/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
index 727be0c5..80c2108c 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
@@ -96,11 +96,7 @@
  public:
   InstrumentedGaiaCookieManagerService(OAuth2TokenService* token_service,
                                        SigninClient* signin_client)
-      : GaiaCookieManagerService(
-            token_service,
-            signin_client,
-            base::BindRepeating(&SigninClient::GetURLLoaderFactory,
-                                base::Unretained(signin_client))) {
+      : GaiaCookieManagerService(token_service, signin_client) {
     total++;
   }
 
@@ -183,7 +179,7 @@
   }
 
   void SimulateGetCheckConnectionInfoSuccess(const std::string& data) {
-    signin_client_->test_url_loader_factory()->AddResponse(
+    signin_client_->GetTestURLLoaderFactory()->AddResponse(
         GaiaUrls::GetInstance()
             ->GetCheckConnectionInfoURLWithSource(GaiaConstants::kChromeSource)
             .spec(),
@@ -193,7 +189,7 @@
 
   void SimulateGetCheckConnectionInfoResult(const std::string& url,
                                             const std::string& result) {
-    signin_client_->test_url_loader_factory()->AddResponse(url, result);
+    signin_client_->GetTestURLLoaderFactory()->AddResponse(url, result);
     base::RunLoop().RunUntilIdle();
   }
 
@@ -205,12 +201,12 @@
   }
 
   bool IsLoadPending(const std::string& url) {
-    return signin_client_->test_url_loader_factory()->IsPending(
+    return signin_client_->GetTestURLLoaderFactory()->IsPending(
         GURL(url).spec());
   }
 
   bool IsLoadPending() {
-    return signin_client_->test_url_loader_factory()->NumPending() > 0;
+    return signin_client_->GetTestURLLoaderFactory()->NumPending() > 0;
   }
 
   const GoogleServiceAuthError& no_error() { return no_error_; }
diff --git a/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc b/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc
index 86c568f..7a42f2e 100644
--- a/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc
+++ b/components/signin/core/browser/mutable_profile_oauth2_token_service_delegate_unittest.cc
@@ -107,7 +107,7 @@
     AccountTrackerService::RegisterPrefs(pref_service_.registry());
     SigninManagerBase::RegisterProfilePrefs(pref_service_.registry());
     client_.reset(new TestSigninClient(&pref_service_));
-    client_->test_url_loader_factory()->AddResponse(
+    client_->GetTestURLLoaderFactory()->AddResponse(
         GaiaUrls::GetInstance()->oauth2_revoke_url().spec(), "");
     LoadTokenDatabase();
     account_tracker_service_.Initialize(&pref_service_, base::FilePath());
@@ -138,7 +138,7 @@
   }
 
   void AddSuccessfulOAuhTokenResponse() {
-    client_->test_url_loader_factory()->AddResponse(
+    client_->GetTestURLLoaderFactory()->AddResponse(
         GaiaUrls::GetInstance()->oauth2_token_url().spec(),
         GetValidTokenResponse("token", 3600));
   }
@@ -792,39 +792,39 @@
   InitializeOAuth2ServiceDelegate(signin::AccountConsistencyMethod::kDisabled);
   const std::string url = GaiaUrls::GetInstance()->oauth2_revoke_url().spec();
   // Revokes will remain in "pending" state.
-  client_->test_url_loader_factory()->ClearResponses();
+  client_->GetTestURLLoaderFactory()->ClearResponses();
 
   oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
-  EXPECT_FALSE(client_->test_url_loader_factory()->IsPending(url));
+  EXPECT_FALSE(client_->GetTestURLLoaderFactory()->IsPending(url));
 
   oauth2_service_delegate_->RevokeCredentials("account_id");
   EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size());
-  EXPECT_TRUE(client_->test_url_loader_factory()->IsPending(url));
+  EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url));
   // Fail and retry.
-  client_->test_url_loader_factory()->SimulateResponseForPendingRequest(
+  client_->GetTestURLLoaderFactory()->SimulateResponseForPendingRequest(
       url, std::string(), net::HTTP_INTERNAL_SERVER_ERROR);
-  EXPECT_TRUE(client_->test_url_loader_factory()->IsPending(url));
+  EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url));
   EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size());
   // Fail and retry.
-  client_->test_url_loader_factory()->SimulateResponseForPendingRequest(
+  client_->GetTestURLLoaderFactory()->SimulateResponseForPendingRequest(
       url, std::string(), net::HTTP_INTERNAL_SERVER_ERROR);
-  EXPECT_TRUE(client_->test_url_loader_factory()->IsPending(url));
+  EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url));
   EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size());
   // Do not retry after third attempt.
-  client_->test_url_loader_factory()->SimulateResponseForPendingRequest(
+  client_->GetTestURLLoaderFactory()->SimulateResponseForPendingRequest(
       url, std::string(), net::HTTP_INTERNAL_SERVER_ERROR);
-  EXPECT_FALSE(client_->test_url_loader_factory()->IsPending(url));
+  EXPECT_FALSE(client_->GetTestURLLoaderFactory()->IsPending(url));
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
 
   // No retry after success.
   oauth2_service_delegate_->UpdateCredentials("account_id", "refresh_token");
   oauth2_service_delegate_->RevokeCredentials("account_id");
   EXPECT_EQ(1u, oauth2_service_delegate_->server_revokes_.size());
-  EXPECT_TRUE(client_->test_url_loader_factory()->IsPending(url));
-  client_->test_url_loader_factory()->SimulateResponseForPendingRequest(
+  EXPECT_TRUE(client_->GetTestURLLoaderFactory()->IsPending(url));
+  client_->GetTestURLLoaderFactory()->SimulateResponseForPendingRequest(
       url, std::string(), net::HTTP_OK);
-  EXPECT_FALSE(client_->test_url_loader_factory()->IsPending(url));
+  EXPECT_FALSE(client_->GetTestURLLoaderFactory()->IsPending(url));
   EXPECT_TRUE(oauth2_service_delegate_->server_revokes_.empty());
 }
 
diff --git a/components/signin/core/browser/oauth_multilogin_helper.cc b/components/signin/core/browser/oauth_multilogin_helper.cc
index 0d43d8a..230678c 100644
--- a/components/signin/core/browser/oauth_multilogin_helper.cc
+++ b/components/signin/core/browser/oauth_multilogin_helper.cc
@@ -100,8 +100,8 @@
 
 void OAuthMultiloginHelper::StartFetchingMultiLogin() {
   DCHECK_EQ(token_id_pairs_.size(), account_ids_.size());
-  gaia_auth_fetcher_ = signin_client_->CreateGaiaAuthFetcher(
-      this, gaia::GaiaSource::kChrome, signin_client_->GetURLLoaderFactory());
+  gaia_auth_fetcher_ =
+      signin_client_->CreateGaiaAuthFetcher(this, gaia::GaiaSource::kChrome);
   gaia_auth_fetcher_->StartOAuthMultilogin(token_id_pairs_);
 }
 
diff --git a/components/signin/core/browser/oauth_multilogin_helper_unittest.cc b/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
index b38322f..0de493c6b 100644
--- a/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
+++ b/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
@@ -146,7 +146,7 @@
   }
 
   network::TestURLLoaderFactory* url_loader() {
-    return test_signin_client_.test_url_loader_factory();
+    return test_signin_client_.GetTestURLLoaderFactory();
   }
 
   std::string multilogin_url() const {
diff --git a/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos_unittest.cc b/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos_unittest.cc
index a32eeff5..088a552 100644
--- a/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos_unittest.cc
+++ b/components/signin/core/browser/profile_oauth2_token_service_delegate_chromeos_unittest.cc
@@ -193,7 +193,7 @@
   }
 
   void AddSuccessfulOAuthTokenResponse() {
-    client_->test_url_loader_factory()->AddResponse(
+    client_->GetTestURLLoaderFactory()->AddResponse(
         GaiaUrls::GetInstance()->oauth2_token_url().spec(),
         GetValidTokenResponse("token", 3600));
   }
diff --git a/components/signin/core/browser/signin_client.h b/components/signin/core/browser/signin_client.h
index e49465d..8587e23 100644
--- a/components/signin/core/browser/signin_client.h
+++ b/components/signin/core/browser/signin_client.h
@@ -95,8 +95,7 @@
   // Creates a new platform-specific GaiaAuthFetcher.
   virtual std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
       GaiaAuthConsumer* consumer,
-      gaia::GaiaSource source,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) = 0;
+      gaia::GaiaSource source) = 0;
 
   // Schedules migration to happen at next startup.
   virtual void SetReadyForDiceMigration(bool is_ready) {}
diff --git a/components/signin/core/browser/signin_manager.cc b/components/signin/core/browser/signin_manager.cc
index 0975933..d97a3e1c 100644
--- a/components/signin/core/browser/signin_manager.cc
+++ b/components/signin/core/browser/signin_manager.cc
@@ -8,14 +8,12 @@
 #include <vector>
 
 #include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/identity_utils.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #include "components/signin/core/browser/signin_pref_names.h"
@@ -31,9 +29,11 @@
     AccountTrackerService* account_tracker_service,
     GaiaCookieManagerService* cookie_manager_service,
     signin::AccountConsistencyMethod account_consistency)
-    : SigninManagerBase(client, token_service, account_tracker_service),
-      cookie_manager_service_(cookie_manager_service),
-      account_consistency_(account_consistency),
+    : SigninManagerBase(client,
+                        token_service,
+                        account_tracker_service,
+                        cookie_manager_service,
+                        account_consistency),
       weak_pointer_factory_(this) {}
 
 SigninManager::~SigninManager() {
@@ -41,104 +41,6 @@
   local_state_pref_registrar_.RemoveAll();
 }
 
-void SigninManager::SignOut(
-    signin_metrics::ProfileSignout signout_source_metric,
-    signin_metrics::SignoutDelete signout_delete_metric) {
-  RemoveAccountsOption remove_option =
-      (account_consistency_ == signin::AccountConsistencyMethod::kDice)
-          ? RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError
-          : RemoveAccountsOption::kRemoveAllAccounts;
-  StartSignOut(signout_source_metric, signout_delete_metric, remove_option);
-}
-
-void SigninManager::SignOutAndRemoveAllAccounts(
-    signin_metrics::ProfileSignout signout_source_metric,
-    signin_metrics::SignoutDelete signout_delete_metric) {
-  StartSignOut(signout_source_metric, signout_delete_metric,
-               RemoveAccountsOption::kRemoveAllAccounts);
-}
-
-void SigninManager::SignOutAndKeepAllAccounts(
-    signin_metrics::ProfileSignout signout_source_metric,
-    signin_metrics::SignoutDelete signout_delete_metric) {
-  StartSignOut(signout_source_metric, signout_delete_metric,
-               RemoveAccountsOption::kKeepAllAccounts);
-}
-
-void SigninManager::StartSignOut(
-    signin_metrics::ProfileSignout signout_source_metric,
-    signin_metrics::SignoutDelete signout_delete_metric,
-    RemoveAccountsOption remove_option) {
-  signin_client()->PreSignOut(
-      base::BindOnce(&SigninManager::OnSignoutDecisionReached,
-                     base::Unretained(this), signout_source_metric,
-                     signout_delete_metric, remove_option),
-      signout_source_metric);
-}
-
-void SigninManager::OnSignoutDecisionReached(
-    signin_metrics::ProfileSignout signout_source_metric,
-    signin_metrics::SignoutDelete signout_delete_metric,
-    RemoveAccountsOption remove_option,
-    SigninClient::SignoutDecision signout_decision) {
-  DCHECK(IsInitialized());
-
-  signin_metrics::LogSignout(signout_source_metric, signout_delete_metric);
-  if (!IsAuthenticated()) {
-    return;
-  }
-
-  // TODO(crbug.com/887756): Consider moving this higher up, or document why
-  // the above blocks are exempt from the |signout_decision| early return.
-  if (signout_decision == SigninClient::SignoutDecision::DISALLOW_SIGNOUT) {
-    DVLOG(1) << "Ignoring attempt to sign out while signout disallowed";
-    return;
-  }
-
-  AccountInfo account_info = GetAuthenticatedAccountInfo();
-  const std::string account_id = GetAuthenticatedAccountId();
-  const std::string username = account_info.email;
-  const base::Time signin_time =
-      base::Time::FromDeltaSinceWindowsEpoch(base::TimeDelta::FromMicroseconds(
-          signin_client()->GetPrefs()->GetInt64(prefs::kSignedInTime)));
-  ClearAuthenticatedAccountId();
-  signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
-  signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesAccountId);
-  signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesUserAccountId);
-  signin_client()->GetPrefs()->ClearPref(prefs::kSignedInTime);
-
-  // Determine the duration the user was logged in and log that to UMA.
-  if (!signin_time.is_null()) {
-    base::TimeDelta signed_in_duration = base::Time::Now() - signin_time;
-    UMA_HISTOGRAM_COUNTS_1M("Signin.SignedInDurationBeforeSignout",
-                            signed_in_duration.InMinutes());
-  }
-
-  // Revoke all tokens before sending signed_out notification, because there
-  // may be components that don't listen for token service events when the
-  // profile is not connected to an account.
-  switch (remove_option) {
-    case RemoveAccountsOption::kRemoveAllAccounts:
-      VLOG(0) << "Revoking all refresh tokens on server. Reason: sign out, "
-              << "IsSigninAllowed: " << IsSigninAllowed();
-      token_service()->RevokeAllCredentials(
-          signin_metrics::SourceForRefreshTokenOperation::
-              kSigninManager_ClearPrimaryAccount);
-      break;
-    case RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError:
-      if (token_service()->RefreshTokenHasError(account_id))
-        token_service()->RevokeCredentials(
-            account_id, signin_metrics::SourceForRefreshTokenOperation::
-                            kSigninManager_ClearPrimaryAccount);
-      break;
-    case RemoveAccountsOption::kKeepAllAccounts:
-      // Do nothing.
-      break;
-  }
-
-  FireGoogleSignedOut(account_info);
-}
-
 void SigninManager::FinalizeInitBeforeLoadingRefreshTokens(
     PrefService* local_state) {
   // local_state can be null during unit tests.
@@ -201,9 +103,11 @@
 }
 
 void SigninManager::OnSigninAllowedPrefChanged() {
-  if (!IsSigninAllowed() && IsAuthenticated())
+  if (!IsSigninAllowed() && IsAuthenticated()) {
+    VLOG(0) << "IsSigninAllowed() set to false, signing out the user";
     SignOut(signin_metrics::SIGNOUT_PREF_CHANGED,
             signin_metrics::SignoutDelete::IGNORE_METRIC);
+  }
 }
 
 // static
@@ -250,12 +154,6 @@
   }
 }
 
-void SigninManager::FireGoogleSignedOut(const AccountInfo& account_info) {
-  if (observer_ != nullptr) {
-    observer_->GoogleSignedOut(account_info);
-  }
-}
-
 void SigninManager::OnRefreshTokensLoaded() {
   token_service()->RemoveObserver(this);
 
diff --git a/components/signin/core/browser/signin_manager.h b/components/signin/core/browser/signin_manager.h
index 5efd7f4..c70c9d9 100644
--- a/components/signin/core/browser/signin_manager.h
+++ b/components/signin/core/browser/signin_manager.h
@@ -30,16 +30,13 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_member.h"
-#include "components/signin/core/browser/account_consistency_method.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "components/signin/core/browser/signin_client.h"
 #include "components/signin/core/browser/signin_manager_base.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #include "net/cookies/canonical_cookie.h"
 
-class GaiaCookieManagerService;
 class PrefService;
 
 namespace identity {
@@ -49,16 +46,6 @@
 class SigninManager : public SigninManagerBase,
                       public OAuth2TokenService::Observer {
  public:
-  // Used to remove accounts from the token service and the account tracker.
-  enum class RemoveAccountsOption {
-    // Do not remove accounts.
-    kKeepAllAccounts,
-    // Remove all the accounts.
-    kRemoveAllAccounts,
-    // Removes the authenticated account if it is in authentication error.
-    kRemoveAuthenticatedAccountIfInError
-  };
-
   // This is used to distinguish URLs belonging to the special web signin flow
   // running in the special signin process from other URLs on the same domain.
   // We do not grant WebUI privilieges / bindings to this process or to URLs of
@@ -78,30 +65,6 @@
   // are actually SigninManager instances.
   static SigninManager* FromSigninManagerBase(SigninManagerBase* manager);
 
-  // Signs a user out, removing the preference, erasing all keys
-  // associated with the authenticated user, and canceling all auth in progress.
-  // On mobile and on desktop pre-DICE, this also removes all accounts from
-  // Chrome by revoking all refresh tokens.
-  // On desktop with DICE enabled, this will remove the authenticated account
-  // from Chrome only if it is in authentication error. No other accounts are
-  // removed.
-  void SignOut(signin_metrics::ProfileSignout signout_source_metric,
-               signin_metrics::SignoutDelete signout_delete_metric);
-
-  // Signs a user out, removing the preference, erasing all keys
-  // associated with the authenticated user, and canceling all auth in progress.
-  // It removes all accounts from Chrome by revoking all refresh tokens.
-  void SignOutAndRemoveAllAccounts(
-      signin_metrics::ProfileSignout signout_source_metric,
-      signin_metrics::SignoutDelete signout_delete_metric);
-
-  // Signs a user out, removing the preference, erasing all keys
-  // associated with the authenticated user, and canceling all auth in progress.
-  // Does not remove the accounts from the token service.
-  void SignOutAndKeepAllAccounts(
-      signin_metrics::ProfileSignout signout_source_metric,
-      signin_metrics::SignoutDelete signout_delete_metric);
-
   // On platforms where SigninManager is responsible for dealing with
   // invalid username policy updates, we need to check this during
   // initialization and sign the user out.
@@ -121,14 +84,6 @@
   // Sets whether sign-in is allowed or not.
   void SetSigninAllowed(bool allowed);
 
- protected:
-  // The sign out process which is started by SigninClient::PreSignOut()
-  virtual void OnSignoutDecisionReached(
-      signin_metrics::ProfileSignout signout_source_metric,
-      signin_metrics::SignoutDelete signout_delete_metric,
-      RemoveAccountsOption remove_option,
-      SigninClient::SignoutDecision signout_decision);
-
  private:
   friend class identity::IdentityManager;
   FRIEND_TEST_ALL_PREFIXES(SigninManagerTest, Prohibited);
@@ -137,26 +92,15 @@
   // Send all observers |GoogleSigninSucceeded| notifications.
   void FireGoogleSigninSucceeded();
 
-  // Send all observers |GoogleSignedOut| notifications.
-  void FireGoogleSignedOut(const AccountInfo& account_info);
-
   // OAuth2TokenService::Observer:
   void OnRefreshTokensLoaded() override;
 
-  // Starts the sign out process.
-  void StartSignOut(signin_metrics::ProfileSignout signout_source_metric,
-                    signin_metrics::SignoutDelete signout_delete_metric,
-                    RemoveAccountsOption remove_option);
-
   void OnSigninAllowedPrefChanged();
   void OnGoogleServicesUsernamePatternChanged();
 
   // Returns true if the passed username is allowed by policy.
   bool IsAllowedUsername(const std::string& username) const;
 
-  // Object used to use the token to push a GAIA cookie into the cookie jar.
-  GaiaCookieManagerService* cookie_manager_service_;
-
   // Helper object to listen for changes to signin preferences stored in non-
   // profile-specific local prefs (like kGoogleServicesUsernamePattern).
   PrefChangeRegistrar local_state_pref_registrar_;
@@ -164,8 +108,6 @@
   // Helper object to listen for changes to the signin allowed preference.
   BooleanPrefMember signin_allowed_;
 
-  signin::AccountConsistencyMethod account_consistency_;
-
   base::WeakPtrFactory<SigninManager> weak_pointer_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SigninManager);
diff --git a/components/signin/core/browser/signin_manager_base.cc b/components/signin/core/browser/signin_manager_base.cc
index c0c9d276..37ee807 100644
--- a/components/signin/core/browser/signin_manager_base.cc
+++ b/components/signin/core/browser/signin_manager_base.cc
@@ -9,6 +9,7 @@
 
 #include "base/command_line.h"
 #include "base/memory/ref_counted.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -16,6 +17,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_client.h"
 #include "components/signin/core/browser/signin_pref_names.h"
@@ -27,11 +29,14 @@
 SigninManagerBase::SigninManagerBase(
     SigninClient* client,
     ProfileOAuth2TokenService* token_service,
-    AccountTrackerService* account_tracker_service)
+    AccountTrackerService* account_tracker_service,
+    GaiaCookieManagerService* cookie_manager_service,
+    signin::AccountConsistencyMethod account_consistency)
     : client_(client),
       token_service_(token_service),
       account_tracker_service_(account_tracker_service),
       initialized_(false),
+      account_consistency_(account_consistency),
       weak_pointer_factory_(this) {
   DCHECK(client_);
   DCHECK(account_tracker_service_);
@@ -244,3 +249,108 @@
   DCHECK(observer_);
   observer_ = nullptr;
 }
+
+#if !defined(OS_CHROMEOS)
+void SigninManagerBase::SignOut(
+    signin_metrics::ProfileSignout signout_source_metric,
+    signin_metrics::SignoutDelete signout_delete_metric) {
+  RemoveAccountsOption remove_option =
+      (account_consistency_ == signin::AccountConsistencyMethod::kDice)
+          ? RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError
+          : RemoveAccountsOption::kRemoveAllAccounts;
+  StartSignOut(signout_source_metric, signout_delete_metric, remove_option);
+}
+
+void SigninManagerBase::SignOutAndRemoveAllAccounts(
+    signin_metrics::ProfileSignout signout_source_metric,
+    signin_metrics::SignoutDelete signout_delete_metric) {
+  StartSignOut(signout_source_metric, signout_delete_metric,
+               RemoveAccountsOption::kRemoveAllAccounts);
+}
+
+void SigninManagerBase::SignOutAndKeepAllAccounts(
+    signin_metrics::ProfileSignout signout_source_metric,
+    signin_metrics::SignoutDelete signout_delete_metric) {
+  StartSignOut(signout_source_metric, signout_delete_metric,
+               RemoveAccountsOption::kKeepAllAccounts);
+}
+
+void SigninManagerBase::StartSignOut(
+    signin_metrics::ProfileSignout signout_source_metric,
+    signin_metrics::SignoutDelete signout_delete_metric,
+    RemoveAccountsOption remove_option) {
+  signin_client()->PreSignOut(
+      base::BindOnce(&SigninManagerBase::OnSignoutDecisionReached,
+                     base::Unretained(this), signout_source_metric,
+                     signout_delete_metric, remove_option),
+      signout_source_metric);
+}
+
+void SigninManagerBase::OnSignoutDecisionReached(
+    signin_metrics::ProfileSignout signout_source_metric,
+    signin_metrics::SignoutDelete signout_delete_metric,
+    RemoveAccountsOption remove_option,
+    SigninClient::SignoutDecision signout_decision) {
+  DCHECK(IsInitialized());
+
+  signin_metrics::LogSignout(signout_source_metric, signout_delete_metric);
+  if (!IsAuthenticated()) {
+    return;
+  }
+
+  // TODO(crbug.com/887756): Consider moving this higher up, or document why
+  // the above blocks are exempt from the |signout_decision| early return.
+  if (signout_decision == SigninClient::SignoutDecision::DISALLOW_SIGNOUT) {
+    DVLOG(1) << "Ignoring attempt to sign out while signout disallowed";
+    return;
+  }
+
+  AccountInfo account_info = GetAuthenticatedAccountInfo();
+  const std::string account_id = GetAuthenticatedAccountId();
+  const std::string username = account_info.email;
+  const base::Time signin_time =
+      base::Time::FromDeltaSinceWindowsEpoch(base::TimeDelta::FromMicroseconds(
+          signin_client()->GetPrefs()->GetInt64(prefs::kSignedInTime)));
+  ClearAuthenticatedAccountId();
+  signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
+  signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesAccountId);
+  signin_client()->GetPrefs()->ClearPref(prefs::kGoogleServicesUserAccountId);
+  signin_client()->GetPrefs()->ClearPref(prefs::kSignedInTime);
+
+  // Determine the duration the user was logged in and log that to UMA.
+  if (!signin_time.is_null()) {
+    base::TimeDelta signed_in_duration = base::Time::Now() - signin_time;
+    UMA_HISTOGRAM_COUNTS_1M("Signin.SignedInDurationBeforeSignout",
+                            signed_in_duration.InMinutes());
+  }
+
+  // Revoke all tokens before sending signed_out notification, because there
+  // may be components that don't listen for token service events when the
+  // profile is not connected to an account.
+  switch (remove_option) {
+    case RemoveAccountsOption::kRemoveAllAccounts:
+      VLOG(0) << "Revoking all refresh tokens on server. Reason: sign out";
+      token_service()->RevokeAllCredentials(
+          signin_metrics::SourceForRefreshTokenOperation::
+              kSigninManager_ClearPrimaryAccount);
+      break;
+    case RemoveAccountsOption::kRemoveAuthenticatedAccountIfInError:
+      if (token_service()->RefreshTokenHasError(account_id))
+        token_service()->RevokeCredentials(
+            account_id, signin_metrics::SourceForRefreshTokenOperation::
+                            kSigninManager_ClearPrimaryAccount);
+      break;
+    case RemoveAccountsOption::kKeepAllAccounts:
+      // Do nothing.
+      break;
+  }
+
+  FireGoogleSignedOut(account_info);
+}
+
+void SigninManagerBase::FireGoogleSignedOut(const AccountInfo& account_info) {
+  if (observer_ != nullptr) {
+    observer_->GoogleSignedOut(account_info);
+  }
+}
+#endif  // !defined(OS_CHROMEOS)
diff --git a/components/signin/core/browser/signin_manager_base.h b/components/signin/core/browser/signin_manager_base.h
index fd388ec84..9297ca09 100644
--- a/components/signin/core/browser/signin_manager_base.h
+++ b/components/signin/core/browser/signin_manager_base.h
@@ -33,10 +33,13 @@
 #include "base/observer_list.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_member.h"
+#include "components/signin/core/browser/account_consistency_method.h"
 #include "components/signin/core/browser/account_info.h"
+#include "components/signin/core/browser/signin_client.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 
 class AccountTrackerService;
+class GaiaCookieManagerService;
 class PrefRegistrySimple;
 class PrefService;
 class ProfileOAuth2TokenService;
@@ -81,11 +84,25 @@
 #endif
   SigninManagerBase(SigninClient* client,
                     ProfileOAuth2TokenService* token_service,
-                    AccountTrackerService* account_tracker_service);
+                    AccountTrackerService* account_tracker_service,
+                    GaiaCookieManagerService* cookie_manager_service,
+                    signin::AccountConsistencyMethod account_consistency);
 #if !defined(OS_CHROMEOS)
  public:
 #endif
 
+#if !defined(OS_CHROMEOS)
+  // Used to remove accounts from the token service and the account tracker.
+  enum class RemoveAccountsOption {
+    // Do not remove accounts.
+    kKeepAllAccounts,
+    // Remove all the accounts.
+    kRemoveAllAccounts,
+    // Removes the authenticated account if it is in authentication error.
+    kRemoveAuthenticatedAccountIfInError
+  };
+#endif
+
   virtual ~SigninManagerBase();
 
   // Registers per-profile prefs.
@@ -130,6 +147,34 @@
   void SetObserver(Observer* observer);
   void ClearObserver();
 
+  // Signout API surfaces (not supported on ChromeOS, where signout is not
+  // permitted).
+#if !defined(OS_CHROMEOS)
+  // Signs a user out, removing the preference, erasing all keys
+  // associated with the authenticated user, and canceling all auth in progress.
+  // On mobile and on desktop pre-DICE, this also removes all accounts from
+  // Chrome by revoking all refresh tokens.
+  // On desktop with DICE enabled, this will remove the authenticated account
+  // from Chrome only if it is in authentication error. No other accounts are
+  // removed.
+  void SignOut(signin_metrics::ProfileSignout signout_source_metric,
+               signin_metrics::SignoutDelete signout_delete_metric);
+
+  // Signs a user out, removing the preference, erasing all keys
+  // associated with the authenticated user, and canceling all auth in progress.
+  // It removes all accounts from Chrome by revoking all refresh tokens.
+  void SignOutAndRemoveAllAccounts(
+      signin_metrics::ProfileSignout signout_source_metric,
+      signin_metrics::SignoutDelete signout_delete_metric);
+
+  // Signs a user out, removing the preference, erasing all keys
+  // associated with the authenticated user, and canceling all auth in progress.
+  // Does not remove the accounts from the token service.
+  void SignOutAndKeepAllAccounts(
+      signin_metrics::ProfileSignout signout_source_metric,
+      signin_metrics::SignoutDelete signout_delete_metric);
+#endif
+
  protected:
   SigninClient* signin_client() const { return client_; }
 
@@ -168,6 +213,23 @@
   // SigninManagerBase.
   friend class SigninManager;
 
+#if !defined(OS_CHROMEOS)
+  // Starts the sign out process.
+  void StartSignOut(signin_metrics::ProfileSignout signout_source_metric,
+                    signin_metrics::SignoutDelete signout_delete_metric,
+                    RemoveAccountsOption remove_option);
+
+  // The sign out process which is started by SigninClient::PreSignOut()
+  void OnSignoutDecisionReached(
+      signin_metrics::ProfileSignout signout_source_metric,
+      signin_metrics::SignoutDelete signout_delete_metric,
+      RemoveAccountsOption remove_option,
+      SigninClient::SignoutDecision signout_decision);
+
+  // Send all observers |GoogleSignedOut| notifications.
+  void FireGoogleSignedOut(const AccountInfo& account_info);
+#endif
+
   SigninClient* client_;
 
   // The ProfileOAuth2TokenService instance associated with this object. Must
@@ -175,6 +237,7 @@
   ProfileOAuth2TokenService* token_service_;
 
   AccountTrackerService* account_tracker_service_;
+
   bool initialized_;
 
   // Account id after successful authentication.
@@ -183,6 +246,8 @@
   // The list of callbacks notified on shutdown.
   base::CallbackList<void()> on_shutdown_callback_list_;
 
+  signin::AccountConsistencyMethod account_consistency_;
+
   base::WeakPtrFactory<SigninManagerBase> weak_pointer_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SigninManagerBase);
diff --git a/components/signin/core/browser/test_signin_client.cc b/components/signin/core/browser/test_signin_client.cc
index 3f945fea..3a74d1a 100644
--- a/components/signin/core/browser/test_signin_client.cc
+++ b/components/signin/core/browser/test_signin_client.cc
@@ -12,8 +12,11 @@
 #include "services/network/test/test_cookie_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-TestSigninClient::TestSigninClient(PrefService* pref_service)
-    : pref_service_(pref_service),
+TestSigninClient::TestSigninClient(
+    PrefService* pref_service,
+    network::TestURLLoaderFactory* test_url_loader_factory)
+    : test_url_loader_factory_(test_url_loader_factory),
+      pref_service_(pref_service),
       are_signin_cookies_allowed_(true),
       network_calls_delayed_(false),
       is_signout_allowed_(true),
@@ -37,7 +40,7 @@
 
 scoped_refptr<network::SharedURLLoaderFactory>
 TestSigninClient::GetURLLoaderFactory() {
-  return test_url_loader_factory_.GetSafeWeakWrapper();
+  return GetTestURLLoaderFactory()->GetSafeWeakWrapper();
 }
 
 network::mojom::CookieManager* TestSigninClient::GetCookieManager() {
@@ -46,6 +49,25 @@
   return cookie_manager_.get();
 }
 
+network::TestURLLoaderFactory* TestSigninClient::GetTestURLLoaderFactory() {
+  if (test_url_loader_factory_)
+    return test_url_loader_factory_;
+
+  if (!default_test_url_loader_factory_) {
+    default_test_url_loader_factory_ =
+        std::make_unique<network::TestURLLoaderFactory>();
+  }
+
+  return default_test_url_loader_factory_.get();
+}
+
+void TestSigninClient::OverrideTestUrlLoaderFactory(
+    network::TestURLLoaderFactory* factory) {
+  DCHECK(!default_test_url_loader_factory_);
+  DCHECK(!test_url_loader_factory_);
+  test_url_loader_factory_ = factory;
+}
+
 std::string TestSigninClient::GetProductVersion() { return ""; }
 
 void TestSigninClient::SetNetworkCallsDelayed(bool value) {
@@ -92,10 +114,9 @@
 
 std::unique_ptr<GaiaAuthFetcher> TestSigninClient::CreateGaiaAuthFetcher(
     GaiaAuthConsumer* consumer,
-    gaia::GaiaSource source,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+    gaia::GaiaSource source) {
   return std::make_unique<GaiaAuthFetcher>(consumer, source,
-                                           url_loader_factory);
+                                           GetURLLoaderFactory());
 }
 
 void TestSigninClient::PreGaiaLogout(base::OnceClosure callback) {
diff --git a/components/signin/core/browser/test_signin_client.h b/components/signin/core/browser/test_signin_client.h
index ac713d6..2347b86 100644
--- a/components/signin/core/browser/test_signin_client.h
+++ b/components/signin/core/browser/test_signin_client.h
@@ -27,7 +27,9 @@
 // part of its interface.
 class TestSigninClient : public SigninClient {
  public:
-  TestSigninClient(PrefService* pref_service);
+  TestSigninClient(
+      PrefService* pref_service,
+      network::TestURLLoaderFactory* test_url_loader_factory = nullptr);
   ~TestSigninClient() override;
 
   // SigninClient implementation that is specialized for unit tests.
@@ -57,9 +59,12 @@
     cookie_manager_ = std::move(cookie_manager);
   }
 
-  network::TestURLLoaderFactory* test_url_loader_factory() {
-    return &test_url_loader_factory_;
-  }
+  // Returns |test_url_loader_factory_| if it is specified. Otherwise, lazily
+  // creates a default factory and returns it.
+  network::TestURLLoaderFactory* GetTestURLLoaderFactory();
+
+  // Pass a TestURLLoader factory to use instead of the default one.
+  void OverrideTestUrlLoaderFactory(network::TestURLLoaderFactory* factory);
 
   void set_are_signin_cookies_allowed(bool value) {
     are_signin_cookies_allowed_ = value;
@@ -87,14 +92,14 @@
   void DelayNetworkCall(base::OnceClosure callback) override;
   std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
       GaiaAuthConsumer* consumer,
-      gaia::GaiaSource source,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-      override;
+      gaia::GaiaSource source) override;
   void PreGaiaLogout(base::OnceClosure callback) override;
   void SetReadyForDiceMigration(bool ready) override;
 
  private:
-  network::TestURLLoaderFactory test_url_loader_factory_;
+  std::unique_ptr<network::TestURLLoaderFactory>
+      default_test_url_loader_factory_;
+  network::TestURLLoaderFactory* test_url_loader_factory_;
 
   PrefService* pref_service_;
   std::unique_ptr<network::mojom::CookieManager> cookie_manager_;
diff --git a/components/signin/ios/browser/account_consistency_service_unittest.mm b/components/signin/ios/browser/account_consistency_service_unittest.mm
index 6afb87d..0a2e3d0 100644
--- a/components/signin/ios/browser/account_consistency_service_unittest.mm
+++ b/components/signin/ios/browser/account_consistency_service_unittest.mm
@@ -130,9 +130,10 @@
     HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry());
 
     web_view_load_expection_count_ = 0;
-    signin_client_.reset(new TestSigninClient(&prefs_));
+    signin_client_.reset(
+        new TestSigninClient(&prefs_, &test_url_loader_factory_));
     identity_test_env_.reset(new identity::IdentityTestEnvironment(
-        &test_url_loader_factory_, &prefs_,
+        /*test_url_loader_factory=*/nullptr, &prefs_,
         signin::AccountConsistencyMethod::kDisabled, signin_client_.get()));
     settings_map_ = new HostContentSettingsMap(
         &prefs_, false /* is_off_the_record */, false /* store_last_modified */,
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 86e7d5a..6a897b39 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -555,6 +555,7 @@
     "base/hash_util_unittest.cc",
     "base/immutable_unittest.cc",
     "base/model_type_unittest.cc",
+    "base/nigori_key_bag_unittest.cc",
     "base/nigori_unittest.cc",
     "base/node_ordinal_unittest.cc",
     "base/ordinal_unittest.cc",
diff --git a/components/sync/base/BUILD.gn b/components/sync/base/BUILD.gn
index 3359e4f..d493003 100644
--- a/components/sync/base/BUILD.gn
+++ b/components/sync/base/BUILD.gn
@@ -36,6 +36,8 @@
     "model_type.h",
     "nigori.cc",
     "nigori.h",
+    "nigori_key_bag.cc",
+    "nigori_key_bag.h",
     "node_ordinal.cc",
     "node_ordinal.h",
     "ordinal.h",
diff --git a/components/sync/base/cryptographer.cc b/components/sync/base/cryptographer.cc
index e308c53..b0badd0 100644
--- a/components/sync/base/cryptographer.cc
+++ b/components/sync/base/cryptographer.cc
@@ -16,8 +16,6 @@
 
 namespace syncer {
 
-const char kNigoriTag[] = "google_chrome_nigori";
-
 KeyParams::KeyParams(KeyDerivationParams derivation_params,
                      const std::string& password)
     : derivation_params(derivation_params), password(password) {}
@@ -26,21 +24,15 @@
 KeyParams::KeyParams(KeyParams&& other) = default;
 KeyParams::~KeyParams() = default;
 
-Cryptographer::Cryptographer(Encryptor* encryptor) : encryptor_(encryptor) {
+Cryptographer::Cryptographer(Encryptor* encryptor)
+    : encryptor_(encryptor), key_bag_(NigoriKeyBag::CreateEmpty()) {
   DCHECK(encryptor);
 }
 
 Cryptographer::Cryptographer(const Cryptographer& other)
     : encryptor_(other.encryptor_),
+      key_bag_(other.key_bag_.Clone()),
       default_nigori_name_(other.default_nigori_name_) {
-  for (auto it = other.nigoris_.begin(); it != other.nigoris_.end(); ++it) {
-    std::string user_key, encryption_key, mac_key;
-    it->second->ExportKeys(&user_key, &encryption_key, &mac_key);
-    auto nigori_copy = std::make_unique<Nigori>();
-    nigori_copy->InitByImport(user_key, encryption_key, mac_key);
-    nigoris_.emplace(it->first, std::move(nigori_copy));
-  }
-
   if (other.pending_keys_) {
     pending_keys_ =
         std::make_unique<sync_pb::EncryptedData>(*(other.pending_keys_));
@@ -63,7 +55,7 @@
 }
 
 bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const {
-  return nigoris_.end() != nigoris_.find(data.key_name());
+  return key_bag_.HasKey(data.key_name());
 }
 
 bool Cryptographer::CanDecryptUsingDefaultKey(
@@ -100,18 +92,12 @@
     }
   }
 
-  auto default_nigori = nigoris_.find(default_nigori_name_);
-  if (default_nigori == nigoris_.end()) {
+  if (!key_bag_.HasKey(default_nigori_name_)) {
     LOG(ERROR) << "Corrupt default key.";
     return false;
   }
 
-  encrypted->set_key_name(default_nigori_name_);
-  if (!default_nigori->second->Encrypt(serialized, encrypted->mutable_blob())) {
-    LOG(ERROR) << "Failed to encrypt data.";
-    return false;
-  }
-  return true;
+  return key_bag_.EncryptWithKey(default_nigori_name_, serialized, encrypted);
 }
 
 bool Cryptographer::Decrypt(const sync_pb::EncryptedData& encrypted,
@@ -126,35 +112,15 @@
 
 bool Cryptographer::DecryptToString(const sync_pb::EncryptedData& encrypted,
                                     std::string* decrypted) const {
-  decrypted->clear();
-  auto it = nigoris_.find(encrypted.key_name());
-  if (nigoris_.end() == it) {
-    // The key used to encrypt the blob is not part of the set of installed
-    // nigoris.
-    LOG(ERROR) << "Cannot decrypt message";
-    return false;
-  }
-
-  if (!it->second->Decrypt(encrypted.blob(), decrypted)) {
-    return false;
-  }
-
-  return true;
+  return key_bag_.Decrypt(encrypted, decrypted);
 }
 
 bool Cryptographer::GetKeys(sync_pb::EncryptedData* encrypted) const {
   DCHECK(encrypted);
-  DCHECK(!nigoris_.empty());
+  DCHECK_NE(size_t(0), key_bag_.size());
 
   // Create a bag of all the Nigori parameters we know about.
-  sync_pb::NigoriKeyBag bag;
-  for (const auto& key_name_and_nigori : nigoris_) {
-    const Nigori& nigori = *key_name_and_nigori.second;
-    sync_pb::NigoriKey* key = bag.add_key();
-    key->set_name(key_name_and_nigori.first);
-    nigori.ExportKeys(key->mutable_user_key(), key->mutable_encryption_key(),
-                      key->mutable_mac_key());
-  }
+  sync_pb::NigoriKeyBag bag = key_bag_.ToProto();
 
   // Encrypt the bag with the default Nigori.
   return Encrypt(bag, encrypted);
@@ -191,14 +157,12 @@
 
 bool Cryptographer::AddKeyImpl(std::unique_ptr<Nigori> initialized_nigori,
                                bool set_as_default) {
-  std::string name;
-  if (!initialized_nigori->Permute(Nigori::Password, kNigoriKeyName, &name)) {
+  std::string key_name = key_bag_.AddKey(std::move(initialized_nigori));
+  if (key_name.empty()) {
     NOTREACHED();
     return false;
   }
 
-  nigoris_[name] = std::move(initialized_nigori);
-
   // Check if the key we just added can decrypt the pending keys and add them
   // too if so.
   if (pending_keys_.get() && CanDecrypt(*pending_keys_)) {
@@ -211,7 +175,7 @@
 
   // The just-added key takes priority over the pending keys as default.
   if (set_as_default)
-    SetDefaultKey(name);
+    SetDefaultKey(key_name);
   return true;
 }
 
@@ -225,10 +189,14 @@
 }
 
 void Cryptographer::SetDefaultKey(const std::string& key_name) {
-  DCHECK(nigoris_.end() != nigoris_.find(key_name));
+  DCHECK(key_bag_.HasKey(key_name));
   default_nigori_name_ = key_name;
 }
 
+bool Cryptographer::is_initialized() const {
+  return !default_nigori_name_.empty();
+}
+
 void Cryptographer::SetPendingKeys(const sync_pb::EncryptedData& encrypted) {
   DCHECK(!CanDecrypt(encrypted));
   DCHECK(!encrypted.blob().empty());
@@ -299,20 +267,7 @@
 }
 
 void Cryptographer::InstallKeyBag(const sync_pb::NigoriKeyBag& bag) {
-  int key_size = bag.key_size();
-  for (int i = 0; i < key_size; ++i) {
-    const sync_pb::NigoriKey key = bag.key(i);
-    // Only use this key if we don't already know about it.
-    if (nigoris_.end() == nigoris_.find(key.name())) {
-      std::unique_ptr<Nigori> new_nigori(new Nigori);
-      if (!new_nigori->InitByImport(key.user_key(), key.encryption_key(),
-                                    key.mac_key())) {
-        NOTREACHED();
-        continue;
-      }
-      nigoris_[key.name()] = std::move(new_nigori);
-    }
-  }
+  key_bag_.AddAllUnknownKeysFrom(NigoriKeyBag::CreateFromProto(bag));
 }
 
 bool Cryptographer::KeybagIsStale(
@@ -331,7 +286,7 @@
                << "Assuming keybag is corrupted.";
     return true;
   }
-  if (static_cast<size_t>(bag.key_size()) < nigoris_.size())
+  if (static_cast<size_t>(bag.key_size()) < key_bag_.size())
     return true;
   return false;
 }
@@ -343,12 +298,8 @@
 std::string Cryptographer::GetDefaultNigoriKeyData() const {
   if (!is_initialized())
     return std::string();
-  auto iter = nigoris_.find(default_nigori_name_);
-  if (iter == nigoris_.end())
-    return std::string();
-  sync_pb::NigoriKey key;
-  iter->second->ExportKeys(key.mutable_user_key(), key.mutable_encryption_key(),
-                           key.mutable_mac_key());
+  sync_pb::NigoriKey key = key_bag_.ExportKey(default_nigori_name_);
+  key.clear_name();
   return key.SerializeAsString();
 }
 
diff --git a/components/sync/base/cryptographer.h b/components/sync/base/cryptographer.h
index 8dd686c..40a5ab8 100644
--- a/components/sync/base/cryptographer.h
+++ b/components/sync/base/cryptographer.h
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "components/sync/base/nigori.h"
+#include "components/sync/base/nigori_key_bag.h"
 #include "components/sync/base/passphrase_enums.h"
 #include "components/sync/protocol/encryption.pb.h"
 
@@ -22,8 +23,6 @@
 
 class Encryptor;
 
-extern const char kNigoriTag[];
-
 // The parameters used to initialize a Nigori instance.
 // TODO(davidovic): Stop relying on KeyParams and inline it, because it's now
 // just a pair of KeyDerivationParams and passphrase.
@@ -159,9 +158,7 @@
   // correspond to a nigori that has already been installed into the keybag.
   void SetDefaultKey(const std::string& key_name);
 
-  bool is_initialized() const {
-    return !nigoris_.empty() && !default_nigori_name_.empty();
-  }
+  bool is_initialized() const;
 
   // Returns whether this Cryptographer is ready to encrypt and decrypt data.
   bool is_ready() const { return is_initialized() && !has_pending_keys(); }
@@ -176,8 +173,8 @@
 
   Encryptor* encryptor() const { return encryptor_; }
 
-  // Returns true if |keybag| is decryptable and either is a subset of nigoris_
-  // and/or has a different default key.
+  // Returns true if |keybag| is decryptable and either is a subset of
+  // |key_bag_| and/or has a different default key.
   bool KeybagIsStale(const sync_pb::EncryptedData& keybag) const;
 
   // Returns the name of the Nigori key currently used for encryption.
@@ -192,8 +189,6 @@
   bool ImportNigoriKey(const std::string& serialized_nigori_key);
 
  private:
-  using NigoriMap = std::map<std::string, std::unique_ptr<const Nigori>>;
-
   // Helper method to instantiate Nigori instances for each set of key
   // parameters in |bag|.
   // Does not update the default nigori.
@@ -208,11 +203,11 @@
 
   Encryptor* const encryptor_;
 
-  // The Nigoris we know about, mapped by key name.
-  NigoriMap nigoris_;
+  // The actual keys we know about.
+  NigoriKeyBag key_bag_;
 
   // The key name associated with the default nigori. If non-empty, must
-  // correspond to a nigori within |nigoris_|.
+  // correspond to a nigori within |key_bag_|.
   std::string default_nigori_name_;
 
   std::unique_ptr<sync_pb::EncryptedData> pending_keys_;
diff --git a/components/sync/base/nigori_key_bag.cc b/components/sync/base/nigori_key_bag.cc
new file mode 100644
index 0000000..f394eb6
--- /dev/null
+++ b/components/sync/base/nigori_key_bag.cc
@@ -0,0 +1,166 @@
+// Copyright 2019 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 "components/sync/base/nigori_key_bag.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "components/sync/base/nigori.h"
+#include "components/sync/protocol/nigori_specifics.pb.h"
+
+namespace syncer {
+namespace {
+
+std::string ComputeNigoriName(const Nigori& nigori) {
+  std::string key_name;
+  nigori.Permute(Nigori::Password, kNigoriKeyName, &key_name);
+  return key_name;
+}
+
+// Note that |key_name| is redundant but computing the name from |nigori| can be
+// expensive.
+sync_pb::NigoriKey NigoriToProto(const Nigori& nigori,
+                                 const std::string& key_name) {
+  DCHECK_EQ(key_name, ComputeNigoriName(nigori));
+
+  sync_pb::NigoriKey proto;
+  proto.set_name(key_name);
+  nigori.ExportKeys(proto.mutable_user_key(), proto.mutable_encryption_key(),
+                    proto.mutable_mac_key());
+  return proto;
+}
+
+std::unique_ptr<Nigori> NigoriFromProto(const sync_pb::NigoriKey& proto) {
+  auto nigori = std::make_unique<Nigori>();
+  if (!nigori->InitByImport(proto.user_key(), proto.encryption_key(),
+                            proto.mac_key())) {
+    return nullptr;
+  }
+  return nigori;
+}
+
+std::unique_ptr<Nigori> CloneNigori(const Nigori& nigori) {
+  std::string user_key;
+  std::string encryption_key;
+  std::string mac_key;
+  nigori.ExportKeys(&user_key, &encryption_key, &mac_key);
+
+  auto nigori_copy = std::make_unique<Nigori>();
+  bool success = nigori_copy->InitByImport(user_key, encryption_key, mac_key);
+  DCHECK(success);
+  return nigori_copy;
+}
+
+}  // namespace
+
+// static
+NigoriKeyBag NigoriKeyBag::CreateEmpty() {
+  return NigoriKeyBag();
+}
+
+// static
+NigoriKeyBag NigoriKeyBag::CreateFromProto(const sync_pb::NigoriKeyBag& proto) {
+  NigoriKeyBag output;
+  for (const sync_pb::NigoriKey& key : proto.key()) {
+    auto nigori = NigoriFromProto(key);
+    if (!nigori) {
+      NOTREACHED();
+      continue;
+    }
+    output.nigori_map_[key.name()] = std::move(nigori);
+  }
+  return output;
+}
+
+NigoriKeyBag::NigoriKeyBag(NigoriKeyBag&& other) = default;
+
+NigoriKeyBag::~NigoriKeyBag() = default;
+
+sync_pb::NigoriKeyBag NigoriKeyBag::ToProto() const {
+  sync_pb::NigoriKeyBag output;
+  for (const auto& key_name_and_nigori : nigori_map_) {
+    *output.add_key() =
+        NigoriToProto(*key_name_and_nigori.second, key_name_and_nigori.first);
+  }
+  return output;
+}
+
+NigoriKeyBag NigoriKeyBag::Clone() const {
+  NigoriKeyBag copy;
+  copy.AddAllUnknownKeysFrom(*this);
+  return copy;
+}
+
+size_t NigoriKeyBag::size() const {
+  return nigori_map_.size();
+}
+
+bool NigoriKeyBag::HasKey(const std::string& key_name) const {
+  return nigori_map_.count(key_name) != 0;
+}
+
+sync_pb::NigoriKey NigoriKeyBag::ExportKey(const std::string& key_name) const {
+  DCHECK(HasKey(key_name));
+  return NigoriToProto(*nigori_map_.find(key_name)->second, key_name);
+}
+
+std::string NigoriKeyBag::AddKey(std::unique_ptr<Nigori> nigori) {
+  DCHECK(nigori);
+  const std::string key_name = ComputeNigoriName(*nigori);
+  if (key_name.empty()) {
+    NOTREACHED();
+    return key_name;
+  }
+  nigori_map_.emplace(key_name, std::move(nigori));
+  return key_name;
+}
+
+void NigoriKeyBag::AddAllUnknownKeysFrom(const NigoriKeyBag& other) {
+  for (const auto& key_name_and_nigori : other.nigori_map_) {
+    // Only use this key if we don't already know about it.
+    nigori_map_.emplace(key_name_and_nigori.first,
+                        CloneNigori(*key_name_and_nigori.second));
+  }
+}
+
+bool NigoriKeyBag::EncryptWithKey(
+    const std::string& key_name,
+    const std::string& input,
+    sync_pb::EncryptedData* encrypted_output) const {
+  DCHECK(encrypted_output);
+  DCHECK(HasKey(key_name));
+
+  encrypted_output->Clear();
+
+  if (!nigori_map_.find(key_name)->second->Encrypt(
+          input, encrypted_output->mutable_blob())) {
+    DLOG(ERROR) << "Failed to encrypt data.";
+    return false;
+  }
+
+  encrypted_output->set_key_name(key_name);
+  return true;
+}
+
+bool NigoriKeyBag::Decrypt(const sync_pb::EncryptedData& encrypted_input,
+                           std::string* decrypted_output) const {
+  DCHECK(decrypted_output);
+
+  decrypted_output->clear();
+
+  auto it = nigori_map_.find(encrypted_input.key_name());
+  if (it == nigori_map_.end()) {
+    // The key used to encrypt the blob is not part of the set of installed
+    // nigoris.
+    DLOG(ERROR) << "Cannot decrypt message";
+    return false;
+  }
+
+  return it->second->Decrypt(encrypted_input.blob(), decrypted_output);
+}
+
+NigoriKeyBag::NigoriKeyBag() = default;
+
+}  // namespace syncer
diff --git a/components/sync/base/nigori_key_bag.h b/components/sync/base/nigori_key_bag.h
new file mode 100644
index 0000000..43b276d
--- /dev/null
+++ b/components/sync/base/nigori_key_bag.h
@@ -0,0 +1,73 @@
+// Copyright 2019 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 COMPONENTS_SYNC_BASE_NIGORI_KEY_BAG_H_
+#define COMPONENTS_SYNC_BASE_NIGORI_KEY_BAG_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+namespace sync_pb {
+class EncryptedData;
+class NigoriKey;
+class NigoriKeyBag;
+}  // namespace sync_pb
+
+namespace syncer {
+
+class Nigori;
+
+// A set of Nigori keys, aka keybag. Note that there is no notion of default
+// key.
+class NigoriKeyBag {
+ public:
+  static NigoriKeyBag CreateEmpty();
+  // Deserialization from proto.
+  static NigoriKeyBag CreateFromProto(const sync_pb::NigoriKeyBag& key_bag);
+
+  NigoriKeyBag(NigoriKeyBag&& other);
+  ~NigoriKeyBag();
+
+  // Serialization to proto.
+  sync_pb::NigoriKeyBag ToProto() const;
+
+  // Makes a deep copy of |*this|.
+  NigoriKeyBag Clone() const;
+
+  size_t size() const;
+  bool HasKey(const std::string& key_name) const;
+
+  // |key_name| must exist in this keybag.
+  sync_pb::NigoriKey ExportKey(const std::string& key_name) const;
+
+  // Adds a new key to the keybag. Returns the name of the key or an empty
+  // string in case of failure.
+  std::string AddKey(std::unique_ptr<Nigori> nigori);
+
+  // Merges all keys from another keybag, which means adding all keys that we
+  // don't know about.
+  void AddAllUnknownKeysFrom(const NigoriKeyBag& other);
+
+  // Encryption of strings (possibly binary). Returns true if success.
+  // |key_name| must be known. |encrypted_output| must not be null.
+  bool EncryptWithKey(const std::string& key_name,
+                      const std::string& input,
+                      sync_pb::EncryptedData* encrypted_output) const;
+
+  // Decryption of strings (possibly binary). Returns true if success.
+  // |decrypted_output| must not be null.
+  bool Decrypt(const sync_pb::EncryptedData& encrypted_input,
+               std::string* decrypted_output) const;
+
+ private:
+  NigoriKeyBag();
+
+  // The Nigoris we know about, mapped by key name.
+  std::map<std::string, std::unique_ptr<const Nigori>> nigori_map_;
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_BASE_NIGORI_KEY_BAG_H_
diff --git a/components/sync/base/nigori_key_bag_unittest.cc b/components/sync/base/nigori_key_bag_unittest.cc
new file mode 100644
index 0000000..b563d5b
--- /dev/null
+++ b/components/sync/base/nigori_key_bag_unittest.cc
@@ -0,0 +1,100 @@
+// Copyright 2019 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 "components/sync/base/nigori_key_bag.h"
+
+#include "components/sync/base/nigori.h"
+#include "components/sync/protocol/nigori_specifics.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+namespace {
+
+using testing::Eq;
+using testing::Ne;
+using testing::SizeIs;
+
+std::unique_ptr<Nigori> CreateTestNigori(const std::string& password) {
+  auto nigori = std::make_unique<Nigori>();
+  nigori->InitByDerivation(KeyDerivationParams::CreateForPbkdf2(), password);
+  return nigori;
+}
+
+TEST(NigoriKeyBagTest, ShouldCreateEmpty) {
+  const NigoriKeyBag key_bag = NigoriKeyBag::CreateEmpty();
+  EXPECT_THAT(key_bag, SizeIs(0));
+  EXPECT_FALSE(key_bag.HasKey("foo"));
+}
+
+TEST(NigoriKeyBagTest, ShouldAddKeys) {
+  NigoriKeyBag key_bag = NigoriKeyBag::CreateEmpty();
+  ASSERT_THAT(key_bag, SizeIs(0));
+
+  const std::string key_name1 = key_bag.AddKey(CreateTestNigori("password1"));
+  EXPECT_THAT(key_name1, Ne(""));
+  EXPECT_THAT(key_bag, SizeIs(1));
+  EXPECT_TRUE(key_bag.HasKey(key_name1));
+
+  const std::string key_name2 = key_bag.AddKey(CreateTestNigori("password2"));
+  EXPECT_THAT(key_name2, Ne(""));
+  EXPECT_THAT(key_name2, Ne(key_name1));
+  EXPECT_THAT(key_bag, SizeIs(2));
+  EXPECT_TRUE(key_bag.HasKey(key_name1));
+  EXPECT_TRUE(key_bag.HasKey(key_name2));
+}
+
+TEST(NigoriKeyBagTest, ShouldConvertEmptyToProto) {
+  EXPECT_EQ(sync_pb::NigoriKeyBag().SerializeAsString(),
+            NigoriKeyBag::CreateEmpty().ToProto().SerializeAsString());
+}
+
+TEST(NigoriKeyBagTest, ShouldConvertNonEmptyToProto) {
+  NigoriKeyBag key_bag = NigoriKeyBag::CreateEmpty();
+  const std::string key_name = key_bag.AddKey(CreateTestNigori("password1"));
+
+  sync_pb::NigoriKeyBag proto = key_bag.ToProto();
+  ASSERT_THAT(proto.key(), SizeIs(1));
+  EXPECT_THAT(proto.key(0).name(), Eq(key_name));
+  EXPECT_THAT(proto.key(0).user_key(), Ne(""));
+  EXPECT_THAT(proto.key(0).encryption_key(), Ne(""));
+  EXPECT_THAT(proto.key(0).mac_key(), Ne(""));
+}
+
+TEST(NigoriKeyBagTest, ShouldCreateEmptyFromProto) {
+  EXPECT_THAT(NigoriKeyBag::CreateFromProto(sync_pb::NigoriKeyBag()),
+              SizeIs(0));
+}
+
+TEST(NigoriKeyBagTest, ShouldCreateNonEmptyFromProto) {
+  NigoriKeyBag original_key_bag = NigoriKeyBag::CreateEmpty();
+  const std::string key_name1 =
+      original_key_bag.AddKey(CreateTestNigori("password1"));
+  const std::string key_name2 =
+      original_key_bag.AddKey(CreateTestNigori("password2"));
+  ASSERT_THAT(original_key_bag, SizeIs(2));
+
+  const NigoriKeyBag restored_key_bag =
+      NigoriKeyBag::CreateFromProto(original_key_bag.ToProto());
+  EXPECT_THAT(restored_key_bag, SizeIs(2));
+  EXPECT_TRUE(restored_key_bag.HasKey(key_name1));
+  EXPECT_TRUE(restored_key_bag.HasKey(key_name2));
+}
+
+TEST(NigoriKeyBagTest, ShouldClone) {
+  NigoriKeyBag original_key_bag = NigoriKeyBag::CreateEmpty();
+  const std::string key_name1 =
+      original_key_bag.AddKey(CreateTestNigori("password1"));
+  const std::string key_name2 =
+      original_key_bag.AddKey(CreateTestNigori("password2"));
+  ASSERT_THAT(original_key_bag, SizeIs(2));
+
+  const NigoriKeyBag cloned_key_bag = original_key_bag.Clone();
+  EXPECT_THAT(cloned_key_bag, SizeIs(2));
+  EXPECT_TRUE(cloned_key_bag.HasKey(key_name1));
+  EXPECT_TRUE(cloned_key_bag.HasKey(key_name2));
+}
+
+}  // namespace
+}  // namespace syncer
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc
index 8ccbcfa1..f6cdade 100644
--- a/components/sync/driver/glue/sync_engine_backend.cc
+++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -606,12 +606,6 @@
   sync_manager_->UpdateInvalidationClientId(client_id);
 }
 
-base::WeakPtr<ModelTypeControllerDelegate>
-SyncEngineBackend::GetNigoriControllerDelegate() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return sync_manager_->GetNigoriControllerDelegate();
-}
-
 bool SyncEngineBackend::HasUnsyncedItemsForTest() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(sync_manager_);
diff --git a/components/sync/driver/glue/sync_engine_backend.h b/components/sync/driver/glue/sync_engine_backend.h
index cb4094d..e4ff0763b 100644
--- a/components/sync/driver/glue/sync_engine_backend.h
+++ b/components/sync/driver/glue/sync_engine_backend.h
@@ -172,10 +172,6 @@
   // Notify about change in client id.
   void DoOnInvalidatorClientIdChange(const std::string& client_id);
 
-  // Returns ModelTypeControllerDelegate for Nigori. USS implementation of
-  // Nigori must be enabled.
-  base::WeakPtr<ModelTypeControllerDelegate> GetNigoriControllerDelegate();
-
   bool HasUnsyncedItemsForTest() const;
 
  private:
diff --git a/components/sync/driver/glue/sync_engine_impl.cc b/components/sync/driver/glue/sync_engine_impl.cc
index 5140252..80bf794d 100644
--- a/components/sync/driver/glue/sync_engine_impl.cc
+++ b/components/sync/driver/glue/sync_engine_impl.cc
@@ -29,7 +29,6 @@
 #include "components/sync/engine/sync_engine_host.h"
 #include "components/sync/engine/sync_manager_factory.h"
 #include "components/sync/engine/sync_string_conversions.h"
-#include "components/sync/model_impl/proxy_model_type_controller_delegate.h"
 #include "components/sync/syncable/base_transaction.h"
 
 namespace syncer {
@@ -450,15 +449,6 @@
   DCHECK(success);
 }
 
-std::unique_ptr<ModelTypeControllerDelegate>
-SyncEngineImpl::GetNigoriControllerDelegate() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return std::make_unique<ProxyModelTypeControllerDelegate>(
-      sync_task_runner_,
-      base::BindRepeating(&SyncEngineBackend::GetNigoriControllerDelegate,
-                          base::RetainedRef(backend_)));
-}
-
 void SyncEngineImpl::OnInvalidatorClientIdChange(const std::string& client_id) {
   sync_task_runner_->PostTask(
       FROM_HERE,
diff --git a/components/sync/driver/glue/sync_engine_impl.h b/components/sync/driver/glue/sync_engine_impl.h
index 9fce092e..fa18e824 100644
--- a/components/sync/driver/glue/sync_engine_impl.h
+++ b/components/sync/driver/glue/sync_engine_impl.h
@@ -92,8 +92,6 @@
                           bool empty_jar,
                           const base::Closure& callback) override;
   void SetInvalidationsForSessionsEnabled(bool enabled) override;
-  std::unique_ptr<ModelTypeControllerDelegate> GetNigoriControllerDelegate()
-      override;
 
   // InvalidationHandler implementation.
   void OnInvalidatorStateChange(InvalidatorState state) override;
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index cae97d3..ead807e7 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -24,7 +24,6 @@
 #include "components/sync/driver/backend_migrator.h"
 #include "components/sync/driver/configure_context.h"
 #include "components/sync/driver/directory_data_type_controller.h"
-#include "components/sync/driver/model_type_controller.h"
 #include "components/sync/driver/sync_api_component_factory.h"
 #include "components/sync/driver/sync_auth_manager.h"
 #include "components/sync/driver/sync_driver_switches.h"
@@ -36,7 +35,6 @@
 #include "components/sync/engine/net/network_resources.h"
 #include "components/sync/engine/polling_constants.h"
 #include "components/sync/engine/sync_encryption_handler.h"
-#include "components/sync/engine/sync_engine_switches.h"
 #include "components/sync/model/sync_error.h"
 #include "components/sync/syncable/user_share.h"
 #include "components/version_info/version_info_values.h"
@@ -582,14 +580,6 @@
   migrator_.reset();
   sync_js_controller_.AttachJsBackend(WeakHandle<JsBackend>());
 
-  if (base::FeatureList::IsEnabled(switches::kSyncUSSNigori)) {
-    // We need to remove ModelTypeController for Nigori before the engine
-    // shutdown because it's no longer valid after shutdown.
-    // TODO(crbug.com/943019): This logic can be removed if Nigori local
-    // model will be moved to UI thread.
-    data_type_controllers_.erase(NIGORI);
-  }
-
   engine_->Shutdown(reason);
   engine_.reset();
 
@@ -868,16 +858,6 @@
     UpdateLastSyncedTime();
   }
 
-  if (base::FeatureList::IsEnabled(switches::kSyncUSSNigori)) {
-    // Nigori's ModelTypeController can only be created after sync engine
-    // initialization. Therefore, it cannot be created with other controllers
-    // in BuildDataTypeControllerMap().
-    // TODO(crbug.com/943019): This logic can be removed if Nigori local
-    // model will be moved to UI thread.
-    data_type_controllers_[NIGORI] = std::make_unique<ModelTypeController>(
-        NIGORI, engine_->GetNigoriControllerDelegate());
-  }
-
   data_type_manager_ =
       sync_client_->GetSyncApiComponentFactory()->CreateDataTypeManager(
           initial_types, debug_info_listener, &data_type_controllers_,
diff --git a/components/sync/engine/fake_sync_engine.cc b/components/sync/engine/fake_sync_engine.cc
index fe06319..c7220657 100644
--- a/components/sync/engine/fake_sync_engine.cc
+++ b/components/sync/engine/fake_sync_engine.cc
@@ -106,11 +106,6 @@
   }
 }
 
-std::unique_ptr<ModelTypeControllerDelegate>
-FakeSyncEngine::GetNigoriControllerDelegate() {
-  return nullptr;
-}
-
 void FakeSyncEngine::SetInvalidationsForSessionsEnabled(bool enabled) {}
 
 }  // namespace syncer
diff --git a/components/sync/engine/fake_sync_engine.h b/components/sync/engine/fake_sync_engine.h
index 5564a465..99703dc 100644
--- a/components/sync/engine/fake_sync_engine.h
+++ b/components/sync/engine/fake_sync_engine.h
@@ -89,9 +89,6 @@
                           const base::Closure& callback) override;
   void SetInvalidationsForSessionsEnabled(bool enabled) override;
 
-  std::unique_ptr<ModelTypeControllerDelegate> GetNigoriControllerDelegate()
-      override;
-
   void set_fail_initial_download(bool should_fail);
 
  private:
diff --git a/components/sync/engine/fake_sync_manager.cc b/components/sync/engine/fake_sync_manager.cc
index 18a4664..0577ca8 100644
--- a/components/sync/engine/fake_sync_manager.cc
+++ b/components/sync/engine/fake_sync_manager.cc
@@ -226,11 +226,6 @@
   return &fake_encryption_handler_;
 }
 
-base::WeakPtr<ModelTypeControllerDelegate>
-FakeSyncManager::GetNigoriControllerDelegate() {
-  return nullptr;
-}
-
 std::vector<std::unique_ptr<ProtocolEvent>>
 FakeSyncManager::GetBufferedProtocolEvents() {
   return std::vector<std::unique_ptr<ProtocolEvent>>();
diff --git a/components/sync/engine/fake_sync_manager.h b/components/sync/engine/fake_sync_manager.h
index 4e921491..82125b3 100644
--- a/components/sync/engine/fake_sync_manager.h
+++ b/components/sync/engine/fake_sync_manager.h
@@ -105,8 +105,6 @@
   std::string bag_of_chips() override;
   bool HasUnsyncedItemsForTest() override;
   SyncEncryptionHandler* GetEncryptionHandler() override;
-  base::WeakPtr<ModelTypeControllerDelegate> GetNigoriControllerDelegate()
-      override;
   std::vector<std::unique_ptr<ProtocolEvent>> GetBufferedProtocolEvents()
       override;
   void RefreshTypes(ModelTypeSet types) override;
diff --git a/components/sync/engine/mock_sync_engine.h b/components/sync/engine/mock_sync_engine.h
index e8ac6d66..73586d7 100644
--- a/components/sync/engine/mock_sync_engine.h
+++ b/components/sync/engine/mock_sync_engine.h
@@ -10,7 +10,6 @@
 
 #include "components/sync/engine/data_type_activation_response.h"
 #include "components/sync/engine/sync_engine.h"
-#include "components/sync/model/model_type_controller_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace syncer {
@@ -61,8 +60,6 @@
   MOCK_METHOD1(ClearServerData, void(const base::Closure&));
   MOCK_METHOD3(OnCookieJarChanged, void(bool, bool, const base::Closure&));
   MOCK_METHOD1(SetInvalidationsForSessionsEnabled, void(bool));
-  MOCK_METHOD0(GetNigoriControllerDelegate,
-               std::unique_ptr<ModelTypeControllerDelegate>());
 };
 
 }  // namespace syncer
diff --git a/components/sync/engine/sync_engine.h b/components/sync/engine/sync_engine.h
index 2aa5f5c2..8d53e56 100644
--- a/components/sync/engine/sync_engine.h
+++ b/components/sync/engine/sync_engine.h
@@ -33,7 +33,6 @@
 
 class CancelationSignal;
 class HttpPostProviderFactory;
-class ModelTypeControllerDelegate;
 class SyncEngineHost;
 class SyncManagerFactory;
 class UnrecoverableErrorHandler;
@@ -192,10 +191,6 @@
   // Enables/Disables invalidations for session sync related datatypes.
   virtual void SetInvalidationsForSessionsEnabled(bool enabled) = 0;
 
-  // Returns ModelTypeControllerDelegate for Nigori.
-  virtual std::unique_ptr<ModelTypeControllerDelegate>
-  GetNigoriControllerDelegate() = 0;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(SyncEngine);
 };
diff --git a/components/sync/engine/sync_manager.h b/components/sync/engine/sync_manager.h
index 588abe5..9dbc2298 100644
--- a/components/sync/engine/sync_manager.h
+++ b/components/sync/engine/sync_manager.h
@@ -50,7 +50,6 @@
 class ExtensionsActivity;
 class JsBackend;
 class JsEventHandler;
-class ModelTypeControllerDelegate;
 class ProtocolEvent;
 class SyncCycleSnapshot;
 class TypeDebugInfoObserver;
@@ -364,11 +363,6 @@
   // Returns the SyncManager's encryption handler.
   virtual SyncEncryptionHandler* GetEncryptionHandler() = 0;
 
-  // Returns ModelTypeControllerDelegate for Nigori. USS implementation of
-  // Nigori must be enabled.
-  virtual base::WeakPtr<ModelTypeControllerDelegate>
-  GetNigoriControllerDelegate() = 0;
-
   // Ask the SyncManager to fetch updates for the given types.
   virtual void RefreshTypes(ModelTypeSet types) = 0;
 
diff --git a/components/sync/engine_impl/apply_control_data_updates_unittest.cc b/components/sync/engine_impl/apply_control_data_updates_unittest.cc
index efcc4d1..b7b5629 100644
--- a/components/sync/engine_impl/apply_control_data_updates_unittest.cc
+++ b/components/sync/engine_impl/apply_control_data_updates_unittest.cc
@@ -33,11 +33,14 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace syncer {
+namespace {
 
 using syncable::MutableEntry;
 using syncable::UNITTEST;
 using syncable::Id;
 
+const char kNigoriTag[] = "google_chrome_nigori";
+
 class ApplyControlDataUpdatesTest : public ::testing::Test {
  public:
  protected:
@@ -878,4 +881,5 @@
   EXPECT_TRUE(directory()->InitialSyncEndedForType(NIGORI));
 }
 
+}  // namespace
 }  // namespace syncer
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc
index f7faf91..6116591 100644
--- a/components/sync/engine_impl/sync_manager_impl.cc
+++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -303,12 +303,8 @@
   syncable::NigoriHandler* nigori_handler = nullptr;
   KeystoreKeysHandler* keystore_keys_handler = nullptr;
   if (base::FeatureList::IsEnabled(switches::kSyncUSSNigori)) {
-    auto nigori_model_type_processor =
-        std::make_unique<NigoriModelTypeProcessor>();
-    nigori_controller_delegate_ =
-        nigori_model_type_processor->GetControllerDelegate();
     auto nigori_sync_bridge_impl = std::make_unique<NigoriSyncBridgeImpl>(
-        std::move(nigori_model_type_processor), args->encryptor);
+        std::make_unique<NigoriModelTypeProcessor>(), args->encryptor);
     keystore_keys_handler = nigori_sync_bridge_impl.get();
     sync_encryption_handler_ = std::move(nigori_sync_bridge_impl);
   } else {
@@ -1049,11 +1045,6 @@
   return sync_encryption_handler_.get();
 }
 
-base::WeakPtr<ModelTypeControllerDelegate>
-SyncManagerImpl::GetNigoriControllerDelegate() {
-  return nigori_controller_delegate_;
-}
-
 std::vector<std::unique_ptr<ProtocolEvent>>
 SyncManagerImpl::GetBufferedProtocolEvents() {
   return protocol_event_buffer_.GetBufferedProtocolEvents();
diff --git a/components/sync/engine_impl/sync_manager_impl.h b/components/sync/engine_impl/sync_manager_impl.h
index aec7ef5..f1081ad 100644
--- a/components/sync/engine_impl/sync_manager_impl.h
+++ b/components/sync/engine_impl/sync_manager_impl.h
@@ -98,8 +98,6 @@
   std::string bag_of_chips() override;
   bool HasUnsyncedItemsForTest() override;
   SyncEncryptionHandler* GetEncryptionHandler() override;
-  base::WeakPtr<ModelTypeControllerDelegate> GetNigoriControllerDelegate()
-      override;
   std::vector<std::unique_ptr<ProtocolEvent>> GetBufferedProtocolEvents()
       override;
   void RegisterDirectoryTypeDebugInfoObserver(
@@ -321,9 +319,6 @@
   // depending on whether USS implementation of Nigori is enabled or not.
   std::unique_ptr<SyncEncryptionHandler> sync_encryption_handler_;
 
-  // Initialized iff USS implementation of Nigori is enabled.
-  base::WeakPtr<ModelTypeControllerDelegate> nigori_controller_delegate_;
-
   std::unique_ptr<SyncEncryptionHandler::Observer> encryption_observer_proxy_;
 
   base::WeakPtrFactory<SyncManagerImpl> weak_ptr_factory_;
diff --git a/components/viz/common/quads/draw_quad_unittest.cc b/components/viz/common/quads/draw_quad_unittest.cc
index 25bba20..3aa7754 100644
--- a/components/viz/common/quads/draw_quad_unittest.cc
+++ b/components/viz/common/quads/draw_quad_unittest.cc
@@ -305,11 +305,12 @@
   CREATE_QUAD_ALL(SurfaceDrawQuad,
                   SurfaceRange(fallback_surface_id, primary_surface_id),
                   SK_ColorWHITE, /*stretch_content_to_fill_bounds=*/false,
-                  /*ignores_input_event=*/false);
+                  /*ignores_input_event=*/false, /*is_reflection=*/false);
   EXPECT_EQ(DrawQuad::Material::kSurfaceContent, copy_quad->material);
   EXPECT_EQ(primary_surface_id, copy_quad->surface_range.end());
   EXPECT_EQ(fallback_surface_id, *copy_quad->surface_range.start());
   EXPECT_FALSE(copy_quad->stretch_content_to_fill_bounds);
+  EXPECT_FALSE(copy_quad->is_reflection);
 }
 
 TEST(DrawQuadTest, CopyTextureDrawQuad) {
diff --git a/components/viz/common/quads/surface_draw_quad.cc b/components/viz/common/quads/surface_draw_quad.cc
index 3ff364e..bf6874c 100644
--- a/components/viz/common/quads/surface_draw_quad.cc
+++ b/components/viz/common/quads/surface_draw_quad.cc
@@ -43,13 +43,15 @@
                              const SurfaceRange& surface_range,
                              SkColor default_background_color,
                              bool stretch_content_to_fill_bounds,
-                             bool ignores_input_event) {
+                             bool ignores_input_event,
+                             bool is_reflection) {
   DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kSurfaceContent, rect,
                    visible_rect, needs_blending);
   this->surface_range = surface_range;
   this->default_background_color = default_background_color;
   this->stretch_content_to_fill_bounds = stretch_content_to_fill_bounds;
   this->ignores_input_event = ignores_input_event;
+  this->is_reflection = is_reflection;
 }
 
 const SurfaceDrawQuad* SurfaceDrawQuad::MaterialCast(const DrawQuad* quad) {
diff --git a/components/viz/common/quads/surface_draw_quad.h b/components/viz/common/quads/surface_draw_quad.h
index 17f3e60f..298f0711 100644
--- a/components/viz/common/quads/surface_draw_quad.h
+++ b/components/viz/common/quads/surface_draw_quad.h
@@ -38,7 +38,8 @@
               const SurfaceRange& surface_range,
               SkColor default_background_color,
               bool stretch_content_to_fill_bounds,
-              bool ignores_input_event);
+              bool ignores_input_event,
+              bool is_reflection);
 
   SurfaceRange surface_range;
   SkColor default_background_color = SK_ColorWHITE;
@@ -46,6 +47,7 @@
   // TODO(crbug.com/914530): Remove once VizHitTestSurfaceLayer is enabled by
   // default.
   bool ignores_input_event = false;
+  bool is_reflection = false;
 
   static const SurfaceDrawQuad* MaterialCast(const DrawQuad* quad);
 
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index e08f6f7..72e39af 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -340,7 +340,8 @@
                      surface_quad->visible_rect, target_transform, clip_rect,
                      surface_quad->stretch_content_to_fill_bounds, dest_pass,
                      ignore_undamaged, damage_rect_in_quad_space,
-                     damage_rect_in_quad_space_valid, rounded_corner_info);
+                     damage_rect_in_quad_space_valid, rounded_corner_info,
+                     surface_quad->is_reflection);
 }
 
 void SurfaceAggregator::EmitSurfaceContent(
@@ -356,7 +357,8 @@
     bool ignore_undamaged,
     gfx::Rect* damage_rect_in_quad_space,
     bool* damage_rect_in_quad_space_valid,
-    const RoundedCornerInfo& rounded_corner_info) {
+    const RoundedCornerInfo& rounded_corner_info,
+    bool is_reflection) {
   // If this surface's id is already in our referenced set then it creates
   // a cycle in the graph and should be dropped.
   SurfaceId surface_id = surface->surface_id();
@@ -424,7 +426,17 @@
                 : empty_map;
   gfx::Transform combined_transform = scaled_quad_to_target_transform;
   combined_transform.ConcatTransform(target_transform);
+
+  // If the SurfaceDrawQuad is marked as being reflected and surface contents
+  // are going to be scaled then keep the RenderPass. This allows the reflected
+  // surface to be drawn with AA enabled for smooth scaling and preserves the
+  // original reflector scaling behaviour which scaled a TextureLayer.
+  bool reflected_and_scaled =
+      is_reflection &&
+      !scaled_quad_to_target_transform.IsIdentityOrTranslation();
+
   bool merge_pass =
+      !reflected_and_scaled &&
       base::IsApproximatelyEqual(source_sqs->opacity, 1.f, kOpacityEpsilon) &&
       copy_requests.empty() && combined_transform.Preserves2dAxisAlignment() &&
       CanMergeRoundedCorner(rounded_corner_info, *render_pass_list.back());
diff --git a/components/viz/service/display/surface_aggregator.h b/components/viz/service/display/surface_aggregator.h
index a9e4c94..139e7c4f 100644
--- a/components/viz/service/display/surface_aggregator.h
+++ b/components/viz/service/display/surface_aggregator.h
@@ -133,7 +133,8 @@
                           bool ignore_undamaged,
                           gfx::Rect* damage_rect_in_quad_space,
                           bool* damage_rect_in_quad_space_valid,
-                          const RoundedCornerInfo& rounded_corner_info);
+                          const RoundedCornerInfo& rounded_corner_info,
+                          bool is_reflection);
 
   void EmitDefaultBackgroundColorQuad(
       const SurfaceDrawQuad* surface_quad,
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index db5ba6b..fb48557 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -1432,6 +1432,169 @@
   EXPECT_EQ(gfx::RectF(25.f, 12.5f), output_rect);
 }
 
+// Verify that a reflected SurfaceDrawQuad with scaling won't have the surfaces
+// root RenderPass merged with the RenderPass that embeds it. This ensures the
+// reflected pixels can be scaled with AA enabled.
+TEST_F(SurfaceAggregatorValidSurfaceTest, ReflectedSurfaceDrawQuadScaled) {
+  const SurfaceId root_surface_id(root_sink_->frame_sink_id(),
+                                  root_local_surface_id_);
+
+  // Submit a CompositorFrame for the primary display. This will get mirrored
+  // by the second display through surface embedding.
+  const gfx::Rect display_rect(0, 0, 100, 100);
+  {
+    auto pass = RenderPass::Create();
+    pass->SetNew(1, display_rect, display_rect, gfx::Transform());
+    auto* sqs = pass->CreateAndAppendSharedQuadState();
+    sqs->opacity = 1.f;
+
+    auto* solid_color_quad =
+        pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+    solid_color_quad->SetNew(sqs, display_rect, display_rect, SK_ColorRED,
+                             false);
+
+    CompositorFrame frame =
+        CompositorFrameBuilder().AddRenderPass(std::move(pass)).Build();
+    root_sink_->SubmitCompositorFrame(root_surface_id.local_surface_id(),
+                                      std::move(frame));
+  }
+
+  auto mirror_display_sink = std::make_unique<CompositorFrameSinkSupport>(
+      nullptr, &manager_, kArbitraryFrameSinkId1, true, kNeedsSyncPoints);
+
+  ParentLocalSurfaceIdAllocator lsi_allocator;
+  lsi_allocator.GenerateId();
+  LocalSurfaceId mirror_display_local_surface_id =
+      lsi_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id();
+
+  // The mirroring display size is smaller than the primary display. The
+  // mirrored content would be scaled to fit.
+  const gfx::Rect mirror_display_rect(80, 80);
+  gfx::Transform scale_transform;
+  scale_transform.Scale(0.8, 0.8);
+
+  {
+    auto pass = RenderPass::Create();
+    pass->SetNew(1, mirror_display_rect, mirror_display_rect, gfx::Transform());
+    auto* sqs = pass->CreateAndAppendSharedQuadState();
+    sqs->quad_to_target_transform = scale_transform;
+    sqs->opacity = 1.f;
+
+    auto* surface_quad = pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
+    surface_quad->SetAll(sqs, display_rect, display_rect,
+                         /*needs_blending=*/false,
+                         SurfaceRange(base::nullopt, root_surface_id),
+                         SK_ColorBLACK,
+                         /*stretch_content_to_fill_bounds=*/true,
+                         /*ignores_input_event=*/false, /*is_reflection=*/true);
+
+    CompositorFrame frame =
+        CompositorFrameBuilder().AddRenderPass(std::move(pass)).Build();
+    mirror_display_sink->SubmitCompositorFrame(mirror_display_local_surface_id,
+                                               std::move(frame));
+  }
+
+  const SurfaceId mirror_display_surface_id(
+      mirror_display_sink->frame_sink_id(), mirror_display_local_surface_id);
+  CompositorFrame frame = AggregateFrame(mirror_display_surface_id);
+
+  // The reflected surface should be a separate RenderPass as it's scaled. The
+  // root RenderPass should have a single RenderPassDrawQuad.
+  EXPECT_EQ(2u, frame.render_pass_list.size());
+
+  auto* root_render_pass = frame.render_pass_list.back().get();
+  EXPECT_EQ(1u, root_render_pass->quad_list.size());
+
+  auto* output_quad = root_render_pass->quad_list.back();
+  EXPECT_EQ(DrawQuad::Material::kRenderPass, output_quad->material);
+
+  // The RenderPassDrawQuad should have the same scale transform that was
+  // applied to the SurfaceDrawQuad.
+  EXPECT_EQ(output_quad->shared_quad_state->quad_to_target_transform,
+            scale_transform);
+}
+
+// Verify that a reflected SurfaceDrawQuad with no scaling has the surfaces root
+// RenderPass merged with the RenderPass that embeds it.
+TEST_F(SurfaceAggregatorValidSurfaceTest, ReflectedSurfaceDrawQuadNotScaled) {
+  const SurfaceId root_surface_id(root_sink_->frame_sink_id(),
+                                  root_local_surface_id_);
+
+  // Submit a CompositorFrame for the primary display. This will get mirrored
+  // by the second display through surface embedding.
+  const gfx::Rect display_rect(0, 0, 100, 100);
+  {
+    auto pass = RenderPass::Create();
+    pass->SetNew(1, display_rect, display_rect, gfx::Transform());
+    auto* sqs = pass->CreateAndAppendSharedQuadState();
+    sqs->opacity = 1.f;
+
+    auto* solid_color_quad =
+        pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+    solid_color_quad->SetNew(sqs, display_rect, display_rect, SK_ColorRED,
+                             false);
+
+    CompositorFrame frame =
+        CompositorFrameBuilder().AddRenderPass(std::move(pass)).Build();
+    root_sink_->SubmitCompositorFrame(root_surface_id.local_surface_id(),
+                                      std::move(frame));
+  }
+
+  auto mirror_display_sink = std::make_unique<CompositorFrameSinkSupport>(
+      nullptr, &manager_, kArbitraryFrameSinkId1, true, kNeedsSyncPoints);
+
+  ParentLocalSurfaceIdAllocator lsi_allocator;
+  lsi_allocator.GenerateId();
+  LocalSurfaceId mirror_display_local_surface_id =
+      lsi_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id();
+
+  // The mirroring display is the same width but different height. The mirrored
+  // content would be letterboxed by translating it.
+  const gfx::Rect mirror_display_rect(120, 100);
+  gfx::Transform translate_transform;
+  translate_transform.Translate(10, 0);
+
+  {
+    auto pass = RenderPass::Create();
+    pass->SetNew(1, mirror_display_rect, mirror_display_rect, gfx::Transform());
+    auto* sqs = pass->CreateAndAppendSharedQuadState();
+    sqs->quad_to_target_transform = translate_transform;
+    sqs->opacity = 1.f;
+
+    auto* surface_quad = pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
+    surface_quad->SetAll(sqs, display_rect, display_rect,
+                         /*needs_blending=*/false,
+                         SurfaceRange(base::nullopt, root_surface_id),
+                         SK_ColorBLACK,
+                         /*stretch_content_to_fill_bounds=*/true,
+                         /*ignores_input_event=*/false, /*is_reflection=*/true);
+
+    CompositorFrame frame =
+        CompositorFrameBuilder().AddRenderPass(std::move(pass)).Build();
+    mirror_display_sink->SubmitCompositorFrame(mirror_display_local_surface_id,
+                                               std::move(frame));
+  }
+
+  const SurfaceId mirror_display_surface_id(
+      mirror_display_sink->frame_sink_id(), mirror_display_local_surface_id);
+  CompositorFrame frame = AggregateFrame(mirror_display_surface_id);
+
+  // The reflected surfaces RenderPass should be merged into the root RenderPass
+  // since it's not being scaled.
+  EXPECT_EQ(1u, frame.render_pass_list.size());
+
+  auto* root_render_pass = frame.render_pass_list.back().get();
+  EXPECT_EQ(1u, root_render_pass->quad_list.size());
+
+  auto* output_quad = root_render_pass->quad_list.back();
+  EXPECT_EQ(DrawQuad::Material::kSolidColor, output_quad->material);
+
+  // The quad from the embedded surface merged into the root RenderPass should
+  // have the same translate transform that was applied to the SurfaceDrawQuad.
+  EXPECT_EQ(output_quad->shared_quad_state->quad_to_target_transform,
+            translate_transform);
+}
+
 // This test verifies that in the presence of both primary Surface and fallback
 // Surface, the fallback will not be used.
 TEST_F(SurfaceAggregatorValidSurfaceTest, FallbackSurfaceReferenceWithPrimary) {
diff --git a/content/browser/browser_thread_impl.cc b/content/browser/browser_thread_impl.cc
index 40fd3dcd..500b4eb 100644
--- a/content/browser/browser_thread_impl.cc
+++ b/content/browser/browser_thread_impl.cc
@@ -260,4 +260,15 @@
   BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(identifier);
 }
 
+// static
+void BrowserThread::PostBestEffortTask(
+    const base::Location& from_here,
+    scoped_refptr<base::TaskRunner> task_runner,
+    base::OnceClosure task) {
+  base::PostTaskWithTraits(
+      FROM_HERE, {content::BrowserThread::IO, base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(base::IgnoreResult(&base::TaskRunner::PostTask),
+                     std::move(task_runner), from_here, std::move(task)));
+}
+
 }  // namespace content
diff --git a/content/browser/browser_thread_unittest.cc b/content/browser/browser_thread_unittest.cc
index f1ea845..44042d81 100644
--- a/content/browser/browser_thread_unittest.cc
+++ b/content/browser/browser_thread_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "content/public/browser/browser_thread.h"
+
 #include <memory>
 
 #include "base/bind.h"
@@ -14,6 +16,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/task/post_task.h"
 #include "base/task/sequence_manager/sequence_manager_impl.h"
+#include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
@@ -31,6 +34,11 @@
 
 namespace {
 
+using ::testing::Invoke;
+
+using StrictMockTask =
+    testing::StrictMock<base::MockCallback<base::Callback<void()>>>;
+
 class SequenceManagerTaskEnvironment : public base::Thread::TaskEnvironment {
  public:
   SequenceManagerTaskEnvironment() {
@@ -288,4 +296,68 @@
   EXPECT_TRUE(did_shutdown);
 }
 
+class BrowserThreadWithCustomSchedulerTest : public testing::Test {
+ private:
+  class ScopedTaskEnvironmentWithCustomScheduler
+      : public base::test::ScopedTaskEnvironment {
+   public:
+    ScopedTaskEnvironmentWithCustomScheduler()
+        : base::test::ScopedTaskEnvironment(
+              SubclassCreatesDefaultTaskRunner{}) {
+      std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler =
+          BrowserUIThreadScheduler::CreateForTesting(sequence_manager(),
+                                                     GetTimeDomain());
+      DeferredInitFromSubclass(
+          browser_ui_thread_scheduler->GetHandle().GetBrowserTaskRunner(
+              QueueType::kDefault));
+      BrowserTaskExecutor::CreateForTesting(
+          std::move(browser_ui_thread_scheduler),
+          std::make_unique<BrowserIOTaskEnvironment>());
+
+      ui_thread_ = BrowserTaskExecutor::CreateIOThread();
+      BrowserTaskExecutor::InitializeIOThread();
+      ui_thread_->RegisterAsBrowserThread();
+    }
+
+    ~ScopedTaskEnvironmentWithCustomScheduler() override {
+      ui_thread_.reset();
+      BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::IO);
+      BrowserTaskExecutor::ResetForTesting();
+    }
+
+   private:
+    std::unique_ptr<BrowserProcessSubThread> ui_thread_;
+  };
+
+ public:
+  using QueueType = BrowserTaskQueues::QueueType;
+
+ protected:
+  ScopedTaskEnvironmentWithCustomScheduler scoped_task_environment_;
+};
+
+TEST_F(BrowserThreadWithCustomSchedulerTest, PostBestEffortTask) {
+  StrictMockTask best_effort_task;
+  StrictMockTask regular_task;
+
+  auto task_runner = base::CreateTaskRunnerWithTraits(
+      {BrowserThread::UI, base::TaskPriority::HIGHEST});
+
+  task_runner->PostTask(FROM_HERE, regular_task.Get());
+  BrowserThread::PostBestEffortTask(FROM_HERE, task_runner,
+                                    best_effort_task.Get());
+
+  EXPECT_CALL(regular_task, Run);
+  scoped_task_environment_.RunUntilIdle();
+
+  testing::Mock::VerifyAndClearExpectations(&regular_task);
+
+  BrowserTaskExecutor::EnableAllQueues();
+  base::RunLoop run_loop;
+  EXPECT_CALL(best_effort_task, Run).WillOnce(Invoke([&]() {
+    run_loop.Quit();
+  }));
+  run_loop.Run();
+}
+
 }  // namespace content
diff --git a/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc b/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc
index 9e90c8da..b5ed62a 100644
--- a/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_blob_to_disk_cache_unittest.cc
@@ -23,10 +23,6 @@
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/disk_cache/disk_cache.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_job_factory_impl.h"
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_impl.h"
 #include "storage/browser/blob/blob_storage_context.h"
@@ -41,30 +37,6 @@
 const char kEntryKey[] = "FooEntry";
 const int kCacheEntryIndex = 1;
 
-class NullURLRequestContextGetter : public net::URLRequestContextGetter {
- public:
-  NullURLRequestContextGetter() : null_task_runner_(new base::NullTaskRunner) {}
-
-  net::URLRequestContext* GetURLRequestContext() override { return nullptr; }
-
-  scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
-      const override {
-    return null_task_runner_;
-  }
-
- private:
-  ~NullURLRequestContextGetter() override {}
-
-  scoped_refptr<base::SingleThreadTaskRunner> null_task_runner_;
-};
-
-// Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns
-// the memory.
-std::unique_ptr<storage::BlobProtocolHandler> CreateMockBlobProtocolHandler(
-    storage::BlobStorageContext* blob_storage_context) {
-  return std::make_unique<storage::BlobProtocolHandler>(blob_storage_context);
-}
-
 // A CacheStorageBlobToDiskCache that can delay reading from blobs.
 class TestCacheStorageBlobToDiskCache : public CacheStorageBlobToDiskCache {
  public:
@@ -93,9 +65,6 @@
   CacheStorageBlobToDiskCacheTest()
       : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
         browser_context_(new TestBrowserContext()),
-        url_request_context_getter_(
-            BrowserContext::GetDefaultStoragePartition(browser_context_.get())->
-                GetURLRequestContext()),
         cache_storage_blob_to_disk_cache_(
             new TestCacheStorageBlobToDiskCache()),
         data_(kTestData),
@@ -104,7 +73,6 @@
 
   void SetUp() override {
     InitBlobStorage();
-    InitURLRequestJobFactory();
     InitBlob();
     InitCache();
   }
@@ -117,17 +85,6 @@
     blob_storage_context_ = blob_storage_context->context();
   }
 
-  void InitURLRequestJobFactory() {
-    url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl);
-    url_request_job_factory_->SetProtocolHandler(
-        "blob", CreateMockBlobProtocolHandler(blob_storage_context_));
-
-    net::URLRequestContext* url_request_context =
-        url_request_context_getter_->GetURLRequestContext();
-
-    url_request_context->set_job_factory(url_request_job_factory_.get());
-  }
-
   void InitBlob() {
     auto blob_data =
         std::make_unique<storage::BlobDataBuilder>("blob-id:myblob");
@@ -190,9 +147,7 @@
 
   TestBrowserThreadBundle browser_thread_bundle_;
   std::unique_ptr<TestBrowserContext> browser_context_;
-  std::unique_ptr<net::URLRequestJobFactoryImpl> url_request_job_factory_;
   storage::BlobStorageContext* blob_storage_context_;
-  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
   std::unique_ptr<storage::BlobDataHandle> blob_handle_;
   std::unique_ptr<disk_cache::Backend> cache_backend_;
   ScopedWritableEntry disk_cache_entry_;
diff --git a/content/browser/cache_storage/cache_storage_cache_unittest.cc b/content/browser/cache_storage/cache_storage_cache_unittest.cc
index c2134af..22fba2fe 100644
--- a/content/browser/cache_storage/cache_storage_cache_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_cache_unittest.cc
@@ -45,8 +45,6 @@
 #include "mojo/public/cpp/system/data_pipe_drainer.h"
 #include "net/base/test_completion_callback.h"
 #include "net/disk_cache/disk_cache.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_job_factory_impl.h"
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/browser/blob/blob_data_snapshot.h"
@@ -453,16 +451,6 @@
     quota_manager_proxy_ = new MockQuotaManagerProxy(
         mock_quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get());
 
-    url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl);
-    url_request_job_factory_->SetProtocolHandler(
-        "blob", CreateMockBlobProtocolHandler(blob_storage_context->context()));
-
-    net::URLRequestContext* url_request_context =
-        BrowserContext::GetDefaultStoragePartition(&browser_context_)->
-            GetURLRequestContext()->GetURLRequestContext();
-
-    url_request_context->set_job_factory(url_request_job_factory_.get());
-
     CreateRequests(blob_storage_context);
 
     response_time_ = base::Time::Now();
@@ -857,7 +845,6 @@
   base::ScopedTempDir temp_dir_;
   TestBrowserThreadBundle browser_thread_bundle_;
   TestBrowserContext browser_context_;
-  std::unique_ptr<net::URLRequestJobFactoryImpl> url_request_job_factory_;
   scoped_refptr<MockSpecialStoragePolicy> quota_policy_;
   scoped_refptr<MockQuotaManager> mock_quota_manager_;
   scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_;
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index c5d6a67..77737d1 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -43,9 +43,6 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
 #include "net/disk_cache/disk_cache.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_job_factory_impl.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "storage/browser/blob/blob_data_builder.h"
 #include "storage/browser/blob/blob_data_handle.h"
@@ -212,14 +209,6 @@
   int notify_content_changed_count = 0;
 };
 
-// Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns
-// the memory.
-std::unique_ptr<storage::BlobProtocolHandler> CreateMockBlobProtocolHandler(
-    storage::BlobStorageContext* blob_storage_context) {
-  return base::WrapUnique(
-      new storage::BlobProtocolHandler(blob_storage_context));
-}
-
 class CacheStorageManagerTest : public testing::Test {
  public:
   CacheStorageManagerTest()
@@ -318,16 +307,6 @@
 
     blob_storage_context_ = blob_storage_context->context();
 
-    url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl);
-    url_request_job_factory_->SetProtocolHandler(
-        "blob", CreateMockBlobProtocolHandler(blob_storage_context->context()));
-
-    net::URLRequestContext* url_request_context =
-        BrowserContext::GetDefaultStoragePartition(&browser_context_)->
-              GetURLRequestContext()->GetURLRequestContext();
-
-    url_request_context->set_job_factory(url_request_job_factory_.get());
-
     base::FilePath temp_dir_path;
     if (!MemoryOnly())
       temp_dir_path = temp_dir_.GetPath();
@@ -377,7 +356,6 @@
     base::RunLoop().RunUntilIdle();
     quota_manager_proxy_ = nullptr;
 
-    url_request_job_factory_.reset();
     blob_storage_context_ = nullptr;
 
     quota_policy_ = nullptr;
@@ -750,7 +728,6 @@
 
   TestBrowserThreadBundle browser_thread_bundle_;
   TestBrowserContext browser_context_;
-  std::unique_ptr<net::URLRequestJobFactoryImpl> url_request_job_factory_;
   storage::BlobStorageContext* blob_storage_context_;
 
   scoped_refptr<MockSpecialStoragePolicy> quota_policy_;
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
index 5643405..7d0d084 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
@@ -49,13 +49,9 @@
 // Timeout after which font scanning and metadata extraction is stopped and the
 // local lookup table is cleared. Font scanning and lookup table construction is
 // only needed pre Windows 10. If the timeout is hit, no local font matching
-// will be performed on this particular pre Win 10 system. Previously we had
-// this set to 15 seconds, which did mean timeouts on a number of systems, but
-// also previously the render was waiting synchronously for a result, which is
-// no longer the case.  Experimentally raise this to 150 to see what scanning
-// times are occurring in the field.
+// will be performed on this particular pre Win 10 system.
 constexpr base::TimeDelta kFontIndexingTimeoutDefault =
-    base::TimeDelta::FromSeconds(150);
+    base::TimeDelta::FromMinutes(5);
 
 // In timeout test case, slow down indexing of one font file to this percentage
 // of the timeout value. Assuming that at least two fonts are indexed, the
@@ -364,7 +360,7 @@
 
 void DWriteFontLookupTableBuilder::PrepareFontUniqueNameTable() {
   TRACE_EVENT0("dwrite,fonts",
-               "DWriteFontLookupTableBuilder::BuildFontUniqueNameTable");
+               "DWriteFontLookupTableBuilder::PrepareFontUniqueNameTable");
   DCHECK(!HasDWriteUniqueFontLookups());
   // The table must only be built once.
   DCHECK(!font_table_built_.IsSignaled());
@@ -407,9 +403,14 @@
   }
   for (UINT32 family_index = 0; family_index < outstanding_family_results_;
        ++family_index) {
+    // Specify base::ThreadPolicy::MUST_USE_FOREGROUND because in
+    // https://crbug.com/960263 we observed a priority inversion when running
+    // DWrite worker tasks in the background.
     base::PostTaskWithTraitsAndReplyWithResult(
         FROM_HERE,
-        {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+        {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+         base::ThreadPolicy::MUST_USE_FOREGROUND,
+         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
         base::BindOnce(
             &DWriteFontLookupTableBuilder::ExtractPathAndNamesFromFamily,
             collection_, family_index, start_time_table_build_,
diff --git a/content/browser/resources/media/peer_connection_update_table.js b/content/browser/resources/media/peer_connection_update_table.js
index ec5a5ed..35a5c93 100644
--- a/content/browser/resources/media/peer_connection_update_table.js
+++ b/content/browser/resources/media/peer_connection_update_table.js
@@ -90,6 +90,7 @@
         signalingStateChange: 'signalingstatechange',
         iceGatheringStateChange: 'icegatheringstatechange',
         iceConnectionStateChange: 'iceconnectionstatechange',
+        connectionStateChange: 'connectionstatechange',
         onIceCandidate: 'icecandidate',
         stop: 'close'
       }[update.type] ||
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index e99a521..d2da01a 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -789,7 +789,12 @@
 
   auto controller_info = blink::mojom::ControllerServiceWorkerInfo::New();
   controller_info->client_id = client_uuid();
-  if (fetch_request_window_id_) {
+  // Set fetch_request_window_id only when |controller_| is available.  Setting
+  // |fetch_request_window_id| should not affect correctness, however, we have
+  // the extensions bug, https://crbug.com/963748, which we don't yet
+  // understand.  That is why we don't set |fetch_request_window_id| if there
+  // is no controller, at least, until we can fix the extension bug.
+  if (controller_ && fetch_request_window_id_) {
     controller_info->fetch_request_window_id =
         base::make_optional(fetch_request_window_id_);
   }
diff --git a/content/browser/service_worker/service_worker_registration_object_host.cc b/content/browser/service_worker/service_worker_registration_object_host.cc
index 9b7944f..f0903c6 100644
--- a/content/browser/service_worker/service_worker_registration_object_host.cc
+++ b/content/browser/service_worker/service_worker_registration_object_host.cc
@@ -151,9 +151,21 @@
     return;
   }
 
-  if (!registration_->GetNewestVersion()) {
+  // Run steps according to section 3.2.7:
+  // https://w3c.github.io/ServiceWorker/#service-worker-registration-update
+
+  // 1. Let |registration| be the service worker registration.
+  ServiceWorkerRegistration* registration = registration_.get();
+  DCHECK(registration);
+
+  // 2. Let |newest_worker| be the result of running Get Newest Worker algorithm
+  // passing |registration| as its argument.
+  ServiceWorkerVersion* newest_worker = registration->GetNewestVersion();
+
+  // 3. If |newest_worker| is null, return a promise rejected with an
+  // "InvalidStateError" DOMException and abort these steps.
+  if (!newest_worker) {
     // This can happen if update() is called during initial script evaluation.
-    // Abort the following steps according to the spec.
     std::move(callback).Run(
         blink::mojom::ServiceWorkerErrorType::kState,
         std::string(kServiceWorkerUpdateErrorPrefix) +
@@ -161,11 +173,29 @@
     return;
   }
 
+  // 4. If the context object’s relevant settings object’s global object
+  // globalObject is a ServiceWorkerGlobalScope object, and globalObject’s
+  // associated service worker's state is installing, return a promise rejected
+  // with an "InvalidStateError" DOMException and abort these steps.
+  if (provider_host_->IsProviderForServiceWorker()) {
+    ServiceWorkerVersion* version = provider_host_->running_hosted_version();
+    DCHECK(version);
+    if (ServiceWorkerVersion::Status::INSTALLING == version->status()) {
+      // This can happen if update() is called during execution of the
+      // install-event-handler.
+      std::move(callback).Run(
+          blink::mojom::ServiceWorkerErrorType::kState,
+          std::string(kServiceWorkerUpdateErrorPrefix) +
+              std::string(ServiceWorkerConsts::kInvalidStateErrorMessage));
+      return;
+    }
+  }
+
   DelayUpdate(
-      provider_host_->provider_type(), registration_.get(),
+      provider_host_->provider_type(), registration,
       provider_host_->running_hosted_version(),
       base::BindOnce(
-          &ExecuteUpdate, context_, registration_->id(),
+          &ExecuteUpdate, context_, registration->id(),
           false /* force_bypass_cache */, false /* skip_script_comparison */,
           base::BindOnce(&ServiceWorkerRegistrationObjectHost::UpdateComplete,
                          weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
diff --git a/content/browser/worker_host/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
index f76f257..12052810 100644
--- a/content/browser/worker_host/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_unittest.cc
@@ -41,10 +41,15 @@
   }
 
   SharedWorkerHostTest()
-      : mock_render_process_host_(&browser_context_),
+      : default_mock_url_loader_factory_(
+            std::make_unique<NotImplementedNetworkURLLoaderFactory>()),
+        mock_render_process_host_(&browser_context_),
         service_(nullptr /* storage_partition */,
                  nullptr /* service_worker_context */,
-                 nullptr /* appcache_service */) {}
+                 nullptr /* appcache_service */) {
+    mock_render_process_host_.OverrideURLLoaderFactory(
+        default_mock_url_loader_factory_.get());
+  }
 
   base::WeakPtr<SharedWorkerHost> CreateHost() {
     GURL url("http://www.example.com/w.js");
@@ -140,6 +145,8 @@
  protected:
   TestBrowserThreadBundle test_browser_thread_bundle_;
   TestBrowserContext browser_context_;
+  std::unique_ptr<network::mojom::URLLoaderFactory>
+      default_mock_url_loader_factory_;
   MockRenderProcessHost mock_render_process_host_;
   std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
 
diff --git a/content/browser/worker_host/shared_worker_service_impl_unittest.cc b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
index ddddded..17aff996 100644
--- a/content/browser/worker_host/shared_worker_service_impl_unittest.cc
+++ b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
@@ -44,13 +44,15 @@
     return connector;
   }
 
-  static bool CheckReceivedFactoryRequest(
-      blink::mojom::SharedWorkerFactoryRequest* request) {
-    if (s_factory_request_received_.empty())
-      return false;
-    *request = std::move(s_factory_request_received_.front());
+  static blink::mojom::SharedWorkerFactoryRequest WaitForFactoryRequest() {
+    if (s_factory_request_received_.empty()) {
+      base::RunLoop run_loop;
+      s_factory_request_callback_ = run_loop.QuitClosure();
+      run_loop.Run();
+    }
+    auto rv = std::move(s_factory_request_received_.front());
     s_factory_request_received_.pop();
-    return true;
+    return rv;
   }
 
   static bool CheckNotReceivedFactoryRequest() {
@@ -58,6 +60,8 @@
   }
 
   static void BindSharedWorkerFactory(mojo::ScopedMessagePipeHandle handle) {
+    if (s_factory_request_callback_)
+      std::move(s_factory_request_callback_).Run();
     s_factory_request_received_.push(
         blink::mojom::SharedWorkerFactoryRequest(std::move(handle)));
   }
@@ -91,6 +95,7 @@
   std::unique_ptr<TestBrowserContext> browser_context_;
   static std::queue<blink::mojom::SharedWorkerFactoryRequest>
       s_factory_request_received_;
+  static base::OnceClosure s_factory_request_callback_;
   std::unique_ptr<MockRenderProcessHostFactory> render_process_host_factory_;
   std::unique_ptr<NotImplementedNetworkURLLoaderFactory> url_loader_factory_;
 
@@ -102,6 +107,9 @@
 std::queue<blink::mojom::SharedWorkerFactoryRequest>
     SharedWorkerServiceImplTest::s_factory_request_received_;
 
+// static
+base::OnceClosure SharedWorkerServiceImplTest::s_factory_request_callback_;
+
 namespace {
 
 void ConnectToSharedWorker(blink::mojom::SharedWorkerConnectorPtr connector,
@@ -144,10 +152,8 @@
                             renderer_host, render_frame_host->GetRoutingID()),
                         kUrl, "name", &client, &local_port);
 
-  base::RunLoop().RunUntilIdle();
-
-  blink::mojom::SharedWorkerFactoryRequest factory_request;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request));
+  blink::mojom::SharedWorkerFactoryRequest factory_request =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory(std::move(factory_request));
   base::RunLoop().RunUntilIdle();
 
@@ -222,8 +228,8 @@
 
   base::RunLoop().RunUntilIdle();
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request));
+  blink::mojom::SharedWorkerFactoryRequest factory_request =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory(std::move(factory_request));
   base::RunLoop().RunUntilIdle();
 
@@ -362,8 +368,8 @@
                         kUrl, kName, &client0, &local_port0);
   base::RunLoop().RunUntilIdle();
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request));
+  blink::mojom::SharedWorkerFactoryRequest factory_request =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory(std::move(factory_request));
   base::RunLoop().RunUntilIdle();
 
@@ -435,8 +441,8 @@
                         kUrl0, kName, &client0, &local_port0);
   base::RunLoop().RunUntilIdle();
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request0;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request0));
+  blink::mojom::SharedWorkerFactoryRequest factory_request0 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory0(std::move(factory_request0));
   base::RunLoop().RunUntilIdle();
 
@@ -460,8 +466,8 @@
                         kUrl1, kName, &client1, &local_port1);
   base::RunLoop().RunUntilIdle();
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request1;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1));
+  blink::mojom::SharedWorkerFactoryRequest factory_request1 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory1(std::move(factory_request1));
   base::RunLoop().RunUntilIdle();
 
@@ -520,8 +526,8 @@
                         kUrl, kName0, &client0, &local_port0);
   base::RunLoop().RunUntilIdle();
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request0;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request0));
+  blink::mojom::SharedWorkerFactoryRequest factory_request0 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory0(std::move(factory_request0));
   base::RunLoop().RunUntilIdle();
 
@@ -545,8 +551,8 @@
                         kUrl, kName1, &client1, &local_port1);
   base::RunLoop().RunUntilIdle();
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request1;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1));
+  blink::mojom::SharedWorkerFactoryRequest factory_request1 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory1(std::move(factory_request1));
   base::RunLoop().RunUntilIdle();
 
@@ -613,8 +619,8 @@
 
   // Check that the worker was created.
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request));
+  blink::mojom::SharedWorkerFactoryRequest factory_request =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory(std::move(factory_request));
 
   base::RunLoop().RunUntilIdle();
@@ -688,12 +694,12 @@
 
   // Check that both workers were created.
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request0;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request0));
+  blink::mojom::SharedWorkerFactoryRequest factory_request0 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory0(std::move(factory_request0));
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request1;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1));
+  blink::mojom::SharedWorkerFactoryRequest factory_request1 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory1(std::move(factory_request1));
 
   base::RunLoop().RunUntilIdle();
@@ -777,12 +783,12 @@
 
   // Check that both workers were created.
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request0;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request0));
+  blink::mojom::SharedWorkerFactoryRequest factory_request0 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory0(std::move(factory_request0));
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request1;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1));
+  blink::mojom::SharedWorkerFactoryRequest factory_request1 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory1(std::move(factory_request1));
 
   base::RunLoop().RunUntilIdle();
@@ -866,8 +872,8 @@
 
   // Starts a worker.
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request0;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request0));
+  blink::mojom::SharedWorkerFactoryRequest factory_request0 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory0(std::move(factory_request0));
 
   base::RunLoop().RunUntilIdle();
@@ -900,8 +906,8 @@
 
   // The previous worker is unavailable, so a new worker is created.
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request1;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1));
+  blink::mojom::SharedWorkerFactoryRequest factory_request1 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory1(std::move(factory_request1));
 
   base::RunLoop().RunUntilIdle();
@@ -987,8 +993,8 @@
 
   // The previous worker is unavailable, so a new worker is created.
 
-  blink::mojom::SharedWorkerFactoryRequest factory_request1;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1));
+  blink::mojom::SharedWorkerFactoryRequest factory_request1 =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory1(std::move(factory_request1));
 
   EXPECT_TRUE(CheckNotReceivedFactoryRequest());
@@ -1064,8 +1070,8 @@
   base::RunLoop().RunUntilIdle();
 
   // Expect a factory request.
-  blink::mojom::SharedWorkerFactoryRequest factory_request;
-  EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request));
+  blink::mojom::SharedWorkerFactoryRequest factory_request =
+      WaitForFactoryRequest();
   MockSharedWorkerFactory factory(std::move(factory_request));
   base::RunLoop().RunUntilIdle();
 
diff --git a/content/public/browser/browser_thread.h b/content/public/browser/browser_thread.h
index 319cc88..4e16864 100644
--- a/content/public/browser/browser_thread.h
+++ b/content/public/browser/browser_thread.h
@@ -109,11 +109,24 @@
   // base::PostTaskWithTraits(
   //   FROM_HERE, {content::BrowserThread::UI, base::TaskPriority::BEST_EFFORT},
   //   base::BindOnce(...));
+  // Or if you need to run in a special TaskRunner by using the
+  // PostBestEffortTask function below
   static void PostAfterStartupTask(
       const base::Location& from_here,
       const scoped_refptr<base::TaskRunner>& task_runner,
       base::OnceClosure task);
 
+  // Posts a |task| to run at BEST_EFFORT priority using an arbitrary
+  // |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).
+  // TODO(crbug.com/793069): Add support for sequence-funneling and remove this
+  // method.
+  static void PostBestEffortTask(const base::Location& from_here,
+                                 scoped_refptr<base::TaskRunner> task_runner,
+                                 base::OnceClosure task);
+
   // Callable on any thread.  Returns whether the given well-known thread is
   // initialized.
   static bool IsThreadInitialized(ID identifier) WARN_UNUSED_RESULT;
diff --git a/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h b/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
index 00026e3..a0ec318 100644
--- a/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
+++ b/content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h
@@ -36,6 +36,9 @@
                void(webrtc::PeerConnectionInterface::IceGatheringState state));
   MOCK_METHOD1(DidChangeIceConnectionState,
                void(webrtc::PeerConnectionInterface::IceConnectionState state));
+  MOCK_METHOD1(
+      DidChangePeerConnectionState,
+      void(webrtc::PeerConnectionInterface::PeerConnectionState state));
   void DidAddReceiverPlanB(
       std::unique_ptr<blink::WebRTCRtpReceiver> web_rtp_receiver) override {
     DidAddReceiverPlanBForMock(&web_rtp_receiver);
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.cc b/content/renderer/media/webrtc/peer_connection_tracker.cc
index c21bed75..f345324 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker.cc
@@ -335,6 +335,27 @@
   }
 }
 
+static const char* GetConnectionStateString(
+    webrtc::PeerConnectionInterface::PeerConnectionState state) {
+  switch (state) {
+    case webrtc::PeerConnectionInterface::PeerConnectionState::kNew:
+      return "new";
+    case webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting:
+      return "connecting";
+    case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected:
+      return "connected";
+    case webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected:
+      return "disconnected";
+    case webrtc::PeerConnectionInterface::PeerConnectionState::kFailed:
+      return "failed";
+    case webrtc::PeerConnectionInterface::PeerConnectionState::kClosed:
+      return "closed";
+    default:
+      NOTREACHED();
+      return "";
+  }
+}
+
 static const char* GetIceGatheringStateString(
     webrtc::PeerConnectionInterface::IceGatheringState state) {
   switch (state) {
@@ -935,6 +956,17 @@
       GetIceConnectionStateString(state));
 }
 
+void PeerConnectionTracker::TrackConnectionStateChange(
+    RTCPeerConnectionHandler* pc_handler,
+    webrtc::PeerConnectionInterface::PeerConnectionState state) {
+  DCHECK_CALLED_ON_VALID_THREAD(main_thread_);
+  int id = GetLocalIDForHandler(pc_handler);
+  if (id == -1)
+    return;
+  SendPeerConnectionUpdate(id, "connectionStateChange",
+                           GetConnectionStateString(state));
+}
+
 void PeerConnectionTracker::TrackIceGatheringStateChange(
     RTCPeerConnectionHandler* pc_handler,
     webrtc::PeerConnectionInterface::IceGatheringState state) {
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.h b/content/renderer/media/webrtc/peer_connection_tracker.h
index ee33830..3f50087 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.h
+++ b/content/renderer/media/webrtc/peer_connection_tracker.h
@@ -175,6 +175,12 @@
       RTCPeerConnectionHandler* pc_handler,
       webrtc::PeerConnectionInterface::IceConnectionState state);
 
+  // Sends an update when the connection state
+  // of a PeerConnection has changed.
+  virtual void TrackConnectionStateChange(
+      RTCPeerConnectionHandler* pc_handler,
+      webrtc::PeerConnectionInterface::PeerConnectionState state);
+
   // Sends an update when the Ice gathering state
   // of a PeerConnection has changed.
   virtual void TrackIceGatheringStateChange(
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 390c837..5bf881c 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -2073,6 +2073,8 @@
 void RTCPeerConnectionHandler::OnConnectionChange(
     webrtc::PeerConnectionInterface::PeerConnectionState new_state) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  if (peer_connection_tracker_)
+    peer_connection_tracker_->TrackConnectionStateChange(this, new_state);
   if (!is_closed_)
     client_->DidChangePeerConnectionState(new_state);
 }
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
index ee2e33d..31204e9e 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
@@ -203,6 +203,10 @@
   MOCK_METHOD2(TrackIceConnectionStateChange,
                void(RTCPeerConnectionHandler* pc_handler,
                     webrtc::PeerConnectionInterface::IceConnectionState state));
+  MOCK_METHOD2(
+      TrackConnectionStateChange,
+      void(RTCPeerConnectionHandler* pc_handler,
+           webrtc::PeerConnectionInterface::PeerConnectionState state));
   MOCK_METHOD2(TrackIceGatheringStateChange,
                void(RTCPeerConnectionHandler* pc_handler,
                     webrtc::PeerConnectionInterface::IceGatheringState state));
@@ -1098,6 +1102,82 @@
   pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
 }
 
+TEST_F(RTCPeerConnectionHandlerTest, OnConnectionChange) {
+  testing::InSequence sequence;
+
+  webrtc::PeerConnectionInterface::PeerConnectionState new_state =
+      webrtc::PeerConnectionInterface::PeerConnectionState::kNew;
+  EXPECT_CALL(*mock_tracker_.get(),
+              TrackConnectionStateChange(
+                  pc_handler_.get(),
+                  webrtc::PeerConnectionInterface::PeerConnectionState::kNew));
+  EXPECT_CALL(*mock_client_.get(),
+              DidChangePeerConnectionState(
+                  webrtc::PeerConnectionInterface::PeerConnectionState::kNew));
+  pc_handler_->observer()->OnConnectionChange(new_state);
+
+  new_state = webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting;
+  EXPECT_CALL(
+      *mock_tracker_.get(),
+      TrackConnectionStateChange(
+          pc_handler_.get(),
+          webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting));
+  EXPECT_CALL(
+      *mock_client_.get(),
+      DidChangePeerConnectionState(
+          webrtc::PeerConnectionInterface::PeerConnectionState::kConnecting));
+  pc_handler_->observer()->OnConnectionChange(new_state);
+
+  new_state = webrtc::PeerConnectionInterface::PeerConnectionState::kConnected;
+  EXPECT_CALL(
+      *mock_tracker_.get(),
+      TrackConnectionStateChange(
+          pc_handler_.get(),
+          webrtc::PeerConnectionInterface::PeerConnectionState::kConnected));
+  EXPECT_CALL(
+      *mock_client_.get(),
+      DidChangePeerConnectionState(
+          webrtc::PeerConnectionInterface::PeerConnectionState::kConnected));
+  pc_handler_->observer()->OnConnectionChange(new_state);
+
+  new_state =
+      webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected;
+  EXPECT_CALL(
+      *mock_tracker_.get(),
+      TrackConnectionStateChange(
+          pc_handler_.get(),
+          webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected));
+  EXPECT_CALL(
+      *mock_client_.get(),
+      DidChangePeerConnectionState(
+          webrtc::PeerConnectionInterface::PeerConnectionState::kDisconnected));
+  pc_handler_->observer()->OnConnectionChange(new_state);
+
+  new_state = webrtc::PeerConnectionInterface::PeerConnectionState::kFailed;
+  EXPECT_CALL(
+      *mock_tracker_.get(),
+      TrackConnectionStateChange(
+          pc_handler_.get(),
+          webrtc::PeerConnectionInterface::PeerConnectionState::kFailed));
+  EXPECT_CALL(
+      *mock_client_.get(),
+      DidChangePeerConnectionState(
+          webrtc::PeerConnectionInterface::PeerConnectionState::kFailed));
+  pc_handler_->observer()->OnConnectionChange(new_state);
+
+  new_state = webrtc::PeerConnectionInterface::PeerConnectionState::kClosed;
+  EXPECT_CALL(
+      *mock_tracker_.get(),
+      TrackConnectionStateChange(
+          pc_handler_.get(),
+          webrtc::PeerConnectionInterface::PeerConnectionState::kClosed));
+  EXPECT_CALL(
+      *mock_client_.get(),
+      DidChangePeerConnectionState(
+          webrtc::PeerConnectionInterface::PeerConnectionState::kClosed));
+  pc_handler_->observer()->OnConnectionChange(new_state);
+}
+
 TEST_F(RTCPeerConnectionHandlerTest, OnIceGatheringChange) {
   testing::InSequence sequence;
   EXPECT_CALL(*mock_tracker_.get(),
diff --git a/content/renderer/media/webrtc/rtc_stats.cc b/content/renderer/media/webrtc/rtc_stats.cc
index 7686d75d..7ac45d2 100644
--- a/content/renderer/media/webrtc/rtc_stats.cc
+++ b/content/renderer/media/webrtc/rtc_stats.cc
@@ -35,6 +35,8 @@
     whitelisted_stats_types_.insert(webrtc::RTCRTPStreamStats::kType);
     whitelisted_stats_types_.insert(webrtc::RTCInboundRTPStreamStats::kType);
     whitelisted_stats_types_.insert(webrtc::RTCOutboundRTPStreamStats::kType);
+    whitelisted_stats_types_.insert(
+        webrtc::RTCRemoteInboundRtpStreamStats::kType);
     whitelisted_stats_types_.insert(webrtc::RTCMediaSourceStats::kType);
     whitelisted_stats_types_.insert(webrtc::RTCAudioSourceStats::kType);
     whitelisted_stats_types_.insert(webrtc::RTCVideoSourceStats::kType);
diff --git a/content/renderer/service_worker/service_worker_provider_context.cc b/content/renderer/service_worker/service_worker_provider_context.cc
index 866096a..b5d38750 100644
--- a/content/renderer/service_worker/service_worker_provider_context.cc
+++ b/content/renderer/service_worker/service_worker_provider_context.cc
@@ -277,8 +277,12 @@
          state->client_id == controller_info->client_id);
   state->client_id = controller_info->client_id;
 
-  if (controller_info->fetch_request_window_id)
+  if (controller_info->fetch_request_window_id) {
+    DCHECK(state->controller);
     state->fetch_request_window_id = *controller_info->fetch_request_window_id;
+  } else {
+    state->fetch_request_window_id = base::UnguessableToken();
+  }
 
   DCHECK((controller_info->mode ==
               blink::mojom::ControllerServiceWorkerMode::kNoController &&
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index fb2844e6..6408d1c 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -3,6 +3,7 @@
 # tags: [ amd amd-0x6613 amd-0x679e amd-0x6821 intel intel-0xa2e
 #         nvidia nvidia-0xfe9 qualcomm-adreno-(tm)-330 qualcomm-adreno-(tm)-418
 #         qualcomm-adreno-(tm)-420 qualcomm-adreno-(tm)-430
+#         qualcomm-adreno-(tm)-540
 # ]
 
 # Seems to be flaky on the new AMD R7 240 drivers.
@@ -173,3 +174,6 @@
 
 # Failing on Nexus 5
 crbug.com/957714 [ android qualcomm-adreno-(tm)-330 ] Pixel_Canvas2DRedBox [ Failure ]
+
+# Failing on Pixel 2 FYI.
+crbug.com/966069 [ android qualcomm-adreno-(tm)-540 ] Pixel_CanvasLowLatency2D [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 3cfcc92..b570c924 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -542,14 +542,18 @@
 [ qualcomm-adreno-(tm)-430 android ] WebglExtension_EXT_sRGB [ Failure ]
 crbug.com/angleproject/2046 [ android qualcomm-adreno-(tm)-420 ] conformance/glsl/misc/uninitialized-local-global-variables.html [ Failure ]
 
-# Nexus 9
+# Android NVIDIA: Nexus 9 and/or Shield TV
 crbug.com/478572 [ android nvidia ] deqp/data/gles2/shaders/functions.html [ Failure ]
 crbug.com/606096 [ android nvidia ] conformance/glsl/bugs/multiplication-assignment.html [ Failure ]
 crbug.com/912161 [ android nvidia ] conformance/glsl/constructors/glsl-construct-ivec4.html [ RetryOnFailure ]
 crbug.com/912161 [ android nvidia ] conformance/glsl/constructors/glsl-construct-mat2.html [ RetryOnFailure ]
-crbug.com/891456 [ android nvidia ] conformance/extensions/oes-texture-half-float-with-video.html [ RetryOnFailure ]
 crbug.com/891456 [ android nvidia ] conformance/extensions/oes-texture-float-with-video.html [ RetryOnFailure ]
+crbug.com/891456 [ android nvidia ] conformance/extensions/oes-texture-half-float-with-video.html [ RetryOnFailure ]
+crbug.com/891456 [ android nvidia ] conformance/textures/image_bitmap_from_video/tex-2d-luminance-luminance-unsigned_byte.html [ RetryOnFailure ]
+crbug.com/891456 [ android nvidia ] conformance/textures/image_bitmap_from_video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ RetryOnFailure ]
 crbug.com/891456 [ android nvidia ] conformance/textures/image_bitmap_from_video/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ RetryOnFailure ]
+crbug.com/891456 [ android nvidia ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_byte.html [ RetryOnFailure ]
+crbug.com/891456 [ android nvidia ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ RetryOnFailure ]
 
 # Flaky timeout on android_n5x_swarming_rel and
 # android-marshmallow-arm64-rel.
diff --git a/docs/android_emulator.md b/docs/android_emulator.md
index 2f78114a..b14f9ff 100644
--- a/docs/android_emulator.md
+++ b/docs/android_emulator.md
@@ -91,8 +91,10 @@
 https://androidstudio.googleblog.com/2018/11/emulator-28016-stable.html)
 for more about how this works.
 ```shell
-$ # Start 12 emulators. Press Ctrl-C to stop them all.
-$ ( for i in $(seq 12); do ~/Android/Sdk/emulator/emulator @EMULATOR_ID -read-only & done; wait )
+$ # Start 8 emulators. Press Ctrl-C to stop them all.
+$ ( for i in $(seq 8); do ~/Android/Sdk/emulator/emulator @EMULATOR_ID -read-only & done; wait )
+$ # Start 12 emulators. More than 10 requires disabling audio on some OS's. Reducing cores increases paralellism.
+$ ( for i in $(seq 12); do ~/Android/Sdk/emulator/emulator @EMULATOR_ID -read-only -no-audio -cores 2 & done; wait )
 ```
 
 ### Writable system partition
diff --git a/ios/chrome/browser/autofill/BUILD.gn b/ios/chrome/browser/autofill/BUILD.gn
index bb49548..097c7043d 100644
--- a/ios/chrome/browser/autofill/BUILD.gn
+++ b/ios/chrome/browser/autofill/BUILD.gn
@@ -71,6 +71,7 @@
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/common/ui_util",
     "//ios/web",
+    "//ios/web/public/js_messaging",
     "//third_party/leveldatabase",
     "//third_party/libaddressinput",
     "//ui/base",
@@ -165,6 +166,7 @@
     "//ios/chrome/browser/web:web_internal",
     "//ios/chrome/test/base",
     "//ios/web",
+    "//ios/web/public/js_messaging",
     "//ios/web/public/test",
     "//testing/gtest",
     "//third_party/leveldatabase",
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
index f97a7c75..470f2f50 100644
--- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -40,12 +40,12 @@
 #include "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/chrome/browser/web/chrome_web_test.h"
 #include "ios/chrome/browser/web_data_service_factory.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "testing/gtest_mac.h"
 
diff --git a/ios/chrome/browser/autofill/automation/BUILD.gn b/ios/chrome/browser/autofill/automation/BUILD.gn
index 4dd97d7..4cc06b9 100644
--- a/ios/chrome/browser/autofill/automation/BUILD.gn
+++ b/ios/chrome/browser/autofill/automation/BUILD.gn
@@ -31,6 +31,7 @@
     "//ios/chrome/test/earl_grey:test_support",
     "//ios/testing/earl_grey:earl_grey_support",
     "//ios/web:earl_grey_test_support",
+    "//ios/web/public/js_messaging",
     "//ios/web/public/test:element_selector",
     "//ios/web/public/test/http_server:http_server",
     "//ui/base",
diff --git a/ios/chrome/browser/autofill/automation/automation_action.mm b/ios/chrome/browser/autofill/automation/automation_action.mm
index b4e623c..160bae14 100644
--- a/ios/chrome/browser/autofill/automation/automation_action.mm
+++ b/ios/chrome/browser/autofill/automation/automation_action.mm
@@ -21,12 +21,12 @@
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/testing/nserror_util.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/earl_grey/web_view_actions.h"
 #import "ios/web/public/test/earl_grey/web_view_matchers.h"
 #include "ios/web/public/test/element_selector.h"
 #import "ios/web/public/test/js_test_util.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/autofill/automation/automation_egtest.mm b/ios/chrome/browser/autofill/automation/automation_egtest.mm
index 2972129..6fb78f5 100644
--- a/ios/chrome/browser/autofill/automation/automation_egtest.mm
+++ b/ios/chrome/browser/autofill/automation/automation_egtest.mm
@@ -28,12 +28,12 @@
 #import "ios/chrome/browser/autofill/form_suggestion_label.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_error_util.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/earl_grey/web_view_actions.h"
 #import "ios/web/public/test/earl_grey/web_view_matchers.h"
 #include "ios/web/public/test/element_selector.h"
 #import "ios/web/public/test/js_test_util.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/autofill/form_structure_browsertest.mm b/ios/chrome/browser/autofill/form_structure_browsertest.mm
index c0ce614..28701889 100644
--- a/ios/chrome/browser/autofill/form_structure_browsertest.mm
+++ b/ios/chrome/browser/autofill/form_structure_browsertest.mm
@@ -29,9 +29,9 @@
 #import "ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h"
 #include "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/chrome/browser/web/chrome_web_test.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller.mm b/ios/chrome/browser/autofill/form_suggestion_controller.mm
index 8ca3883..1d60124 100644
--- a/ios/chrome/browser/autofill/form_suggestion_controller.mm
+++ b/ios/chrome/browser/autofill/form_suggestion_controller.mm
@@ -20,10 +20,10 @@
 #import "ios/chrome/browser/autofill/form_suggestion_view.h"
 #import "ios/chrome/browser/passwords/password_generation_utils.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/url_scheme_util.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 #import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/autofill/js_autofill_manager_unittest.mm b/ios/chrome/browser/autofill/js_autofill_manager_unittest.mm
index 00dfd24..432bfe4 100644
--- a/ios/chrome/browser/autofill/js_autofill_manager_unittest.mm
+++ b/ios/chrome/browser/autofill/js_autofill_manager_unittest.mm
@@ -13,9 +13,9 @@
 #import "components/autofill/ios/browser/js_autofill_manager.h"
 #include "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/chrome/browser/web/chrome_web_test.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/test/js_test_util.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
-#include "ios/web/public/web_state/web_frame_util.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "testing/gtest_mac.h"
 
diff --git a/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm b/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm
index d090d2b..8534e67 100644
--- a/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm
+++ b/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm
@@ -10,10 +10,10 @@
 #import "base/test/ios/wait_util.h"
 #import "ios/chrome/browser/web/chrome_web_client.h"
 #include "ios/chrome/browser/web/chrome_web_test.h"
+#import "ios/web/public/js_messaging/web_frame.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/js_test_util.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
-#import "ios/web/public/web_state/web_frame.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "testing/gtest_mac.h"
 
diff --git a/ios/chrome/browser/interstitials/BUILD.gn b/ios/chrome/browser/interstitials/BUILD.gn
index a3bc454..cb25fb7 100644
--- a/ios/chrome/browser/interstitials/BUILD.gn
+++ b/ios/chrome/browser/interstitials/BUILD.gn
@@ -26,6 +26,7 @@
     "//ios/chrome/browser/ui/util",
     "//ios/web",
     "//ios/web/public",
+    "//ios/web/public/security",
     "//ui/base",
     "//url",
   ]
diff --git a/ios/chrome/browser/invalidation/BUILD.gn b/ios/chrome/browser/invalidation/BUILD.gn
index bb0eec8..c416184 100644
--- a/ios/chrome/browser/invalidation/BUILD.gn
+++ b/ios/chrome/browser/invalidation/BUILD.gn
@@ -14,7 +14,6 @@
     "//base",
     "//components/gcm_driver",
     "//components/invalidation/impl",
-    "//components/invalidation/impl:json_unsafe_parser",
     "//components/keyed_service/ios",
     "//components/pref_registry",
     "//components/prefs",
@@ -22,6 +21,7 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/gcm",
     "//ios/chrome/browser/gcm/instance_id",
+    "//ios/chrome/browser/json_parser",
     "//ios/chrome/browser/signin",
     "//ios/web",
     "//net",
diff --git a/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.mm b/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.mm
index 6c549c1..373babd 100644
--- a/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.mm
+++ b/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.mm
@@ -14,7 +14,6 @@
 #include "components/gcm_driver/instance_id/instance_id_profile_service.h"
 #include "components/invalidation/impl/fcm_invalidation_service.h"
 #include "components/invalidation/impl/invalidator_storage.h"
-#include "components/invalidation/impl/json_unsafe_parser.h"
 #include "components/invalidation/impl/profile_identity_provider.h"
 #include "components/invalidation/impl/profile_invalidation_provider.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
@@ -24,6 +23,7 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/gcm/instance_id/ios_chrome_instance_id_profile_service_factory.h"
 #include "ios/chrome/browser/gcm/ios_chrome_gcm_profile_service_factory.h"
+#include "ios/chrome/browser/json_parser/in_process_json_parser.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/web/public/web_client.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -83,7 +83,7 @@
               browser_state)
               ->driver(),
           browser_state->GetPrefs(),
-          base::BindRepeating(&syncer::JsonUnsafeParser::Parse),
+          base::BindRepeating(&InProcessJsonParser::Parse),
           browser_state->GetURLLoaderFactory());
   service->Init();
 
diff --git a/ios/chrome/browser/json_parser/BUILD.gn b/ios/chrome/browser/json_parser/BUILD.gn
new file mode 100644
index 0000000..666b01d
--- /dev/null
+++ b/ios/chrome/browser/json_parser/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2019 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.
+
+source_set("json_parser") {
+  sources = [
+    "in_process_json_parser.cc",
+    "in_process_json_parser.h",
+  ]
+  public_deps = [
+    "//base",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "in_process_json_parser_unittest.cc",
+  ]
+  deps = [
+    ":json_parser",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/ios/chrome/browser/json_parser/in_process_json_parser.cc b/ios/chrome/browser/json_parser/in_process_json_parser.cc
new file mode 100644
index 0000000..581c41a
--- /dev/null
+++ b/ios/chrome/browser/json_parser/in_process_json_parser.cc
@@ -0,0 +1,49 @@
+// Copyright 2019 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 "ios/chrome/browser/json_parser/in_process_json_parser.h"
+
+#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/strings/stringprintf.h"
+#include "base/task/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/values.h"
+
+namespace {
+void ParseJsonOnBackgroundThread(
+    scoped_refptr<base::TaskRunner> task_runner,
+    const std::string& unsafe_json,
+    const InProcessJsonParser::SuccessCallback& success_callback,
+    const InProcessJsonParser::ErrorCallback& error_callback) {
+  DCHECK(task_runner);
+  base::JSONReader::ValueWithError value_with_error =
+      base::JSONReader::ReadAndReturnValueWithError(unsafe_json,
+                                                    base::JSON_PARSE_RFC);
+  if (value_with_error.value) {
+    task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(success_callback, std::move(*value_with_error.value)));
+  } else {
+    task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            error_callback,
+            base::StringPrintf(
+                "%s (%d:%d)", value_with_error.error_message.c_str(),
+                value_with_error.error_line, value_with_error.error_column)));
+  }
+}
+}  // namespace
+
+// static
+void InProcessJsonParser::Parse(const std::string& unsafe_json,
+                                const SuccessCallback& success_callback,
+                                const ErrorCallback& error_callback) {
+  base::PostTaskWithTraits(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(&ParseJsonOnBackgroundThread,
+                     base::ThreadTaskRunnerHandle::Get(), unsafe_json,
+                     success_callback, error_callback));
+}
diff --git a/ios/chrome/browser/json_parser/in_process_json_parser.h b/ios/chrome/browser/json_parser/in_process_json_parser.h
new file mode 100644
index 0000000..b79b9bd8
--- /dev/null
+++ b/ios/chrome/browser/json_parser/in_process_json_parser.h
@@ -0,0 +1,40 @@
+// Copyright 2019 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 IOS_CHROME_BROWSER_JSON_PARSER_IN_PROCESS_JSON_PARSER_H_
+#define IOS_CHROME_BROWSER_JSON_PARSER_IN_PROCESS_JSON_PARSER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+
+namespace base {
+class Value;
+}
+
+// Mimics SafeJsonParser but parses in-process using base::JSONReader. This
+// is potentially unsafe (if there are bug in the JSON parsing logic), but
+// there is no way to parse in a separate process like on other platforms.
+// This class should be used when code depends on SafeJsonParser-like API.
+//
+// If iOS ever partially adaopt Swift, pre-sanitization in that language,
+// like Android version does), would be a better option. The support would
+// have to be added to SafeJsonParser and this class removed.
+class InProcessJsonParser {
+ public:
+  // TODO(crbug.com/964232): Convert to base::OnceCallback with SafeJsonParser.
+  using SuccessCallback = base::Callback<void(base::Value)>;
+  using ErrorCallback = base::Callback<void(const std::string&)>;
+
+  // As with SafeJsonParser, runs either |success_callback| or |error_callback|
+  // on the calling thread, but not before the call returns.
+  static void Parse(const std::string& unsafe_json,
+                    const SuccessCallback& success_callback,
+                    const ErrorCallback& error_callback);
+
+  InProcessJsonParser() = delete;
+};
+
+#endif  // IOS_CHROME_BROWSER_JSON_PARSER_IN_PROCESS_JSON_PARSER_H_
diff --git a/ios/chrome/browser/json_parser/in_process_json_parser_unittest.cc b/ios/chrome/browser/json_parser/in_process_json_parser_unittest.cc
new file mode 100644
index 0000000..c940a99e
--- /dev/null
+++ b/ios/chrome/browser/json_parser/in_process_json_parser_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright 2019 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 "ios/chrome/browser/json_parser/in_process_json_parser.h"
+
+#include "base/callback_helpers.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(InProcessJsonParserTest, TestSuccess) {
+  base::test::ScopedTaskEnvironment environment;
+
+  base::RunLoop run_loop;
+  InProcessJsonParser::Parse(
+      R"json({"key": 1})json",
+      base::AdaptCallbackForRepeating(base::BindOnce(
+          [](base::Closure quit_closure, base::Value value) {
+            ASSERT_TRUE(value.is_dict());
+            ASSERT_TRUE(value.FindIntKey("key"));
+            EXPECT_EQ(1, *value.FindIntKey("key"));
+            std::move(quit_closure).Run();
+          },
+          run_loop.QuitClosure())),
+      base::AdaptCallbackForRepeating(base::BindOnce(
+          [](base::Closure quit_closure, const std::string& error) {
+            EXPECT_FALSE(true) << "unexpected json parse error: " << error;
+            std::move(quit_closure).Run();
+          },
+          run_loop.QuitClosure())));
+  run_loop.Run();
+}
+
+TEST(InProcessJsonParserTest, TestFailure) {
+  base::test::ScopedTaskEnvironment environment;
+
+  base::RunLoop run_loop;
+  InProcessJsonParser::Parse(
+      R"json(invalid)json",
+      base::AdaptCallbackForRepeating(base::BindOnce(
+          [](base::Closure quit_closure, base::Value value) {
+            EXPECT_FALSE(true) << "unexpected json parse success: " << value;
+            std::move(quit_closure).Run();
+          },
+          run_loop.QuitClosure())),
+      base::AdaptCallbackForRepeating(base::BindOnce(
+          [](base::Closure quit_closure, const std::string& error) {
+            EXPECT_TRUE(!error.empty());
+            std::move(quit_closure).Run();
+          },
+          run_loop.QuitClosure())));
+  run_loop.Run();
+}
diff --git a/ios/chrome/browser/ntp_snippets/BUILD.gn b/ios/chrome/browser/ntp_snippets/BUILD.gn
index 3aa15dd..928431f6 100644
--- a/ios/chrome/browser/ntp_snippets/BUILD.gn
+++ b/ios/chrome/browser/ntp_snippets/BUILD.gn
@@ -27,6 +27,7 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/favicon",
     "//ios/chrome/browser/history",
+    "//ios/chrome/browser/json_parser",
     "//ios/chrome/browser/leveldb_proto:factory",
     "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/signin",
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
index b62de6c..0b4eea9 100644
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
+++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
@@ -10,7 +10,6 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
-#include "base/json/json_reader.h"
 #include "base/task/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
@@ -41,6 +40,7 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
+#include "ios/chrome/browser/json_parser/in_process_json_parser.h"
 #include "ios/chrome/browser/leveldb_proto/proto_database_provider_factory.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
@@ -63,25 +63,6 @@
 using ntp_snippets::RemoteSuggestionsStatusServiceImpl;
 using ntp_snippets::UserClassifier;
 
-namespace {
-
-void ParseJson(const std::string& json,
-               const ntp_snippets::SuccessCallback& success_callback,
-               const ntp_snippets::ErrorCallback& error_callback) {
-  base::JSONReader json_reader;
-  base::Optional<base::Value> value = json_reader.ReadToValue(json);
-  if (value) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(success_callback, std::move(*value)));
-  } else {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(error_callback, json_reader.GetErrorMessage()));
-  }
-}
-
-}  // namespace
-
 namespace ntp_snippets {
 
 std::unique_ptr<KeyedService>
@@ -160,8 +141,8 @@
   }
   auto suggestions_fetcher = std::make_unique<RemoteSuggestionsFetcherImpl>(
       identity_manager, url_loader_factory, prefs, nullptr,
-      base::BindRepeating(&ParseJson), GetFetchEndpoint(), api_key,
-      service->user_classifier());
+      base::BindRepeating(&InProcessJsonParser::Parse), GetFetchEndpoint(),
+      api_key, service->user_classifier());
 
   leveldb_proto::ProtoDatabaseProvider* db_provider =
       leveldb_proto::ProtoDatabaseProviderFactory::GetForBrowserState(
diff --git a/ios/chrome/browser/ntp_tiles/BUILD.gn b/ios/chrome/browser/ntp_tiles/BUILD.gn
index a5622a3e..d0f3ac650 100644
--- a/ios/chrome/browser/ntp_tiles/BUILD.gn
+++ b/ios/chrome/browser/ntp_tiles/BUILD.gn
@@ -19,11 +19,11 @@
     "//components/image_fetcher/ios",
     "//components/keyed_service/core",
     "//components/ntp_tiles",
-    "//components/ntp_tiles:json_unsafe_parser",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/favicon",
     "//ios/chrome/browser/history",
+    "//ios/chrome/browser/json_parser",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/suggestions",
     "//ios/web",
diff --git a/ios/chrome/browser/ntp_tiles/ios_popular_sites_factory.cc b/ios/chrome/browser/ntp_tiles/ios_popular_sites_factory.cc
index 3f2e89b..05d203c 100644
--- a/ios/chrome/browser/ntp_tiles/ios_popular_sites_factory.cc
+++ b/ios/chrome/browser/ntp_tiles/ios_popular_sites_factory.cc
@@ -6,10 +6,10 @@
 
 #include "base/bind.h"
 #include "base/files/file_path.h"
-#include "components/ntp_tiles/json_unsafe_parser.h"
 #include "components/ntp_tiles/popular_sites_impl.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/json_parser/in_process_json_parser.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #include "ios/web/public/web_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -24,5 +24,5 @@
       GetApplicationContext()->GetVariationsService(),
       base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
           browser_state->GetURLLoaderFactory()),
-      base::Bind(ntp_tiles::JsonUnsafeParser::Parse));
+      base::BindRepeating(&InProcessJsonParser::Parse));
 }
diff --git a/ios/chrome/browser/passwords/BUILD.gn b/ios/chrome/browser/passwords/BUILD.gn
index 2f8c43a..5a37d09 100644
--- a/ios/chrome/browser/passwords/BUILD.gn
+++ b/ios/chrome/browser/passwords/BUILD.gn
@@ -87,6 +87,7 @@
     "//ios/public/provider/chrome/browser/ui",
     "//ios/third_party/material_components_ios:material_components_ios",
     "//ios/web/common",
+    "//ios/web/public/js_messaging",
     "//net",
     "//third_party/material_design_icons:ic_account_circle",
     "//ui/base",
@@ -190,6 +191,8 @@
     "//ios/chrome/browser/web:web_internal",
     "//ios/testing:ocmock_support",
     "//ios/web",
+    "//ios/web/public/js_messaging",
+    "//ios/web/public/security",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//net:test_support",
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index 2b1add5..1482b1f4 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -62,10 +62,10 @@
 #include "ios/chrome/browser/web/tab_id_tab_helper.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/web/common/origin_util.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #include "ios/web/public/url_scheme_util.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "ui/base/l10n/l10n_util_mac.h"
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index b95790a..8af68ef 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -37,13 +37,13 @@
 #import "ios/chrome/browser/ui/autofill/form_input_accessory_mediator.h"
 #include "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/chrome/browser/web/chrome_web_test.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #import "ios/web/public/test/web_js_test.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ios/chrome/browser/payments/BUILD.gn b/ios/chrome/browser/payments/BUILD.gn
index 51231dc..9b329be 100644
--- a/ios/chrome/browser/payments/BUILD.gn
+++ b/ios/chrome/browser/payments/BUILD.gn
@@ -47,6 +47,7 @@
     "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/signin",
     "//ios/web",
+    "//ios/web/public/security",
     "//net",
     "//services/identity/public/cpp:cpp",
     "//ui/base",
diff --git a/ios/chrome/browser/reading_list/BUILD.gn b/ios/chrome/browser/reading_list/BUILD.gn
index 2237330..2d47225 100644
--- a/ios/chrome/browser/reading_list/BUILD.gn
+++ b/ios/chrome/browser/reading_list/BUILD.gn
@@ -48,6 +48,7 @@
     "//ios/chrome/common",
     "//ios/web",
     "//ios/web/public",
+    "//ios/web/public/security",
     "//net",
     "//ui/base",
     "//url",
diff --git a/ios/chrome/browser/sessions/BUILD.gn b/ios/chrome/browser/sessions/BUILD.gn
index e72675c19..b8045ac 100644
--- a/ios/chrome/browser/sessions/BUILD.gn
+++ b/ios/chrome/browser/sessions/BUILD.gn
@@ -53,6 +53,7 @@
     "//components/sessions",
     "//ios/chrome/browser/browser_state",
     "//ios/web",
+    "//ios/web/public/session",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
@@ -86,6 +87,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/web",
+    "//ios/web/public/session",
     "//testing/gtest",
     "//third_party/ocmock",
   ]
diff --git a/ios/chrome/browser/sessions/session_service_ios.mm b/ios/chrome/browser/sessions/session_service_ios.mm
index 675ff92..d35842ae 100644
--- a/ios/chrome/browser/sessions/session_service_ios.mm
+++ b/ios/chrome/browser/sessions/session_service_ios.mm
@@ -19,9 +19,9 @@
 #include "base/threading/scoped_blocking_call.h"
 #import "ios/chrome/browser/sessions/session_ios.h"
 #import "ios/chrome/browser/sessions/session_window_ios.h"
-#import "ios/web/public/crw_navigation_item_storage.h"
-#import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
-#import "ios/web/public/crw_session_storage.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/sessions/session_service_ios_unittest.mm b/ios/chrome/browser/sessions/session_service_ios_unittest.mm
index ea9b54f..99a51de 100644
--- a/ios/chrome/browser/sessions/session_service_ios_unittest.mm
+++ b/ios/chrome/browser/sessions/session_service_ios_unittest.mm
@@ -19,7 +19,7 @@
 #import "ios/chrome/browser/sessions/session_ios.h"
 #import "ios/chrome/browser/sessions/session_service_ios.h"
 #import "ios/chrome/browser/sessions/session_window_ios.h"
-#import "ios/web/public/crw_session_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
diff --git a/ios/chrome/browser/sessions/session_window_ios_unittest.mm b/ios/chrome/browser/sessions/session_window_ios_unittest.mm
index e8dcf66..a76f128 100644
--- a/ios/chrome/browser/sessions/session_window_ios_unittest.mm
+++ b/ios/chrome/browser/sessions/session_window_ios_unittest.mm
@@ -6,7 +6,7 @@
 
 #import <Foundation/Foundation.h>
 
-#import "ios/web/public/crw_session_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
diff --git a/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc b/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
index e693366..f782dce 100644
--- a/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
+++ b/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
@@ -110,8 +110,7 @@
   return identity::IdentityTestEnvironment::BuildIdentityManagerForTests(
       SigninClientFactory::GetForBrowserState(chrome_browser_state),
       chrome_browser_state->GetPrefs(), base::FilePath(),
-      signin::AccountConsistencyMethod::kMirror,
-      /*test_url_loader_factory=*/nullptr, std::move(extra_params));
+      signin::AccountConsistencyMethod::kMirror, std::move(extra_params));
 }
 
 // static
diff --git a/ios/chrome/browser/signin/ios_chrome_signin_client.h b/ios/chrome/browser/signin/ios_chrome_signin_client.h
index ddb9fd0..cdcb9f7f 100644
--- a/ios/chrome/browser/signin/ios_chrome_signin_client.h
+++ b/ios/chrome/browser/signin/ios_chrome_signin_client.h
@@ -35,9 +35,7 @@
   std::string GetProductVersion() override;
   std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
       GaiaAuthConsumer* consumer,
-      gaia::GaiaSource source,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-      override;
+      gaia::GaiaSource source) override;
   void PreGaiaLogout(base::OnceClosure callback) override;
   PrefService* GetPrefs() override;
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
diff --git a/ios/chrome/browser/signin/ios_chrome_signin_client.mm b/ios/chrome/browser/signin/ios_chrome_signin_client.mm
index f9900765..1fa899f 100644
--- a/ios/chrome/browser/signin/ios_chrome_signin_client.mm
+++ b/ios/chrome/browser/signin/ios_chrome_signin_client.mm
@@ -95,10 +95,9 @@
 
 std::unique_ptr<GaiaAuthFetcher> IOSChromeSigninClient::CreateGaiaAuthFetcher(
     GaiaAuthConsumer* consumer,
-    gaia::GaiaSource source,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+    gaia::GaiaSource source) {
   return std::make_unique<GaiaAuthFetcherIOS>(
-      consumer, source, url_loader_factory, browser_state_);
+      consumer, source, GetURLLoaderFactory(), browser_state_);
 }
 
 void IOSChromeSigninClient::PreGaiaLogout(base::OnceClosure callback) {
diff --git a/ios/chrome/browser/ssl/BUILD.gn b/ios/chrome/browser/ssl/BUILD.gn
index 5bbbbaccd..99e3dee 100644
--- a/ios/chrome/browser/ssl/BUILD.gn
+++ b/ios/chrome/browser/ssl/BUILD.gn
@@ -41,6 +41,7 @@
     "//ios/public/provider/chrome/browser",
     "//ios/web/common",
     "//ios/web/public",
+    "//ios/web/public/security",
     "//net",
     "//ui/base",
     "//url",
@@ -74,6 +75,7 @@
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/web:test_support",
     "//ios/web",
+    "//ios/web/public/security",
     "//ios/web/public/test",
     "//net",
     "//net:test_support",
diff --git a/ios/chrome/browser/tabs/BUILD.gn b/ios/chrome/browser/tabs/BUILD.gn
index eb08ffa..b1eff512 100644
--- a/ios/chrome/browser/tabs/BUILD.gn
+++ b/ios/chrome/browser/tabs/BUILD.gn
@@ -133,6 +133,8 @@
     "//ios/public/provider/chrome/browser",
     "//ios/web",
     "//ios/web/public",
+    "//ios/web/public/security",
+    "//ios/web/public/session",
     "//net",
     "//ui/base",
     "//url",
@@ -188,6 +190,7 @@
     "//ios/testing:ocmock_support",
     "//ios/web/common",
     "//ios/web/navigation:core",
+    "//ios/web/public/session",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//ios/web/test/fakes:fakes",
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index 6798f03..8f256928 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -74,7 +74,7 @@
 #include "ios/web/public/referrer.h"
 #include "ios/web/public/security/ssl_status.h"
 #include "ios/web/public/security/web_interstitial.h"
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 #include "ios/web/public/url_scheme_util.h"
 #include "ios/web/public/web_client.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index 835abb6ce..336f703 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -60,9 +60,9 @@
 #include "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/security/certificate_policy_cache.h"
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
+#include "ios/web/public/session/session_certificate_policy_cache.h"
 #import "ios/web/public/web_state/navigation_context.h"
-#include "ios/web/public/web_state/session_certificate_policy_cache.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #include "ios/web/public/web_task_traits.h"
 #include "ios/web/public/web_thread.h"
diff --git a/ios/chrome/browser/tabs/tab_model_unittest.mm b/ios/chrome/browser/tabs/tab_model_unittest.mm
index 3e9a27f..564580c 100644
--- a/ios/chrome/browser/tabs/tab_model_unittest.mm
+++ b/ios/chrome/browser/tabs/tab_model_unittest.mm
@@ -33,10 +33,10 @@
 #include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
 #include "ios/web/common/features.h"
 #import "ios/web/navigation/navigation_manager_impl.h"
-#import "ios/web/public/crw_session_storage.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/referrer.h"
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/crw_session_storage.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 #include "ios/web/public/test/scoped_testing_web_client.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "ios/web/public/web_thread.h"
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn
index 341cb76..aa3dd65 100644
--- a/ios/chrome/browser/ui/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -58,6 +58,7 @@
     "//ios/third_party/material_components_ios",
     "//ios/third_party/material_roboto_font_loader_ios",
     "//ios/web",
+    "//ios/web/public/js_messaging",
     "//ui/base",
   ]
   public_deps = [
@@ -144,6 +145,7 @@
     "//ios/chrome/browser/autofill",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
+    "//ios/web/public/js_messaging",
     "//ios/web/public/test/http_server",
     "//services/network:test_support",
   ]
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
index 2b395c2..6e5c0571 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory_mediator.mm
@@ -26,10 +26,10 @@
 #import "ios/chrome/browser/ui/util/keyboard_observer_helper.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/url_scheme_util.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
index 841716a..ab17f1a 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
@@ -57,6 +57,7 @@
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web_state_list:web_state_list",
     "//ios/web/public:public",
+    "//ios/web/public/js_messaging",
     "//ui/base:base",
   ]
   libs = [ "UIKit.framework" ]
@@ -180,6 +181,7 @@
     "//ios/chrome/browser/ui/payments:requesters",
     "//ios/chrome/browser/web_state_list:web_state_list",
     "//ios/web/public:public",
+    "//ios/web/public/js_messaging",
     "//ui/base:base",
   ]
   libs = [ "UIKit.framework" ]
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/card_coordinator.mm b/ios/chrome/browser/ui/autofill/manual_fill/card_coordinator.mm
index ff7bef2..e34354f5 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/card_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/card_coordinator.mm
@@ -18,8 +18,8 @@
 #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester.mm b/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester.mm
index 6b4ec62..1240b22 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester.mm
@@ -11,8 +11,8 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/full_card_request_result_delegate_bridge.h"
 #include "ios/chrome/browser/ui/payments/full_card_requester.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
index 5ee8845..644b1ba 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
@@ -22,10 +22,10 @@
 #import "ios/chrome/browser/ui/autofill/manual_fill/form_observer_helper.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/chrome/grit/ios_strings.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#include "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#include "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 #include "url/gurl.h"
diff --git a/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm b/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm
index 1a48dce5..a1229564 100644
--- a/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm
@@ -26,9 +26,9 @@
 #import "ios/chrome/test/earl_grey/chrome_error_util.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/http_server/http_server.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
diff --git a/ios/chrome/browser/ui/fullscreen/BUILD.gn b/ios/chrome/browser/ui/fullscreen/BUILD.gn
index a755029..d7bc9e5 100644
--- a/ios/chrome/browser/ui/fullscreen/BUILD.gn
+++ b/ios/chrome/browser/ui/fullscreen/BUILD.gn
@@ -100,6 +100,7 @@
     "//ios/public/provider/chrome/browser/ui",
     "//ios/web",
     "//ios/web/common",
+    "//ios/web/public/security",
     "//ui/gfx/geometry",
   ]
 }
@@ -147,6 +148,7 @@
     "//ios/chrome/browser/web_state_list:test_support",
     "//ios/web/common",
     "//ios/web/public",
+    "//ios/web/public/security",
     "//ios/web/public/test/fakes",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/ios/chrome/browser/ui/location_bar/BUILD.gn b/ios/chrome/browser/ui/location_bar/BUILD.gn
index 0e357ae..3cc4c32 100644
--- a/ios/chrome/browser/ui/location_bar/BUILD.gn
+++ b/ios/chrome/browser/ui/location_bar/BUILD.gn
@@ -73,6 +73,7 @@
     "//ios/third_party/material_components_ios",
     "//ios/third_party/material_roboto_font_loader_ios",
     "//ios/web/public:public",
+    "//ios/web/public/security",
     "//skia",
     "//third_party/google_toolbox_for_mac",
     "//ui/base",
@@ -108,6 +109,7 @@
     "//ios/chrome/browser/ssl",
     "//ios/chrome/browser/web_state_list",
     "//ios/web/public",
+    "//ios/web/public/security",
   ]
   libs = [ "UIKit.framework" ]
 }
diff --git a/ios/chrome/browser/ui/page_info/BUILD.gn b/ios/chrome/browser/ui/page_info/BUILD.gn
index 5d23fc2d..a86df900 100644
--- a/ios/chrome/browser/ui/page_info/BUILD.gn
+++ b/ios/chrome/browser/ui/page_info/BUILD.gn
@@ -30,6 +30,7 @@
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/common",
     "//ios/web",
+    "//ios/web/public/security",
     "//ui/base",
     "//ui/gfx",
     "//url",
diff --git a/ios/chrome/browser/ui/payments/BUILD.gn b/ios/chrome/browser/ui/payments/BUILD.gn
index e13adcdf..0ae58fe 100644
--- a/ios/chrome/browser/ui/payments/BUILD.gn
+++ b/ios/chrome/browser/ui/payments/BUILD.gn
@@ -92,6 +92,8 @@
     "//ios/third_party/material_roboto_font_loader_ios",
     "//ios/web/common",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
+    "//ios/web/public/security",
     "//third_party/libaddressinput",
     "//third_party/libaddressinput:strings_grit",
     "//ui/base",
@@ -242,6 +244,7 @@
     "//ios/testing:ocmock_support",
     "//ios/third_party/material_components_ios",
     "//ios/web",
+    "//ios/web/public/js_messaging",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//services/identity/public/cpp:cpp",
diff --git a/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm b/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
index a4d30f6..51f2e4d 100644
--- a/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
+++ b/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
@@ -19,10 +19,10 @@
 #include "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
 #import "ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h"
 #import "ios/chrome/test/scoped_key_window.h"
+#import "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/fakes/crw_test_js_injection_receiver.h"
 #include "ios/web/public/test/fakes/fake_web_frame.h"
-#import "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/ocmock/gtest_support.h"
diff --git a/ios/chrome/browser/ui/payments/payment_request_manager.mm b/ios/chrome/browser/ui/payments/payment_request_manager.mm
index be6ee58..d8901d1 100644
--- a/ios/chrome/browser/ui/payments/payment_request_manager.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_manager.mm
@@ -58,6 +58,9 @@
 #import "ios/chrome/browser/ui/payments/payment_request_error_coordinator.h"
 #include "ios/web/common/origin_util.h"
 #include "ios/web/public/favicon_status.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #include "ios/web/public/navigation_item.h"
 #include "ios/web/public/navigation_manager.h"
 #include "ios/web/public/security/ssl_status.h"
@@ -66,9 +69,6 @@
 #import "ios/web/public/web_state/navigation_context.h"
 #import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
 #include "ios/web/public/web_state/url_verification_constants.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #include "third_party/libaddressinput/chromium/chrome_metadata_source.h"
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 6c7a5ad..60fa112e 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -76,6 +76,7 @@
 #import "ios/public/provider/chrome/browser/signin/signin_resources_provider.h"
 #include "ios/public/provider/chrome/browser/voice/voice_search_prefs.h"
 #include "services/identity/public/cpp/identity_manager.h"
+#import "services/identity/public/objc/identity_manager_observer_bridge.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -88,11 +89,6 @@
 NSString* const kSettingsSearchEngineCellId = @"Search Engine";
 NSString* const kSettingsVoiceSearchCellId = @"Voice Search Settings";
 
-@interface SettingsTableViewController (NotificationBridgeDelegate)
-// Notifies this controller that the sign in state has changed.
-- (void)onSignInStateChanged;
-@end
-
 namespace {
 
 const CGFloat kAccountProfilePhotoDimension = 40.0f;
@@ -157,47 +153,6 @@
 NSString* kDevViewSourceKey = @"DevViewSource";
 #endif  // CHROMIUM_BUILD && !defined(NDEBUG)
 
-#pragma mark - IdentityObserverBridge Class
-
-class IdentityObserverBridge : public identity::IdentityManager::Observer {
- public:
-  IdentityObserverBridge(ios::ChromeBrowserState* browserState,
-                         SettingsTableViewController* owner);
-  ~IdentityObserverBridge() override {}
-
-  // IdentityManager::Observer implementation:
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override;
-  void OnPrimaryAccountCleared(
-      const CoreAccountInfo& previous_primary_account_info) override;
-
- private:
-  __weak SettingsTableViewController* owner_;
-  ScopedObserver<identity::IdentityManager, IdentityObserverBridge> observer_;
-};
-
-IdentityObserverBridge::IdentityObserverBridge(
-    ios::ChromeBrowserState* browserState,
-    SettingsTableViewController* owner)
-    : owner_(owner), observer_(this) {
-  DCHECK(owner_);
-  identity::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForBrowserState(browserState);
-  if (!identity_manager)
-    return;
-  observer_.Add(identity_manager);
-}
-
-void IdentityObserverBridge::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  [owner_ onSignInStateChanged];
-}
-
-void IdentityObserverBridge::OnPrimaryAccountCleared(
-    const CoreAccountInfo& previous_primary_account_info) {
-  [owner_ onSignInStateChanged];
-}
-
 }  // namespace
 
 #pragma mark - SettingsTableViewController
@@ -206,6 +161,7 @@
     BooleanObserver,
     ChromeIdentityServiceObserver,
     GoogleServicesSettingsCoordinatorDelegate,
+    IdentityManagerObserverBridgeDelegate,
     PrefObserverDelegate,
     SettingsControllerProtocol,
     SearchEngineObserving,
@@ -217,7 +173,8 @@
   ios::ChromeBrowserState* _browserState;  // weak
   // Bridge for TemplateURLServiceObserver.
   std::unique_ptr<SearchEngineObserverBridge> _searchEngineObserverBridge;
-  std::unique_ptr<IdentityObserverBridge> _identityObserverBridge;
+  std::unique_ptr<identity::IdentityManagerObserverBridge>
+      _identityObserverBridge;
   std::unique_ptr<SyncObserverBridge> _syncObserverBridge;
   // Whether the impression of the Signin button has already been recorded.
   BOOL _hasRecordedSigninImpression;
@@ -299,8 +256,12 @@
     _searchEngineObserverBridge.reset(new SearchEngineObserverBridge(
         self,
         ios::TemplateURLServiceFactory::GetForBrowserState(_browserState)));
-    _identityObserverBridge.reset(
-        new IdentityObserverBridge(_browserState, self));
+    identity::IdentityManager* identityManager =
+        IdentityManagerFactory::GetForBrowserState(_browserState);
+    if (identityManager) {
+      _identityObserverBridge.reset(
+          new identity::IdentityManagerObserverBridge(identityManager, self));
+    }
     syncer::SyncService* syncService =
         ProfileSyncServiceFactory::GetForBrowserState(_browserState);
     _syncObserverBridge.reset(new SyncObserverBridge(self, syncService));
@@ -1120,7 +1081,7 @@
 - (void)showSignInWithIdentity:(ChromeIdentity*)identity
                    promoAction:(signin_metrics::PromoAction)promoAction
                     completion:(ShowSigninCommandCompletionCallback)completion {
-  DCHECK(!self.signinInteractionCoordinator.isActive);
+  DCHECK(![self.signinInteractionCoordinator isActive]);
   if (!self.signinInteractionCoordinator) {
     self.signinInteractionCoordinator = [[SigninInteractionCoordinator alloc]
         initWithBrowserState:_browserState
@@ -1156,21 +1117,6 @@
                 animated:YES];
 }
 
-#pragma mark NotificationBridgeDelegate
-
-- (void)onSignInStateChanged {
-  // While the sign-in interaction coordinator is presenting UI, the TableView
-  // should not be updated. Otherwise, it would lead to have an UI glitch either
-  // while the sign in UI is appearing or while it is disappearing. The
-  // TableView will be reloaded once the animation is finished.
-  // See: -[SettingsTableViewController didFinishSignin:].
-  if (!self.signinInteractionCoordinator.isActive) {
-    // Sign in state changes are rare. Just reload the entire table when
-    // this happens.
-    [self reloadData];
-  }
-}
-
 #pragma mark SettingsControllerProtocol
 
 - (void)settingsWillBeDismissed {
@@ -1317,7 +1263,7 @@
 - (void)configureSigninPromoWithConfigurator:
             (SigninPromoViewConfigurator*)configurator
                              identityChanged:(BOOL)identityChanged {
-  DCHECK(!self.signinInteractionCoordinator.isActive);
+  DCHECK(![self.signinInteractionCoordinator isActive]);
   if (![self.tableViewModel hasItemForItemType:ItemTypeSigninPromo
                              sectionIdentifier:SectionIdentifierSignIn]) {
     return;
@@ -1361,4 +1307,28 @@
   _googleServicesSettingsCoordinator = nil;
 }
 
+#pragma mark - IdentityManagerObserverBridgeDelegate
+
+// Notifies this controller that the sign in state has changed.
+- (void)signinStateDidChange {
+  // While the sign-in interaction coordinator is presenting UI, the TableView
+  // should not be updated. Otherwise, it would lead to an UI glitch either
+  // while the sign in UI is appearing or disappearing. The TableView will be
+  // reloaded once the animation is finished.
+  // See: -[SettingsTableViewController didFinishSignin:].
+  if ([self.signinInteractionCoordinator isActive])
+    return;
+  // Sign in state changes are rare. Just reload the entire table when
+  // this happens.
+  [self reloadData];
+}
+- (void)onPrimaryAccountSet:(const CoreAccountInfo&)primaryAccountInfo {
+  [self signinStateDidChange];
+}
+
+- (void)onPrimaryAccountCleared:
+    (const CoreAccountInfo&)previousPrimaryAccountInfo {
+  [self signinStateDidChange];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/webui/BUILD.gn b/ios/chrome/browser/ui/webui/BUILD.gn
index 5e5866af..e2d27aa6 100644
--- a/ios/chrome/browser/ui/webui/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/BUILD.gn
@@ -69,6 +69,7 @@
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/common",
     "//ios/web",
+    "//ios/web/public/js_messaging",
     "//net",
     "//services/service_manager/public/cpp",
     "//third_party/brotli:dec",
diff --git a/ios/chrome/browser/ui/webui/inspect/inspect_ui.mm b/ios/chrome/browser/ui/webui/inspect/inspect_ui.mm
index ed5a0d7..adf6260 100644
--- a/ios/chrome/browser/ui/webui/inspect/inspect_ui.mm
+++ b/ios/chrome/browser/ui/webui/inspect/inspect_ui.mm
@@ -20,9 +20,9 @@
 #import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
 #include "ios/chrome/grit/ios_resources.h"
 #include "ios/chrome/grit/ios_strings.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "ios/web/public/web_ui_ios_data_source.h"
 #include "ios/web/public/webui/web_ui_ios.h"
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index 4167817..99745ff1 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -65,6 +65,7 @@
   deps = [
     "//base",
     "//ios/web/public",
+    "//ios/web/public/session",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
@@ -194,6 +195,7 @@
   deps = [
     "//base",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
   ]
   libs = [ "Foundation.framework" ]
 }
diff --git a/ios/chrome/browser/web/java_script_console/java_script_console_tab_helper.mm b/ios/chrome/browser/web/java_script_console/java_script_console_tab_helper.mm
index 2879a00..c2fe463 100644
--- a/ios/chrome/browser/web/java_script_console/java_script_console_tab_helper.mm
+++ b/ios/chrome/browser/web/java_script_console/java_script_console_tab_helper.mm
@@ -9,7 +9,7 @@
 #include "base/bind.h"
 #include "base/values.h"
 #include "ios/chrome/browser/web/java_script_console/java_script_console_message.h"
-#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/web/tab_id_tab_helper.mm b/ios/chrome/browser/web/tab_id_tab_helper.mm
index 40ae6999..e124bf0 100644
--- a/ios/chrome/browser/web/tab_id_tab_helper.mm
+++ b/ios/chrome/browser/web/tab_id_tab_helper.mm
@@ -6,7 +6,7 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/web_state_list/BUILD.gn b/ios/chrome/browser/web_state_list/BUILD.gn
index 560bc47..92a391b 100644
--- a/ios/chrome/browser/web_state_list/BUILD.gn
+++ b/ios/chrome/browser/web_state_list/BUILD.gn
@@ -32,6 +32,7 @@
     "//components/favicon/ios",
     "//ios/chrome/browser/sessions:serialisation",
     "//ios/web",
+    "//ios/web/public/session",
   ]
   libs = [ "Foundation.framework" ]
   configs += [ "//build/config/compiler:enable_arc" ]
@@ -70,6 +71,7 @@
     "//ios/chrome/browser/sessions:serialisation",
     "//ios/chrome/browser/web",
     "//ios/web",
+    "//ios/web/public/session",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//net",
diff --git a/ios/chrome/browser/web_state_list/web_state_list_serialization.mm b/ios/chrome/browser/web_state_list/web_state_list_serialization.mm
index c0ab88e..dbf20112 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_serialization.mm
+++ b/ios/chrome/browser/web_state_list/web_state_list_serialization.mm
@@ -15,7 +15,7 @@
 #import "ios/chrome/browser/sessions/session_window_ios.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/web_state_list/web_state_list_serialization_unittest.mm b/ios/chrome/browser/web_state_list/web_state_list_serialization_unittest.mm
index c2f2cd569..7caa5b5d 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_serialization_unittest.mm
+++ b/ios/chrome/browser/web_state_list/web_state_list_serialization_unittest.mm
@@ -11,8 +11,8 @@
 #import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
-#import "ios/web/public/crw_session_storage.h"
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/crw_session_storage.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index a4afb25c..4d0ac98 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -154,6 +154,7 @@
     "//ios/chrome/browser/history:unit_tests",
     "//ios/chrome/browser/infobars:unit_tests",
     "//ios/chrome/browser/itunes_urls:unit_tests",
+    "//ios/chrome/browser/json_parser:unit_tests",
     "//ios/chrome/browser/language:unit_tests",
     "//ios/chrome/browser/main:unit_tests",
     "//ios/chrome/browser/metrics:unit_tests",
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index a355859..1cf31544 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -44,7 +44,10 @@
     "//ios/web/navigation",
     "//ios/web/net",
     "//ios/web/public",
+    "//ios/web/public/security",
+    "//ios/web/public/session",
     "//ios/web/security",
+    "//ios/web/session",
     "//ios/web/web_state",
     "//ios/web/web_state:error_translation_util",
     "//ios/web/web_state:web_view_internal_creation_util",
@@ -202,7 +205,9 @@
     "//ios/web/common:unittests",
     "//ios/web/download:download_unittests",
     "//ios/web/find_in_page:find_in_page_unittests",
+    "//ios/web/js_messaging:unittests",
     "//ios/web/security:unittests",
+    "//ios/web/session:unittests",
     "//ios/web/web_view:unittests",
   ]
 
@@ -265,6 +270,7 @@
     "//ios/web/navigation:navigation_manager_util",
     "//ios/web/navigation:wk_navigation_util",
     "//ios/web/public",
+    "//ios/web/public/session",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//ios/web/test:mojo_bindings",
@@ -318,6 +324,7 @@
     "//ios/web/navigation",
     "//ios/web/net/cookies",
     "//ios/web/public",
+    "//ios/web/public/security",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//ios/web/security",
@@ -382,8 +389,6 @@
   ]
 
   sources = [
-    "public/crw_session_certificate_policy_cache_storage_unittest.mm",
-    "public/serializable_user_data_manager_unittest.mm",
     "public/user_agent_unittest.mm",
     "public/web_state/page_viewport_state_unittest.mm",
   ]
@@ -404,13 +409,17 @@
     "//ios/web",
     "//ios/web/common",
     "//ios/web/find_in_page",
+    "//ios/web/js_messaging",
     "//ios/web/navigation",
     "//ios/web/navigation:wk_navigation_util",
     "//ios/web/public",
+    "//ios/web/public/security",
+    "//ios/web/public/session",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//ios/web/security",
     "//ios/web/security",
+    "//ios/web/session",
     "//ios/web/test:mojo_bindings",
     "//ios/web/test:test_constants",
     "//ios/web/test:test_support",
@@ -418,8 +427,6 @@
     "//ios/web/web_state",
     "//ios/web/web_state:context_menu",
     "//ios/web/web_state:error_translation_util",
-    "//ios/web/web_state:session_certificate_policy_cache",
-    "//ios/web/web_state:web_frame",
     "//ios/web/web_state:web_view_internal_creation_util",
     "//net:test_support",
     "//services/service_manager/public/cpp",
@@ -434,12 +441,7 @@
     "web_state/context_menu_params_utils_unittest.mm",
     "web_state/error_translation_util_unittest.mm",
     "web_state/page_display_state_unittest.mm",
-    "web_state/session_certificate_policy_cache_impl_unittest.mm",
-    "web_state/session_certificate_policy_cache_storage_builder_unittest.mm",
     "web_state/ui/wk_security_origin_util_unittest.mm",
-    "web_state/web_frame_impl_unittest.mm",
-    "web_state/web_frame_util_unittest.mm",
-    "web_state/web_frames_manager_impl_unittest.mm",
     "web_state/web_state_delegate_bridge_unittest.mm",
     "web_state/web_state_impl_unittest.mm",
     "web_state/web_state_observer_bridge_unittest.mm",
@@ -462,6 +464,7 @@
     "//ios/testing:ocmock_support",
     "//ios/web/find_in_page",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//ios/web/test:mojo_bindings",
@@ -506,6 +509,7 @@
     "//ios/web/navigation:block_universal_links_buildflags",
     "//ios/web/navigation:core",
     "//ios/web/public",
+    "//ios/web/public/session",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//ios/web/security",
@@ -584,9 +588,13 @@
     "//ios/web:resources_grit",
     "//ios/web/common",
     "//ios/web/download:download_inttests",
+    "//ios/web/js_messaging:inttests",
     "//ios/web/navigation:core",
     "//ios/web/navigation:wk_navigation_util",
     "//ios/web/public/find_in_page",
+    "//ios/web/public/js_messaging",
+    "//ios/web/public/security",
+    "//ios/web/public/session",
     "//ios/web/public/test",
     "//ios/web/public/test:element_selector",
     "//ios/web/public/test/fakes",
@@ -597,7 +605,6 @@
     "//ios/web/test:test_constants",
     "//ios/web/test:test_support",
     "//ios/web/web_state",
-    "//ios/web/web_state:web_frame",
     "//mojo/core/embedder",
     "//net:test_support",
     "//services/network/public/cpp",
@@ -621,9 +628,6 @@
     "web_state/favicon_callbacks_inttest.mm",
     "web_state/http_auth_inttest.mm",
     "web_state/keep_render_process_alive_inttest.mm",
-    "web_state/web_frame_impl_inttest.mm",
-    "web_state/web_frame_web_state_observer_inttest.mm",
-    "web_state/web_frames_manager_inttest.mm",
     "web_state/web_state_observer_inttest.mm",
     "webui/web_ui_mojo_inttest.mm",
   ]
diff --git a/ios/web/crw_navigation_item_storage.mm b/ios/web/crw_navigation_item_storage.mm
index 041c5c8..2754c9a0 100644
--- a/ios/web/crw_navigation_item_storage.mm
+++ b/ios/web/crw_navigation_item_storage.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/public/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
 
 #include "base/strings/sys_string_conversions.h"
 #import "ios/web/navigation/nscoder_util.h"
diff --git a/ios/web/find_in_page/BUILD.gn b/ios/web/find_in_page/BUILD.gn
index caa3e3a..51fc333 100644
--- a/ios/web/find_in_page/BUILD.gn
+++ b/ios/web/find_in_page/BUILD.gn
@@ -5,9 +5,10 @@
 source_set("find_in_page") {
   deps = [
     "//base",
+    "//ios/web/js_messaging",
     "//ios/web/public/",
     "//ios/web/public/find_in_page",
-    "//ios/web/web_state:web_frame",
+    "//ios/web/public/js_messaging",
     "//ios/web/web_state:web_state_impl_header",
   ]
 
@@ -30,6 +31,7 @@
     "//base/test:test_support",
     "//ios/web/public",
     "//ios/web/public/find_in_page",
+    "//ios/web/public/js_messaging",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//testing/gtest",
diff --git a/ios/web/find_in_page/find_in_page_manager_impl.mm b/ios/web/find_in_page/find_in_page_manager_impl.mm
index 649cef9..b402152 100644
--- a/ios/web/find_in_page/find_in_page_manager_impl.mm
+++ b/ios/web/find_in_page/find_in_page_manager_impl.mm
@@ -9,9 +9,9 @@
 #include "base/values.h"
 #import "ios/web/find_in_page/find_in_page_constants.h"
 #import "ios/web/public/find_in_page/find_in_page_manager_delegate.h"
-#import "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
+#import "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #include "ios/web/public/web_task_traits.h"
 #import "ios/web/web_state/web_state_impl.h"
 
diff --git a/ios/web/find_in_page/find_in_page_manager_inttest.mm b/ios/web/find_in_page/find_in_page_manager_inttest.mm
index dcbfa60e..cc6b832 100644
--- a/ios/web/find_in_page/find_in_page_manager_inttest.mm
+++ b/ios/web/find_in_page/find_in_page_manager_inttest.mm
@@ -5,10 +5,10 @@
 #import "base/test/ios/wait_util.h"
 #include "ios/testing/embedded_test_server_handlers.h"
 #import "ios/web/public/find_in_page/find_in_page_manager.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/test/fakes/fake_find_in_page_manager_delegate.h"
 #import "ios/web/public/test/navigation_test_util.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
-#include "ios/web/public/web_state/web_frame_util.h"
 #include "net/base/escape.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/request_handler_util.h"
diff --git a/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm b/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
index f3c20ae13..4d6faa4b 100644
--- a/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
+++ b/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
@@ -8,12 +8,12 @@
 #import "base/test/ios/wait_util.h"
 #include "base/values.h"
 #import "ios/web/find_in_page/find_in_page_constants.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/fakes/fake_find_in_page_manager_delegate.h"
 #include "ios/web/public/test/fakes/fake_web_frame.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/web_test.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/web/js_messaging/BUILD.gn b/ios/web/js_messaging/BUILD.gn
new file mode 100644
index 0000000..e65f92fc7
--- /dev/null
+++ b/ios/web/js_messaging/BUILD.gn
@@ -0,0 +1,71 @@
+# Copyright 2019 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.
+
+import("//ios/build/config.gni")
+
+source_set("js_messaging") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  deps = [
+    "//base",
+    "//crypto",
+    "//ios/web/public",
+    "//ios/web/public/js_messaging",
+    "//ios/web/web_state:web_state_impl_header",
+    "//url",
+  ]
+
+  sources = [
+    "web_frame_impl.h",
+    "web_frame_impl.mm",
+    "web_frame_util.mm",
+    "web_frames_manager_impl.h",
+    "web_frames_manager_impl.mm",
+  ]
+}
+
+source_set("unittests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  deps = [
+    ":js_messaging",
+    "//base",
+    "//base/test:test_support",
+    "//crypto",
+    "//ios/web/public/js_messaging",
+    "//ios/web/public/test",
+    "//ios/web/public/test/fakes",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "web_frame_impl_unittest.mm",
+    "web_frame_util_unittest.mm",
+    "web_frames_manager_impl_unittest.mm",
+  ]
+}
+
+source_set("inttests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//ios/web/common",
+    "//ios/web/js_messaging",
+    "//ios/web/public",
+    "//ios/web/public/js_messaging",
+    "//ios/web/public/test",
+    "//ios/web/public/test:util",
+    "//ios/web/public/test/fakes",
+    "//ios/web/test:test_support",
+    "//net:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+  sources = [
+    "web_frame_impl_inttest.mm",
+    "web_frame_web_state_observer_inttest.mm",
+    "web_frames_manager_inttest.mm",
+  ]
+}
diff --git a/ios/web/web_state/web_frame_impl.h b/ios/web/js_messaging/web_frame_impl.h
similarity index 96%
rename from ios/web/web_state/web_frame_impl.h
rename to ios/web/js_messaging/web_frame_impl.h
index ff4ac1e..aa3ab2e 100644
--- a/ios/web/web_state/web_frame_impl.h
+++ b/ios/web/js_messaging/web_frame_impl.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_STATE_WEB_FRAME_IMPL_H_
-#define IOS_WEB_WEB_STATE_WEB_FRAME_IMPL_H_
+#ifndef IOS_WEB_JS_MESSAGING_WEB_FRAME_IMPL_H_
+#define IOS_WEB_JS_MESSAGING_WEB_FRAME_IMPL_H_
 
-#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame.h"
 
 #include <map>
 #include <string>
@@ -148,4 +148,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_WEB_STATE_WEB_FRAME_IMPL_H_
+#endif  // IOS_WEB_JS_MESSAGING_WEB_FRAME_IMPL_H_
diff --git a/ios/web/web_state/web_frame_impl.mm b/ios/web/js_messaging/web_frame_impl.mm
similarity index 99%
rename from ios/web/web_state/web_frame_impl.mm
rename to ios/web/js_messaging/web_frame_impl.mm
index e89f8a2..643cb89 100644
--- a/ios/web/web_state/web_frame_impl.mm
+++ b/ios/web/js_messaging/web_frame_impl.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/web_state/web_frame_impl.h"
+#include "ios/web/js_messaging/web_frame_impl.h"
 
 #import <Foundation/Foundation.h>
 
diff --git a/ios/web/web_state/web_frame_impl_inttest.mm b/ios/web/js_messaging/web_frame_impl_inttest.mm
similarity index 83%
rename from ios/web/web_state/web_frame_impl_inttest.mm
rename to ios/web/js_messaging/web_frame_impl_inttest.mm
index f6c3641c..8a18d02 100644
--- a/ios/web/web_state/web_frame_impl_inttest.mm
+++ b/ios/web/js_messaging/web_frame_impl_inttest.mm
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/web_state/web_frame_impl.h"
+#include "ios/web/js_messaging/web_frame_impl.h"
 
 #include "base/bind.h"
 #include "base/ios/ios_util.h"
 #import "base/test/ios/wait_util.h"
+#include "ios/web/js_messaging/web_frames_manager_impl.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/test/fakes/test_web_client.h"
 #import "ios/web/public/test/js_test_util.h"
 #import "ios/web/public/test/web_js_test.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#include "ios/web/web_state/web_frames_manager_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -104,10 +104,9 @@
   ASSERT_TRUE(LoadHtml("<p>"));
 
   // Inject a function which will never return in order to test feature timeout.
-  ExecuteJavaScript(
-      @"__gCrWeb.testFunctionNeverReturns = function() {"
-       "  while(true) {}"
-       "};");
+  ExecuteJavaScript(@"__gCrWeb.testFunctionNeverReturns = function() {"
+                     "  while(true) {}"
+                     "};");
 
   WebFrame* main_frame = GetMainWebFrame(web_state());
   ASSERT_TRUE(main_frame);
@@ -141,33 +140,32 @@
 
   // Inject function into main frame to intercept encrypted message targeted for
   // the iframe.
-  ExecuteJavaScript(
-      @"var sensitiveValue = 0;"
-       "__gCrWeb.message.incrementSensitiveValue = function() {"
-       "  sensitiveValue = sensitiveValue + 1;"
-       "  return sensitiveValue;"
-       "};"
+  ExecuteJavaScript(@"var sensitiveValue = 0;"
+                     "__gCrWeb.message.incrementSensitiveValue = function() {"
+                     "  sensitiveValue = sensitiveValue + 1;"
+                     "  return sensitiveValue;"
+                     "};"
 
-       "var originalRouteMessage = __gCrWeb.message.routeMessage;"
-       "var interceptedMessagePayload = '';"
-       "var interceptedMessageIv = '';"
-       "var interceptedMessageFrameId = '';"
+                     "var originalRouteMessage = __gCrWeb.message.routeMessage;"
+                     "var interceptedMessagePayload = '';"
+                     "var interceptedMessageIv = '';"
+                     "var interceptedMessageFrameId = '';"
 
-       "var replayInterceptedMessage = function() {"
-       "  originalRouteMessage("
-       "    interceptedMessagePayload,"
-       "    interceptedMessageIv,"
-       "    interceptedMessageFrameId"
-       "  );"
-       "};"
+                     "var replayInterceptedMessage = function() {"
+                     "  originalRouteMessage("
+                     "    interceptedMessagePayload,"
+                     "    interceptedMessageIv,"
+                     "    interceptedMessageFrameId"
+                     "  );"
+                     "};"
 
-       "__gCrWeb.message.routeMessage ="
-       "    function(payload, iv, target_frame_id) {"
-       "  interceptedMessagePayload = payload;"
-       "  interceptedMessageIv = iv;"
-       "  interceptedMessageFrameId = target_frame_id;"
-       "  replayInterceptedMessage();"
-       "};");
+                     "__gCrWeb.message.routeMessage ="
+                     "    function(payload, iv, target_frame_id) {"
+                     "  interceptedMessagePayload = payload;"
+                     "  interceptedMessageIv = iv;"
+                     "  interceptedMessageFrameId = target_frame_id;"
+                     "  replayInterceptedMessage();"
+                     "};");
 
   NSTimeInterval js_timeout = kWaitForJSCompletionTimeout;
 
@@ -221,9 +219,8 @@
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
     return manager->GetAllWebFrames().size() == 1;
   }));
-  ExecuteJavaScript(
-      @"__gCrWeb.message.invokeOnHost({'command':"
-      @"'senderFrameTestCommand.mainframe'});");
+  ExecuteJavaScript(@"__gCrWeb.message.invokeOnHost({'command':"
+                    @"'senderFrameTestCommand.mainframe'});");
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
     return command_received;
   }));
diff --git a/ios/web/web_state/web_frame_impl_unittest.mm b/ios/web/js_messaging/web_frame_impl_unittest.mm
similarity index 99%
rename from ios/web/web_state/web_frame_impl_unittest.mm
rename to ios/web/js_messaging/web_frame_impl_unittest.mm
index b5d5b915..6782d8a 100644
--- a/ios/web/web_state/web_frame_impl_unittest.mm
+++ b/ios/web/js_messaging/web_frame_impl_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/web_state/web_frame_impl.h"
+#include "ios/web/js_messaging/web_frame_impl.h"
 
 #import "base/base64.h"
 #include "base/bind.h"
diff --git a/ios/web/web_state/web_frame_util.mm b/ios/web/js_messaging/web_frame_util.mm
similarity index 88%
rename from ios/web/web_state/web_frame_util.mm
rename to ios/web/js_messaging/web_frame_util.mm
index a81ef93a..74c1bf51 100644
--- a/ios/web/web_state/web_frame_util.mm
+++ b/ios/web/js_messaging/web_frame_util.mm
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/public/web_state/web_frame_util.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 
 #include "base/logging.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frames_manager.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/web/web_state/web_frame_util_unittest.mm b/ios/web/js_messaging/web_frame_util_unittest.mm
similarity index 98%
rename from ios/web/web_state/web_frame_util_unittest.mm
rename to ios/web/js_messaging/web_frame_util_unittest.mm
index da26eb3..8a805c2 100644
--- a/ios/web/web_state/web_frame_util_unittest.mm
+++ b/ios/web/js_messaging/web_frame_util_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/public/web_state/web_frame_util.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 
 #include "base/test/gtest_util.h"
 #include "ios/web/public/test/fakes/fake_web_frame.h"
diff --git a/ios/web/web_state/web_frame_web_state_observer_inttest.mm b/ios/web/js_messaging/web_frame_web_state_observer_inttest.mm
similarity index 96%
rename from ios/web/web_state/web_frame_web_state_observer_inttest.mm
rename to ios/web/js_messaging/web_frame_web_state_observer_inttest.mm
index 826f5c3..dedb1e86 100644
--- a/ios/web/web_state/web_frame_web_state_observer_inttest.mm
+++ b/ios/web/js_messaging/web_frame_web_state_observer_inttest.mm
@@ -7,11 +7,11 @@
 #include "base/ios/ios_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "ios/web/common/features.h"
-#import "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
+#include "ios/web/js_messaging/web_frames_manager_impl.h"
+#import "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_observer.h"
-#include "ios/web/web_state/web_frames_manager_impl.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/web/web_state/web_frames_manager_impl.h b/ios/web/js_messaging/web_frames_manager_impl.h
similarity index 91%
rename from ios/web/web_state/web_frames_manager_impl.h
rename to ios/web/js_messaging/web_frames_manager_impl.h
index dba457a7..17f6259 100644
--- a/ios/web/web_state/web_frames_manager_impl.h
+++ b/ios/web/js_messaging/web_frames_manager_impl.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_STATE_WEB_FRAMES_MANAGER_IMPL_H_
-#define IOS_WEB_WEB_STATE_WEB_FRAMES_MANAGER_IMPL_H_
+#ifndef IOS_WEB_JS_MESSAGING_WEB_FRAMES_MANAGER_IMPL_H_
+#define IOS_WEB_JS_MESSAGING_WEB_FRAMES_MANAGER_IMPL_H_
 
-#import "ios/web/public/web_state/web_frames_manager.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 
 namespace web {
 
diff --git a/ios/web/web_state/web_frames_manager_impl.mm b/ios/web/js_messaging/web_frames_manager_impl.mm
similarity index 96%
rename from ios/web/web_state/web_frames_manager_impl.mm
rename to ios/web/js_messaging/web_frames_manager_impl.mm
index 9b34495..869d7ec1 100644
--- a/ios/web/web_state/web_frames_manager_impl.mm
+++ b/ios/web/js_messaging/web_frames_manager_impl.mm
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/web_state/web_frames_manager_impl.h"
+#include "ios/web/js_messaging/web_frames_manager_impl.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/web/web_state/web_frames_manager_impl_unittest.mm b/ios/web/js_messaging/web_frames_manager_impl_unittest.mm
similarity index 98%
rename from ios/web/web_state/web_frames_manager_impl_unittest.mm
rename to ios/web/js_messaging/web_frames_manager_impl_unittest.mm
index aaa74e55..6117f09 100644
--- a/ios/web/web_state/web_frames_manager_impl_unittest.mm
+++ b/ios/web/js_messaging/web_frames_manager_impl_unittest.mm
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/web_state/web_frames_manager_impl.h"
+#include "ios/web/js_messaging/web_frames_manager_impl.h"
 
+#include "ios/web/js_messaging/web_frame_impl.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
-#include "ios/web/web_state/web_frame_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
diff --git a/ios/web/web_state/web_frames_manager_inttest.mm b/ios/web/js_messaging/web_frames_manager_inttest.mm
similarity index 98%
rename from ios/web/web_state/web_frames_manager_inttest.mm
rename to ios/web/js_messaging/web_frames_manager_inttest.mm
index d90d61d..39bd557dd 100644
--- a/ios/web/web_state/web_frames_manager_inttest.mm
+++ b/ios/web/js_messaging/web_frames_manager_inttest.mm
@@ -5,12 +5,12 @@
 #include "base/ios/ios_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "ios/web/common/features.h"
+#include "ios/web/js_messaging/web_frames_manager_impl.h"
+#include "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/navigation_manager.h"
 #import "ios/web/public/test/web_view_content_test_util.h"
 #import "ios/web/public/test/web_view_interaction_test_util.h"
-#include "ios/web/public/web_state/web_frame.h"
 #import "ios/web/test/web_int_test.h"
-#include "ios/web/web_state/web_frames_manager_impl.h"
 #include "net/test/embedded_test_server/default_handlers.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/ios/web/navigation/BUILD.gn b/ios/web/navigation/BUILD.gn
index 0b1c2af..efc61662 100644
--- a/ios/web/navigation/BUILD.gn
+++ b/ios/web/navigation/BUILD.gn
@@ -23,7 +23,9 @@
     "//ios/web/navigation:wk_navigation_util",
     "//ios/web/public",
     "//ios/web/public/download",
-    "//ios/web/web_state:session_certificate_policy_cache",
+    "//ios/web/public/security",
+    "//ios/web/public/session",
+    "//ios/web/session",
     "//ios/web/web_state:user_interaction",
     "//ios/web/web_state:web_state_impl_header",
     "//ios/web/web_state/ui:crw_web_view_navigation_proxy",
@@ -77,6 +79,7 @@
     "//components/url_formatter:url_formatter",
     "//ios/web/navigation:wk_navigation_util",
     "//ios/web/public",
+    "//ios/web/public/security",
     "//ui/base",
   ]
 
diff --git a/ios/web/navigation/crw_navigation_item_storage_unittest.mm b/ios/web/navigation/crw_navigation_item_storage_unittest.mm
index 76da767..41470b5a 100644
--- a/ios/web/navigation/crw_navigation_item_storage_unittest.mm
+++ b/ios/web/navigation/crw_navigation_item_storage_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/public/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
 
 #import <Foundation/Foundation.h>
 #include <stdint.h>
diff --git a/ios/web/navigation/crw_session_storage_unittest.mm b/ios/web/navigation/crw_session_storage_unittest.mm
index fa6d9e1..bbb7078 100644
--- a/ios/web/navigation/crw_session_storage_unittest.mm
+++ b/ios/web/navigation/crw_session_storage_unittest.mm
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/public/crw_session_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
 
 #include "base/strings/sys_string_conversions.h"
 #import "ios/web/navigation/navigation_item_impl.h"
 #import "ios/web/navigation/navigation_item_storage_test_util.h"
 #import "ios/web/navigation/serializable_user_data_manager_impl.h"
-#import "ios/web/public/crw_navigation_item_storage.h"
 #include "ios/web/public/referrer.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
 #import "net/base/mac/url_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
diff --git a/ios/web/navigation/navigation_item_storage_builder.mm b/ios/web/navigation/navigation_item_storage_builder.mm
index 899f58c6..a7b0dbb 100644
--- a/ios/web/navigation/navigation_item_storage_builder.mm
+++ b/ios/web/navigation/navigation_item_storage_builder.mm
@@ -5,7 +5,7 @@
 #import "ios/web/navigation/navigation_item_storage_builder.h"
 
 #import "ios/web/navigation/navigation_item_impl.h"
-#import "ios/web/public/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/navigation/navigation_item_storage_test_util.mm b/ios/web/navigation/navigation_item_storage_test_util.mm
index f7198fb..85f87a8e 100644
--- a/ios/web/navigation/navigation_item_storage_test_util.mm
+++ b/ios/web/navigation/navigation_item_storage_test_util.mm
@@ -4,7 +4,7 @@
 
 #import "ios/web/navigation/navigation_item_storage_test_util.h"
 
-#import "ios/web/public/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/navigation/serializable_user_data_manager_impl.h b/ios/web/navigation/serializable_user_data_manager_impl.h
index 7817356..448001a 100644
--- a/ios/web/navigation/serializable_user_data_manager_impl.h
+++ b/ios/web/navigation/serializable_user_data_manager_impl.h
@@ -6,7 +6,7 @@
 #define IOS_WEB_NAVIGATION_SERIALIZABLE_USER_DATA_MANAGER_IMPL_H_
 
 #include "base/macros.h"
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 
 namespace web {
 
diff --git a/ios/web/navigation/session_storage_builder.mm b/ios/web/navigation/session_storage_builder.mm
index a3dd74b..2d7957e 100644
--- a/ios/web/navigation/session_storage_builder.mm
+++ b/ios/web/navigation/session_storage_builder.mm
@@ -12,10 +12,10 @@
 #import "ios/web/navigation/navigation_item_impl.h"
 #import "ios/web/navigation/navigation_item_storage_builder.h"
 #include "ios/web/navigation/navigation_manager_impl.h"
-#import "ios/web/public/crw_session_storage.h"
-#import "ios/web/public/serializable_user_data_manager.h"
-#import "ios/web/web_state/session_certificate_policy_cache_impl.h"
-#include "ios/web/web_state/session_certificate_policy_cache_storage_builder.h"
+#import "ios/web/public/session/crw_session_storage.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
+#import "ios/web/session/session_certificate_policy_cache_impl.h"
+#include "ios/web/session/session_certificate_policy_cache_storage_builder.h"
 #import "ios/web/web_state/web_state_impl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/web/net/BUILD.gn b/ios/web/net/BUILD.gn
index 2420e9a..e665612a 100644
--- a/ios/web/net/BUILD.gn
+++ b/ios/web/net/BUILD.gn
@@ -10,6 +10,9 @@
     "//ios/net",
     "//ios/web:core",
     "//ios/web/public",
+    "//ios/web/public/security",
+    "//ios/web/security",
+    "//net",
   ]
 
   sources = [
diff --git a/ios/web/public/BUILD.gn b/ios/web/public/BUILD.gn
index f79d10d..2277155 100644
--- a/ios/web/public/BUILD.gn
+++ b/ios/web/public/BUILD.gn
@@ -22,12 +22,6 @@
   sources = [
     "browser_state.h",
     "browser_url_rewriter.h",
-    "certificate_policy_cache.h",
-    "crw_navigation_item_storage.h",
-    "crw_session_certificate_policy_cache_storage.h",
-    "crw_session_certificate_policy_cache_storage.mm",
-    "crw_session_storage.h",
-    "crw_session_storage.mm",
     "favicon_status.cc",
     "favicon_status.h",
     "favicon_url.cc",
@@ -42,11 +36,6 @@
     "reload_type.h",
     "security/cert_policy.h",
     "security/certificate_policy_cache.h",
-    "security/security_style.h",
-    "security/ssl_status.h",
-    "security/web_interstitial.h",
-    "security/web_interstitial_delegate.h",
-    "serializable_user_data_manager.h",
     "service_manager_connection.h",
     "url_data_source_ios.h",
     "url_scheme_util.h",
@@ -61,7 +50,6 @@
     "web_state/navigation_context.h",
     "web_state/page_display_state.h",
     "web_state/page_display_state.mm",
-    "web_state/session_certificate_policy_cache.h",
     "web_state/ui/crw_context_menu_delegate.h",
     "web_state/ui/crw_native_content.h",
     "web_state/ui/crw_native_content_holder.h",
@@ -69,10 +57,6 @@
     "web_state/ui/crw_web_view_proxy.h",
     "web_state/ui/crw_web_view_scroll_view_proxy.h",
     "web_state/url_verification_constants.h",
-    "web_state/web_frame.h",
-    "web_state/web_frame_user_data.h",
-    "web_state/web_frame_util.h",
-    "web_state/web_frames_manager.h",
     "web_state/web_state.h",
     "web_state/web_state_delegate.h",
     "web_state/web_state_delegate_bridge.h",
diff --git a/ios/web/public/certificate_policy_cache.h b/ios/web/public/certificate_policy_cache.h
deleted file mode 100644
index 72da330..0000000
--- a/ios/web/public/certificate_policy_cache.h
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2012 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 IOS_WEB_PUBLIC_CERTIFICATE_POLICY_CACHE_H_
-#define IOS_WEB_PUBLIC_CERTIFICATE_POLICY_CACHE_H_
-
-#include "ios/web/public/security/certificate_policy_cache.h"
-
-#endif  // IOS_WEB_PUBLIC_CERTIFICATE_POLICY_CACHE_H_
diff --git a/ios/web/public/js_messaging/BUILD.gn b/ios/web/public/js_messaging/BUILD.gn
new file mode 100644
index 0000000..4ce249f
--- /dev/null
+++ b/ios/web/public/js_messaging/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2019 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.
+
+source_set("js_messaging") {
+  deps = [
+    "//base",
+    "//ios/web/public/",
+    "//url",
+  ]
+
+  sources = [
+    "web_frame.h",
+    "web_frame_user_data.h",
+    "web_frame_util.h",
+    "web_frames_manager.h",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/web/public/js_messaging/README.md b/ios/web/public/js_messaging/README.md
new file mode 100644
index 0000000..1baf3d29
--- /dev/null
+++ b/ios/web/public/js_messaging/README.md
@@ -0,0 +1 @@
+This directory contains API to send messages from the native code to JavaScript injected by //ios/web clients.
diff --git a/ios/web/public/web_state/web_frame.h b/ios/web/public/js_messaging/web_frame.h
similarity index 94%
rename from ios/web/public/web_state/web_frame.h
rename to ios/web/public/js_messaging/web_frame.h
index ce21502..eeb1cbb 100644
--- a/ios/web/public/web_state/web_frame.h
+++ b/ios/web/public/js_messaging/web_frame.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_H_
-#define IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_H_
+#ifndef IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAME_H_
+#define IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAME_H_
 
 #include <string>
 
@@ -71,4 +71,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_H_
+#endif  // IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAME_H_
diff --git a/ios/web/public/web_state/web_frame_user_data.h b/ios/web/public/js_messaging/web_frame_user_data.h
similarity index 90%
rename from ios/web/public/web_state/web_frame_user_data.h
rename to ios/web/public/js_messaging/web_frame_user_data.h
index 49d212e..d5737dcb 100644
--- a/ios/web/public/web_state/web_frame_user_data.h
+++ b/ios/web/public/js_messaging/web_frame_user_data.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_USER_DATA_H_
-#define IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_USER_DATA_H_
+#ifndef IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAME_USER_DATA_H_
+#define IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAME_USER_DATA_H_
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/supports_user_data.h"
-#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame.h"
 
 namespace web {
 
@@ -64,4 +64,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_USER_DATA_H_
+#endif  // IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAME_USER_DATA_H_
diff --git a/ios/web/public/web_state/web_frame_util.h b/ios/web/public/js_messaging/web_frame_util.h
similarity index 85%
rename from ios/web/public/web_state/web_frame_util.h
rename to ios/web/public/js_messaging/web_frame_util.h
index a2cf8dc4..628a7f9 100644
--- a/ios/web/public/web_state/web_frame_util.h
+++ b/ios/web/public/js_messaging/web_frame_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_UTIL_H_
-#define IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_UTIL_H_
+#ifndef IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAME_UTIL_H_
+#define IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAME_UTIL_H_
 
 #include <set>
 #include <string>
@@ -32,4 +32,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_UTIL_H_
+#endif  // IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAME_UTIL_H_
diff --git a/ios/web/public/web_state/web_frames_manager.h b/ios/web/public/js_messaging/web_frames_manager.h
similarity index 90%
rename from ios/web/public/web_state/web_frames_manager.h
rename to ios/web/public/js_messaging/web_frames_manager.h
index 7e654be..3174452 100644
--- a/ios/web/public/web_state/web_frames_manager.h
+++ b/ios/web/public/js_messaging/web_frames_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAMES_MANAGER_H_
-#define IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAMES_MANAGER_H_
+#ifndef IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAMES_MANAGER_H_
+#define IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAMES_MANAGER_H_
 
 #include <set>
 #include <string>
@@ -51,4 +51,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAMES_MANAGER_H_
+#endif  // IOS_WEB_PUBLIC_JS_MESSAGING_WEB_FRAMES_MANAGER_H_
diff --git a/ios/web/public/security/BUILD.gn b/ios/web/public/security/BUILD.gn
new file mode 100644
index 0000000..af5bab2d
--- /dev/null
+++ b/ios/web/public/security/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2019 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.
+
+source_set("security") {
+  deps = [
+    "//base",
+    "//net",
+  ]
+
+  sources = [
+    "cert_policy.h",
+    "certificate_policy_cache.h",
+    "security_style.h",
+    "ssl_status.h",
+    "web_interstitial.h",
+    "web_interstitial_delegate.h",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/web/public/session/BUILD.gn b/ios/web/public/session/BUILD.gn
new file mode 100644
index 0000000..83d5b47
--- /dev/null
+++ b/ios/web/public/session/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2019 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.
+
+source_set("session") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  deps = [
+    "//base",
+    "//ios/web/public",
+    "//ios/web/public:referrer",
+    "//ios/web/public:user_agent",
+    "//net",
+    "//url",
+  ]
+  sources = [
+    "crw_navigation_item_storage.h",
+    "crw_session_certificate_policy_cache_storage.h",
+    "crw_session_storage.h",
+    "serializable_user_data_manager.h",
+    "session_certificate_policy_cache.h",
+  ]
+}
diff --git a/ios/web/public/session/README.md b/ios/web/public/session/README.md
new file mode 100644
index 0000000..e6b5967
--- /dev/null
+++ b/ios/web/public/session/README.md
@@ -0,0 +1 @@
+This directory contains API to save and restore the navigation and browsing session.
diff --git a/ios/web/public/crw_navigation_item_storage.h b/ios/web/public/session/crw_navigation_item_storage.h
similarity index 90%
rename from ios/web/public/crw_navigation_item_storage.h
rename to ios/web/public/session/crw_navigation_item_storage.h
index 800aea8..a004354 100644
--- a/ios/web/public/crw_navigation_item_storage.h
+++ b/ios/web/public/session/crw_navigation_item_storage.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_CRW_NAVIGATION_ITEM_STORAGE_H_
-#define IOS_WEB_PUBLIC_CRW_NAVIGATION_ITEM_STORAGE_H_
+#ifndef IOS_WEB_PUBLIC_SESSION_CRW_NAVIGATION_ITEM_STORAGE_H_
+#define IOS_WEB_PUBLIC_SESSION_CRW_NAVIGATION_ITEM_STORAGE_H_
 
 #import <Foundation/Foundation.h>
 #include <stddef.h>
@@ -48,7 +48,7 @@
 
 // NSCoding-compliant class used to serialize NavigationItem's persisted
 // properties.
-@interface CRWNavigationItemStorage : NSObject<NSCoding>
+@interface CRWNavigationItemStorage : NSObject <NSCoding>
 
 @property(nonatomic, assign) GURL virtualURL;
 @property(nonatomic, assign) web::Referrer referrer;
@@ -62,4 +62,4 @@
 
 @end
 
-#endif  // IOS_WEB_PUBLIC_CRW_NAVIGATION_ITEM_STORAGE_H_
+#endif  // IOS_WEB_PUBLIC_SESSION_CRW_NAVIGATION_ITEM_STORAGE_H_
diff --git a/ios/web/public/crw_session_certificate_policy_cache_storage.h b/ios/web/public/session/crw_session_certificate_policy_cache_storage.h
similarity index 82%
rename from ios/web/public/crw_session_certificate_policy_cache_storage.h
rename to ios/web/public/session/crw_session_certificate_policy_cache_storage.h
index 3ea26c0..eab9590 100644
--- a/ios/web/public/crw_session_certificate_policy_cache_storage.h
+++ b/ios/web/public/session/crw_session_certificate_policy_cache_storage.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_CRW_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_H_
-#define IOS_WEB_PUBLIC_CRW_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_H_
+#ifndef IOS_WEB_PUBLIC_SESSION_CRW_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_H_
+#define IOS_WEB_PUBLIC_SESSION_CRW_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_H_
 
 #import <Foundation/Foundation.h>
 
@@ -25,7 +25,7 @@
 }  // namespace web
 
 // A serializable representation of a certificate.
-@interface CRWSessionCertificateStorage : NSObject<NSCoding>
+@interface CRWSessionCertificateStorage : NSObject <NSCoding>
 
 // Designated initializer.
 - (instancetype)initWithCertificate:(scoped_refptr<net::X509Certificate>)cert
@@ -52,11 +52,11 @@
 }  // namespace web
 
 // A serializable representation of a list of allowed certificates.
-@interface CRWSessionCertificatePolicyCacheStorage : NSObject<NSCoding>
+@interface CRWSessionCertificatePolicyCacheStorage : NSObject <NSCoding>
 
 // The certificate policy storages for this session.
 @property(nonatomic, strong) NSSet* certificateStorages;
 
 @end
 
-#endif  // IOS_WEB_PUBLIC_CRW_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_H_
+#endif  // IOS_WEB_PUBLIC_SESSION_CRW_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_H_
diff --git a/ios/web/public/crw_session_storage.h b/ios/web/public/session/crw_session_storage.h
similarity index 82%
rename from ios/web/public/crw_session_storage.h
rename to ios/web/public/session/crw_session_storage.h
index 2904d56..cb22a8a 100644
--- a/ios/web/public/crw_session_storage.h
+++ b/ios/web/public/session/crw_session_storage.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_CRW_SESSION_STORAGE_H_
-#define IOS_WEB_PUBLIC_CRW_SESSION_STORAGE_H_
+#ifndef IOS_WEB_PUBLIC_SESSION_CRW_SESSION_STORAGE_H_
+#define IOS_WEB_PUBLIC_SESSION_CRW_SESSION_STORAGE_H_
 
 #import <Foundation/Foundation.h>
 #include <memory>
@@ -16,7 +16,7 @@
 
 // NSCoding-compliant class used to serialize session state.
 // TODO(crbug.com/685388): Investigate using code from the sessions component.
-@interface CRWSessionStorage : NSObject<NSCoding>
+@interface CRWSessionStorage : NSObject <NSCoding>
 
 @property(nonatomic, assign) BOOL hasOpener;
 @property(nonatomic, assign) NSInteger lastCommittedItemIndex;
@@ -32,4 +32,4 @@
 
 @end
 
-#endif  // IOS_WEB_PUBLIC_CRW_SESSION_STORAGE_H_
+#endif  // IOS_WEB_PUBLIC_SESSION_CRW_SESSION_STORAGE_H_
diff --git a/ios/web/public/serializable_user_data_manager.h b/ios/web/public/session/serializable_user_data_manager.h
similarity index 90%
rename from ios/web/public/serializable_user_data_manager.h
rename to ios/web/public/session/serializable_user_data_manager.h
index efd06b672..4c46b74 100644
--- a/ios/web/public/serializable_user_data_manager.h
+++ b/ios/web/public/session/serializable_user_data_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_SERIALIZABLE_USER_DATA_MANAGER_H_
-#define IOS_WEB_PUBLIC_SERIALIZABLE_USER_DATA_MANAGER_H_
+#ifndef IOS_WEB_PUBLIC_SESSION_SERIALIZABLE_USER_DATA_MANAGER_H_
+#define IOS_WEB_PUBLIC_SESSION_SERIALIZABLE_USER_DATA_MANAGER_H_
 
 #import <Foundation/Foundation.h>
 #include <memory>
@@ -68,4 +68,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_PUBLIC_SERIALIZABLE_USER_DATA_MANAGER_H_
+#endif  // IOS_WEB_PUBLIC_SESSION_SERIALIZABLE_USER_DATA_MANAGER_H_
diff --git a/ios/web/public/web_state/session_certificate_policy_cache.h b/ios/web/public/session/session_certificate_policy_cache.h
similarity index 81%
rename from ios/web/public/web_state/session_certificate_policy_cache.h
rename to ios/web/public/session/session_certificate_policy_cache.h
index 2fcabd0..e4c5059 100644
--- a/ios/web/public/web_state/session_certificate_policy_cache.h
+++ b/ios/web/public/session/session_certificate_policy_cache.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_WEB_STATE_SESSION_CERTIFICATE_POLICY_CACHE_H_
-#define IOS_WEB_PUBLIC_WEB_STATE_SESSION_CERTIFICATE_POLICY_CACHE_H_
+#ifndef IOS_WEB_PUBLIC_SESSION_SESSION_CERTIFICATE_POLICY_CACHE_H_
+#define IOS_WEB_PUBLIC_SESSION_SESSION_CERTIFICATE_POLICY_CACHE_H_
 
 #include "base/memory/ref_counted.h"
 #include "net/cert/cert_status_flags.h"
@@ -28,4 +28,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_PUBLIC_WEB_STATE_SESSION_CERTIFICATE_POLICY_CACHE_H_
+#endif  // IOS_WEB_PUBLIC_SESSION_SESSION_CERTIFICATE_POLICY_CACHE_H_
diff --git a/ios/web/public/test/fakes/BUILD.gn b/ios/web/public/test/fakes/BUILD.gn
index 2e8d161..490c4aa1 100644
--- a/ios/web/public/test/fakes/BUILD.gn
+++ b/ios/web/public/test/fakes/BUILD.gn
@@ -9,11 +9,14 @@
   deps = [
     "//base",
     "//ios/web/common",
+    "//ios/web/js_messaging",
     "//ios/web/navigation:core",
     "//ios/web/public/download",
     "//ios/web/public/find_in_page",
+    "//ios/web/public/js_messaging",
+    "//ios/web/public/security",
+    "//ios/web/public/session",
     "//ios/web/test:test_constants",
-    "//ios/web/web_state:web_frame",
     "//ios/web/web_state/ui:crw_web_view_navigation_proxy",
     "//ios/web/web_state/ui:web_view_js_utils",
     "//ios/web/webui:webui",
diff --git a/ios/web/public/test/fakes/fake_web_frame.h b/ios/web/public/test/fakes/fake_web_frame.h
index 78e84e4d..88e67f545c 100644
--- a/ios/web/public/test/fakes/fake_web_frame.h
+++ b/ios/web/public/test/fakes/fake_web_frame.h
@@ -9,7 +9,7 @@
 #include <memory>
 #include <vector>
 
-#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame.h"
 
 namespace web {
 
diff --git a/ios/web/public/test/fakes/test_web_state.mm b/ios/web/public/test/fakes/test_web_state.mm
index 1470762f..ac7931a6 100644
--- a/ios/web/public/test/fakes/test_web_state.mm
+++ b/ios/web/public/test/fakes/test_web_state.mm
@@ -12,12 +12,12 @@
 #import "base/strings/sys_string_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #import "ios/web/common/crw_content_view.h"
-#import "ios/web/public/crw_navigation_item_storage.h"
-#import "ios/web/public/crw_session_storage.h"
-#import "ios/web/public/serializable_user_data_manager.h"
-#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/js_messaging/web_frames_manager_impl.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 #import "ios/web/public/web_state/web_state_policy_decider.h"
-#include "ios/web/web_state/web_frames_manager_impl.h"
 #include "ui/gfx/image/image.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/web/security/BUILD.gn b/ios/web/security/BUILD.gn
index ec58a9765..6505171 100644
--- a/ios/web/security/BUILD.gn
+++ b/ios/web/security/BUILD.gn
@@ -13,6 +13,7 @@
     "//ios/web/navigation:core",
     "//ios/web/navigation:navigation_manager_util",
     "//ios/web/public",
+    "//ios/web/public/security",
     "//ios/web/web_state:web_state_impl_header",
     "//ios/web/web_state/ui:web_view_js_utils",
     "//net",
@@ -49,6 +50,7 @@
     "//ios/web",
     "//ios/web/navigation",
     "//ios/web/public",
+    "//ios/web/public/security",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//ios/web/test/fakes",
diff --git a/ios/web/session/BUILD.gn b/ios/web/session/BUILD.gn
new file mode 100644
index 0000000..fed1aa8
--- /dev/null
+++ b/ios/web/session/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2019 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.
+
+source_set("session") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  deps = [
+    "//base",
+    "//ios/web/public",
+    "//ios/web/public/session",
+    "//net",
+  ]
+  sources = [
+    "crw_session_certificate_policy_cache_storage.mm",
+    "crw_session_storage.mm",
+    "session_certificate_policy_cache_impl.h",
+    "session_certificate_policy_cache_impl.mm",
+    "session_certificate_policy_cache_storage_builder.h",
+    "session_certificate_policy_cache_storage_builder.mm",
+  ]
+}
+
+source_set("unittests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  deps = [
+    "//ios/web/public",
+    "//ios/web/public/session",
+    "//ios/web/public/test",
+    "//ios/web/public/test/fakes",
+    "//ios/web/session",
+    "//net",
+    "//net:test_support",
+    "//testing/gtest",
+  ]
+  sources = [
+    "crw_session_certificate_policy_cache_storage_unittest.mm",
+    "serializable_user_data_manager_unittest.mm",
+    "session_certificate_policy_cache_impl_unittest.mm",
+    "session_certificate_policy_cache_storage_builder_unittest.mm",
+  ]
+}
diff --git a/ios/web/public/crw_session_certificate_policy_cache_storage.mm b/ios/web/session/crw_session_certificate_policy_cache_storage.mm
similarity index 96%
rename from ios/web/public/crw_session_certificate_policy_cache_storage.mm
rename to ios/web/session/crw_session_certificate_policy_cache_storage.mm
index 3755559..604496c 100644
--- a/ios/web/public/crw_session_certificate_policy_cache_storage.mm
+++ b/ios/web/session/crw_session_certificate_policy_cache_storage.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
+#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
 
 #import "base/strings/sys_string_conversions.h"
 #include "net/base/hash_value.h"
@@ -104,8 +104,9 @@
   NSString* hostName = [aDecoder decodeObjectForKey:web::kHostSerializationKey];
   NSNumber* certStatus =
       [aDecoder decodeObjectForKey:web::kStatusSerializationKey];
-  return
-      [self initWithCertData:certData hostName:hostName certStatus:certStatus];
+  return [self initWithCertData:certData
+                       hostName:hostName
+                     certStatus:certStatus];
 }
 
 - (void)encodeWithCoder:(NSCoder*)aCoder {
diff --git a/ios/web/public/crw_session_certificate_policy_cache_storage_unittest.mm b/ios/web/session/crw_session_certificate_policy_cache_storage_unittest.mm
similarity index 97%
rename from ios/web/public/crw_session_certificate_policy_cache_storage_unittest.mm
rename to ios/web/session/crw_session_certificate_policy_cache_storage_unittest.mm
index 7ef1cde..e78d0cf 100644
--- a/ios/web/public/crw_session_certificate_policy_cache_storage_unittest.mm
+++ b/ios/web/session/crw_session_certificate_policy_cache_storage_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
+#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
 
 #include "net/cert/x509_certificate.h"
 #include "net/cert/x509_util.h"
diff --git a/ios/web/public/crw_session_storage.mm b/ios/web/session/crw_session_storage.mm
similarity index 94%
rename from ios/web/public/crw_session_storage.mm
rename to ios/web/session/crw_session_storage.mm
index 4c82e74..ef72109 100644
--- a/ios/web/public/crw_session_storage.mm
+++ b/ios/web/session/crw_session_storage.mm
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/public/crw_session_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
 
-#import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/public/serializable_user_data_manager_unittest.mm b/ios/web/session/serializable_user_data_manager_unittest.mm
similarity index 97%
rename from ios/web/public/serializable_user_data_manager_unittest.mm
rename to ios/web/session/serializable_user_data_manager_unittest.mm
index b4947ac6..3130d695 100644
--- a/ios/web/public/serializable_user_data_manager_unittest.mm
+++ b/ios/web/session/serializable_user_data_manager_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 
 #import "ios/web/public/test/fakes/test_web_state.h"
 #import "testing/gtest_mac.h"
diff --git a/ios/web/web_state/session_certificate_policy_cache_impl.h b/ios/web/session/session_certificate_policy_cache_impl.h
similarity index 81%
rename from ios/web/web_state/session_certificate_policy_cache_impl.h
rename to ios/web/session/session_certificate_policy_cache_impl.h
index 41ebfdab..55698758 100644
--- a/ios/web/web_state/session_certificate_policy_cache_impl.h
+++ b/ios/web/session/session_certificate_policy_cache_impl.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_STATE_SESSION_CERTIFICATE_POLICY_CACHE_IMPL_H_
-#define IOS_WEB_WEB_STATE_SESSION_CERTIFICATE_POLICY_CACHE_IMPL_H_
+#ifndef IOS_WEB_SESSION_SESSION_CERTIFICATE_POLICY_CACHE_IMPL_H_
+#define IOS_WEB_SESSION_SESSION_CERTIFICATE_POLICY_CACHE_IMPL_H_
 
 #import <Foundation/Foundation.h>
 
-#include "ios/web/public/web_state/session_certificate_policy_cache.h"
+#include "ios/web/public/session/session_certificate_policy_cache.h"
 
 namespace net {
 class X509Certificate;
@@ -45,4 +45,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_WEB_STATE_SESSION_CERTIFICATE_POLICY_CACHE_IMPL_H_
+#endif  // IOS_WEB_SESSION_SESSION_CERTIFICATE_POLICY_CACHE_IMPL_H_
diff --git a/ios/web/web_state/session_certificate_policy_cache_impl.mm b/ios/web/session/session_certificate_policy_cache_impl.mm
similarity index 95%
rename from ios/web/web_state/session_certificate_policy_cache_impl.mm
rename to ios/web/session/session_certificate_policy_cache_impl.mm
index 28a5b90..194a72f 100644
--- a/ios/web/web_state/session_certificate_policy_cache_impl.mm
+++ b/ios/web/session/session_certificate_policy_cache_impl.mm
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/session_certificate_policy_cache_impl.h"
+#import "ios/web/session/session_certificate_policy_cache_impl.h"
 
 #include "base/bind.h"
 #include "base/task/post_task.h"
-#import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
 #include "ios/web/public/security/certificate_policy_cache.h"
+#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
 #include "ios/web/public/web_task_traits.h"
 #include "ios/web/public/web_thread.h"
 
diff --git a/ios/web/web_state/session_certificate_policy_cache_impl_unittest.mm b/ios/web/session/session_certificate_policy_cache_impl_unittest.mm
similarity index 96%
rename from ios/web/web_state/session_certificate_policy_cache_impl_unittest.mm
rename to ios/web/session/session_certificate_policy_cache_impl_unittest.mm
index 1699066..9211e9e 100644
--- a/ios/web/web_state/session_certificate_policy_cache_impl_unittest.mm
+++ b/ios/web/session/session_certificate_policy_cache_impl_unittest.mm
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/session_certificate_policy_cache_impl.h"
+#import "ios/web/session/session_certificate_policy_cache_impl.h"
 
 #include "base/bind.h"
 #include "base/task/post_task.h"
 #import "base/test/ios/wait_util.h"
-#import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
 #include "ios/web/public/security/certificate_policy_cache.h"
+#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
 #include "ios/web/public/test/fakes/test_browser_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "ios/web/public/web_task_traits.h"
diff --git a/ios/web/web_state/session_certificate_policy_cache_storage_builder.h b/ios/web/session/session_certificate_policy_cache_storage_builder.h
similarity index 78%
rename from ios/web/web_state/session_certificate_policy_cache_storage_builder.h
rename to ios/web/session/session_certificate_policy_cache_storage_builder.h
index e48a88a4..1c0c3f9 100644
--- a/ios/web/web_state/session_certificate_policy_cache_storage_builder.h
+++ b/ios/web/session/session_certificate_policy_cache_storage_builder.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_STATE_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_BUILDER_H_
-#define IOS_WEB_WEB_STATE_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_BUILDER_H_
+#ifndef IOS_WEB_SESSION_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_BUILDER_H_
+#define IOS_WEB_SESSION_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_BUILDER_H_
 
 #include <memory>
 
@@ -27,4 +27,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_WEB_STATE_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_BUILDER_H_
+#endif  // IOS_WEB_SESSION_SESSION_CERTIFICATE_POLICY_CACHE_STORAGE_BUILDER_H_
diff --git a/ios/web/web_state/session_certificate_policy_cache_storage_builder.mm b/ios/web/session/session_certificate_policy_cache_storage_builder.mm
similarity index 83%
rename from ios/web/web_state/session_certificate_policy_cache_storage_builder.mm
rename to ios/web/session/session_certificate_policy_cache_storage_builder.mm
index 399a598..a95a159 100644
--- a/ios/web/web_state/session_certificate_policy_cache_storage_builder.mm
+++ b/ios/web/session/session_certificate_policy_cache_storage_builder.mm
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/web_state/session_certificate_policy_cache_storage_builder.h"
+#include "ios/web/session/session_certificate_policy_cache_storage_builder.h"
 
 #import <Foundation/Foundation.h>
 
-#import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
-#import "ios/web/web_state/session_certificate_policy_cache_impl.h"
+#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
+#import "ios/web/session/session_certificate_policy_cache_impl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/web_state/session_certificate_policy_cache_storage_builder_unittest.mm b/ios/web/session/session_certificate_policy_cache_storage_builder_unittest.mm
similarity index 90%
rename from ios/web/web_state/session_certificate_policy_cache_storage_builder_unittest.mm
rename to ios/web/session/session_certificate_policy_cache_storage_builder_unittest.mm
index c51fcd3b..f5069625 100644
--- a/ios/web/web_state/session_certificate_policy_cache_storage_builder_unittest.mm
+++ b/ios/web/session/session_certificate_policy_cache_storage_builder_unittest.mm
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/session_certificate_policy_cache_storage_builder.h"
+#import "ios/web/session/session_certificate_policy_cache_storage_builder.h"
 
-#import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
+#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
-#import "ios/web/web_state/session_certificate_policy_cache_impl.h"
+#import "ios/web/session/session_certificate_policy_cache_impl.h"
 #include "net/cert/x509_certificate.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
@@ -63,5 +63,5 @@
   web::SessionCertificatePolicyCacheStorageBuilder builder;
   std::unique_ptr<web::SessionCertificatePolicyCacheImpl> cache =
       builder.BuildSessionCertificatePolicyCache(cache_storage);
-  EXPECT_NSEQ([cache_storage certificateStorages], cache->GetAllowedCerts());
+  EXPECT_NSEQ([cache_storage certificateStorages], cache -> GetAllowedCerts());
 }
diff --git a/ios/web/test/fakes/BUILD.gn b/ios/web/test/fakes/BUILD.gn
index 569ea755..bbfc030 100644
--- a/ios/web/test/fakes/BUILD.gn
+++ b/ios/web/test/fakes/BUILD.gn
@@ -10,7 +10,8 @@
     "//base",
     "//ios/web/navigation",
     "//ios/web/navigation:core",
-    "//ios/web/public:public",
+    "//ios/web/public",
+    "//ios/web/public/security",
     "//ios/web/web_state/ui:crw_web_view_navigation_proxy",
     "//ios/web/web_state/ui:wk_web_view_configuration_provider",
     "//testing/gmock",
diff --git a/ios/web/web_state/BUILD.gn b/ios/web/web_state/BUILD.gn
index 487820bf2..9b1b060 100644
--- a/ios/web/web_state/BUILD.gn
+++ b/ios/web/web_state/BUILD.gn
@@ -7,7 +7,6 @@
 source_set("web_state") {
   deps = [
     ":context_menu",
-    ":session_certificate_policy_cache",
     ":web_state_impl_header",
     "//base",
     "//ios/web/common",
@@ -15,8 +14,10 @@
     "//ios/web/navigation:core",
     "//ios/web/navigation:wk_navigation_util",
     "//ios/web/public",
+    "//ios/web/public/session",
     "//ios/web/security",
     "//ios/web/security",
+    "//ios/web/session",
     "//ios/web/web_state/ui",
     "//ios/web/web_state/ui:crw_web_view_navigation_proxy",
     "//ios/web/webui",
@@ -47,6 +48,7 @@
     "//base",
     "//ios/web/navigation:core",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
   ]
 
   sources = [
@@ -56,22 +58,6 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
-source_set("session_certificate_policy_cache") {
-  deps = [
-    "//base",
-    "//ios/web/public",
-  ]
-
-  sources = [
-    "session_certificate_policy_cache_impl.h",
-    "session_certificate_policy_cache_impl.mm",
-    "session_certificate_policy_cache_storage_builder.h",
-    "session_certificate_policy_cache_storage_builder.mm",
-  ]
-
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
 source_set("error_translation_util") {
   deps = [
     "//base",
@@ -138,26 +124,6 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
-source_set("web_frame") {
-  deps = [
-    ":web_state_impl_header",
-    "//base",
-    "//crypto",
-    "//ios/web/public",
-    "//url",
-  ]
-
-  sources = [
-    "web_frame_impl.h",
-    "web_frame_impl.mm",
-    "web_frame_util.mm",
-    "web_frames_manager_impl.h",
-    "web_frames_manager_impl.mm",
-  ]
-
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
 source_set("user_interaction") {
   sources = [
     "user_interaction_event.h",
diff --git a/ios/web/web_state/bad_ssl_response_inttest.mm b/ios/web/web_state/bad_ssl_response_inttest.mm
index 245dfb6..d169a62e 100644
--- a/ios/web/web_state/bad_ssl_response_inttest.mm
+++ b/ios/web/web_state/bad_ssl_response_inttest.mm
@@ -6,15 +6,15 @@
 #import "base/test/ios/wait_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "ios/web/common/features.h"
-#import "ios/web/public/crw_session_certificate_policy_cache_storage.h"
-#import "ios/web/public/crw_session_storage.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/security/certificate_policy_cache.h"
+#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
+#include "ios/web/public/session/session_certificate_policy_cache.h"
 #import "ios/web/public/test/fakes/test_web_client.h"
 #import "ios/web/public/test/navigation_test_util.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
 #import "ios/web/public/test/web_view_content_test_util.h"
-#include "ios/web/public/web_state/session_certificate_policy_cache.h"
 #include "net/cert/x509_certificate.h"
 #include "net/ssl/ssl_info.h"
 #include "net/test/embedded_test_server/default_handlers.h"
diff --git a/ios/web/web_state/js/find_in_page_js_unittest.mm b/ios/web/web_state/js/find_in_page_js_unittest.mm
index 6263add..f636e6c 100644
--- a/ios/web/web_state/js/find_in_page_js_unittest.mm
+++ b/ios/web/web_state/js/find_in_page_js_unittest.mm
@@ -9,12 +9,12 @@
 #include "base/run_loop.h"
 #import "base/test/ios/wait_util.h"
 #import "ios/web/find_in_page/find_in_page_constants.h"
+#import "ios/web/public/js_messaging/web_frame.h"
+#import "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
 #import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
 #import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
-#import "ios/web/public/web_state/web_frame.h"
-#import "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #include "testing/gtest_mac.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/web/web_state/ui/BUILD.gn b/ios/web/web_state/ui/BUILD.gn
index e0521edf..74fbcc18 100644
--- a/ios/web/web_state/ui/BUILD.gn
+++ b/ios/web/web_state/ui/BUILD.gn
@@ -17,6 +17,7 @@
     "//ios/web/browsing_data",
     "//ios/web/common",
     "//ios/web/find_in_page",
+    "//ios/web/js_messaging",
     "//ios/web/navigation",
     "//ios/web/navigation:core",
     "//ios/web/navigation:navigation_manager_util",
@@ -24,13 +25,14 @@
     "//ios/web/net:net",
     "//ios/web/public",
     "//ios/web/public/download",
+    "//ios/web/public/js_messaging",
+    "//ios/web/public/security",
     "//ios/web/security",
+    "//ios/web/session",
     "//ios/web/web_state:context_menu",
     "//ios/web/web_state:error_translation_util",
     "//ios/web/web_state:page_viewport_state",
-    "//ios/web/web_state:session_certificate_policy_cache",
     "//ios/web/web_state:user_interaction",
-    "//ios/web/web_state:web_frame",
     "//ios/web/web_state:web_state_impl_header",
     "//ios/web/web_state:web_view_internal_creation_util",
     "//ios/web/web_state/js",
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 66e4212..84194827 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -50,6 +50,8 @@
 #include "ios/web/common/url_util.h"
 #import "ios/web/find_in_page/find_in_page_manager_impl.h"
 #include "ios/web/history_state_util.h"
+#import "ios/web/js_messaging/web_frame_impl.h"
+#import "ios/web/js_messaging/web_frames_manager_impl.h"
 #import "ios/web/navigation/crw_navigation_item_holder.h"
 #import "ios/web/navigation/crw_pending_navigation_info.h"
 #import "ios/web/navigation/crw_wk_navigation_handler.h"
@@ -67,6 +69,8 @@
 #import "ios/web/public/download/download_controller.h"
 #include "ios/web/public/favicon_url.h"
 #import "ios/web/public/java_script_dialog_presenter.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/referrer.h"
@@ -80,8 +84,6 @@
 #import "ios/web/public/web_state/ui/crw_native_content_provider.h"
 #import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
 #include "ios/web/public/web_state/url_verification_constants.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_policy_decider.h"
 #include "ios/web/public/webui/web_ui_ios.h"
@@ -91,9 +93,9 @@
 #import "ios/web/security/crw_ssl_status_updater.h"
 #import "ios/web/security/web_interstitial_impl.h"
 #import "ios/web/security/wk_web_view_security_util.h"
+#import "ios/web/session/session_certificate_policy_cache_impl.h"
 #import "ios/web/web_state/error_translation_util.h"
 #import "ios/web/web_state/page_viewport_state.h"
-#import "ios/web/web_state/session_certificate_policy_cache_impl.h"
 #import "ios/web/web_state/ui/controller/crw_legacy_native_content_controller.h"
 #import "ios/web/web_state/ui/controller/crw_legacy_native_content_controller_delegate.h"
 #import "ios/web/web_state/ui/crw_context_menu_controller.h"
@@ -111,8 +113,6 @@
 #import "ios/web/web_state/ui/wk_security_origin_util.h"
 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
 #import "ios/web/web_state/user_interaction_state.h"
-#import "ios/web/web_state/web_frame_impl.h"
-#import "ios/web/web_state/web_frames_manager_impl.h"
 #import "ios/web/web_state/web_state_impl.h"
 #import "ios/web/web_state/web_view_internal_creation_util.h"
 #import "ios/web/web_view/wk_web_view_util.h"
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index 440b11f..bf852a42 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -24,11 +24,11 @@
 #import "ios/web/navigation/navigation_item_impl.h"
 #import "ios/web/navigation/navigation_manager_impl.h"
 #import "ios/web/navigation/wk_navigation_action_policy_util.h"
-#import "ios/web/public/crw_navigation_item_storage.h"
-#import "ios/web/public/crw_session_storage.h"
 #import "ios/web/public/download/download_controller.h"
 #import "ios/web/public/download/download_task.h"
 #include "ios/web/public/referrer.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
 #include "ios/web/public/test/fakes/fake_download_controller_delegate.h"
 #import "ios/web/public/test/fakes/fake_web_state_policy_decider.h"
 #include "ios/web/public/test/fakes/test_browser_state.h"
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index f53b0f8..4fe8983 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -22,7 +22,7 @@
 #import "ios/web/navigation/navigation_manager_impl.h"
 #import "ios/web/public/java_script_dialog_callback.h"
 #include "ios/web/public/java_script_dialog_type.h"
-#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_delegate.h"
 #import "ios/web/public/web_state/web_state_policy_decider.h"
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 801d046..f669cdd 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -23,12 +23,12 @@
 #import "ios/web/navigation/wk_based_navigation_manager_impl.h"
 #import "ios/web/navigation/wk_navigation_util.h"
 #include "ios/web/public/browser_state.h"
-#import "ios/web/public/crw_navigation_item_storage.h"
-#import "ios/web/public/crw_session_storage.h"
 #include "ios/web/public/favicon_url.h"
 #import "ios/web/public/java_script_dialog_presenter.h"
 #import "ios/web/public/navigation_item.h"
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 #import "ios/web/public/web_client.h"
 #import "ios/web/public/web_state/context_menu_params.h"
 #import "ios/web/public/web_state/ui/crw_native_content.h"
@@ -40,8 +40,8 @@
 #include "ios/web/public/web_thread.h"
 #include "ios/web/public/webui/web_ui_ios_controller.h"
 #import "ios/web/security/web_interstitial_impl.h"
+#import "ios/web/session/session_certificate_policy_cache_impl.h"
 #include "ios/web/web_state/global_web_state_event_tracker.h"
-#import "ios/web/web_state/session_certificate_policy_cache_impl.h"
 #import "ios/web/web_state/ui/crw_js_injector.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
 #import "ios/web/web_state/ui/crw_web_controller_container_view.h"
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index 1e4d22f8..8fa8cab6 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -22,10 +22,10 @@
 #import "ios/web/navigation/navigation_item_impl.h"
 #import "ios/web/navigation/serializable_user_data_manager_impl.h"
 #import "ios/web/navigation/wk_navigation_util.h"
-#import "ios/web/public/crw_navigation_item_storage.h"
-#import "ios/web/public/crw_session_storage.h"
 #import "ios/web/public/java_script_dialog_presenter.h"
-#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
+#import "ios/web/public/session/serializable_user_data_manager.h"
 #import "ios/web/public/test/fakes/fake_navigation_context.h"
 #import "ios/web/public/test/fakes/fake_web_frame.h"
 #include "ios/web/public/test/fakes/test_browser_state.h"
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm
index 0ccf2b6..4ab0a6a 100644
--- a/ios/web/web_state/web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -18,10 +18,10 @@
 #include "ios/testing/embedded_test_server_handlers.h"
 #include "ios/web/common/features.h"
 #include "ios/web/navigation/wk_navigation_util.h"
-#import "ios/web/public/crw_navigation_item_storage.h"
-#import "ios/web/public/crw_session_storage.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
 #import "ios/web/public/test/fakes/test_native_content.h"
 #import "ios/web/public/test/fakes/test_native_content_provider.h"
 #include "ios/web/public/test/fakes/test_web_state_observer.h"
diff --git a/ios/web/web_state/web_state_unittest.mm b/ios/web/web_state/web_state_unittest.mm
index 587aa88d..ac97678 100644
--- a/ios/web/web_state/web_state_unittest.mm
+++ b/ios/web/web_state/web_state_unittest.mm
@@ -18,10 +18,10 @@
 #import "ios/web/navigation/navigation_manager_impl.h"
 #import "ios/web/navigation/wk_based_navigation_manager_impl.h"
 #import "ios/web/navigation/wk_navigation_util.h"
-#import "ios/web/public/crw_navigation_item_storage.h"
-#import "ios/web/public/crw_session_storage.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
+#import "ios/web/public/session/crw_navigation_item_storage.h"
+#import "ios/web/public/session/crw_session_storage.h"
 #import "ios/web/public/test/fakes/test_web_client.h"
 #import "ios/web/public/test/fakes/test_web_state_delegate.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 61b3a55..0cb4452c 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -309,6 +309,8 @@
   "//ios/web/common",
   "//ios/web/public",
   "//ios/web/public/app",
+  "//ios/web/public/security",
+  "//ios/web/public/js_messaging",
   "//ios/web/public/global_state",
   "//net",
   "//net:extras",
@@ -430,6 +432,8 @@
     "//components/signin/core/browser:internals_test_support",
     "//components/signin/ios/browser:test_support",
     "//components/sync:test_support",
+    "//ios/web/public/js_messaging",
+    "//ios/web/public/security",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
     "//services/identity/public/cpp:test_support",
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller.mm b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
index 52128d0..7b91e67 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
@@ -24,10 +24,10 @@
 #include "components/autofill/ios/form_util/form_activity_params.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/sync/driver/sync_service.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #include "ios/web_view/internal/app/application_context.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h"
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
index 5091756..7237aa4 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
@@ -19,12 +19,12 @@
 #include "components/autofill/ios/form_util/form_activity_params.h"
 #import "components/autofill/ios/form_util/form_activity_tab_helper.h"
 #import "components/autofill/ios/form_util/test_form_activity_tab_helper.h"
+#include "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/test/fakes/crw_test_js_injection_receiver.h"
 #import "ios/web/public/test/fakes/fake_web_frame.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "ios/web/public/web_client.h"
-#include "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h"
 #include "ios/web_view/internal/web_view_browser_state.h"
 #import "ios/web_view/public/cwv_autofill_controller_delegate.h"
diff --git a/ios/web_view/internal/cwv_web_view.mm b/ios/web_view/internal/cwv_web_view.mm
index 1b67512..70fad8a 100644
--- a/ios/web_view/internal/cwv_web_view.mm
+++ b/ios/web_view/internal/cwv_web_view.mm
@@ -17,6 +17,7 @@
 #include "components/language/ios/browser/ios_language_detection_tab_helper.h"
 #include "google_apis/google_api_keys.h"
 #include "ios/web/public/favicon_url.h"
+#import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/referrer.h"
@@ -27,7 +28,6 @@
 #import "ios/web/public/web_state/navigation_context.h"
 #import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
 #import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
-#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_delegate_bridge.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
diff --git a/ios/web_view/internal/passwords/cwv_password_controller.mm b/ios/web_view/internal/passwords/cwv_password_controller.mm
index ee05bce..0a46eb9 100644
--- a/ios/web_view/internal/passwords/cwv_password_controller.mm
+++ b/ios/web_view/internal/passwords/cwv_password_controller.mm
@@ -17,9 +17,9 @@
 #import "components/password_manager/ios/password_form_helper.h"
 #import "components/password_manager/ios/password_suggestion_helper.h"
 #import "ios/web/common/origin_util.h"
+#include "ios/web/public/js_messaging/web_frame.h"
+#include "ios/web/public/js_messaging/web_frame_util.h"
 #include "ios/web/public/url_scheme_util.h"
-#include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_frame_util.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h"
 #import "ios/web_view/internal/passwords/web_view_password_manager_client.h"
diff --git a/ios/web_view/internal/signin/ios_web_view_signin_client.h b/ios/web_view/internal/signin/ios_web_view_signin_client.h
index 3e95a9dd..e447fb48 100644
--- a/ios/web_view/internal/signin/ios_web_view_signin_client.h
+++ b/ios/web_view/internal/signin/ios_web_view_signin_client.h
@@ -52,9 +52,7 @@
   void DelayNetworkCall(base::OnceClosure callback) override;
   std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
       GaiaAuthConsumer* consumer,
-      gaia::GaiaSource source,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-      override;
+      gaia::GaiaSource source) override;
 
   // CWVSyncController setter/getter.
   void SetSyncController(CWVSyncController* sync_controller);
diff --git a/ios/web_view/internal/signin/ios_web_view_signin_client.mm b/ios/web_view/internal/signin/ios_web_view_signin_client.mm
index 2e22b4f4..eb23708 100644
--- a/ios/web_view/internal/signin/ios_web_view_signin_client.mm
+++ b/ios/web_view/internal/signin/ios_web_view_signin_client.mm
@@ -95,10 +95,9 @@
 
 std::unique_ptr<GaiaAuthFetcher> IOSWebViewSigninClient::CreateGaiaAuthFetcher(
     GaiaAuthConsumer* consumer,
-    gaia::GaiaSource source,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+    gaia::GaiaSource source) {
   return std::make_unique<GaiaAuthFetcher>(consumer, source,
-                                           url_loader_factory);
+                                           GetURLLoaderFactory());
 }
 
 void IOSWebViewSigninClient::SetSyncController(
diff --git a/media/gpu/linux/platform_video_frame_utils.cc b/media/gpu/linux/platform_video_frame_utils.cc
index b9826ce..d71bb94 100644
--- a/media/gpu/linux/platform_video_frame_utils.cc
+++ b/media/gpu/linux/platform_video_frame_utils.cc
@@ -40,6 +40,8 @@
   auto pixmap =
       factory->CreateNativePixmap(gfx::kNullAcceleratedWidget, VK_NULL_HANDLE,
                                   coded_size, buffer_format, buffer_usage);
+  if (!pixmap)
+    return nullptr;
 
   const size_t num_planes = VideoFrame::NumPlanes(pixel_format);
   std::vector<VideoFrameLayout::Plane> planes(num_planes);
diff --git a/services/data_decoder/public/cpp/BUILD.gn b/services/data_decoder/public/cpp/BUILD.gn
index 11a5c050..a929f29 100644
--- a/services/data_decoder/public/cpp/BUILD.gn
+++ b/services/data_decoder/public/cpp/BUILD.gn
@@ -6,44 +6,47 @@
 
 source_set("cpp") {
   sources = [
-    "decode_image.cc",
-    "decode_image.h",
-    "json_sanitizer.cc",
-    "json_sanitizer.h",
     "safe_json_parser.cc",
     "safe_json_parser.h",
-    "safe_json_parser_impl.cc",
-    "safe_json_parser_impl.h",
-    "safe_xml_parser.cc",
-    "safe_xml_parser.h",
   ]
 
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
-  public_deps = [
-    "//services/data_decoder/public/mojom",
-    "//services/service_manager/public/cpp",
+  deps = [
+    "//base",
   ]
 
-  if (is_android) {
-    sources -= [ "json_sanitizer.cc" ]
+  public_deps = [
+    "//services/data_decoder/public/mojom",
+  ]
+
+  if (!is_ios) {
     sources += [
-      "json_sanitizer_android.cc",
-      "safe_json_parser_android.cc",
-      "safe_json_parser_android.h",
+      "decode_image.cc",
+      "decode_image.h",
+      "json_sanitizer.h",
+      "safe_json_parser_impl.cc",
+      "safe_json_parser_impl.h",
+      "safe_xml_parser.cc",
+      "safe_xml_parser.h",
     ]
-    deps = [
-      "android:safe_json_jni_headers",
-      "//base",
-    ]
+    public_deps += [ "//services/service_manager/public/cpp" ]
+    if (is_android) {
+      sources += [
+        "json_sanitizer_android.cc",
+        "safe_json_parser_android.cc",
+        "safe_json_parser_android.h",
+      ]
+      deps += [ "android:safe_json_jni_headers" ]
+    } else {
+      sources += [ "json_sanitizer.cc" ]
+    }
   }
 }
 
-static_library("test_support") {
+source_set("test_support") {
   testonly = true
   sources = [
-    "test_data_decoder_service.cc",
-    "test_data_decoder_service.h",
     "testing_json_parser.cc",
     "testing_json_parser.h",
   ]
@@ -51,20 +54,33 @@
   deps = [
     ":cpp",
     "//base",
-    "//services/data_decoder:lib",
     "//services/service_manager/public/cpp/test:test_support",
   ]
+
+  if (!is_ios) {
+    sources += [
+      "test_data_decoder_service.cc",
+      "test_data_decoder_service.h",
+    ]
+
+    deps += [
+      "//services/data_decoder:lib",
+      "//services/service_manager/public/cpp/test:test_support",
+    ]
+  }
 }
 
 source_set("manifest") {
-  sources = [
-    "manifest.cc",
-    "manifest.h",
-  ]
+  if (!is_ios) {
+    sources = [
+      "manifest.cc",
+      "manifest.h",
+    ]
 
-  deps = [
-    "//base",
-    "//services/data_decoder/public/mojom",
-    "//services/service_manager/public/cpp",
-  ]
+    deps = [
+      "//base",
+      "//services/data_decoder/public/mojom",
+      "//services/service_manager/public/cpp",
+    ]
+  }
 }
diff --git a/services/data_decoder/public/cpp/safe_json_parser.cc b/services/data_decoder/public/cpp/safe_json_parser.cc
index 16b56d43..6c348ac 100644
--- a/services/data_decoder/public/cpp/safe_json_parser.cc
+++ b/services/data_decoder/public/cpp/safe_json_parser.cc
@@ -28,7 +28,10 @@
   if (g_factory)
     return g_factory(unsafe_json, success_callback, error_callback);
 
-#if defined(OS_ANDROID)
+#if defined(OS_IOS)
+  NOTREACHED() << "SafeJsonParser is not supported on iOS (except in tests)";
+  return nullptr;
+#elif defined(OS_ANDROID)
   return new SafeJsonParserAndroid(unsafe_json, success_callback,
                                    error_callback);
 #else
diff --git a/services/identity/public/cpp/accounts_cookie_mutator_unittest.cc b/services/identity/public/cpp/accounts_cookie_mutator_unittest.cc
index 68af7ac..2511f20 100644
--- a/services/identity/public/cpp/accounts_cookie_mutator_unittest.cc
+++ b/services/identity/public/cpp/accounts_cookie_mutator_unittest.cc
@@ -63,7 +63,7 @@
  public:
   AccountsCookieMutatorTest()
       : test_signin_client_(&prefs_),
-        identity_test_env_(test_url_loader_factory(),
+        identity_test_env_(/*test_url_loader_factory=*/nullptr,
                            &prefs_,
                            signin::AccountConsistencyMethod::kDisabled,
                            &test_signin_client_) {}
@@ -80,7 +80,7 @@
   void PrepareURLLoaderResponsesForAction(AccountsCookiesMutatorAction action) {
     switch (action) {
       case AccountsCookiesMutatorAction::kAddAccountToCookie:
-        test_url_loader_factory()->AddResponse(
+        GetTestURLLoaderFactory()->AddResponse(
             GaiaUrls::GetInstance()
                 ->oauth1_login_url()
                 .Resolve(base::StringPrintf("?source=%s&issueuberauth=1",
@@ -88,14 +88,14 @@
                 .spec(),
             kTestUberToken, net::HTTP_OK);
 
-        test_url_loader_factory()->AddResponse(
+        GetTestURLLoaderFactory()->AddResponse(
             GaiaUrls::GetInstance()
                 ->GetCheckConnectionInfoURLWithSource(
                     GaiaConstants::kChromeSource)
                 .spec(),
             std::string(), net::HTTP_OK);
 
-        test_url_loader_factory()->AddResponse(
+        GetTestURLLoaderFactory()->AddResponse(
             GaiaUrls::GetInstance()
                 ->merge_session_url()
                 .Resolve(base::StringPrintf(
@@ -105,7 +105,7 @@
             std::string(), net::HTTP_OK);
         break;
       case AccountsCookiesMutatorAction::kSetAccountsInCookie:
-        test_url_loader_factory()->AddResponse(
+        GetTestURLLoaderFactory()->AddResponse(
             GaiaUrls::GetInstance()
                 ->oauth_multilogin_url()
                 .Resolve(base::StringPrintf("?source=%s",
@@ -114,11 +114,11 @@
             std::string(kTestOAuthMultiLoginResponse), net::HTTP_OK);
         break;
       case AccountsCookiesMutatorAction::kTriggerCookieJarUpdateNoAccounts:
-        signin::SetListAccountsResponseNoAccounts(test_url_loader_factory());
+        signin::SetListAccountsResponseNoAccounts(GetTestURLLoaderFactory());
         break;
       case AccountsCookiesMutatorAction::kTriggerCookieJarUpdateOneAccount:
         signin::SetListAccountsResponseOneAccount(
-            kTestAccountEmail, kTestAccountGaiaId, test_url_loader_factory());
+            kTestAccountEmail, kTestAccountGaiaId, GetTestURLLoaderFactory());
         break;
     }
   }
@@ -135,8 +135,8 @@
     return identity_test_env_.identity_manager()->GetAccountsCookieMutator();
   }
 
-  network::TestURLLoaderFactory* test_url_loader_factory() {
-    return test_signin_client_.test_url_loader_factory();
+  network::TestURLLoaderFactory* GetTestURLLoaderFactory() {
+    return test_signin_client_.GetTestURLLoaderFactory();
   }
 
  private:
@@ -396,7 +396,7 @@
 // Test that trying to log out all sessions generates the right network request.
 TEST_F(AccountsCookieMutatorTest, LogOutAllAccounts) {
   base::RunLoop run_loop;
-  test_url_loader_factory()->SetInterceptor(base::BindRepeating(
+  GetTestURLLoaderFactory()->SetInterceptor(base::BindRepeating(
       [](base::OnceClosure quit_closure,
          const network::ResourceRequest& request) {
         EXPECT_EQ(request.url.spec(),
diff --git a/services/identity/public/cpp/identity_manager_unittest.cc b/services/identity/public/cpp/identity_manager_unittest.cc
index 009b5e88..bd196ec7 100644
--- a/services/identity/public/cpp/identity_manager_unittest.cc
+++ b/services/identity/public/cpp/identity_manager_unittest.cc
@@ -212,7 +212,8 @@
 
 class IdentityManagerTest : public testing::Test {
  protected:
-  IdentityManagerTest() : signin_client_(&pref_service_) {
+  IdentityManagerTest()
+      : signin_client_(&pref_service_, &test_url_loader_factory_) {
     IdentityManager::RegisterProfilePrefs(pref_service_.registry());
     IdentityManager::RegisterLocalStatePrefs(pref_service_.registry());
 
@@ -280,14 +281,8 @@
         std::make_unique<CustomFakeProfileOAuth2TokenService>(&pref_service_);
 
     auto gaia_cookie_manager_service =
-        std::make_unique<GaiaCookieManagerService>(
-            token_service.get(), &signin_client_,
-            base::BindRepeating(
-                [](network::TestURLLoaderFactory* test_url_loader_factory)
-                    -> scoped_refptr<network::SharedURLLoaderFactory> {
-                  return test_url_loader_factory->GetSafeWeakWrapper();
-                },
-                test_url_loader_factory()));
+        std::make_unique<GaiaCookieManagerService>(token_service.get(),
+                                                   &signin_client_);
 
     auto account_tracker_service = std::make_unique<AccountTrackerService>();
     account_tracker_service->Initialize(&pref_service_, base::FilePath());
@@ -301,7 +296,8 @@
     DCHECK_EQ(account_consistency, signin::AccountConsistencyMethod::kDisabled)
         << "AccountConsistency is not used by SigninManagerBase";
     auto signin_manager = std::make_unique<SigninManagerBase>(
-        &signin_client_, token_service.get(), account_tracker_service.get());
+        &signin_client_, token_service.get(), account_tracker_service.get(),
+        gaia_cookie_manager_service.get(), account_consistency);
 #else
     auto signin_manager = std::make_unique<SigninManager>(
         &signin_client_, token_service.get(), account_tracker_service.get(),
@@ -374,8 +370,8 @@
  private:
   base::MessageLoop message_loop_;
   sync_preferences::TestingPrefServiceSyncable pref_service_;
-  TestSigninClient signin_client_;
   network::TestURLLoaderFactory test_url_loader_factory_;
+  TestSigninClient signin_client_;
   std::unique_ptr<IdentityManager> identity_manager_;
   std::unique_ptr<TestIdentityManagerObserver> identity_manager_observer_;
   std::unique_ptr<TestIdentityManagerDiagnosticsObserver>
diff --git a/services/identity/public/cpp/identity_test_environment.cc b/services/identity/public/cpp/identity_test_environment.cc
index 4d05dcf..0cf120a 100644
--- a/services/identity/public/cpp/identity_test_environment.cc
+++ b/services/identity/public/cpp/identity_test_environment.cc
@@ -111,7 +111,9 @@
               pref_service,
               test_signin_client),
           test_url_loader_factory,
-          account_consistency) {}
+          account_consistency) {
+  DCHECK(!test_url_loader_factory || !test_signin_client);
+}
 
 IdentityTestEnvironment::IdentityTestEnvironment(
     IdentityManager* identity_manager)
@@ -137,19 +139,21 @@
     std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner,
     network::TestURLLoaderFactory* test_url_loader_factory,
     signin::AccountConsistencyMethod account_consistency)
-    : test_url_loader_factory_(test_url_loader_factory),
-      weak_ptr_factory_(this) {
+    : weak_ptr_factory_(this) {
   dependencies_owner_ = std::move(dependencies_owner);
   TestSigninClient* test_signin_client = dependencies_owner_->signin_client();
+  if (test_url_loader_factory)
+    test_signin_client->OverrideTestUrlLoaderFactory(test_url_loader_factory);
+
   sync_preferences::TestingPrefServiceSyncable* test_pref_service =
       dependencies_owner_->pref_service();
 
   IdentityManager::RegisterProfilePrefs(test_pref_service->registry());
   IdentityManager::RegisterLocalStatePrefs(test_pref_service->registry());
 
-  owned_identity_manager_ = BuildIdentityManagerForTests(
-      test_signin_client, test_pref_service, base::FilePath(),
-      account_consistency, test_url_loader_factory);
+  owned_identity_manager_ =
+      BuildIdentityManagerForTests(test_signin_client, test_pref_service,
+                                   base::FilePath(), account_consistency);
 
   Initialize();
 }
@@ -168,7 +172,6 @@
     PrefService* pref_service,
     base::FilePath user_data_dir,
     signin::AccountConsistencyMethod account_consistency,
-    network::TestURLLoaderFactory* test_url_loader_factory,
     ExtraParams extra_params) {
   auto account_tracker_service = std::make_unique<AccountTrackerService>();
   account_tracker_service->Initialize(pref_service, user_data_dir);
@@ -198,7 +201,8 @@
 #if defined(OS_CHROMEOS)
   std::unique_ptr<SigninManagerBase> signin_manager =
       std::make_unique<SigninManagerBase>(signin_client, token_service.get(),
-                                          account_tracker_service.get());
+                                          account_tracker_service.get(),
+                                          nullptr, account_consistency);
 #else
   std::unique_ptr<SigninManagerBase> signin_manager =
       std::make_unique<SigninManager>(signin_client, token_service.get(),
@@ -208,19 +212,8 @@
   signin_manager->Initialize(pref_service);
 
   std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service;
-  if (test_url_loader_factory != nullptr) {
-    gaia_cookie_manager_service = std::make_unique<GaiaCookieManagerService>(
-        token_service.get(), signin_client,
-        base::BindRepeating(
-            [](network::TestURLLoaderFactory* test_url_loader_factory)
-                -> scoped_refptr<network::SharedURLLoaderFactory> {
-              return test_url_loader_factory->GetSafeWeakWrapper();
-            },
-            test_url_loader_factory));
-  } else {
     gaia_cookie_manager_service = std::make_unique<GaiaCookieManagerService>(
         token_service.get(), signin_client);
-  }
 
   std::unique_ptr<PrimaryAccountMutator> primary_account_mutator;
   std::unique_ptr<AccountsMutator> accounts_mutator;
@@ -327,11 +320,10 @@
 
 void IdentityTestEnvironment::SetCookieAccounts(
     const std::vector<CookieParams>& cookie_accounts) {
-  DCHECK(test_url_loader_factory_)
-      << "IdentityTestEnvironment constructor must have been passed a "
-         "test_url_loader_factory in order to use this method.";
-  identity::SetCookieAccounts(identity_manager(), test_url_loader_factory_,
-                              cookie_accounts);
+  identity::SetCookieAccounts(
+      identity_manager(),
+      dependencies_owner_->signin_client()->GetTestURLLoaderFactory(),
+      cookie_accounts);
 }
 
 void IdentityTestEnvironment::SetAutomaticIssueOfAccessTokens(bool grant) {
diff --git a/services/identity/public/cpp/identity_test_environment.h b/services/identity/public/cpp/identity_test_environment.h
index fbbc1cbd..2f726f39 100644
--- a/services/identity/public/cpp/identity_test_environment.h
+++ b/services/identity/public/cpp/identity_test_environment.h
@@ -66,8 +66,14 @@
   // dependencies directly (namely AccountTrackerService, PO2TS), but still be
   // able to tweak preferences on demand.
   //
-  // Last, this constructor can take an optional parameter |account_consistency|
-  // as parameter, to specify the account consistency policy that will be used.
+  // |account_consistency| specifies the account consistency policy that will be
+  // used.
+  //
+  // A specific TestSigninClient instance can be passed optionally. If it is
+  // null, the test environment will automatically build one internally.
+  //
+  // Note: at least one of |test_url_loader_factory| and |test_signin_client|
+  // must be nulltpr. They cannot both be specified at the same time.
   IdentityTestEnvironment(
       network::TestURLLoaderFactory* test_url_loader_factory = nullptr,
       sync_preferences::TestingPrefServiceSyncable* pref_service = nullptr,
@@ -334,10 +340,6 @@
   // Owner of all dependencies that don't belong to IdentityManager.
   std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner_;
 
-  // Used to set fake responses for cookie-related requests.
-  // This can be null if no TestURLLoaderFactory was passed via the constructor.
-  network::TestURLLoaderFactory* test_url_loader_factory_ = nullptr;
-
   // This will be null if a TestSigninClient was provided to
   // IdentityTestEnvironment's constructor.
   std::unique_ptr<TestSigninClient> owned_signin_client_;
@@ -377,7 +379,6 @@
       base::FilePath user_data_dir,
       signin::AccountConsistencyMethod account_consistency =
           signin::AccountConsistencyMethod::kDisabled,
-      network::TestURLLoaderFactory* test_url_loader_factory = nullptr,
       ExtraParams extra_params = {});
 
   // Shared constructor initialization logic.
diff --git a/services/viz/public/cpp/compositing/quads_struct_traits.cc b/services/viz/public/cpp/compositing/quads_struct_traits.cc
index b1064163..73f95fb 100644
--- a/services/viz/public/cpp/compositing/quads_struct_traits.cc
+++ b/services/viz/public/cpp/compositing/quads_struct_traits.cc
@@ -119,6 +119,7 @@
   viz::SurfaceDrawQuad* quad = static_cast<viz::SurfaceDrawQuad*>(out);
   quad->default_background_color = data.default_background_color();
   quad->stretch_content_to_fill_bounds = data.stretch_content_to_fill_bounds();
+  quad->is_reflection = data.is_reflection();
   return data.ReadSurfaceRange(&quad->surface_range);
 }
 
diff --git a/services/viz/public/cpp/compositing/quads_struct_traits.h b/services/viz/public/cpp/compositing/quads_struct_traits.h
index fa1af87..f7c06fb2 100644
--- a/services/viz/public/cpp/compositing/quads_struct_traits.h
+++ b/services/viz/public/cpp/compositing/quads_struct_traits.h
@@ -324,6 +324,12 @@
     return quad->stretch_content_to_fill_bounds;
   }
 
+  static bool is_reflection(const viz::DrawQuad& input) {
+    const viz::SurfaceDrawQuad* quad =
+        viz::SurfaceDrawQuad::MaterialCast(&input);
+    return quad->is_reflection;
+  }
+
   static bool Read(viz::mojom::SurfaceQuadStateDataView data,
                    viz::DrawQuad* out);
 };
diff --git a/services/viz/public/interfaces/compositing/quads.mojom b/services/viz/public/interfaces/compositing/quads.mojom
index 4b818b0..59e50b20 100644
--- a/services/viz/public/interfaces/compositing/quads.mojom
+++ b/services/viz/public/interfaces/compositing/quads.mojom
@@ -67,6 +67,7 @@
   SurfaceRange surface_range;
   uint32 default_background_color;
   bool stretch_content_to_fill_bounds;
+  bool is_reflection;
 };
 
 struct TextureQuadState {
diff --git a/third_party/blink/renderer/core/css/css_path_value.h b/third_party/blink/renderer/core/css/css_path_value.h
index 15267ec..6ff0f908 100644
--- a/third_party/blink/renderer/core/css/css_path_value.h
+++ b/third_party/blink/renderer/core/css/css_path_value.h
@@ -23,10 +23,10 @@
  public:
   static CSSPathValue& EmptyPathValue();
 
-  CSSPathValue(scoped_refptr<StylePath>,
-               PathSerializationFormat = kNoTransformation);
-  CSSPathValue(std::unique_ptr<SVGPathByteStream>,
-               PathSerializationFormat = kNoTransformation);
+  explicit CSSPathValue(scoped_refptr<StylePath>,
+                        PathSerializationFormat = kNoTransformation);
+  explicit CSSPathValue(std::unique_ptr<SVGPathByteStream>,
+                        PathSerializationFormat = kNoTransformation);
 
   StylePath* GetStylePath() const { return style_path_.get(); }
   String CustomCSSText() const;
diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc b/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
index f2e4b7e..d2cb2e1 100644
--- a/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
+++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
@@ -143,8 +143,6 @@
   if (document->IsSecureContext()) {
     UseCounter::Count(document,
                       WebFeature::kApplicationCacheManifestSelectSecureOrigin);
-    UseCounter::CountCrossOriginIframe(
-        *document, WebFeature::kApplicationCacheManifestSelectSecureOrigin);
   } else {
     Deprecation::CountDeprecation(
         document, WebFeature::kApplicationCacheManifestSelectInsecureOrigin);
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
index a23d7927..91f46bc 100644
--- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
+++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -518,6 +518,9 @@
   auto* resource_timing_notifier =
       MakeGarbageCollected<NullWorkerResourceTimingNotifier>();
 
+  FetchClientSettingsObjectSnapshot* fetch_client_setting_object =
+      CreateFetchClientSettingsObject();
+
   // If this is a new (not installed) service worker, we are in the Update
   // algorithm here:
   // > Switching on job's worker type, run these substeps with the following
@@ -530,7 +533,7 @@
 
     case mojom::ScriptType::kClassic:
       worker_thread_->FetchAndRunClassicScript(
-          worker_start_data_.script_url, *CreateFetchClientSettingsObject(),
+          worker_start_data_.script_url, *fetch_client_setting_object,
           *resource_timing_notifier, v8_inspector::V8StackTraceId());
       return;
     // > "module": Fetch a module worker script graph given job’s serialized
@@ -538,7 +541,7 @@
     // > to-be-created environment settings object for this service worker.
     case mojom::ScriptType::kModule:
       worker_thread_->FetchAndRunModuleScript(
-          worker_start_data_.script_url, *CreateFetchClientSettingsObject(),
+          worker_start_data_.script_url, *fetch_client_setting_object,
           *resource_timing_notifier,
           network::mojom::FetchCredentialsMode::kOmit);
       return;
@@ -549,17 +552,28 @@
 FetchClientSettingsObjectSnapshot*
 WebEmbeddedWorkerImpl::CreateFetchClientSettingsObject() {
   DCHECK(shadow_page_->WasInitialized());
-  // TODO(crbug.com/967265): Currently, we use the shadow page's Document as an
-  // outside_settings_object as a workaround. For new worker case, this should
-  // be the Document that called navigator.serviceWorker.register(). For
+  // TODO(crbug.com/967265): Currently we create an incomplete outside settings
+  // object from |worker_start_data_| but we should create a proper outside
+  // settings objects depending on the situation. For new worker case, this
+  // should be the Document that called navigator.serviceWorker.register(). For
   // ServiceWorkerRegistration#update() case, it should be the Document that
   // called update(). For soft update case, it seems to be 'null' document.
   //
   // To get a correct settings, we need to make a way to pass the settings
   // object over mojo IPCs.
-  Document* document = shadow_page_->GetDocument();
+
+  const KURL& script_url = worker_start_data_.script_url;
+  scoped_refptr<const SecurityOrigin> security_origin =
+      SecurityOrigin::Create(script_url);
   return MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
-      document->Fetcher()->GetProperties().GetFetchClientSettingsObject());
+      script_url /* global_object_url */, script_url /* base_url */,
+      security_origin, network::mojom::ReferrerPolicy::kDefault,
+      script_url.GetString() /* outgoing_referrer */,
+      CalculateHttpsState(security_origin.get()),
+      AllowedByNosniff::MimeTypeCheck::kLax, worker_start_data_.address_space,
+      kBlockAllMixedContent /* insecure_requests_policy */,
+      FetchClientSettingsObject::InsecureNavigationsSet(),
+      false /* mixed_autoupgrade_opt_out */);
 }
 
 void WebEmbeddedWorkerImpl::WaitForShutdownForTesting() {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 86c54aca..d8717c7 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1115,7 +1115,7 @@
     },
     {
       name: "PaymentRetry",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "PerformanceManagerInstrumentation",
diff --git a/third_party/blink/tools/blinkpy/common/net/git_cl.py b/third_party/blink/tools/blinkpy/common/net/git_cl.py
index 5b648dc4..0491eaae 100644
--- a/third_party/blink/tools/blinkpy/common/net/git_cl.py
+++ b/third_party/blink/tools/blinkpy/common/net/git_cl.py
@@ -52,11 +52,14 @@
         self._cwd = cwd
         self._git_executable_name = Git.find_executable_name(host.executive, host.platform)
 
-    def run(self, args):
+    def run(self, args, return_stderr=False):
         """Runs git-cl with the given arguments and returns the output.
 
         Args:
             args: A list of arguments passed to `git cl`.
+            return_stderr: Whether to include stderr in the returned output (the
+                default is False because git-cl will show a warning when running
+                on Swarming bots with local git cache).
 
         Returns:
             A string (the output from git-cl).
@@ -64,9 +67,7 @@
         command = [self._git_executable_name, 'cl'] + args
         if self._auth_refresh_token_json and args[0] in _COMMANDS_THAT_TAKE_REFRESH_TOKEN:
             command += ['--auth-refresh-token-json', self._auth_refresh_token_json]
-        # Suppress the stderr of git-cl because git-cl will show a warning when
-        # running on Swarming bots with local git cache.
-        return self._host.executive.run_command(command, cwd=self._cwd, ignore_stderr=True)
+        return self._host.executive.run_command(command, cwd=self._cwd, return_stderr=return_stderr)
 
     def trigger_try_jobs(self, builders, bucket=None):
         """Triggers try jobs on the given builders.
diff --git a/third_party/blink/tools/blinkpy/common/system/executive.py b/third_party/blink/tools/blinkpy/common/system/executive.py
index 3830b0a..0cf0369 100644
--- a/third_party/blink/tools/blinkpy/common/system/executive.py
+++ b/third_party/blink/tools/blinkpy/common/system/executive.py
@@ -311,17 +311,13 @@
                     error_handler=None,
                     return_exit_code=False,
                     return_stderr=True,
-                    ignore_stderr=False,
-                    decode_output=True,
-                    debug_logging=True):
+                    decode_output=True, debug_logging=True):
         """Popen wrapper for convenience and to work around python bugs."""
         assert isinstance(args, list) or isinstance(args, tuple)
         start_time = time.time()
 
-        assert not (return_stderr and ignore_stderr)
         stdin, string_to_communicate = self._compute_stdin(input)
-        stderr = self.STDOUT if return_stderr else (
-            self.DEVNULL if ignore_stderr else None)
+        stderr = self.STDOUT if return_stderr else None
 
         process = self.popen(args,
                              stdin=stdin,
diff --git a/third_party/blink/tools/blinkpy/common/system/executive_mock.py b/third_party/blink/tools/blinkpy/common/system/executive_mock.py
index ad7c0f1..aedb0fd0 100644
--- a/third_party/blink/tools/blinkpy/common/system/executive_mock.py
+++ b/third_party/blink/tools/blinkpy/common/system/executive_mock.py
@@ -123,7 +123,6 @@
                     error_handler=None,
                     return_exit_code=False,
                     return_stderr=True,
-                    ignore_stderr=False,
                     decode_output=True,
                     debug_logging=True):
         self._append_call(args, cwd=cwd, input=input, env=env)
@@ -157,7 +156,7 @@
         output = self._output
         if return_stderr:
             output += self._stderr
-        if decode_output and not isinstance(output, unicode):
+        if decode_output and type(output) is not unicode:
             output = output.decode('utf-8')
 
         return output
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 6b070bd..667ebd2b 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -628,3 +628,5 @@
 crbug.com/874695 virtual/streams-native/http/tests/fetch/workers/thorough/ [ Slow ]
 
 crbug.com/962831 [ Release ] http/tests/devtools/network/preview-searchable.js [ Slow ]
+
+crbug.com/967526 http/tests/devtools/console/console-uncaught-promise.js [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index a617fef4..05e4d57 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2620,7 +2620,6 @@
 crbug.com/626703 crbug.com/946710 http/tests/devtools/extensions/extensions-sidebar.js [ Crash Failure Pass Timeout ]
 crbug.com/751952 fast/text/international/complex-text-rectangle.html [ Timeout Pass ]
 crbug.com/751952 [ Win ] editing/selection/modify_extend/extend_by_character.html [ Failure Pass ]
-crbug.com/751952 http/tests/devtools/console/console-uncaught-promise.js [ Pass Failure ]
 
 crbug.com/800898 external/wpt/FileAPI/url/url-with-fetch.any.worker.html [ Pass Failure ]
 crbug.com/800898 external/wpt/FileAPI/url/url-with-xhr.any.worker.html [ Pass Failure ]
@@ -3124,8 +3123,6 @@
 crbug.com/626703 external/wpt/css/css-conditional/at-supports-040.html [ Failure ]
 ### See crbug.com/891427 comment near the top of this file:
 ###crbug.com/626703 [ Win7 ] external/wpt/resource-timing/resource_timing_buffer_full_eventually.html [ Timeout ]
-crbug.com/626703 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/update-not-allowed.https.html [ Timeout ]
-crbug.com/626703 external/wpt/service-workers/service-worker/update-not-allowed.https.html [ Timeout ]
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-margins-2.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/white-space/line-edge-white-space-collapse-002.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/white-space/line-edge-white-space-collapse-001.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/test-helpers.sub.js b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/test-helpers.sub.js
index 14101319..af8dad3 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/test-helpers.sub.js
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/test-helpers.sub.js
@@ -75,9 +75,11 @@
   }
 
   return new Promise(test.step_func(function(resolve) {
-      registration.addEventListener('updatefound', test.step_func(function() {
-          resolve(registration.installing);
-        }));
+      var handler = test.step_func(function() {
+        registration.removeEventListener('updatefound', handler);
+        resolve(registration.installing);
+      });
+      registration.addEventListener('updatefound', handler);
     }));
 }
 
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/update-during-installation-worker.js b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/update-during-installation-worker.js
index dabeec0..3f89881 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/update-during-installation-worker.js
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/update-during-installation-worker.js
@@ -1,23 +1,57 @@
 'use strict';
 
-const installMayFinish = new Promise(resolve => {
-    self.finishInstall = resolve;
+const installEventFired = new Promise(resolve => {
+  self.fireInstallEvent = resolve;
 });
 
-let report = { installEventFired: false };
+const installFinished = new Promise(resolve => {
+  self.finishInstall = resolve;
+});
 
 addEventListener('install', event => {
-    report.installEventFired = true;
-    let attemptUpdate = registration.update().catch(exception => {
-        report.exception = exception.name;
-    });
-    event.waitUntil(Promise.all([installMayFinish, attemptUpdate]));
+  fireInstallEvent();
+  event.waitUntil(installFinished);
 });
 
 addEventListener('message', event => {
-    if (event.data === 'finishInstall') {
+  // Use a dedicated MessageChannel for every request so senders can wait for
+  // individual requests to finish, and concurrent requests (to different
+  // workers) don't cause race conditions.
+  const port = event.data;
+  port.onmessage = (event) => {
+    switch (event.data) {
+      case 'awaitInstallEvent':
+        installEventFired.then(() => {
+            port.postMessage('installEventFired');
+        });
+        break;
+
+      case 'finishInstall':
+        installFinished.then(() => {
+            port.postMessage('installFinished');
+        });
         finishInstall();
-    } else {
-        event.source.postMessage(report);
+        break;
+
+      case 'callUpdate': {
+        const channel = new MessageChannel();
+        registration.update().then(() => {
+            channel.port2.postMessage({
+                success: true,
+            });
+        }).catch((exception) => {
+            channel.port2.postMessage({
+                success: false,
+                exception: exception.name,
+            });
+        });
+        port.postMessage(channel.port1, [channel.port1]);
+        break;
+      }
+
+      default:
+        port.postMessage('Unexpected command ' + event.data);
+        break;
     }
+  };
 });
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/update-during-installation-worker.py b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/update-during-installation-worker.py
new file mode 100644
index 0000000..95e4522
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/update-during-installation-worker.py
@@ -0,0 +1,11 @@
+import time
+
+def main(request, response):
+    headers = [('Content-Type', 'application/javascript'),
+               ('Cache-Control', 'max-age=0')]
+    # Add timestamp to the worker so update() finds a new worker every time.
+    body = '''
+// %s
+importScripts('update-during-installation-worker.js');
+    '''.strip() % time.clock()
+    return headers, body
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/update-not-allowed.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/update-not-allowed.https.html
index 71fe1730..0a54aa9 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/update-not-allowed.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/update-not-allowed.https.html
@@ -5,54 +5,136 @@
 <script>
 'use strict';
 
+function send_message_to_worker_and_wait_for_response(worker, message) {
+  return new Promise(resolve => {
+    // Use a dedicated channel for every request to avoid race conditions on
+    // concurrent requests.
+    const channel = new MessageChannel();
+    worker.postMessage(channel.port1, [channel.port1]);
+
+    let messageReceived = false;
+    channel.port2.onmessage = event => {
+      assert_false(messageReceived, 'Already received response for ' + message);
+      messageReceived = true;
+      resolve(event.data);
+    };
+    channel.port2.postMessage(message);
+  });
+}
+
+async function ensure_install_event_fired(worker) {
+  const response = await send_message_to_worker_and_wait_for_response(worker, 'awaitInstallEvent');
+  assert_equals('installEventFired', response);
+  assert_equals('installing', worker.state, 'Expected worker to be installing.');
+}
+
+async function finish_install(worker) {
+  await ensure_install_event_fired(worker);
+  const response = await send_message_to_worker_and_wait_for_response(worker, 'finishInstall');
+  assert_equals('installFinished', response);
+}
+
+async function activate_service_worker(t, worker) {
+  await finish_install(worker);
+  // By waiting for both states at the same time, the test fails
+  // quickly if the installation fails, avoiding a timeout.
+  await Promise.race([wait_for_state(t, worker, 'activated'),
+                      wait_for_state(t, worker, 'redundant')]);
+  assert_equals('activated', worker.state, 'Service worker should be activated.');
+}
+
+async function update_within_service_worker(worker) {
+  // This function returns a Promise that resolves when update()
+  // has been called but is not necessarily finished yet.
+  // Call finish() on the returned object to wait for update() settle.
+  const port = await send_message_to_worker_and_wait_for_response(worker, 'callUpdate');
+  let messageReceived = false;
+  return {
+    finish: () => {
+      return new Promise(resolve => {
+        port.onmessage = event => {
+          assert_false(messageReceived, 'Update already finished.');
+          messageReceived = true;
+          resolve(event.data);
+        };
+      });
+    },
+  };
+}
+
+async function update_from_client_and_await_installing_version(test, registration) {
+  const updatefound = wait_for_update(test, registration);
+  registration.update();
+  await updatefound;
+  return registration.installing;
+}
+
 async function spin_up_service_worker(test) {
-    const script = 'resources/update-during-installation-worker.js';
-    const scope = 'resources/blank.html';
+  const script = 'resources/update-during-installation-worker.py';
+  const scope = 'resources/blank.html';
 
-    let registration = await service_worker_unregister_and_register(test, script, scope);
-    test.add_cleanup(() => {
-        if (registration.installing) {
-            registration.installing.postMessage('finishInstall');
-        }
-        registration.unregister();
-    });
+  const registration = await service_worker_unregister_and_register(test, script, scope);
+  test.add_cleanup(async () => {
+    if (registration.installing) {
+      // If there is an installing worker, we need to finish installing it.
+      // Otherwise, the tests fails with an timeout because unregister() blocks
+      // until the install-event-handler finishes.
+      const worker = registration.installing;
+      await send_message_to_worker_and_wait_for_response(worker, 'awaitInstallEvent');
+      await send_message_to_worker_and_wait_for_response(worker, 'finishInstall');
+    }
+    return registration.unregister();
+  });
 
-    return registration;
+  return registration;
 }
 
 promise_test(async t => {
-    const registration = await spin_up_service_worker(t);
-    const worker = registration.installing;
+  const registration = await spin_up_service_worker(t);
+  const worker = registration.installing;
+  await ensure_install_event_fired(worker);
 
-    // spin_up_service_worker installs a cleanup hook that ensures the
-    // worker finished its installation by sending it a
-    // 'finishInstall' message, thus making sure that the registration
-    // will be cleanly removed at the end of the test.
-    assert_equals(worker.state, 'installing');
-    promise_rejects(t, 'InvalidStateError', registration.update());
-}, 'ServiceWorkerRegistration.update() from client throws while installing service worker.')
+  const result = registration.update();
+  await activate_service_worker(t, worker);
+  return result;
+}, 'ServiceWorkerRegistration.update() from client succeeds while installing service worker.');
 
 promise_test(async t => {
-    const registration = await spin_up_service_worker(t);
-    const worker = registration.installing;
-    worker.postMessage('finishInstall');
+  const registration = await spin_up_service_worker(t);
+  const worker = registration.installing;
+  await ensure_install_event_fired(worker);
 
-    // By waiting for both states at the same time, the test fails
-    // quickly if the installation fails, avoiding a timeout.
-    await Promise.race([wait_for_state(t, worker, 'activated'),
-                        wait_for_state(t, worker, 'redundant')]);
-    assert_equals(worker.state, 'activated', 'Service worker should be activated.');
+  // Add event listener to fail the test if update() succeeds.
+  const updatefound = t.step_func(async () => {
+    registration.removeEventListener('updatefound', updatefound);
+    // Activate new worker so non-compliant browsers don't fail with timeout.
+    await activate_service_worker(t, registration.installing);
+    assert_unreached("update() should have failed");
+  });
+  registration.addEventListener('updatefound', updatefound);
 
-    const response = await new Promise(resolve => {
-        navigator.serviceWorker.onmessage = event => { resolve(event.data); };
-        worker.postMessage('PING');
-    });
+  const update = await update_within_service_worker(worker);
+  // Activate worker to ensure update() finishes and the test doesn't timeout
+  // in non-compliant browsers.
+  await activate_service_worker(t, worker);
 
-    // We check that the service worker instance that replied to the
-    // message is the same one that received the 'install' event since
-    // it's possible for them to be two distinct execution
-    // environments.
-    assert_true(response.installEventFired, 'Service worker should have been installed.');
-    assert_equals(response.exception, 'InvalidStateError', 'update() should have thrown.');
+  const response = await update.finish();
+  assert_false(response.success, 'update() should have failed.');
+  assert_equals('InvalidStateError', response.exception, 'update() should have thrown InvalidStateError.');
 }, 'ServiceWorkerRegistration.update() from installing service worker throws.');
+
+promise_test(async t => {
+  const registration = await spin_up_service_worker(t);
+  const worker1 = registration.installing;
+  await activate_service_worker(t, worker1);
+
+  const worker2 = await update_from_client_and_await_installing_version(t, registration);
+  await ensure_install_event_fired(worker2);
+
+  const update = await update_within_service_worker(worker1);
+  // Activate the new version so that update() finishes and the test doesn't timeout.
+  await activate_service_worker(t, worker2);
+  const response = await update.finish();
+  assert_true(response.success, 'update() from active service worker should have succeeded.');
+}, 'ServiceWorkerRegistration.update() from active service worker succeeds while installing service worker.');
 </script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/console/console-uncaught-promise-expected.txt b/third_party/blink/web_tests/http/tests/devtools/console/console-uncaught-promise-expected.txt
index 4ae14fa..792b3db 100644
--- a/third_party/blink/web_tests/http/tests/devtools/console/console-uncaught-promise-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/console/console-uncaught-promise-expected.txt
@@ -66,6 +66,5 @@
 runNextPromiseTest @ console-uncaught-promise.js:18
 (anonymous) @ console-uncaught-promise.js:106
 A bad HTTP response code (404) was received when fetching the script.
-Failed to load resource: net::ERR_INVALID_RESPONSE
 inspected-page.html:1 Uncaught (in promise) TypeError: Failed to register a ServiceWorker: A bad HTTP response code (404) was received when fetching the script.
 
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 44f3c933..b8cd064c 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -4593,6 +4593,7 @@
     attribute @@toStringTag
     getter details
     getter methodName
+    getter onpayerdetailchange
     getter payerEmail
     getter payerName
     getter payerPhone
@@ -4601,7 +4602,9 @@
     getter shippingOption
     method complete
     method constructor
+    method retry
     method toJSON
+    setter onpayerdetailchange
 interface Performance : EventTarget
     attribute @@toStringTag
     getter memory
diff --git a/tools/clang/scripts/download_lld_mac.py b/tools/clang/scripts/download_lld_mac.py
index dace92d..3bb2b68 100755
--- a/tools/clang/scripts/download_lld_mac.py
+++ b/tools/clang/scripts/download_lld_mac.py
@@ -20,7 +20,9 @@
   if not os.path.exists(LLD_LINK_PATH):
     return False
   lld_rev = subprocess.check_output([LLD_LINK_PATH, '--version'])
-  return (re.match(r'LLD.*\(.*trunk (\d+)\)', lld_rev).group(1) ==
+  # Version output example:
+  # LLD 9.0.0 (https://github.com/llvm/llvm-project/ 342571e8d6eb1afb151ae1103431798e3d24054f)
+  return (re.match(r'LLD.*\(.*git.*llvm.* ([0-9a-f]+)\)', lld_rev).group(1) ==
              update.CLANG_REVISION)
 
 
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index b7ec74c..876fb66d1 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -36,9 +36,9 @@
 # Do NOT CHANGE this if you don't know what you're doing -- see
 # https://chromium.googlesource.com/chromium/src/+/master/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
-CLANG_REVISION = '342571e8d6eb1afb151ae1103431798e3d24054f'
-CLANG_SVN_REVISION = '361565'
-CLANG_SUB_REVISION = 1
+CLANG_REVISION = '67510fac36d27b2e22c7cd955fc167136b737b93'
+CLANG_SVN_REVISION = '361212'
+CLANG_SUB_REVISION = 2
 
 PACKAGE_VERSION = '%s-%s-%s' % (CLANG_SVN_REVISION, CLANG_REVISION[:8],
                                 CLANG_SUB_REVISION)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index e076131..bd6332bf 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -34498,8 +34498,10 @@
   <int value="149914698" label="SearchReadyOmnibox:disabled"/>
   <int value="151022756" label="ArcAvailableForChildAccount:disabled"/>
   <int value="151101719" label="HtmlBaseUsernameDetector:enabled"/>
+  <int value="153347646" label="SmartDimModelV3:disabled"/>
   <int value="157217034" label="enable-tab-for-desktop-share"/>
   <int value="157318016" label="AutomaticTabDiscarding:enabled"/>
+  <int value="160838658" label="SmartDimModelV3:enabled"/>
   <int value="161409456" label="AutofillUseMobileLabelDisambiguation:disabled"/>
   <int value="161694478" label="OmniboxNewAnswerLayout:enabled"/>
   <int value="173288154" label="PrintPdfAsImage:enabled"/>
@@ -58373,6 +58375,14 @@
   <int value="64" label=".gdoc"/>
   <int value="65" label=".gsheet"/>
   <int value="66" label=".gslides"/>
+  <int value="67" label=".arw"/>
+  <int value="68" label=".cr2"/>
+  <int value="69" label=".dng"/>
+  <int value="70" label=".nef"/>
+  <int value="71" label=".nrw"/>
+  <int value="72" label=".orf"/>
+  <int value="73" label=".raf"/>
+  <int value="74" label=".rw2"/>
 </enum>
 
 <enum name="VirtualKeyboardContainerType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 670a6a3e..e0f8b08a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -10834,6 +10834,28 @@
   </summary>
 </histogram>
 
+<histogram name="AutoScreenBrightness.ModelTraining.Inaccuracy.NoUpdate"
+    units="%" expires_after="2019-12-31">
+  <owner>jiameng@chromium.org</owner>
+  <owner>napper@chromium.org</owner>
+  <summary>
+    When training data comes in, the model may or may not be updated. We measure
+    the error of the model as compared with the target value from training data.
+    This metric measures the error when model is not updated. Chrome OS only.
+  </summary>
+</histogram>
+
+<histogram name="AutoScreenBrightness.ModelTraining.Inaccuracy.Update"
+    units="%" expires_after="2019-12-31">
+  <owner>jiameng@chromium.org</owner>
+  <owner>napper@chromium.org</owner>
+  <summary>
+    When training data comes in, the model may or may not be updated. We measure
+    the error of the model as compared with the target value from training data.
+    This metric measures the error when model is updated. Chrome OS only.
+  </summary>
+</histogram>
+
 <histogram name="AutoScreenBrightness.ModelTraining.ModelUserConsistent"
     enum="Boolean" expires_after="2019-12-31">
   <owner>jiameng@chromium.org</owner>
@@ -136444,7 +136466,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCBackgroundScavenger" units="ms" expires_after="M77">
+<histogram name="V8.GCBackgroundScavenger" units="ms"
+    expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <summary>
     Time spent in background tasks doing scavenging in one GC cycle. It is
@@ -136452,7 +136475,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCBackgroundSweeping" units="ms" expires_after="M77">
+<histogram name="V8.GCBackgroundSweeping" units="ms" expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <summary>
     Time spent in background tasks doing sweeping in one GC cycle. It is
@@ -136465,21 +136488,23 @@
   <summary>Time spent in mark-sweep phase of GC.</summary>
 </histogram>
 
-<histogram name="V8.GCCompactorBackground" units="ms" expires_after="M77">
+<histogram name="V8.GCCompactorBackground" units="ms"
+    expires_after="2020-06-01">
   <owner>hpayer@chromium.org</owner>
   <summary>
     Time spent in mark-sweep phase of GC in a background isolate.
   </summary>
 </histogram>
 
-<histogram name="V8.GCCompactorForeground" units="ms" expires_after="M77">
+<histogram name="V8.GCCompactorForeground" units="ms"
+    expires_after="2020-06-01">
   <owner>hpayer@chromium.org</owner>
   <summary>
     Time spent in mark-sweep phase of GC in a foreground isolate.
   </summary>
 </histogram>
 
-<histogram name="V8.GCContext" units="ms" expires_after="M77">
+<histogram name="V8.GCContext" units="ms" expires_after="2020-06-01">
   <owner>hpayer@chromium.org</owner>
   <summary>Time spent doing a full GC during an IdleNotification.</summary>
 </histogram>
@@ -136493,7 +136518,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC.Clear" units="ms" expires_after="M77">
+<histogram name="V8.GCFinalizeMC.Clear" units="ms" expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <summary>
@@ -136502,7 +136527,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC.Epilogue" units="ms" expires_after="M77">
+<histogram name="V8.GCFinalizeMC.Epilogue" units="ms"
+    expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <summary>
@@ -136510,7 +136536,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC.Evacuate" units="ms" expires_after="M77">
+<histogram name="V8.GCFinalizeMC.Evacuate" units="ms"
+    expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <summary>
@@ -136519,7 +136546,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC.Finish" units="ms" expires_after="M77">
+<histogram name="V8.GCFinalizeMC.Finish" units="ms" expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <summary>
@@ -136528,7 +136555,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC.Mark" units="ms">
+<histogram name="V8.GCFinalizeMC.Mark" units="ms" expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <summary>
@@ -136537,7 +136564,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC.Prologue" units="ms" expires_after="M77">
+<histogram name="V8.GCFinalizeMC.Prologue" units="ms"
+    expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <summary>
@@ -136545,7 +136573,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC.Sweep" units="ms" expires_after="M77">
+<histogram name="V8.GCFinalizeMC.Sweep" units="ms" expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <summary>
@@ -136554,7 +136582,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMCBackground" units="ms" expires_after="M77">
+<histogram name="V8.GCFinalizeMCBackground" units="ms"
+    expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <summary>
@@ -136582,7 +136611,7 @@
 </histogram>
 
 <histogram name="V8.GCFinalizeMCReduceMemoryBackground" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <summary>
@@ -136592,7 +136621,7 @@
 </histogram>
 
 <histogram name="V8.GCFinalizeMCReduceMemoryForeground" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <summary>
@@ -136601,7 +136630,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCIdleNotification" units="ms" expires_after="M77">
+<histogram name="V8.GCIdleNotification" units="ms" expires_after="2020-06-01">
   <owner>hpayer@chromium.org</owner>
   <summary>Time spent in IdleNotifications.</summary>
 </histogram>
@@ -136637,13 +136666,13 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCIncrementalMarking" units="ms" expires_after="M77">
+<histogram name="V8.GCIncrementalMarking" units="ms" expires_after="2020-06-01">
   <owner>hpayer@chromium.org</owner>
   <summary>Time spent doing incremental marking steps during GC.</summary>
 </histogram>
 
 <histogram name="V8.GCIncrementalMarkingFinalize" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>hpayer@chromium.org</owner>
   <summary>Time spent in finalizing incremental marking.</summary>
 </histogram>
@@ -136653,7 +136682,8 @@
   <summary>Reason an incremental marking was started in V8.</summary>
 </histogram>
 
-<histogram name="V8.GCIncrementalMarkingStart" units="ms" expires_after="M77">
+<histogram name="V8.GCIncrementalMarkingStart" units="ms"
+    expires_after="2020-06-01">
   <owner>hpayer@chromium.org</owner>
   <summary>Time spent in starting incremental marking.</summary>
 </histogram>
@@ -136666,7 +136696,8 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCLowMemoryNotification" units="ms" expires_after="M77">
+<histogram name="V8.GCLowMemoryNotification" units="ms"
+    expires_after="2020-06-01">
   <owner>hpayer@chromium.org</owner>
   <summary>Time spent in LowMemoryNotifications.</summary>
 </histogram>
@@ -136696,7 +136727,7 @@
   <summary>Reason a mark-compact garbage collection was started in V8.</summary>
 </histogram>
 
-<histogram name="V8.GCMarkingSum" units="ms" expires_after="2020-04-01">
+<histogram name="V8.GCMarkingSum" units="ms" expires_after="2020-06-01">
   <owner>mlippautz@chromium.org</owner>
   <summary>
     Sum of all durations of all marking phases (incremental and non-incremental)
@@ -136718,12 +136749,14 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCScavenger.ScavengeRoots" units="ms" expires_after="M77">
+<histogram name="V8.GCScavenger.ScavengeRoots" units="ms"
+    expires_after="2020-06-01">
   <owner>mlippautz@chromium.org</owner>
   <summary>Time spent in scavenging the roots during a V8 scavenge.</summary>
 </histogram>
 
-<histogram name="V8.GCScavengerBackground" units="ms" expires_after="M77">
+<histogram name="V8.GCScavengerBackground" units="ms"
+    expires_after="2020-06-01">
   <owner>hpayer@chromium.org</owner>
   <summary>
     Time spent in scavenging phase of GC in a background isolate.
@@ -136731,7 +136764,7 @@
 </histogram>
 
 <histogram name="V8.GCScavengeReason" enum="GarbageCollectionReason"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>ulan@chromium.org</owner>
   <summary>Reason a scavenge garbage collection was started in V8.</summary>
 </histogram>
@@ -136781,7 +136814,7 @@
 </histogram>
 
 <histogram name="V8.MemoryExternalFragmentationLoSpace" units="%"
-    expires_after="M77">
+    expires_after="2020-06-01">
   <owner>hpayer@chromium.org</owner>
   <summary>
     External memory fragmentation in the large object space after each GC in
diff --git a/tools/wayland_aux/Makefile b/tools/wayland_aux/Makefile
index 91f9017..56a9fc7 100644
--- a/tools/wayland_aux/Makefile
+++ b/tools/wayland_aux/Makefile
@@ -1,9 +1,12 @@
 THIS_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
 XDGSHELL := $(THIS_DIR)/../../third_party/wayland-protocols/src/unstable/xdg-shell/xdg-shell-unstable-v6.xml $(wildcard *.py)
 
-all: x.in.svg x.de.svg l.in.svg l.de.svg
+all: x.in.svg x.de.svg l.in.svg l.de.svg fuzz_actions.proto
 	@echo $(XDG_SHELL)
 
+fuzz_actions.proto: $(XDGSHELL)
+	python main.py --spec $< -t proto > $@
+
 %.in.svg: %.in.gv
 	fdp -Tsvg $< -o $@
 
@@ -25,3 +28,4 @@
 clean:
 	rm -f *.svg *.gv
 	rm -rf __pycache__
+	rm -f fuzz_actions.proto
diff --git a/tools/wayland_aux/main.py b/tools/wayland_aux/main.py
index 9152a00..70aebc1 100644
--- a/tools/wayland_aux/main.py
+++ b/tools/wayland_aux/main.py
@@ -12,6 +12,7 @@
 import sys
 import xml.etree.ElementTree as xml
 import gv_diagram
+import proto_gen
 
 
 def strip_protocol(protocol):
@@ -41,7 +42,8 @@
                       nargs='+', required=True)
   parser.add_argument('-t', '--type',
                       help='Output different types of graph',
-                      choices=['interfaces', 'deps'], required=True)
+                      choices=['interfaces', 'deps',
+                               'harness', 'proto'], required=True)
   parser.add_argument('-x', '--extra',
                       help='add extra detail to the normal printout',
                       action='store_true')
@@ -50,11 +52,15 @@
   protocols = [strip_protocol(read_protocol(path)) for path in parsed.spec]
   extra = parsed.extra
   if parsed.type == 'deps':
-    drawer = gv_diagram.DepsPrinter(extra)
+    gv_diagram.DepsPrinter(extra).draw(protocols)
+  elif parsed.type == 'interfaces':
+    gv_diagram.InterfacesPrinter(extra).draw(protocols)
+  elif parsed.type == 'harness':
+    pass
+  elif parsed.type == 'proto':
+    proto_gen.generate(protocols)
   else:
-    drawer = gv_diagram.InterfacesPrinter(extra)
-
-  drawer.draw(protocols)
+    raise Exception('%s not implemented' % parsed.type)
 
 
 if __name__ == '__main__':
diff --git a/tools/wayland_aux/proto_gen.py b/tools/wayland_aux/proto_gen.py
new file mode 100644
index 0000000..1fddc628
--- /dev/null
+++ b/tools/wayland_aux/proto_gen.py
@@ -0,0 +1,121 @@
+# Copyright (c) 2019 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.
+
+"""Generates a protobuf for fuzzing purposes.
+
+Use the generate() function to print a protobuf file, which allows libfuzzer to
+fuzz the given protocols..
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import protocol_util
+
+
+class Generator(object):
+  """Base class for generating something from a list of protocols.
+
+  Provides several utilities for generating files of things from a list of
+  protocols.
+  """
+
+  def out(self, text):
+    print(text)
+
+  def generate(self, protocols):
+    pass
+
+
+proto_type_conversions = {
+    'object': 'uint32',
+    'int': 'int32',
+    'uint': 'uint32',
+    'string': 'string',
+    'fd': 'uint32',
+}
+
+
+class ProtoGenerator(Generator):
+  """Generate a libfuzzable protobuf.
+
+  Creates a protobuf for use when fuzzing wayland. This protobuf defines all the
+  client-side messages which the fuzzer might call.
+  """
+
+  def named(self, *args):
+    return '_'.join((e.attrib['name'] for e in args))
+
+  def get_type(self, ty):
+    if ty in proto_type_conversions:
+      return proto_type_conversions[ty]
+    raise Exception('unknown conversion for type: ' + ty)
+
+  def generate_action(self, name, args):
+    self.out('message %s {' % name)
+    for (idx, (a_type, a_name)) in enumerate([('uint32', 'receiver')] + args):
+      self.out('required %s %s = %d;'%(a_type, a_name, idx+1))
+    self.out('}')
+
+  def generate_req_action(self, ptc, ifc, msg):
+    """Generate an action protobuf message.
+
+    Args:
+      ptc: the <protocol> xml element.
+      ifc: the <interface> xml element.
+      msg: the <request> xml element (given that <event>s don't need messages).
+    """
+    args = []
+    name = self.named(ptc, ifc, msg)
+    if name == 'wayland_wl_registry_bind':
+      args = [('global', 'global'), ('uint32', 'name'), ('uint32', 'version')]
+    else:
+      args = [(self.get_type(arg.attrib['type']), arg.attrib['name'])
+              for arg in msg.findall('arg')
+              if arg.attrib['type'] != 'new_id']
+    if protocol_util.is_constructor(msg):
+      c_type = protocol_util.get_constructed(msg)
+      self.out('// Constructs %s' % (c_type if c_type else '???'))
+    self.generate_action(name, args)
+
+  def generate(self, protocols):
+    self.out('syntax = "proto2";')
+    self.out('package = exo.wayland_fuzzer;')
+
+    # Make the globals enum for identifying globals to bind.
+    self.out('enum global {')
+    self.out('GLOBAL_UNSPECIFIED=0;')
+    for (idx, (pro, ifc)) in enumerate(protocol_util.get_globals(protocols)):
+      self.out('%s = %d;'%(self.named(pro, ifc), idx+1))
+    self.out('}')
+
+    # List all the possible actions.
+    self.out('message actions {')
+    self.out('repeated action acts = 1;')
+    self.out('}')
+    self.out('message action {')
+    self.out('oneof act {')
+    actions = ['meta_dispatch', 'meta_roundtrip'] + [
+        self.named(p, i, m)
+        for (p, i, m) in protocol_util.all_messages(protocols)
+        if protocol_util.is_request(m)]
+    for (idx, act) in enumerate(actions):
+      self.out('%s act_%s = %d;' % (act, act, idx+1))
+    self.out('}')
+    self.out('}')
+    self.generate_action('meta_dispatch', [])
+    self.generate_action('meta_roundtrip', [])
+    for (p, i, m) in protocol_util.all_messages(protocols):
+      if protocol_util.is_request(m):
+        self.generate_req_action(p, i, m)
+
+
+def generate(protocols):
+  """Make a protobuf for fuzzing the |protocols|.
+
+  Args:
+    protocols: a list of xml.etree.ElementTree.Element where each element is a
+      wayland <protocol> node. This list will be converted to a protobuf file
+      that can be used for fuzzing.
+  """
+  ProtoGenerator().generate(protocols)
diff --git a/tools/wayland_aux/protocol_util.py b/tools/wayland_aux/protocol_util.py
index 83b9012..59d886e 100644
--- a/tools/wayland_aux/protocol_util.py
+++ b/tools/wayland_aux/protocol_util.py
@@ -25,6 +25,14 @@
     yield r
 
 
+def is_event(message):
+  return message.tag == 'event'
+
+
+def is_request(message):
+  return message.tag == 'request'
+
+
 def is_constructor(message):
   """Check if a message is a constructor.
 
@@ -50,3 +58,67 @@
   """
   return 'type' in message.attrib and message.attrib['type'] == 'destructor'
 
+
+def all_interfaces(protocols):
+  """Get the interfaces in these protocols.
+
+  Args:
+    protocols: the list of protocols you want the interfaces of.
+
+  Yields:
+    Tuples (p, i) of (p)rotocol (i)nterface.
+  """
+  for p in protocols:
+    for i in p.findall('interface'):
+      yield (p, i)
+
+
+def all_messages(protocols):
+  """Get the messages in these protocols.
+
+  Args:
+    protocols: the list of protocols you want the messages of.
+
+  Yields:
+    Tuples (p, i, m) of (p)rotocol, (i)nterface, and (m)essage.
+  """
+  for (p, i) in all_interfaces(protocols):
+    for m in grab_interface_messages(i):
+      yield (p, i, m)
+
+
+def get_constructed(message):
+  """Gets the interface constructed by a message.
+
+  Note that even if is_constructor(message) returns true, get_constructed can
+  still return None when the message constructs an unknown interface (e.g.
+  wl_registry.bind()).
+
+  Args:
+    message: the message which may be a constructor.
+
+  Returns:
+    The name of the constructed interface (if there is one), or None.
+  """
+  for arg in message.findall('arg'):
+    if 'type' in arg.attrib and arg.attrib['type'] == 'new_id':
+      return arg.attrib.get('interface', None)
+  return None
+
+
+def get_globals(protocols):
+  """List all of the global interfaces (i.e. those without a constructor).
+
+  Args:
+    protocols: the list of protocols you want the globals for.
+
+  Yields:
+    Tuples (p, i) of (p)rotocol, (i)nterface, where the interface is a global.
+  """
+  non_globals = set(get_constructed(m)
+                    for (p, i, m) in all_messages(protocols)
+                    if get_constructed(m))
+  for (p, i) in all_interfaces(protocols):
+    if i.attrib['name'] not in non_globals:
+      yield (p, i)
+
diff --git a/ui/base/ime/constants.cc b/ui/base/ime/constants.cc
index e955607..9acc87b 100644
--- a/ui/base/ime/constants.cc
+++ b/ui/base/ime/constants.cc
@@ -6,6 +6,18 @@
 
 namespace ui {
 
+// Here, we define attributes of ui::Event::Properties objects
+// kPropertyFromVK
 const char kPropertyFromVK[] = "from_vk";
 
+// Properties of the kPropertyFromVK attribute
+
+// kFromVKIsMirroring is the index of the isMirrorring property on the
+// kPropertyFromVK attribute. This is non-zero if mirroring and zero if not
+// mirroring
+const size_t kPropertyFromVKIsMirroringIndex = 0;
+// kFromVKSize is the size of the kPropertyFromVK attribute
+// It is equal to the number of kPropertyFromVK
+const size_t kPropertyFromVKSize = 1;
+
 }  // namespace ui
diff --git a/ui/base/ime/constants.h b/ui/base/ime/constants.h
index 3224e34e..aab369a 100644
--- a/ui/base/ime/constants.h
+++ b/ui/base/ime/constants.h
@@ -6,6 +6,7 @@
 #define UI_BASE_IME_CONSTANTS_H_
 
 #include "base/component_export.h"
+#include "stddef.h"
 
 namespace ui {
 
@@ -16,6 +17,12 @@
 // Textfield).
 COMPONENT_EXPORT(UI_BASE_IME) extern const char kPropertyFromVK[];
 
+// kPropertyFromVKIsMirroringIndex is an index into kPropertyFromVK
+// and is used when the key event occurs when mirroring is detected.
+COMPONENT_EXPORT(UI_BASE_IME)
+extern const size_t kPropertyFromVKIsMirroringIndex;
+COMPONENT_EXPORT(UI_BASE_IME) extern const size_t kPropertyFromVKSize;
+
 }  // namespace ui
 
 #endif  // UI_BASE_IME_CONSTANTS_H_
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 311fd55..3a2da7e 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -895,10 +895,10 @@
                                cc::DeadlinePolicy::UseInfiniteDeadline());
   surface_layer_->SetBackgroundColor(SK_ColorBLACK);
   surface_layer_->SetSafeOpaqueBackgroundColor(SK_ColorBLACK);
-  // TODO(kylechar): Include UV transform and don't stretch to fill bounds.
   surface_layer_->SetStretchContentToFillBounds(true);
+  surface_layer_->SetIsReflection(true);
 
-  // The reflecting surface uses the native size of the display.
+  // The reflecting surface uses the native size of the reflected display.
   frame_size_in_dip_ = frame_size_in_pixels;
   RecomputeDrawsContentAndUVRect();
 }
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 2614000..83fe502 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -152,6 +152,14 @@
   ]
 }
 
+js_library("fake_file_selection_handler") {
+  testonly = true
+  deps = [
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js/cr:event_target",
+  ]
+}
+
 js_unittest("actions_model_unittest") {
   deps = [
     ":actions_model",
@@ -433,6 +441,18 @@
   externs_list = [ "../../../externs/background/progress_center.js" ]
 }
 
+js_unittest("file_transfer_controller_unittest") {
+  deps = [
+    ":fake_file_selection_handler",
+    ":file_transfer_controller",
+    "//ui/file_manager/base/js:mock_chrome",
+    "//ui/file_manager/file_manager/background/js:mock_volume_manager",
+    "//ui/file_manager/file_manager/foreground/js:mock_directory_model",
+    "//ui/file_manager/file_manager/foreground/js/metadata:mock_metadata",
+    "//ui/webui/resources/js:webui_resource_test",
+  ]
+}
+
 js_library("file_watcher") {
   deps = [
     "../../common/js:async_util",
@@ -727,6 +747,7 @@
 
 js_unittest("task_controller_unittest") {
   deps = [
+    ":fake_file_selection_handler",
     ":task_controller",
     "metadata:mock_metadata",
     "//ui/file_manager/base/js:mock_chrome",
@@ -789,6 +810,7 @@
     ":actions_model_unittest",
     ":file_list_model_unittest",
     ":file_tasks_unittest",
+    ":file_transfer_controller_unittest",
     ":import_controller_unittest",
     ":list_thumbnail_loader_unittest",
     ":navigation_list_model_unittest",
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js
index 9c18901..6a22ab2 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_model.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -274,13 +274,12 @@
       // If the change is deletion of currentDir, move up to its parent
       // directory.
       directoryEntry.getDirectory(
-          directoryEntry.fullPath, {create: false}, () => {}, () => {
+          directoryEntry.fullPath, {create: false}, () => {}, async () => {
             const volumeInfo =
                 this.volumeManager_.getVolumeInfo(assert(directoryEntry));
             if (volumeInfo) {
-              volumeInfo.resolveDisplayRoot().then(displayRoot => {
-                this.changeDirectoryEntry(displayRoot);
-              });
+              const displayRoot = await volumeInfo.resolveDisplayRoot();
+              this.changeDirectoryEntry(displayRoot);
             }
           });
     }
@@ -323,7 +322,7 @@
    * Invoked when filters are changed.
    * @private
    */
-  onFilterChanged_() {
+  async onFilterChanged_() {
     const currentDirectory = this.getCurrentDirEntry();
     if (currentDirectory && util.isNativeEntry(currentDirectory) &&
         !this.fileFilter_.filter(
@@ -332,9 +331,8 @@
       // change the current directory to the current volume's root.
       const volumeInfo = this.volumeManager_.getVolumeInfo(currentDirectory);
       if (volumeInfo) {
-        volumeInfo.resolveDisplayRoot().then(displayRoot => {
-          this.changeDirectoryEntry(displayRoot);
-        });
+        const displayRoot = await volumeInfo.resolveDisplayRoot();
+        this.changeDirectoryEntry(displayRoot);
       }
     } else {
       this.rescanSoon(false);
@@ -364,9 +362,7 @@
     const indexes = this.fileListSelection_.selectedIndexes;
     const fileList = this.getFileList();
     if (fileList) {
-      return indexes.map(i => {
-        return fileList.item(i);
-      });
+      return indexes.map(i => fileList.item(i));
     }
     return [];
   }
@@ -822,7 +818,7 @@
    * @param {EntriesChangedEvent} event Entry change event.
    * @private
    */
-  onEntriesChanged_(event) {
+  async onEntriesChanged_(event) {
     const kind = event.kind;
     const entries = event.entries;
     // TODO(hidehiko): We should update directory model even the search result
@@ -838,32 +834,32 @@
 
     switch (kind) {
       case util.EntryChangedKind.CREATED:
-        const parentPromises = [];
-        for (let i = 0; i < entries.length; i++) {
-          parentPromises.push(new Promise((resolve, reject) => {
-            entries[i].getParent(resolve, reject);
-          }));
+        try {
+          const parentPromises =
+              entries.map(entry => new Promise((resolve, reject) => {
+                            entry.getParent(resolve, reject);
+                          }));
+          const parents = await Promise.all(parentPromises);
+          const entriesToAdd = [];
+
+          for (let i = 0; i < parents.length; i++) {
+            if (!util.isSameEntry(parents[i], this.getCurrentDirEntry())) {
+              continue;
+            }
+
+            const index = this.findIndexByEntry_(entries[i]);
+            if (index >= 0) {
+              this.getFileList().replaceItem(
+                  this.getFileList().item(index), entries[i]);
+            } else {
+              entriesToAdd.push(entries[i]);
+            }
+          }
+
+          this.partialUpdate_(entriesToAdd, []);
+        } catch (error) {
+          console.error(error.stack || error);
         }
-        Promise.all(parentPromises)
-            .then(parents => {
-              const entriesToAdd = [];
-              for (let i = 0; i < parents.length; i++) {
-                if (!util.isSameEntry(parents[i], this.getCurrentDirEntry())) {
-                  continue;
-                }
-                const index = this.findIndexByEntry_(entries[i]);
-                if (index >= 0) {
-                  this.getFileList().replaceItem(
-                      this.getFileList().item(index), entries[i]);
-                } else {
-                  entriesToAdd.push(entries[i]);
-                }
-              }
-              this.partialUpdate_(entriesToAdd, []);
-            })
-            .catch(error => {
-              console.error(error.stack || error);
-            });
         break;
 
       case util.EntryChangedKind.DELETED:
@@ -953,38 +949,36 @@
   /**
    * Updates data model and selects new directory.
    * @param {!DirectoryEntry} newDirectory Directory entry to be selected.
-   * @return {Promise} A promise which is resolved when new directory is
+   * @return {!Promise<void>} A promise which is resolved when new directory is
    *     selected. If current directory has changed during the operation, this
    *     will be rejected.
    */
-  updateAndSelectNewDirectory(newDirectory) {
+  async updateAndSelectNewDirectory(newDirectory) {
     // Refresh the cache.
     this.metadataModel_.notifyEntriesCreated([newDirectory]);
     const dirContents = this.currentDirContents_;
+    const sequence = this.changeDirectorySequence_;
+    await new Promise(resolve => {
+      dirContents.prefetchMetadata([newDirectory], false, resolve);
+    });
 
-    return new Promise((onFulfilled, onRejected) => {
-             dirContents.prefetchMetadata([newDirectory], false, onFulfilled);
-           })
-        .then((sequence => {
-                // If current directory has changed during the prefetch, do not
-                // try to select new directory.
-                if (sequence !== this.changeDirectorySequence_) {
-                  return Promise.reject();
-                }
+    // If current directory has changed during the prefetch, do not try to
+    // select new directory.
+    if (sequence !== this.changeDirectorySequence_) {
+      return Promise.reject();
+    }
 
-                // If target directory is already in the list, just select it.
-                const existing = this.getFileList().slice().filter(e => {
-                  return e.name === newDirectory.name;
-                });
-                if (existing.length) {
-                  this.selectEntry(newDirectory);
-                } else {
-                  this.fileListSelection_.beginChange();
-                  this.getFileList().splice(0, 0, newDirectory);
-                  this.selectEntry(newDirectory);
-                  this.fileListSelection_.endChange();
-                }
-              }).bind(null, this.changeDirectorySequence_));
+    // If target directory is already in the list, just select it.
+    const existing =
+        this.getFileList().slice().filter(e => e.name === newDirectory.name);
+    if (existing.length) {
+      this.selectEntry(newDirectory);
+    } else {
+      this.fileListSelection_.beginChange();
+      this.getFileList().splice(0, 0, newDirectory);
+      this.selectEntry(newDirectory);
+      this.fileListSelection_.endChange();
+    }
   }
 
   /**
@@ -1013,7 +1007,7 @@
    */
   changeDirectoryEntry(dirEntry, opt_callback) {
     // Increment the sequence value.
-    this.changeDirectorySequence_++;
+    const sequence = ++this.changeDirectorySequence_;
     this.clearSearch_();
 
     // When switching to MyFiles volume, we should use a FilesAppEntry if
@@ -1031,49 +1025,46 @@
       this.currentDirContents_.cancelScan();
     }
 
-    this.directoryChangeQueue_.run(
-        ((sequence, queueTaskCallback) => {
-          this.fileWatcher_.changeWatchedDirectory(dirEntry).then(() => {
-            if (this.changeDirectorySequence_ !== sequence) {
-              queueTaskCallback();
-              return;
-            }
+    this.directoryChangeQueue_.run(async queueTaskCallback => {
+      await this.fileWatcher_.changeWatchedDirectory(dirEntry);
+      if (this.changeDirectorySequence_ !== sequence) {
+        queueTaskCallback();
+        return;
+      }
 
-            const newDirectoryContents = this.createDirectoryContents_(
-                this.currentFileListContext_, dirEntry, '');
-            if (!newDirectoryContents) {
-              queueTaskCallback();
-              return;
-            }
+      const newDirectoryContents = this.createDirectoryContents_(
+          this.currentFileListContext_, dirEntry, '');
+      if (!newDirectoryContents) {
+        queueTaskCallback();
+        return;
+      }
 
-            const previousDirEntry =
-                this.currentDirContents_.getDirectoryEntry();
-            this.clearAndScan_(newDirectoryContents, result => {
-              // Calls the callback of the method when successful.
-              if (result && opt_callback) {
-                opt_callback();
-              }
+      const previousDirEntry = this.currentDirContents_.getDirectoryEntry();
+      this.clearAndScan_(newDirectoryContents, result => {
+        // Calls the callback of the method when successful.
+        if (result && opt_callback) {
+          opt_callback();
+        }
 
-              // Notify that the current task of this.directoryChangeQueue_
-              // is completed.
-              setTimeout(queueTaskCallback, 0);
-            });
+        // Notify that the current task of this.directoryChangeQueue_
+        // is completed.
+        setTimeout(queueTaskCallback, 0);
+      });
 
-            // For tests that open the dialog to empty directories, everything
-            // is loaded at this point.
-            util.testSendMessage('directory-change-complete');
-            const previousVolumeInfo = previousDirEntry ?
-                this.volumeManager_.getVolumeInfo(previousDirEntry) :
-                null;
-            // VolumeInfo for dirEntry.
-            const currentVolumeInfo = this.getCurrentVolumeInfo();
-            const event = new Event('directory-changed');
-            event.previousDirEntry = previousDirEntry;
-            event.newDirEntry = dirEntry;
-            event.volumeChanged = previousVolumeInfo !== currentVolumeInfo;
-            this.dispatchEvent(event);
-          });
-        }).bind(null, this.changeDirectorySequence_));
+      // For tests that open the dialog to empty directories, everything
+      // is loaded at this point.
+      util.testSendMessage('directory-change-complete');
+      const previousVolumeInfo = previousDirEntry ?
+          this.volumeManager_.getVolumeInfo(previousDirEntry) :
+          null;
+      // VolumeInfo for dirEntry.
+      const currentVolumeInfo = this.getCurrentVolumeInfo();
+      const event = new Event('directory-changed');
+      event.previousDirEntry = previousDirEntry;
+      event.newDirEntry = dirEntry;
+      event.volumeChanged = previousVolumeInfo !== currentVolumeInfo;
+      this.dispatchEvent(event);
+    });
   }
 
   /**
@@ -1218,7 +1209,7 @@
     // mounted, switch to it.
     if (this.getCurrentRootType() ===
         VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT) {
-      for (let newVolume of event.added) {
+      for (const newVolume of event.added) {
         if (newVolume.volumeType === VolumeManagerCommon.VolumeType.DRIVE) {
           newVolume.resolveDisplayRoot().then((displayRoot) => {
             this.changeDirectoryEntry(displayRoot);
@@ -1265,7 +1256,7 @@
     }
 
     const rootType = this.getCurrentRootType();
-    for (let volume of removedVolumes) {
+    for (const volume of removedVolumes) {
       if (volume.fakeEntries[rootType]) {
         return true;
       }
@@ -1397,37 +1388,36 @@
       return;
     }
 
-    this.changeDirectorySequence_++;
-    this.directoryChangeQueue_.run(
-        ((sequence, callback) => {
-          if (this.changeDirectorySequence_ !== sequence) {
-            callback();
-            return;
-          }
+    const sequence = ++this.changeDirectorySequence_;
+    this.directoryChangeQueue_.run(callback => {
+      if (this.changeDirectorySequence_ !== sequence) {
+        callback();
+        return;
+      }
 
-          if (!(query || '').trimLeft()) {
-            if (this.isSearching()) {
-              const newDirContents = this.createDirectoryContents_(
-                  this.currentFileListContext_, assert(currentDirEntry));
-              this.clearAndScan_(newDirContents, callback);
-            } else {
-              callback();
-            }
-            return;
-          }
-
+      if (!(query || '').trimLeft()) {
+        if (this.isSearching()) {
           const newDirContents = this.createDirectoryContents_(
-              this.currentFileListContext_, assert(currentDirEntry), query);
-          if (!newDirContents) {
-            callback();
-            return;
-          }
-
-          this.onSearchCompleted_ = onSearchRescan;
-          this.onClearSearch_ = onClearSearch;
-          this.addEventListener('scan-completed', this.onSearchCompleted_);
+              this.currentFileListContext_, assert(currentDirEntry));
           this.clearAndScan_(newDirContents, callback);
-        }).bind(null, this.changeDirectorySequence_));
+        } else {
+          callback();
+        }
+        return;
+      }
+
+      const newDirContents = this.createDirectoryContents_(
+          this.currentFileListContext_, assert(currentDirEntry), query);
+      if (!newDirContents) {
+        callback();
+        return;
+      }
+
+      this.onSearchCompleted_ = onSearchRescan;
+      this.onClearSearch_ = onClearSearch;
+      this.addEventListener('scan-completed', this.onSearchCompleted_);
+      this.clearAndScan_(newDirContents, callback);
+    });
   }
 
   /**
diff --git a/ui/file_manager/file_manager/foreground/js/fake_file_selection_handler.js b/ui/file_manager/file_manager/foreground/js/fake_file_selection_handler.js
new file mode 100644
index 0000000..944d2d5
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/fake_file_selection_handler.js
@@ -0,0 +1,34 @@
+// Copyright 2019 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.
+
+/**
+ * Mock FileSelectionHandler.
+ * @extends {FileSelectionHandler}
+ */
+class FakeFileSelectionHandler {
+  constructor() {
+    this.selection = /** @type {!FileSelection} */ ({});
+    this.updateSelection([], []);
+    this.eventTarget_ = new cr.EventTarget();
+  }
+
+  computeAdditionalCallback() {}
+
+  updateSelection(entries, mimeTypes) {
+    this.selection = /** @type {!FileSelection} */ ({
+      entries: entries,
+      mimeTypes: mimeTypes,
+      computeAdditional: (metadataModel) => {
+        this.computeAdditionalCallback();
+        return new Promise((resolve) => {
+          resolve();
+        });
+      },
+    });
+  }
+
+  addEventListener(...args) {
+    return this.eventTarget_.addEventListener(...args);
+  }
+}
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index de604a8..39be149 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -501,13 +501,14 @@
 
   /**
    * One time initialization for the file system and related things.
+   * @return {!Promise<void>}
    * @private
    */
-  initFileSystemUI_() {
+  async initFileSystemUI_() {
     this.ui_.listContainer.startBatchUpdates();
 
-    this.initFileList_();
-    this.setupCurrentDirectory_();
+    const fileListPromise = this.initFileList_();
+    const currentDirectoryPromise = this.setupCurrentDirectory_();
 
     const self = this;
 
@@ -622,12 +623,12 @@
         this.dialogType === DialogType.FULL_PAGE));
 
     this.ui_.attachFilesTooltip();
-
     this.ui_.decorateFilesMenuItems();
-
     this.ui_.selectionMenuButton.hidden = false;
 
-    console.warn('Files app sync startup finished');
+    console.warn('Files app sync started');
+    await Promise.all([fileListPromise, currentDirectoryPromise]);
+    console.warn('Files app sync finished');
   }
 
   /**
@@ -762,9 +763,10 @@
     this.initEssentialUI_();
     this.initAdditionalUI_();
     await this.initSettingsPromise_;
-    this.initFileSystemUI_();
+    const fileSystemUIPromise = this.initFileSystemUI_();
     this.initUIFocus_();
     metrics.recordInterval('Load.InitUI');
+    return fileSystemUIPromise;
   }
 
   /**
@@ -994,9 +996,10 @@
 
   /**
    * Constructs table and grid (heavy operation).
+   * @return {!Promise<void>}
    * @private
    */
-  initFileList_() {
+  async initFileList_() {
     const singleSelection = this.dialogType == DialogType.SELECT_OPEN_FILE ||
         this.dialogType == DialogType.SELECT_FOLDER ||
         this.dialogType == DialogType.SELECT_UPLOAD_FOLDER ||
@@ -1029,7 +1032,7 @@
 
     // TODO(mtomasz, yoshiki): Create navigation list earlier, and here just
     // attach the directory model.
-    this.initDirectoryTree_();
+    const directoryTreePromise = this.initDirectoryTree_();
 
     this.ui_.listContainer.listThumbnailLoader = new ListThumbnailLoader(
         this.directoryModel_, assert(this.thumbnailModel_),
@@ -1085,12 +1088,15 @@
         this.dialogType, this.ui_.dialogFooter, this.directoryModel_,
         this.metadataModel_, this.volumeManager_, this.fileFilter_,
         this.namingController_, this.selectionHandler_, this.launchParams_);
+
+    return directoryTreePromise;
   }
 
   /**
+   * @return {!Promise<void>}
    * @private
    */
-  initDirectoryTree_() {
+  async initDirectoryTree_() {
     const directoryTree = /** @type {DirectoryTree} */
         (this.dialogDom_.querySelector('#directory-tree'));
     const fakeEntriesVisible =
@@ -1119,7 +1125,7 @@
         loadTimeData.getBoolean('CROSTINI_ENABLED'));
     this.crostini_.setEnabled(
         constants.PLUGIN_VM, loadTimeData.getBoolean('PLUGIN_VM_ENABLED'));
-    this.setupCrostini_();
+    const crostiniPromise = this.setupCrostini_();
     chrome.fileManagerPrivate.onCrostiniChanged.addListener(
         this.onCrostiniChanged_.bind(this));
 
@@ -1127,10 +1133,12 @@
       this.onPreferencesChanged_();
     });
     this.onPreferencesChanged_();
+    return crostiniPromise;
   }
 
   /**
-   * Setup crostini 'Linux files'.
+   * Sets up Crostini 'Linux files'.
+   * @return {!Promise<void>}
    * @private
    */
   async setupCrostini_() {
@@ -1150,11 +1158,12 @@
     // Only observe firstForSession when using full-page FilesApp.
     // I.e., don't show toast in a dialog.
     let showToast = false;
-    const getSharedPaths = (vmName) => {
+    const getSharedPaths = async (vmName) => {
+      if (!this.crostini_.isEnabled(vmName)) {
+        return 0;
+      }
+
       return new Promise(resolve => {
-        if (!this.crostini_.isEnabled(vmName)) {
-          return resolve(0);
-        }
         chrome.fileManagerPrivate.getCrostiniSharedPaths(
             this.dialogType === DialogType.FULL_PAGE, vmName,
             (entries, firstForSession) => {
@@ -1203,20 +1212,24 @@
 
   /**
    * @param {chrome.fileManagerPrivate.CrostiniEvent} event
+   * @return {!Promise<void>}
    * @private
    */
-  onCrostiniChanged_(event) {
-    if (event.eventType === 'enable') {
-      this.crostini_.setEnabled(event.vmName, true);
-      this.setupCrostini_();
-    } else if (event.eventType === 'disable') {
-      this.crostini_.setEnabled(event.vmName, false);
-      this.setupCrostini_();
+  async onCrostiniChanged_(event) {
+    switch (event.eventType) {
+      case chrome.fileManagerPrivate.CrostiniEventType.ENABLE:
+        this.crostini_.setEnabled(event.vmName, true);
+        return this.setupCrostini_();
+
+      case chrome.fileManagerPrivate.CrostiniEventType.DISABLE:
+        this.crostini_.setEnabled(event.vmName, false);
+        return this.setupCrostini_();
     }
   }
 
   /**
    * Sets up the current directory during initialization.
+   * @return {!Promise<void>}
    * @private
    */
   async setupCurrentDirectory_() {
@@ -1365,7 +1378,7 @@
     tracker.stop();
     if (!tracker.hasChanged) {
       // Finish setup current directory.
-      this.finishSetupCurrentDirectory_(
+      await this.finishSetupCurrentDirectory_(
           nextCurrentDirEntry, selectionEntry, this.launchParams_.targetName);
     }
   }
@@ -1375,32 +1388,38 @@
    * @param {Entry=} opt_selectionEntry Entry to be selected.
    * @param {string=} opt_suggestedName Suggested name for a non-existing
    *     selection.
+   * @return {!Promise<void> }
    * @private
    */
-  finishSetupCurrentDirectory_(
+  async finishSetupCurrentDirectory_(
       directoryEntry, opt_selectionEntry, opt_suggestedName) {
     // Open the directory, and select the selection (if passed).
-    if (directoryEntry) {
-      const entryDescription = util.entryDebugString(directoryEntry);
-      console.warn(
-          `Files app start up: Changing to directory: ${entryDescription}`);
-      this.directoryModel_.changeDirectoryEntry(directoryEntry, () => {
+    const promise = (async () => {
+      if (directoryEntry) {
+        const entryDescription = util.entryDebugString(directoryEntry);
+        console.warn(
+            `Files app start up: Changing to directory: ${entryDescription}`);
+        await new Promise(resolve => {
+          this.directoryModel_.changeDirectoryEntry(
+              assert(directoryEntry), resolve);
+        });
         if (opt_selectionEntry) {
           this.directoryModel_.selectEntry(opt_selectionEntry);
         }
         console.warn(
             `Files app start up: Changed to directory: ${entryDescription}`);
-        this.ui_.addLoadedAttribute();
-      });
-    } else {
-      console.warn('No entry for finishSetupCurrentDirectory_');
+      } else {
+        console.warn('No entry for finishSetupCurrentDirectory_');
+      }
       this.ui_.addLoadedAttribute();
-    }
+    })();
 
     if (this.dialogType === DialogType.SELECT_SAVEAS_FILE) {
       this.ui_.dialogFooter.filenameInput.value = opt_suggestedName || '';
       this.ui_.dialogFooter.selectTargetNameInFilenameInput();
     }
+
+    return promise;
   }
 
   /**
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks.js b/ui/file_manager/file_manager/foreground/js/file_tasks.js
index 446f802..fe6c5b3 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks.js
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks.js
@@ -1236,10 +1236,12 @@
 };
 
 /**
- * The list of known extensions to record UMA.
- * Note: Because the data is recorded by the index, so new item shouldn't be
- * inserted.
- * Must match the ViewFileType entry in enums.xml.
+ * List of file extensions to record in UMA.
+ *
+ * Note: since the data is recorded by list index, new items should be added
+ * to the end of this list.
+ *
+ * The list must also match the FileBrowser ViewFileType entry in enums.xml.
  *
  * @const {Array<string>}
  */
@@ -1266,7 +1268,9 @@
   '.torrent',  '.txt',         '.zip',
   'directory', 'no extension', 'unknown extension',
   '.mhtml',    '.gdoc',        '.gsheet',
-  '.gslides'
+  '.gslides',  '.arw',         '.cr2',
+  '.dng',      '.nef',         '.nrw',
+  '.orf',      '.raf',         '.rw2'
 ]);
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index e44c6eef..d9ed830 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -1122,13 +1122,17 @@
   }
 
   /**
-   * @return {boolean} Returns false if {@code <input type="text">} element is
-   *     currently active. Otherwise, returns true.
+   * @return {boolean} Returns false if {@code <input type="text"> or
+   *     <cr-input>} element is currently active. Otherwise, returns true.
    * @private
    */
   isDocumentWideEvent_() {
-    return this.document_.activeElement.nodeName.toLowerCase() !== 'input' ||
-        this.document_.activeElement.type.toLowerCase() !== 'text';
+    const element = this.document_.activeElement;
+    const tagName = this.document_.activeElement.nodeName.toLowerCase();
+
+    return !(
+        (tagName === 'input' && element.type === 'text') ||
+        tagName === 'cr-input');
   }
 
   /**
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
new file mode 100644
index 0000000..e11244c
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller_unittest.js
@@ -0,0 +1,194 @@
+// Copyright 2019 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.
+
+/** @type {!ListContainer} */
+let listContainer;
+
+/** @type {!FileTransferController} */
+let fileTransferController;
+
+/** @type {!DirectoryTree} */
+let directoryTree;
+
+/**
+ * Mock chrome APIs.
+ * @type {!Object}
+ */
+let mockChrome;
+
+function setUp() {
+  // Setup page DOM.
+  document.body.innerHTML = [
+    '<style>',
+    '  .hide {',
+    '    display: none;',
+    '  }',
+    '</style>',
+    '<command id="cut">',
+    '<command id="copy">',
+    '<div class="dialog-container">',
+    '  <div tabindex="0" id="directory-tree">',
+    '  </div>',
+    '  <div id="list-container">',
+    '    <div id="detail-table">',
+    '      <list id="file-list" contextmenu="#file-context-menu" tabindex="0">',
+    '      </list>',
+    '    </div>',
+    '    <grid id="file-grid" contextmenu="#file-context-menu" ',
+    '          tabindex="0" hidden>',
+    '    </grid>',
+    '    <paper-progress class="loading-indicator" hidden></paper-progress>',
+    '  </div>',
+    '  <div id="dialog">',
+    '  </div>',
+    '  <div id="test-elements">',
+    '    <input type="text" id="free-text">',
+    '    <cr-input id="test-input" tabindex="0">',
+    '    <input type="button" id="button">',
+    '</div>',
+  ].join('');
+
+  // Mock LoadTimeData strings.
+  window.loadTimeData.getString = id => id;
+
+  // Mock chome APIs.
+  mockChrome = {
+    fileManagerPrivate: {
+      enableExternalFileScheme: () => {},
+      getProfiles: (callback) => {
+        setTimeout(callback, 0, [], '', '');
+      }
+    },
+  };
+  installMockChrome(mockChrome);
+
+  // Initialize cr.ui.Command with the <command>s.
+  cr.ui.decorate('command', cr.ui.Command);
+
+  // Setup MultiProfileShareDialog.
+  const multiProfileShareDialog =
+      new MultiProfileShareDialog(queryRequiredElement('#dialog'));
+
+  // Fake confirmation callback.
+  const confirmationDialog = (isMove, messages) => Promise.resolve(true);
+
+  // Fake ProgressCenter;
+  const progressCenter = /** @type {!ProgressCenter} */ ({});
+
+  // Fake FileOperationManager.
+  const fileOperationManager = /** @type {!FileOperationManager} */ ({});
+
+  // Fake MetadataModel.
+  const metadataModel = new MockMetadataModel({});
+
+  // Fake ThumbnailModel.
+  const thumbnailModel = /** @type {!ThumbnailModel} */ ({});
+
+  // Fake DirectoryModel.
+  const directoryModel = createFakeDirectoryModel();
+
+  // Fake VolumeManager.
+  const volumeManager = new MockVolumeManager();
+
+  // Fake FileSelectionHandler.
+  const selectionHandler = new FakeFileSelectionHandler();
+
+  // Fake HistoryLoader.
+  const historyLoader = /** @type {!importer.HistoryLoader} */ ({
+    getHistory: () => {
+      return Promise.resolve();
+    },
+  });
+
+  // Fake A11yAnnounce.
+  const a11Messages = [];
+  const a11y = /** @type {!A11yAnnounce} */ ({
+    speakA11yMessage: (text) => {
+      a11Messages.push(text);
+    },
+  });
+
+  // Setup FileTable.
+  const table =
+      /** @type {!FileTable} */ (queryRequiredElement('#detail-table'));
+  FileTable.decorate(
+      table, metadataModel, volumeManager, historyLoader, a11y,
+      true /* fullPage */);
+  table.list = document.querySelector('#file-list');
+  const dataModel = new FileListModel(metadataModel);
+  table.list.dataModel = dataModel;
+
+  // Setup FileGrid.
+  const grid = /** @type {!FileGrid} */ (queryRequiredElement('#file-grid'));
+  FileGrid.decorate(grid, metadataModel, volumeManager, historyLoader);
+
+  // Setup the ListContainer and its dependencies
+  listContainer =
+      new ListContainer(queryRequiredElement('#list-container'), table, grid);
+  listContainer.dataModel = dataModel;
+  listContainer.selectionModel = new cr.ui.ListSelectionModel();
+  listContainer.setCurrentListType(ListContainer.ListType.DETAIL);
+
+  // Setup DirectoryTree elements.
+  directoryTree =
+      /** @type {!DirectoryTree} */ (queryRequiredElement('#directory-tree'));
+
+  // Initialize FileTransferController.
+  fileTransferController = new FileTransferController(
+      document,
+      listContainer,
+      directoryTree,
+      multiProfileShareDialog,
+      confirmationDialog,
+      progressCenter,
+      fileOperationManager,
+      metadataModel,
+      thumbnailModel,
+      directoryModel,
+      volumeManager,
+      selectionHandler,
+  );
+}
+
+/**
+ * Tests isDocumentWideEvent_.
+ *
+ * @suppress {accessControls} To be able to access private method
+ * isDocumentWideEvent_
+ */
+function testIsDocumentWideEvent() {
+  const input = document.querySelector('#free-text');
+  const crInput = document.querySelector('#test-input');
+  const button = document.querySelector('#button');
+
+  // Should return true when body is focused.
+  document.body.focus();
+  assertEquals(document.body, document.activeElement);
+  assertTrue(fileTransferController.isDocumentWideEvent_());
+
+  // Should return true when button is focused.
+  button.focus();
+  assertEquals(button, document.activeElement);
+  assertTrue(fileTransferController.isDocumentWideEvent_());
+
+  // Should return true when tree is focused.
+  directoryTree.focus();
+  assertEquals(directoryTree, document.activeElement);
+  assertTrue(fileTransferController.isDocumentWideEvent_());
+
+  // Should return true when FileList is focused.
+  listContainer.focus();
+  assertEquals(listContainer.table.list, document.activeElement);
+  assertTrue(fileTransferController.isDocumentWideEvent_());
+
+  // Should return true when document is focused.
+  input.focus();
+  assertEquals(input, document.activeElement);
+  assertFalse(fileTransferController.isDocumentWideEvent_());
+
+  // Should return true when document is focused.
+  crInput.focus();
+  assertEquals(crInput, document.activeElement);
+  assertFalse(fileTransferController.isDocumentWideEvent_());
+}
diff --git a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
index 301a988..37e12f7c 100644
--- a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
@@ -250,7 +250,7 @@
     this.quickViewUma_.onEntryChanged(entry);
     return Promise
         .all([
-          this.metadataModel_.get([entry], ['thumbnailUrl', 'mediaMimeType']),
+          this.metadataModel_.get([entry], ['thumbnailUrl']),
           this.getAvailableTasks_(entry)
         ])
         .then(values => {
@@ -295,7 +295,7 @@
    */
   getQuickViewParameters_(entry, items, tasks) {
     const item = items[0];
-    const typeInfo = FileType.getType(entry, item.mediaMimeType);
+    const typeInfo = FileType.getType(entry);
     const type = typeInfo.type;
 
     /** @type {!QuickViewParams} */
@@ -376,14 +376,6 @@
               } else {
                 break;
               }
-            case 'text':
-              if (typeInfo.subtype === 'TXT') {
-                params.contentUrl = URL.createObjectURL(file);
-                params.browsable = true;
-                return params;
-              } else {
-                break;
-              }
           }
           const browsable = tasks.some(task => {
             return ['view-in-browser', 'view-pdf'].includes(
@@ -443,11 +435,12 @@
 ];
 
 /**
- * List of unsupported image subtypes
+ * List of unsupported image subtypes excluded from being displayed in
+ * QuickView. An "unsupported type" message is shown instead.
  * @private @const {!Array<string>}
  */
 QuickViewController.UNSUPPORTED_IMAGE_SUBTYPES_ = [
-  'TIFF',
+  'TIFF',  // crbug.com/624109
 ];
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
index 061e964..d0cc8cd 100644
--- a/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/task_controller_unittest.js
@@ -107,35 +107,6 @@
 }
 
 /**
- * Mock FileSelectionHandler.
- * @extends {FileSelectionHandler}
- */
-class FakeFileSelectionHandler {
-  constructor() {
-    this.selection = /** @type {!FileSelection} */ ({});
-    this.updateSelection([], []);
-    this.eventTarget_ = new cr.EventTarget();
-  }
-  computeAdditionalCallback() {}
-  updateSelection(entries, mimeTypes) {
-    this.selection = /** @type {!FileSelection} */ ({
-      entries: entries,
-      mimeTypes: mimeTypes,
-      computeAdditional: (metadataModel) => {
-        this.computeAdditionalCallback();
-        return new Promise((resolve) => {
-          resolve();
-        });
-      },
-    });
-  }
-
-  addEventListener(...args) {
-    return this.eventTarget_.addEventListener(...args);
-  }
-}
-
-/**
  * Setup test case fileManagerPrivate.
  */
 function setupFileManagerPrivate() {
diff --git a/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js b/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js
index 74a8ba4..2034390 100644
--- a/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js
+++ b/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js
@@ -16,6 +16,12 @@
  * @suppress {checkTypes}
  */
 chrome.fileManagerPrivate = {
+  CrostiniEventType: {
+    ENABLE: 'enable',
+    DISABLE: 'disable',
+    SHARE: 'share',
+    UNSHARE: 'unshare',
+  },
   Verb: {
     OPEN_WITH: 'open_with',
     ADD_TO: 'add_to',
diff --git a/ui/file_manager/image_loader/piex/images.golden.txt b/ui/file_manager/image_loader/piex/images.golden.txt
index 114aa314..0ceb1dc2 100644
--- a/ui/file_manager/image_loader/piex/images.golden.txt
+++ b/ui/file_manager/image_loader/piex/images.golden.txt
@@ -1,43 +1,31 @@
 test: images/SONY_A500_01.ARW
-test: images/SONY_A500_01.ARW SONY DSLR-A500
 test: images/SONY_A500_01.ARW preview hash 962c7faa4a {"colorSpace":"sRgb","orientation":1,"format":0,"offset":163891,"length":903326,"width":1616,"height":1080,"type":"preview"}
 test: images/SONY_A500_01.ARW thumbnail hash 5b77ade47 {"colorSpace":"sRgb","orientation":1,"format":0,"offset":47796,"length":4813,"width":160,"height":120,"type":"thumbnail"}
 test: images/EOS_XS_REBEL.CR2
-test: images/EOS_XS_REBEL.CR2 Canon Canon EOS DIGITAL REBEL XS
 test: images/EOS_XS_REBEL.CR2 preview hash 1c0e9e8cbf6 {"colorSpace":"sRgb","orientation":8,"format":0,"offset":46568,"length":842753,"width":1936,"height":1288,"type":"preview"}
 test: images/EOS_XS_REBEL.CR2 thumbnail hash 2dadc4e1ea {"colorSpace":"sRgb","orientation":8,"format":0,"offset":38124,"length":8441,"width":160,"height":120,"type":"thumbnail"}
 test: images/RAW_CANON_1DM2.CR2
-test: images/RAW_CANON_1DM2.CR2 Canon Canon EOS-1D Mark II
 test: images/RAW_CANON_1DM2.CR2 preview hash ffea0d8b0c {"colorSpace":"adobeRgb","orientation":1,"format":0,"offset":9996,"length":551740,"width":1536,"height":1024,"type":"preview"}
 test: images/RAW_CANON_1DM2.CR2 thumbnail hash 17c37d76a2 {"colorSpace":"adobeRgb","orientation":1,"format":0,"offset":561766,"length":8680,"width":160,"height":120,"type":"thumbnail"}
 test: images/L100_4220.DNG
-test: images/L100_4220.DNG Leica Camera AG M9 Digital Camera
 test: images/L100_4220.DNG thumbnail hash e55ce7a0ea {"colorSpace":"sRgb","orientation":1,"format":1,"offset":7044,"length":207360,"width":320,"height":216,"size":207360,"type":"thumbnail"}
 test: images/RAW_LEICA_M8.DNG
-test: images/RAW_LEICA_M8.DNG Leica Camera AG M8 Digital Camera
 test: images/RAW_LEICA_M8.DNG thumbnail hash abea89dfb7 {"colorSpace":"sRgb","orientation":1,"format":1,"offset":3936,"length":230400,"width":320,"height":240,"size":230400,"type":"thumbnail"}
 test: images/FUJI_E550_RAW.RAF
-test: images/FUJI_E550_RAW.RAF FUJIFILM FinePix E550   
 test: images/FUJI_E550_RAW.RAF preview hash ada7914438 {"colorSpace":"sRgb","orientation":1,"format":0,"offset":148,"length":667499,"width":0,"height":0,"type":"preview"}
 test: images/FUJI_E550_RAW.RAF thumbnail hash 136fb97598 {"colorSpace":"sRgb","orientation":1,"format":0,"offset":1466,"length":9312,"width":0,"height":0,"type":"thumbnail"}
 test: images/NIKON_UB20_O35.NEF
-test: images/NIKON_UB20_O35.NEF NIKON CORPORATION NIKON D1X
 test: images/NIKON_UB20_O35.NEF thumbnail hash 71591f3c17 {"colorSpace":"sRgb","orientation":1,"format":1,"offset":32418,"length":57600,"width":160,"height":120,"size":57600,"type":"thumbnail"}
 test: images/NIKON_GDN0447.NEF
-test: images/NIKON_GDN0447.NEF NIKON CORPORATION NIKON D700
 test: images/NIKON_GDN0447.NEF preview hash a49796a236 {"colorSpace":"sRgb","orientation":1,"format":0,"offset":175616,"length":1747382,"width":4256,"height":2832,"type":"preview"}
 test: images/NIKON_GDN0447.NEF thumbnail hash ec18fa833 {"colorSpace":"sRgb","orientation":1,"format":1,"offset":117638,"length":57600,"width":160,"height":120,"size":57600,"type":"thumbnail"}
 test: images/OLYMPUS_SC877.ORF
-test: images/OLYMPUS_SC877.ORF OLYMPUS IMAGING CORP.   E-M1            
 test: images/OLYMPUS_SC877.ORF preview hash 66b589cb7f {"colorSpace":"sRgb","orientation":1,"format":0,"offset":52224,"length":922386,"width":0,"height":0,"type":"preview"}
 test: images/OLYMPUS_SC877.ORF thumbnail hash 1ca4452baf {"colorSpace":"sRgb","orientation":1,"format":0,"offset":23808,"length":9441,"width":0,"height":0,"type":"thumbnail"}
 test: images/NIKON_CPIX78.NRW
-test: images/NIKON_CPIX78.NRW NIKON COOLPIX P7800
 test: images/NIKON_CPIX78.NRW preview hash e7f9b6542b {"colorSpace":"sRgb","orientation":1,"format":0,"offset":24421226,"length":2620351,"width":4000,"height":3000,"type":"preview"}
 test: images/NIKON_CPIX78.NRW thumbnail hash f7bdde6f0 {"colorSpace":"sRgb","orientation":1,"format":0,"offset":31429,"length":4261,"width":160,"height":120,"type":"thumbnail"}
 test: images/PANASONIC_DMC.RW2
-test: images/PANASONIC_DMC.RW2 Panasonic DMC-TZ70
 test: images/PANASONIC_DMC.RW2 preview hash c4b5f92809 {"colorSpace":"sRgb","orientation":1,"format":0,"offset":1536,"length":690176,"width":1920,"height":1440,"type":"preview"}
 test: images/PANASONIC_DMC.RW2 thumbnail hash 405666a33 {"colorSpace":"sRgb","orientation":1,"format":0,"offset":16384,"length":5560,"width":0,"height":0,"type":"thumbnail"}
 test: images/UNKNOWN_FORMAT.JPG
-test: images/UNKNOWN_FORMAT.JPG  
diff --git a/ui/file_manager/image_loader/piex/piex.cpp b/ui/file_manager/image_loader/piex/piex.cpp
index 54c2c611..08c4f25 100644
--- a/ui/file_manager/image_loader/piex/piex.cpp
+++ b/ui/file_manager/image_loader/piex/piex.cpp
@@ -65,14 +65,36 @@
   static emscripten::val GetProperties(const piex::PreviewImageData& image) {
     auto result = emscripten::val::object();
 
-    result.set("maker", emscripten::val(image.maker));
-    result.set("model", emscripten::val(image.model));
+    result.set("details", GetDetails(image));
     result.set("preview", GetPreview(image));
     result.set("thumbnail", GetThumbnail(image));
 
     return result;
   }
 
+  static emscripten::val GetDetails(const piex::PreviewImageData& image) {
+    auto object = emscripten::val::object();
+
+    object.set("cameraMaker", emscripten::val(image.maker));
+    object.set("cameraModel", emscripten::val(image.model));
+
+    object.set("aperture", GetRational(image.fnumber));
+    object.set("focalLength", GetRational(image.focal_length));
+    object.set("exposureTime", GetRational(image.exposure_time));
+    object.set("isoSpeed", emscripten::val(image.iso));
+
+    object.set("width", emscripten::val(image.full_width));
+    object.set("height", emscripten::val(image.full_height));
+    object.set("orientation", emscripten::val(image.exif_orientation));
+    object.set("colorSpace", emscripten::val("sRGB"));
+    const auto space = static_cast<uint32_t>(image.color_space);
+    if (space == piex::PreviewImageData::kAdobeRgb)
+      object.set("colorSpace", emscripten::val("AdobeRGB1998"));
+    object.set("date", emscripten::val(image.date_time));
+
+    return object;
+  }
+
   static emscripten::val GetPreview(const piex::PreviewImageData& image) {
     const auto undefined = emscripten::val::undefined();
 
@@ -121,6 +143,12 @@
       return emscripten::val("adobeRgb");
     return emscripten::val("sRgb");
   }
+
+  static float GetRational(const piex::PreviewImageData::Rational& number) {
+    if (number.denominator != 0)
+      return float(number.numerator) / number.denominator;
+    return 0.0f;
+  }
 };
 
 emscripten::val image(int data, size_t size) {
diff --git a/ui/file_manager/image_loader/piex/piex.js.wasm b/ui/file_manager/image_loader/piex/piex.js.wasm
index 23cf629..ca7b6e0 100644
--- a/ui/file_manager/image_loader/piex/piex.js.wasm
+++ b/ui/file_manager/image_loader/piex/piex.js.wasm
@@ -1 +1 @@
-var Module=typeof Module!=="undefined"?Module:{};(function(){var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=function(status,toThrow){throw toThrow};Module["preRun"]=[];Module["postRun"]=[];var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}else{return scriptDirectory+path}}if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}Module["read"]=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)};Module["setWindowTitle"]=function(title){document.title=title}}else{}var out=Module["print"]||(typeof console!=="undefined"?console.log.bind(console):typeof print!=="undefined"?print:null);var err=Module["printErr"]||(typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn.bind(console)||out);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var asm2wasmImports={"f64-rem":function(x,y){return x%y},"debugger":function(){debugger}};var functionPointers=new Array(0);if(typeof WebAssembly!=="object"){err("no native wasm support detected")}var wasmMemory;var wasmTable;var ABORT=false;var EXITSTATUS=0;var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(u8Array,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(u8Array[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var str="";while(idx<endPtr){var u0=u8Array[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|u8Array[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBuffer(buf){Module["buffer"]=buffer=buf}function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var DYNAMIC_BASE=17248,DYNAMICTOP_PTR=8800;var TOTAL_STACK=8192;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||16777216;if(TOTAL_MEMORY<TOTAL_STACK)err("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+TOTAL_MEMORY+"! (TOTAL_STACK="+TOTAL_STACK+")");if(Module["buffer"]){buffer=Module["buffer"]}else{if(typeof WebAssembly==="object"&&typeof WebAssembly.Memory==="function"){wasmMemory=new WebAssembly.Memory({"initial":TOTAL_MEMORY/WASM_PAGE_SIZE});buffer=wasmMemory.buffer}else{buffer=new ArrayBuffer(TOTAL_MEMORY)}Module["buffer"]=buffer}updateGlobalBufferViews();HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}var wasmBinaryFile="piex.out.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(Module["wasmBinary"]){return new Uint8Array(Module["wasmBinary"])}if(Module["readBinary"]){return Module["readBinary"](wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!Module["wasmBinary"]&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(env){var info={"env":env,"global":{"NaN":NaN,Infinity:Infinity},"global.Math":Math,"asm2wasm":asm2wasmImports};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}if(!Module["wasmBinary"]&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){WebAssembly.instantiateStreaming(fetch(wasmBinaryFile,{credentials:"same-origin"}),info).then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})}else{instantiateArrayBuffer(receiveInstantiatedSource)}return{}}Module["asm"]=function(global,env,providedBuffer){env["memory"]=wasmMemory;env["table"]=wasmTable=new WebAssembly.Table({"initial":186,"maximum":186,"element":"anyfunc"});env["__memory_base"]=1024;env["__table_base"]=0;var exports=createWasm(env);return exports};__ATINIT__.push({func:function(){globalCtors()}});function ___assert_fail(condition,filename,line,func){abort("Assertion failed: "+UTF8ToString(condition)+", at: "+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])}function ___cxa_pure_virtual(){ABORT=true;throw"Pure virtual function called!"}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes}var embind_charCodes=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return function(){"use strict";return body.apply(this,arguments)}}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i<myTypes.length;++i){registerType(myTypes[i],myTypeConverters[i])}}var typeConverters=new Array(dependentTypes.length);var unregisteredTypes=[];var registered=0;dependentTypes.forEach(function(dt,i){if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(function(){typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}}function registerType(rawType,registeredInstance,options){options=options||{};if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}var name=registeredInstance.name;if(!rawType){throwBindingError('type "'+name+'" must have a positive integer typeid pointer')}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError("Cannot register type '"+name+"' twice")}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(function(cb){cb()})}}function __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){++count}}return count}function get_first_emval(){for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){return emval_handle_array[i]}}return null}function init_emval(){Module["count_emval_handles"]=count_emval_handles;Module["get_first_emval"]=get_first_emval}function __emval_register(value){switch(value){case undefined:{return 1}case null:{return 2}case true:{return 3}case false:{return 4}default:{var handle=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;emval_handle_array[handle]={refcount:1,value:value};return handle}}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=emval_handle_array[handle].value;__emval_decref(handle);return rv},"toWireType":function(destructors,value){return __emval_register(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc){var argCount=argTypes.length;if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!")}var isClassMethodFunc=argTypes[1]!==null&&classType!==null;var needsDestructorStack=false;for(var i=1;i<argTypes.length;++i){if(argTypes[i]!==null&&argTypes[i].destructorFunction===undefined){needsDestructorStack=true;break}}var returns=argTypes[0].name!=="void";var argsWired=new Array(argCount-2);return function(){if(arguments.length!==argCount-2){throwBindingError("function "+humanName+" called with "+arguments.length+" arguments, expected "+(argCount-2)+" args!")}var destructors=needsDestructorStack?[]:null;var thisWired;if(isClassMethodFunc){thisWired=argTypes[1].toWireType(destructors,this)}for(var i=0;i<argCount-2;++i){argsWired[i]=argTypes[i+2].toWireType(destructors,arguments[i])}var invokerFuncArgs=isClassMethodFunc?[cppTargetFunc,thisWired]:[cppTargetFunc];var rv=cppInvokerFunc.apply(null,invokerFuncArgs.concat(argsWired));if(needsDestructorStack){runDestructors(destructors)}else{for(var i=isClassMethodFunc?1:2;i<argTypes.length;i++){var param=i===1?thisWired:argsWired[i-2];if(argTypes[i].destructorFunction!==null){argTypes[i].destructorFunction(param)}}}if(returns){return argTypes[0].fromWireType(rv)}}}function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function heap32VectorToArray(count,firstElement){var array=[];for(var i=0;i<count;i++){array.push(HEAP32[(firstElement>>2)+i])}return array}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function embind__requireFunction(signature,rawFunction){signature=readLatin1String(signature);function makeDynCaller(dynCall){return function(){var args=new Array(arguments.length+1);args[0]=rawFunction;for(var i=0;i<arguments.length;i++){args[i+1]=arguments[i]}return dynCall.apply(null,args)}}var fp;if(Module["FUNCTION_TABLE_"+signature]!==undefined){fp=Module["FUNCTION_TABLE_"+signature][rawFunction]}else if(typeof FUNCTION_TABLE!=="undefined"){fp=FUNCTION_TABLE[rawFunction]}else{var dc=Module["dynCall_"+signature];if(dc===undefined){dc=Module["dynCall_"+signature.replace(/f/g,"d")];if(dc===undefined){throwBindingError("No dynCall invoker for signature: "+signature)}}fp=makeDynCaller(dc)}if(typeof fp!=="function"){throwBindingError("unknown function pointer with signature "+signature+": "+rawFunction)}return fp}var UnboundTypeError=undefined;function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv}function throwUnboundTypeError(message,types){var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(message+": "+unboundTypes.map(getTypeName).join([", "]))}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<<bitshift>>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(value<minRange||value>maxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(heap["buffer"],data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var endChar=HEAPU8[value+4+length];var endCharSwap=0;if(endChar!=0){endCharSwap=endChar;HEAPU8[value+4+length]=0}var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(HEAPU8[currentBytePtr]==0){var stringSegment=UTF8ToString(decodeStartPtr);if(str===undefined)str=stringSegment;else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}if(endCharSwap!=0)HEAPU8[value+4+length]=endCharSwap}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[value+4+i])}str=a.join("")}_free(value);return str},"toWireType":function(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var getLength;var valueIsOfTypeString=typeof value==="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){getLength=function(){return lengthBytesUTF8(value)}}else{getLength=function(){return value.length}}var length=getLength();var ptr=_malloc(4+length+1);HEAPU32[ptr>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+4+i]=value[i]}}}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_std_wstring(rawType,charSize,name){name=readLatin1String(name);var getHeap,shift;if(charSize===2){getHeap=function(){return HEAPU16};shift=1}else if(charSize===4){getHeap=function(){return HEAPU32};shift=2}registerType(rawType,{name:name,"fromWireType":function(value){var HEAP=getHeap();var length=HEAPU32[value>>2];var a=new Array(length);var start=value+4>>shift;for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAP[start+i])}_free(value);return a.join("")},"toWireType":function(destructors,value){var HEAP=getHeap();var length=value.length;var ptr=_malloc(4+length*charSize);HEAPU32[ptr>>2]=length;var start=ptr+4>>shift;for(var i=0;i<length;++i){HEAP[start+i]=value.charCodeAt(i)}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_new_object(){return __emval_register({})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_set_property(handle,key,value){handle=requireHandle(handle);key=requireHandle(key);value=requireHandle(value);handle[key]=value}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __emval_take_value(type,argv){type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](argv);return __emval_register(v)}function _abort(){Module["abort"]()}function _emscripten_get_heap_size(){return TOTAL_MEMORY}function abortOnCannotGrowMemory(requestedSize){abort("Cannot enlarge memory arrays to size "+requestedSize+" bytes. Either (1) compile with  -s TOTAL_MEMORY=X  with X higher than the current value "+TOTAL_MEMORY+", (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ")}function emscripten_realloc_buffer(size){var PAGE_MULTIPLE=65536;size=alignUp(size,PAGE_MULTIPLE);var old=Module["buffer"];var oldSize=old.byteLength;try{var result=wasmMemory.grow((size-oldSize)/65536);if(result!==(-1|0)){return Module["buffer"]=wasmMemory.buffer}else{return null}}catch(e){return null}}function _emscripten_resize_heap(requestedSize){var oldSize=_emscripten_get_heap_size();var PAGE_MULTIPLE=65536;var LIMIT=2147483648-PAGE_MULTIPLE;if(requestedSize>LIMIT){return false}var MIN_TOTAL_MEMORY=16777216;var newSize=Math.max(oldSize,MIN_TOTAL_MEMORY);while(newSize<requestedSize){if(newSize<=536870912){newSize=alignUp(2*newSize,PAGE_MULTIPLE)}else{newSize=Math.min(alignUp((3*newSize+2147483648)/4,PAGE_MULTIPLE),LIMIT)}}var replacement=emscripten_realloc_buffer(newSize);if(!replacement||replacement.byteLength!=newSize){return false}updateGlobalBuffer(replacement);updateGlobalBufferViews();TOTAL_MEMORY=newSize;HEAPU32[DYNAMICTOP_PTR>>2]=requestedSize;return true}function _llvm_trap(){abort("trap!")}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest)}function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");InternalError=Module["InternalError"]=extendError(Error,"InternalError");init_emval();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");var asmGlobalArg={};var asmLibraryArg={"f":abort,"e":___assert_fail,"v":___cxa_pure_virtual,"k":___setErrNo,"q":__embind_register_bool,"p":__embind_register_emval,"j":__embind_register_float,"o":__embind_register_function,"g":__embind_register_integer,"d":__embind_register_memory_view,"n":__embind_register_std_string,"z":__embind_register_std_wstring,"y":__embind_register_void,"m":__emval_decref,"l":__emval_incref,"x":__emval_new_cstring,"w":__emval_new_object,"h":__emval_set_property,"i":__emval_take_value,"c":_abort,"u":_emscripten_get_heap_size,"t":_emscripten_memcpy_big,"s":_emscripten_resize_heap,"b":_llvm_trap,"r":abortOnCannotGrowMemory,"a":DYNAMICTOP_PTR};var asm=Module["asm"](asmGlobalArg,asmLibraryArg,buffer);Module["asm"]=asm;var ___errno_location=Module["___errno_location"]=function(){return Module["asm"]["A"].apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return Module["asm"]["B"].apply(null,arguments)};var _free=Module["_free"]=function(){return Module["asm"]["C"].apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return Module["asm"]["D"].apply(null,arguments)};var globalCtors=Module["globalCtors"]=function(){return Module["asm"]["O"].apply(null,arguments)};var dynCall_ii=Module["dynCall_ii"]=function(){return Module["asm"]["E"].apply(null,arguments)};var dynCall_iii=Module["dynCall_iii"]=function(){return Module["asm"]["F"].apply(null,arguments)};var dynCall_iiii=Module["dynCall_iiii"]=function(){return Module["asm"]["G"].apply(null,arguments)};var dynCall_iiiii=Module["dynCall_iiiii"]=function(){return Module["asm"]["H"].apply(null,arguments)};var dynCall_v=Module["dynCall_v"]=function(){return Module["asm"]["I"].apply(null,arguments)};var dynCall_vi=Module["dynCall_vi"]=function(){return Module["asm"]["J"].apply(null,arguments)};var dynCall_viii=Module["dynCall_viii"]=function(){return Module["asm"]["K"].apply(null,arguments)};var dynCall_viiii=Module["dynCall_viiii"]=function(){return Module["asm"]["L"].apply(null,arguments)};var dynCall_viiiii=Module["dynCall_viiiii"]=function(){return Module["asm"]["M"].apply(null,arguments)};var dynCall_viiiiii=Module["dynCall_viiiiii"]=function(){return Module["asm"]["N"].apply(null,arguments)};Module["asm"]=asm;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};function run(args){args=args||Module["arguments"];if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}if(what!==undefined){out(what);err(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;throw"abort("+what+"). Build with -s ASSERTIONS=1 for more info."}Module["abort"]=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}Module["noExitRuntime"]=true;run()})();
+var Module=typeof Module!=="undefined"?Module:{};(function(){var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}Module["arguments"]=[];Module["thisProgram"]="./this.program";Module["quit"]=function(status,toThrow){throw toThrow};Module["preRun"]=[];Module["postRun"]=[];var ENVIRONMENT_IS_WEB=true;var ENVIRONMENT_IS_WORKER=false;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}else{return scriptDirectory+path}}if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}Module["read"]=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){Module["readBinary"]=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}Module["readAsync"]=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)};Module["setWindowTitle"]=function(title){document.title=title}}else{}var out=Module["print"]||(typeof console!=="undefined"?console.log.bind(console):typeof print!=="undefined"?print:null);var err=Module["printErr"]||(typeof printErr!=="undefined"?printErr:typeof console!=="undefined"&&console.warn.bind(console)||out);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=undefined;var asm2wasmImports={"f64-rem":function(x,y){return x%y},"debugger":function(){debugger}};var functionPointers=new Array(0);if(typeof WebAssembly!=="object"){err("no native wasm support detected")}var wasmMemory;var wasmTable;var ABORT=false;var EXITSTATUS=0;var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(u8Array,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(u8Array[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&u8Array.subarray&&UTF8Decoder){return UTF8Decoder.decode(u8Array.subarray(idx,endPtr))}else{var str="";while(idx<endPtr){var u0=u8Array[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|u8Array[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBuffer(buf){Module["buffer"]=buffer=buf}function updateGlobalBufferViews(){Module["HEAP8"]=HEAP8=new Int8Array(buffer);Module["HEAP16"]=HEAP16=new Int16Array(buffer);Module["HEAP32"]=HEAP32=new Int32Array(buffer);Module["HEAPU8"]=HEAPU8=new Uint8Array(buffer);Module["HEAPU16"]=HEAPU16=new Uint16Array(buffer);Module["HEAPU32"]=HEAPU32=new Uint32Array(buffer);Module["HEAPF32"]=HEAPF32=new Float32Array(buffer);Module["HEAPF64"]=HEAPF64=new Float64Array(buffer)}var DYNAMIC_BASE=17328,DYNAMICTOP_PTR=8880;var TOTAL_STACK=8192;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||16777216;if(TOTAL_MEMORY<TOTAL_STACK)err("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+TOTAL_MEMORY+"! (TOTAL_STACK="+TOTAL_STACK+")");if(Module["buffer"]){buffer=Module["buffer"]}else{if(typeof WebAssembly==="object"&&typeof WebAssembly.Memory==="function"){wasmMemory=new WebAssembly.Memory({"initial":TOTAL_MEMORY/WASM_PAGE_SIZE});buffer=wasmMemory.buffer}else{buffer=new ArrayBuffer(TOTAL_MEMORY)}Module["buffer"]=buffer}updateGlobalBufferViews();HEAP32[DYNAMICTOP_PTR>>2]=DYNAMIC_BASE;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Module["dynCall_v"](func)}else{Module["dynCall_vi"](func,callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return String.prototype.startsWith?filename.startsWith(dataURIPrefix):filename.indexOf(dataURIPrefix)===0}var wasmBinaryFile="piex.out.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(Module["wasmBinary"]){return new Uint8Array(Module["wasmBinary"])}if(Module["readBinary"]){return Module["readBinary"](wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!Module["wasmBinary"]&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return new Promise(function(resolve,reject){resolve(getBinary())})}function createWasm(env){var info={"env":env,"global":{"NaN":NaN,Infinity:Infinity},"global.Math":Math,"asm2wasm":asm2wasmImports};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}if(!Module["wasmBinary"]&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){WebAssembly.instantiateStreaming(fetch(wasmBinaryFile,{credentials:"same-origin"}),info).then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");instantiateArrayBuffer(receiveInstantiatedSource)})}else{instantiateArrayBuffer(receiveInstantiatedSource)}return{}}Module["asm"]=function(global,env,providedBuffer){env["memory"]=wasmMemory;env["table"]=wasmTable=new WebAssembly.Table({"initial":186,"maximum":186,"element":"anyfunc"});env["__memory_base"]=1024;env["__table_base"]=0;var exports=createWasm(env);return exports};__ATINIT__.push({func:function(){globalCtors()}});function ___assert_fail(condition,filename,line,func){abort("Assertion failed: "+UTF8ToString(condition)+", at: "+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])}function ___cxa_pure_virtual(){ABORT=true;throw"Pure virtual function called!"}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes}var embind_charCodes=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return function(){"use strict";return body.apply(this,arguments)}}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i<myTypes.length;++i){registerType(myTypes[i],myTypeConverters[i])}}var typeConverters=new Array(dependentTypes.length);var unregisteredTypes=[];var registered=0;dependentTypes.forEach(function(dt,i){if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(function(){typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}}function registerType(rawType,registeredInstance,options){options=options||{};if(!("argPackAdvance"in registeredInstance)){throw new TypeError("registerType registeredInstance requires argPackAdvance")}var name=registeredInstance.name;if(!rawType){throwBindingError('type "'+name+'" must have a positive integer typeid pointer')}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError("Cannot register type '"+name+"' twice")}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(function(cb){cb()})}}function __embind_register_bool(rawType,name,size,trueValue,falseValue){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(wt){return!!wt},"toWireType":function(destructors,o){return o?trueValue:falseValue},"argPackAdvance":8,"readValueFromPointer":function(pointer){var heap;if(size===1){heap=HEAP8}else if(size===2){heap=HEAP16}else if(size===4){heap=HEAP32}else{throw new TypeError("Unknown boolean type size: "+name)}return this["fromWireType"](heap[pointer>>shift])},destructorFunction:null})}var emval_free_list=[];var emval_handle_array=[{},{value:undefined},{value:null},{value:true},{value:false}];function __emval_decref(handle){if(handle>4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){++count}}return count}function get_first_emval(){for(var i=5;i<emval_handle_array.length;++i){if(emval_handle_array[i]!==undefined){return emval_handle_array[i]}}return null}function init_emval(){Module["count_emval_handles"]=count_emval_handles;Module["get_first_emval"]=get_first_emval}function __emval_register(value){switch(value){case undefined:{return 1}case null:{return 2}case true:{return 3}case false:{return 4}default:{var handle=emval_free_list.length?emval_free_list.pop():emval_handle_array.length;emval_handle_array[handle]={refcount:1,value:value};return handle}}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}function __embind_register_emval(rawType,name){name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(handle){var rv=emval_handle_array[handle].value;__emval_decref(handle);return rv},"toWireType":function(destructors,value){return __emval_register(value)},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:null})}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function craftInvokerFunction(humanName,argTypes,classType,cppInvokerFunc,cppTargetFunc){var argCount=argTypes.length;if(argCount<2){throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!")}var isClassMethodFunc=argTypes[1]!==null&&classType!==null;var needsDestructorStack=false;for(var i=1;i<argTypes.length;++i){if(argTypes[i]!==null&&argTypes[i].destructorFunction===undefined){needsDestructorStack=true;break}}var returns=argTypes[0].name!=="void";var argsWired=new Array(argCount-2);return function(){if(arguments.length!==argCount-2){throwBindingError("function "+humanName+" called with "+arguments.length+" arguments, expected "+(argCount-2)+" args!")}var destructors=needsDestructorStack?[]:null;var thisWired;if(isClassMethodFunc){thisWired=argTypes[1].toWireType(destructors,this)}for(var i=0;i<argCount-2;++i){argsWired[i]=argTypes[i+2].toWireType(destructors,arguments[i])}var invokerFuncArgs=isClassMethodFunc?[cppTargetFunc,thisWired]:[cppTargetFunc];var rv=cppInvokerFunc.apply(null,invokerFuncArgs.concat(argsWired));if(needsDestructorStack){runDestructors(destructors)}else{for(var i=isClassMethodFunc?1:2;i<argTypes.length;i++){var param=i===1?thisWired:argsWired[i-2];if(argTypes[i].destructorFunction!==null){argTypes[i].destructorFunction(param)}}}if(returns){return argTypes[0].fromWireType(rv)}}}function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function heap32VectorToArray(count,firstElement){var array=[];for(var i=0;i<count;i++){array.push(HEAP32[(firstElement>>2)+i])}return array}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function embind__requireFunction(signature,rawFunction){signature=readLatin1String(signature);function makeDynCaller(dynCall){return function(){var args=new Array(arguments.length+1);args[0]=rawFunction;for(var i=0;i<arguments.length;i++){args[i+1]=arguments[i]}return dynCall.apply(null,args)}}var fp;if(Module["FUNCTION_TABLE_"+signature]!==undefined){fp=Module["FUNCTION_TABLE_"+signature][rawFunction]}else if(typeof FUNCTION_TABLE!=="undefined"){fp=FUNCTION_TABLE[rawFunction]}else{var dc=Module["dynCall_"+signature];if(dc===undefined){dc=Module["dynCall_"+signature.replace(/f/g,"d")];if(dc===undefined){throwBindingError("No dynCall invoker for signature: "+signature)}}fp=makeDynCaller(dc)}if(typeof fp!=="function"){throwBindingError("unknown function pointer with signature "+signature+": "+rawFunction)}return fp}var UnboundTypeError=undefined;function getTypeName(type){var ptr=___getTypeName(type);var rv=readLatin1String(ptr);_free(ptr);return rv}function throwUnboundTypeError(message,types){var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(message+": "+unboundTypes.map(getTypeName).join([", "]))}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<<bitshift>>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(value<minRange||value>maxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(heap["buffer"],data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var endChar=HEAPU8[value+4+length];var endCharSwap=0;if(endChar!=0){endCharSwap=endChar;HEAPU8[value+4+length]=0}var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(HEAPU8[currentBytePtr]==0){var stringSegment=UTF8ToString(decodeStartPtr);if(str===undefined)str=stringSegment;else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}if(endCharSwap!=0)HEAPU8[value+4+length]=endCharSwap}else{var a=new Array(length);for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAPU8[value+4+i])}str=a.join("")}_free(value);return str},"toWireType":function(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var getLength;var valueIsOfTypeString=typeof value==="string";if(!(valueIsOfTypeString||value instanceof Uint8Array||value instanceof Uint8ClampedArray||value instanceof Int8Array)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){getLength=function(){return lengthBytesUTF8(value)}}else{getLength=function(){return value.length}}var length=getLength();var ptr=_malloc(4+length+1);HEAPU32[ptr>>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i<length;++i){var charCode=value.charCodeAt(i);if(charCode>255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i<length;++i){HEAPU8[ptr+4+i]=value[i]}}}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_std_wstring(rawType,charSize,name){name=readLatin1String(name);var getHeap,shift;if(charSize===2){getHeap=function(){return HEAPU16};shift=1}else if(charSize===4){getHeap=function(){return HEAPU32};shift=2}registerType(rawType,{name:name,"fromWireType":function(value){var HEAP=getHeap();var length=HEAPU32[value>>2];var a=new Array(length);var start=value+4>>shift;for(var i=0;i<length;++i){a[i]=String.fromCharCode(HEAP[start+i])}_free(value);return a.join("")},"toWireType":function(destructors,value){var HEAP=getHeap();var length=value.length;var ptr=_malloc(4+length*charSize);HEAPU32[ptr>>2]=length;var start=ptr+4>>shift;for(var i=0;i<length;++i){HEAP[start+i]=value.charCodeAt(i)}if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_new_object(){return __emval_register({})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_set_property(handle,key,value){handle=requireHandle(handle);key=requireHandle(key);value=requireHandle(value);handle[key]=value}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __emval_take_value(type,argv){type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](argv);return __emval_register(v)}function _abort(){Module["abort"]()}function _emscripten_get_heap_size(){return TOTAL_MEMORY}function abortOnCannotGrowMemory(requestedSize){abort("Cannot enlarge memory arrays to size "+requestedSize+" bytes. Either (1) compile with  -s TOTAL_MEMORY=X  with X higher than the current value "+TOTAL_MEMORY+", (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ")}function emscripten_realloc_buffer(size){var PAGE_MULTIPLE=65536;size=alignUp(size,PAGE_MULTIPLE);var old=Module["buffer"];var oldSize=old.byteLength;try{var result=wasmMemory.grow((size-oldSize)/65536);if(result!==(-1|0)){return Module["buffer"]=wasmMemory.buffer}else{return null}}catch(e){return null}}function _emscripten_resize_heap(requestedSize){var oldSize=_emscripten_get_heap_size();var PAGE_MULTIPLE=65536;var LIMIT=2147483648-PAGE_MULTIPLE;if(requestedSize>LIMIT){return false}var MIN_TOTAL_MEMORY=16777216;var newSize=Math.max(oldSize,MIN_TOTAL_MEMORY);while(newSize<requestedSize){if(newSize<=536870912){newSize=alignUp(2*newSize,PAGE_MULTIPLE)}else{newSize=Math.min(alignUp((3*newSize+2147483648)/4,PAGE_MULTIPLE),LIMIT)}}var replacement=emscripten_realloc_buffer(newSize);if(!replacement||replacement.byteLength!=newSize){return false}updateGlobalBuffer(replacement);updateGlobalBufferViews();TOTAL_MEMORY=newSize;HEAPU32[DYNAMICTOP_PTR>>2]=requestedSize;return true}function _llvm_trap(){abort("trap!")}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest)}function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");InternalError=Module["InternalError"]=extendError(Error,"InternalError");init_emval();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");var asmGlobalArg={};var asmLibraryArg={"f":abort,"e":___assert_fail,"v":___cxa_pure_virtual,"k":___setErrNo,"q":__embind_register_bool,"p":__embind_register_emval,"j":__embind_register_float,"o":__embind_register_function,"g":__embind_register_integer,"d":__embind_register_memory_view,"n":__embind_register_std_string,"z":__embind_register_std_wstring,"y":__embind_register_void,"m":__emval_decref,"l":__emval_incref,"x":__emval_new_cstring,"w":__emval_new_object,"h":__emval_set_property,"i":__emval_take_value,"c":_abort,"u":_emscripten_get_heap_size,"t":_emscripten_memcpy_big,"s":_emscripten_resize_heap,"b":_llvm_trap,"r":abortOnCannotGrowMemory,"a":DYNAMICTOP_PTR};var asm=Module["asm"](asmGlobalArg,asmLibraryArg,buffer);Module["asm"]=asm;var ___errno_location=Module["___errno_location"]=function(){return Module["asm"]["A"].apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return Module["asm"]["B"].apply(null,arguments)};var _free=Module["_free"]=function(){return Module["asm"]["C"].apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return Module["asm"]["D"].apply(null,arguments)};var globalCtors=Module["globalCtors"]=function(){return Module["asm"]["O"].apply(null,arguments)};var dynCall_ii=Module["dynCall_ii"]=function(){return Module["asm"]["E"].apply(null,arguments)};var dynCall_iii=Module["dynCall_iii"]=function(){return Module["asm"]["F"].apply(null,arguments)};var dynCall_iiii=Module["dynCall_iiii"]=function(){return Module["asm"]["G"].apply(null,arguments)};var dynCall_iiiii=Module["dynCall_iiiii"]=function(){return Module["asm"]["H"].apply(null,arguments)};var dynCall_v=Module["dynCall_v"]=function(){return Module["asm"]["I"].apply(null,arguments)};var dynCall_vi=Module["dynCall_vi"]=function(){return Module["asm"]["J"].apply(null,arguments)};var dynCall_viii=Module["dynCall_viii"]=function(){return Module["asm"]["K"].apply(null,arguments)};var dynCall_viiii=Module["dynCall_viiii"]=function(){return Module["asm"]["L"].apply(null,arguments)};var dynCall_viiiii=Module["dynCall_viiiii"]=function(){return Module["asm"]["M"].apply(null,arguments)};var dynCall_viiiiii=Module["dynCall_viiiiii"]=function(){return Module["asm"]["N"].apply(null,arguments)};Module["asm"]=asm;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};function run(args){args=args||Module["arguments"];if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}if(what!==undefined){out(what);err(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;throw"abort("+what+"). Build with -s ASSERTIONS=1 for more info."}Module["abort"]=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}Module["noExitRuntime"]=true;run()})();
diff --git a/ui/file_manager/image_loader/piex/piex.out.wasm b/ui/file_manager/image_loader/piex/piex.out.wasm
index 99b45e0..b67ba2f0 100644
--- a/ui/file_manager/image_loader/piex/piex.out.wasm
+++ b/ui/file_manager/image_loader/piex/piex.out.wasm
Binary files differ
diff --git a/ui/file_manager/image_loader/piex/tests.html b/ui/file_manager/image_loader/piex/tests.html
index 5f0c6ae8..36325ea5a 100644
--- a/ui/file_manager/image_loader/piex/tests.html
+++ b/ui/file_manager/image_loader/piex/tests.html
@@ -214,10 +214,6 @@
       return renderRGB(name, image);
   }
 
-  function removeNullBytes(string) {
-    return (string || '').replace(/\0/g, '');
-  }
-
   window.Module = {
     onRuntimeInitialized: () => document.title = 'READY',
   };
@@ -256,9 +252,6 @@
       let thumb = imageBuffer.thumbnail();
       imageBuffer.close();
       time = window.performance.now() - time;
-      const maker = removeNullBytes(result.maker);
-      const model = removeNullBytes(result.model);
-      console.log('test:', image, maker, model);
       window.images_ = 0;
       renderResult(image, preview);
       renderResult(image, thumb);
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index 72bbab5..e902cc2e 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -673,16 +673,6 @@
     typeText: 'Plain text',
   }),
 
-  plainText: new TestEntryInfo({
-    type: EntryType.FILE,
-    sourceFileName: 'plaintext',
-    targetPath: 'plaintext',
-    lastModifiedTime: 'Sep 4, 1998, 12:34 PM',
-    nameText: 'plaintext',
-    sizeText: '32 bytes',
-    typeText: 'Plain text',
-  }),
-
   tallHtml: new TestEntryInfo({
     type: EntryType.FILE,
     sourceFileName: 'tall.html',
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index 0529f8b..46a34ee 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -64,8 +64,6 @@
     "host/wayland_pointer.h",
     "host/wayland_screen.cc",
     "host/wayland_screen.h",
-    "host/wayland_shared_memory_buffer_manager.cc",
-    "host/wayland_shared_memory_buffer_manager.h",
     "host/wayland_shm.cc",
     "host/wayland_shm.h",
     "host/wayland_shm_buffer.cc",
@@ -103,6 +101,7 @@
     "//base",
     "//build/config/linux/libdrm",
     "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
     "//skia",
     "//third_party/wayland:wayland_client",
     "//third_party/wayland-protocols:linux_dmabuf_protocol",
diff --git a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
index 847c0fe9..90e4e49 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
+++ b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
@@ -36,7 +36,7 @@
 
 GbmPixmapWayland::~GbmPixmapWayland() {
   if (gbm_bo_ && widget_ != gfx::kNullAcceleratedWidget)
-    connection_->DestroyZwpLinuxDmabuf(widget_, GetUniqueId());
+    connection_->DestroyBuffer(widget_, GetUniqueId());
 }
 
 bool GbmPixmapWayland::InitializeBuffer(gfx::Size size,
@@ -83,7 +83,7 @@
   // The pixmap can be created as a staging buffer and not be mapped to any of
   // the existing widgets.
   if (widget_ != gfx::kNullAcceleratedWidget)
-    CreateZwpLinuxDmabuf();
+    CreateDmabufBasedBuffer();
   return true;
 }
 
@@ -160,7 +160,7 @@
   return handle;
 }
 
-void GbmPixmapWayland::CreateZwpLinuxDmabuf() {
+void GbmPixmapWayland::CreateDmabufBasedBuffer() {
   uint64_t modifier = gbm_bo_->GetFormatModifier();
 
   std::vector<uint32_t> strides;
@@ -179,11 +179,9 @@
     PLOG(FATAL) << "dup";
     return;
   }
-  base::File file(fd.release());
-
   // Asks Wayland to create a wl_buffer based on the |file| fd.
-  connection_->CreateZwpLinuxDmabuf(
-      widget_, std::move(file), GetBufferSize(), strides, offsets, modifiers,
+  connection_->CreateDmabufBasedBuffer(
+      widget_, std::move(fd), GetBufferSize(), strides, offsets, modifiers,
       gbm_bo_->GetFormat(), plane_count, GetUniqueId());
 }
 
diff --git a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h
index 9cb9f8f0..e6f4e53 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h
+++ b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.h
@@ -53,7 +53,7 @@
   ~GbmPixmapWayland() override;
 
   // Asks Wayland to create a dmabuf based wl_buffer.
-  void CreateZwpLinuxDmabuf();
+  void CreateDmabufBasedBuffer();
 
   // gbm_bo wrapper for struct gbm_bo.
   std::unique_ptr<GbmBuffer> gbm_bo_;
diff --git a/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc b/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc
index 420c047..baa93a1 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc
@@ -31,7 +31,7 @@
 
 WaylandCanvasSurface::~WaylandCanvasSurface() {
   if (sk_surface_)
-    connection_->DestroyShmBuffer(widget_, buffer_id_);
+    connection_->DestroyBuffer(widget_, buffer_id_);
 }
 
 sk_sp<SkSurface> WaylandCanvasSurface::GetSurface() {
@@ -53,9 +53,8 @@
       base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
           std::move(shm_region));
   base::subtle::ScopedFDPair fd_pair = platform_shm.PassPlatformHandle();
-  base::File file(fd_pair.fd.release());
-  connection_->CreateShmBufferForWidget(widget_, std::move(file), length, size_,
-                                        ++buffer_id_);
+  connection_->CreateShmBasedBuffer(widget_, std::move(fd_pair.fd), length,
+                                    size_, ++buffer_id_);
 
   auto shm_mapping_on_heap =
       std::make_unique<base::WritableSharedMemoryMapping>(
@@ -80,13 +79,15 @@
   // smaller than the old size).
   if (sk_surface_) {
     sk_surface_.reset();
-    connection_->DestroyShmBuffer(widget_, buffer_id_);
+    connection_->DestroyBuffer(widget_, buffer_id_);
   }
   size_ = viewport_size;
 }
 
 void WaylandCanvasSurface::PresentCanvas(const gfx::Rect& damage) {
-  connection_->PresentShmBufferForWidget(widget_, damage, buffer_id_);
+  // TODO(https://crbug.com/930664): add support for submission and presentation
+  // callbacks.
+  connection_->CommitBuffer(widget_, buffer_id_, damage);
 }
 
 std::unique_ptr<gfx::VSyncProvider>
diff --git a/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc
index 6ca46a9e..e29b645a 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc
@@ -9,18 +9,15 @@
 #include "base/bind.h"
 #include "base/process/process.h"
 #include "mojo/public/cpp/bindings/associated_interface_ptr.h"
-#include "third_party/khronos/EGL/egl.h"
+#include "mojo/public/cpp/system/platform_handle.h"
 #include "ui/ozone/common/linux/drm_util_linux.h"
 #include "ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h"
 #include "ui/ozone/platform/wayland/gpu/wayland_surface_factory.h"
-#include "ui/ozone/platform/wayland/host/wayland_connection.h"
 
 namespace ui {
 
-WaylandConnectionProxy::WaylandConnectionProxy(WaylandConnection* connection,
-                                               WaylandSurfaceFactory* factory)
-    : connection_(connection),
-      factory_(factory),
+WaylandConnectionProxy::WaylandConnectionProxy(WaylandSurfaceFactory* factory)
+    : factory_(factory),
       associated_binding_(this),
       gpu_thread_runner_(base::ThreadTaskRunnerHandle::Get()) {}
 
@@ -68,9 +65,9 @@
     surface->OnPresentation(buffer_id, feedback);
 }
 
-void WaylandConnectionProxy::CreateZwpLinuxDmabuf(
+void WaylandConnectionProxy::CreateDmabufBasedBuffer(
     gfx::AcceleratedWidget widget,
-    base::File file,
+    base::ScopedFD dmabuf_fd,
     gfx::Size size,
     const std::vector<uint32_t>& strides,
     const std::vector<uint32_t>& offsets,
@@ -83,16 +80,60 @@
   // ensure proper functionality.
   gpu_thread_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&WaylandConnectionProxy::CreateZwpLinuxDmabufInternal,
-                     base::Unretained(this), widget, std::move(file),
+      base::BindOnce(&WaylandConnectionProxy::CreateDmabufBasedBufferInternal,
+                     base::Unretained(this), widget, std::move(dmabuf_fd),
                      std::move(size), std::move(strides), std::move(offsets),
                      std::move(modifiers), current_format, planes_count,
                      buffer_id));
 }
 
-void WaylandConnectionProxy::CreateZwpLinuxDmabufInternal(
+void WaylandConnectionProxy::CreateShmBasedBuffer(gfx::AcceleratedWidget widget,
+                                                  base::ScopedFD shm_fd,
+                                                  size_t length,
+                                                  const gfx::Size size,
+                                                  uint32_t buffer_id) {
+  DCHECK(gpu_thread_runner_);
+  // Do a mojo call on the GpuMainThread instead of the io child thread to
+  // ensure proper functionality.
+  gpu_thread_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&WaylandConnectionProxy::CreateShmBasedBufferInternal,
+                     base::Unretained(this), widget, std::move(shm_fd), length,
+                     std::move(size), buffer_id));
+}
+
+void WaylandConnectionProxy::CommitBuffer(gfx::AcceleratedWidget widget,
+                                          uint32_t buffer_id,
+                                          const gfx::Rect& damage_region) {
+  DCHECK(gpu_thread_runner_);
+
+  // Do a mojo call on the GpuMainThread instead of the io child thread to
+  // ensure proper functionality.
+  gpu_thread_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&WaylandConnectionProxy::CommitBufferInternal,
+                     base::Unretained(this), widget, buffer_id, damage_region));
+}
+
+void WaylandConnectionProxy::DestroyBuffer(gfx::AcceleratedWidget widget,
+                                           uint32_t buffer_id) {
+  DCHECK(gpu_thread_runner_);
+
+  // Do a mojo call on the GpuMainThread instead of the io child thread to
+  // ensure proper functionality.
+  gpu_thread_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&WaylandConnectionProxy::DestroyBufferInternal,
+                                base::Unretained(this), widget, buffer_id));
+}
+
+void WaylandConnectionProxy::AddBindingWaylandConnectionClient(
+    ozone::mojom::WaylandConnectionClientRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void WaylandConnectionProxy::CreateDmabufBasedBufferInternal(
     gfx::AcceleratedWidget widget,
-    base::File file,
+    base::ScopedFD dmabuf_fd,
     gfx::Size size,
     const std::vector<uint32_t>& strides,
     const std::vector<uint32_t>& offsets,
@@ -109,143 +150,52 @@
 
   DCHECK(gpu_thread_runner_->BelongsToCurrentThread());
   DCHECK(wc_ptr_);
-  wc_ptr_->CreateZwpLinuxDmabuf(widget, std::move(file), size, strides, offsets,
-                                modifiers, current_format, planes_count,
-                                buffer_id);
+
+  wc_ptr_->CreateDmabufBasedBuffer(
+      widget,
+      mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(dmabuf_fd))),
+      size, strides, offsets, modifiers, current_format, planes_count,
+      buffer_id);
 }
 
-void WaylandConnectionProxy::DestroyZwpLinuxDmabuf(
+void WaylandConnectionProxy::CreateShmBasedBufferInternal(
     gfx::AcceleratedWidget widget,
-    uint32_t buffer_id) {
-  DCHECK(gpu_thread_runner_);
-
-  // Do a mojo call on the GpuMainThread instead of the io child thread to
-  // ensure proper functionality.
-  gpu_thread_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&WaylandConnectionProxy::DestroyZwpLinuxDmabufInternal,
-                     base::Unretained(this), widget, buffer_id));
-}
-
-void WaylandConnectionProxy::DestroyZwpLinuxDmabufInternal(
-    gfx::AcceleratedWidget widget,
+    base::ScopedFD shm_fd,
+    size_t length,
+    const gfx::Size size,
     uint32_t buffer_id) {
   DCHECK(gpu_thread_runner_->BelongsToCurrentThread());
-  DCHECK(wc_ptr_);
 
-  wc_ptr_->DestroyZwpLinuxDmabuf(widget, buffer_id);
+  // The interface pointer is passed on an IO child thread, which is different
+  // from the thread, which is used to call these methods. Thus, rebind the
+  // interface on a first call to ensure mojo calls will always happen on a
+  // sequence we want.
+  if (!wc_ptr_.is_bound())
+    BindHostInterface();
+
+  DCHECK(wc_ptr_);
+  wc_ptr_->CreateShmBasedBuffer(
+      widget, mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(shm_fd))),
+      length, size, buffer_id);
 }
 
-void WaylandConnectionProxy::CommitBuffer(gfx::AcceleratedWidget widget,
-                                          uint32_t buffer_id,
-                                          const gfx::Rect& damage_region) {
+void WaylandConnectionProxy::CommitBufferInternal(
+    gfx::AcceleratedWidget widget,
+    uint32_t buffer_id,
+    const gfx::Rect& damage_region) {
   DCHECK(gpu_thread_runner_->BelongsToCurrentThread());
   DCHECK(wc_ptr_);
 
   wc_ptr_->CommitBuffer(widget, buffer_id, damage_region);
 }
 
-void WaylandConnectionProxy::CreateShmBufferForWidget(
-    gfx::AcceleratedWidget widget,
-    base::File file,
-    size_t length,
-    const gfx::Size size,
-    uint32_t buffer_id) {
-  DCHECK(gpu_thread_runner_);
-  // Do a mojo call on the GpuMainThread instead of the io child thread to
-  // ensure proper functionality.
-  gpu_thread_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&WaylandConnectionProxy::CreateShmBufferInternal,
-                     base::Unretained(this), widget, std::move(file), length,
-                     std::move(size), buffer_id));
-}
-
-void WaylandConnectionProxy::CreateShmBufferInternal(
-    gfx::AcceleratedWidget widget,
-    base::File file,
-    size_t length,
-    const gfx::Size size,
-    uint32_t buffer_id) {
-  DCHECK(gpu_thread_runner_->BelongsToCurrentThread());
-  if (!wc_ptr_.is_bound())
-    BindHostInterface();
-
-  DCHECK(wc_ptr_);
-  wc_ptr_->CreateShmBufferForWidget(widget, std::move(file), length, size,
-                                    buffer_id);
-}
-
-void WaylandConnectionProxy::PresentShmBufferForWidget(
-    gfx::AcceleratedWidget widget,
-    const gfx::Rect& damage,
-    uint32_t buffer_id) {
-  DCHECK(gpu_thread_runner_);
-  // Do a mojo call on the GpuMainThread instead of the io child thread to
-  // ensure proper functionality.
-  gpu_thread_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&WaylandConnectionProxy::PresentShmBufferForWidgetInternal,
-                     base::Unretained(this), widget, damage, buffer_id));
-}
-
-void WaylandConnectionProxy::PresentShmBufferForWidgetInternal(
-    gfx::AcceleratedWidget widget,
-    const gfx::Rect& damage,
-    uint32_t buffer_id) {
-  DCHECK(gpu_thread_runner_->BelongsToCurrentThread());
-  DCHECK(wc_ptr_);
-  wc_ptr_->PresentShmBufferForWidget(widget, damage, buffer_id);
-}
-
-void WaylandConnectionProxy::DestroyShmBuffer(gfx::AcceleratedWidget widget,
-                                              uint32_t buffer_id) {
-  DCHECK(gpu_thread_runner_);
-  // Do a mojo call on the GpuMainThread instead of the io child thread to
-  // ensure proper functionality.
-  gpu_thread_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&WaylandConnectionProxy::DestroyShmBufferInternal,
-                     base::Unretained(this), widget, buffer_id));
-}
-
-void WaylandConnectionProxy::DestroyShmBufferInternal(
+void WaylandConnectionProxy::DestroyBufferInternal(
     gfx::AcceleratedWidget widget,
     uint32_t buffer_id) {
   DCHECK(gpu_thread_runner_->BelongsToCurrentThread());
   DCHECK(wc_ptr_);
-  wc_ptr_->DestroyShmBuffer(widget, buffer_id);
-}
 
-WaylandWindow* WaylandConnectionProxy::GetWindow(
-    gfx::AcceleratedWidget widget) const {
-  if (connection_)
-    return connection_->GetWindow(widget);
-  return nullptr;
-}
-
-void WaylandConnectionProxy::ScheduleFlush() {
-  if (connection_)
-    return connection_->ScheduleFlush();
-
-  LOG(FATAL) << "Flush mustn't be called directly on the WaylandConnection, "
-                "when multi-process moe is used";
-}
-
-intptr_t WaylandConnectionProxy::Display() const {
-  if (connection_)
-    return reinterpret_cast<intptr_t>(connection_->display());
-
-#if defined(WAYLAND_GBM)
-  return EGL_DEFAULT_DISPLAY;
-#else
-  return 0;
-#endif
-}
-
-void WaylandConnectionProxy::AddBindingWaylandConnectionClient(
-    ozone::mojom::WaylandConnectionClientRequest request) {
-  bindings_.AddBinding(this, std::move(request));
+  wc_ptr_->DestroyBuffer(widget, buffer_id);
 }
 
 void WaylandConnectionProxy::BindHostInterface() {
diff --git a/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h
index 6d81169..00eca36 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h
+++ b/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h
@@ -13,9 +13,7 @@
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/gl/gl_surface.h"
 #include "ui/ozone/platform/wayland/common/wayland_util.h"
-#include "ui/ozone/platform/wayland/host/wayland_connection.h"
 #include "ui/ozone/public/interfaces/wayland/wayland_connection.mojom.h"
 
 #if defined(WAYLAND_GBM)
@@ -33,17 +31,14 @@
 class WaylandSurfaceFactory;
 class WaylandWindow;
 
-// Provides a proxy connection to a WaylandConnection object on
-// browser process side. When in multi-process mode, this is used to create
-// Wayland dmabufs and ask it to do commits. When in single process mode,
-// this class just forwards calls directly to WaylandConnection.
+// Forwards calls through an associated mojo connection to WaylandBufferManager
+// on the browser process side.
 //
 // It's guaranteed that WaylandConnectionProxy makes mojo calls on the right
 // sequence.
 class WaylandConnectionProxy : public ozone::mojom::WaylandConnectionClient {
  public:
-  WaylandConnectionProxy(WaylandConnection* connection,
-                         WaylandSurfaceFactory* factory);
+  explicit WaylandConnectionProxy(WaylandSurfaceFactory* factory);
   ~WaylandConnectionProxy() override;
 
   // WaylandConnectionProxy overrides:
@@ -59,23 +54,28 @@
                       uint32_t buffer_id,
                       const gfx::PresentationFeedback& feedback) override;
 
-  // Methods, which must be used when GPU is hosted on a different process
-  // aka gpu process.
+  // Methods, which can be used when in both in-process-gpu and out of process
+  // modes. These calls are forwarded to the browser process through the
+  // WaylandConnection mojo interface. See more in
+  // ui/ozone/public/interfaces/wayland/wayland_connection.mojom.
   //
-  // Asks Wayland to create a wl_buffer based on a shared buffer file
-  // descriptor backed (gbm_bo).
-  void CreateZwpLinuxDmabuf(gfx::AcceleratedWidget widget,
-                            base::File file,
-                            gfx::Size size,
-                            const std::vector<uint32_t>& strides,
-                            const std::vector<uint32_t>& offsets,
-                            const std::vector<uint64_t>& modifiers,
-                            uint32_t current_format,
-                            uint32_t planes_count,
-                            uint32_t buffer_id);
+  // Asks Wayland to create generic dmabuf-based wl_buffer.
+  void CreateDmabufBasedBuffer(gfx::AcceleratedWidget widget,
+                               base::ScopedFD dmabuf_fd,
+                               gfx::Size size,
+                               const std::vector<uint32_t>& strides,
+                               const std::vector<uint32_t>& offsets,
+                               const std::vector<uint64_t>& modifiers,
+                               uint32_t current_format,
+                               uint32_t planes_count,
+                               uint32_t buffer_id);
 
-  // Asks Wayland to destroy a wl_buffer.
-  void DestroyZwpLinuxDmabuf(gfx::AcceleratedWidget widget, uint32_t buffer_id);
+  // Asks Wayland to create a shared memory based wl_buffer.
+  void CreateShmBasedBuffer(gfx::AcceleratedWidget widget,
+                            base::ScopedFD shm_fd,
+                            size_t length,
+                            const gfx::Size size,
+                            uint32_t buffer_id);
 
   // Asks Wayland to find a wl_buffer with the |buffer_id| and attach the
   // buffer to the WaylandWindow's surface, which backs the following |widget|.
@@ -91,6 +91,9 @@
                     uint32_t buffer_id,
                     const gfx::Rect& damage_region);
 
+  // Asks Wayland to destroy a wl_buffer.
+  void DestroyBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id);
+
 #if defined(WAYLAND_GBM)
   // Returns a gbm_device based on a DRM render node.
   GbmDevice* gbm_device() const { return gbm_device_.get(); }
@@ -99,76 +102,32 @@
   }
 #endif
 
-  // Methods that are used to manage shared buffers when software rendering is
-  // used:
-  //
-  // Asks Wayland to create a buffer based on shared memory |file| handle for
-  // specific |widget|. There can be only one buffer per widget.
-  void CreateShmBufferForWidget(gfx::AcceleratedWidget widget,
-                                base::File file,
-                                size_t length,
-                                const gfx::Size size,
-                                uint32_t buffer_id);
-
-  // Asks to damage and commit previously created buffer for the |widget|.
-  void PresentShmBufferForWidget(gfx::AcceleratedWidget widget,
-                                 const gfx::Rect& damage,
-                                 uint32_t buffer_id);
-
-  // Asks to destroy shared memory based buffer for the |widget|.
-  void DestroyShmBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id);
-
-  // Methods, which must be used when a single process mode is used (GPU is
-  // hosted in the browser process).
-  //
-  // Return a WaylandWindow based on the |widget|.
-  WaylandWindow* GetWindow(gfx::AcceleratedWidget widget) const;
-  // Schedule flush in the Wayland message loop.
-  void ScheduleFlush();
-
-  // Methods, which can be used with both single- and multi-process modes.
-  //
-  // Returns a pointer to native display. When used in single process mode,
-  // a wl_display pointer is returned. For the the mode, when there are GPU
-  // and browser processes, EGL_DEFAULT_DISPLAY is returned.
-  intptr_t Display() const;
-
   // Adds a WaylandConnectionClient binding.
   void AddBindingWaylandConnectionClient(
       ozone::mojom::WaylandConnectionClientRequest request);
 
-  WaylandConnection* connection() const { return connection_; }
-
  private:
-  void CreateZwpLinuxDmabufInternal(gfx::AcceleratedWidget widget,
-                                    base::File file,
-                                    gfx::Size size,
-                                    const std::vector<uint32_t>& strides,
-                                    const std::vector<uint32_t>& offsets,
-                                    const std::vector<uint64_t>& modifiers,
-                                    uint32_t current_format,
-                                    uint32_t planes_count,
+  void CreateDmabufBasedBufferInternal(gfx::AcceleratedWidget widget,
+                                       base::ScopedFD dmabuf_fd,
+                                       gfx::Size size,
+                                       const std::vector<uint32_t>& strides,
+                                       const std::vector<uint32_t>& offsets,
+                                       const std::vector<uint64_t>& modifiers,
+                                       uint32_t current_format,
+                                       uint32_t planes_count,
+                                       uint32_t buffer_id);
+  void CreateShmBasedBufferInternal(gfx::AcceleratedWidget widget,
+                                    base::ScopedFD shm_fd,
+                                    size_t length,
+                                    const gfx::Size size,
                                     uint32_t buffer_id);
-  void DestroyZwpLinuxDmabufInternal(gfx::AcceleratedWidget widget,
-                                     uint32_t buffer_id);
-
-  void CreateShmBufferInternal(gfx::AcceleratedWidget widget,
-                               base::File file,
-                               size_t length,
-                               const gfx::Size size,
-                               uint32_t buffer_id);
-  void PresentShmBufferForWidgetInternal(gfx::AcceleratedWidget widget,
-                                         const gfx::Rect& damage,
-                                         uint32_t buffer_id);
-  void DestroyShmBufferInternal(gfx::AcceleratedWidget widget,
-                                uint32_t buffer_id);
+  void CommitBufferInternal(gfx::AcceleratedWidget widget,
+                            uint32_t buffer_id,
+                            const gfx::Rect& damage_region);
+  void DestroyBufferInternal(gfx::AcceleratedWidget widget, uint32_t buffer_id);
 
   void BindHostInterface();
 
-  // Non-owned pointer to a WaylandConnection. It is only used in a single
-  // process mode, when a shared dmabuf approach is not used.
-  WaylandConnection* const connection_;
-
   // Non-owned. Only used to get registered surfaces and notify them about
   // submission and presentation of buffers.
   WaylandSurfaceFactory* const factory_;
diff --git a/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc b/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
index dd5971a3..14926cce 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_surface_factory.cc
@@ -14,6 +14,7 @@
 #include "ui/ozone/platform/wayland/gpu/gl_surface_wayland.h"
 #include "ui/ozone/platform/wayland/gpu/wayland_canvas_surface.h"
 #include "ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h"
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
 #include "ui/ozone/platform/wayland/host/wayland_window.h"
 
 #if defined(WAYLAND_GBM)
@@ -28,9 +29,12 @@
 
 class GLOzoneEGLWayland : public GLOzoneEGL {
  public:
-  GLOzoneEGLWayland(WaylandConnectionProxy* connection,
+  GLOzoneEGLWayland(WaylandConnection* connection,
+                    WaylandConnectionProxy* connection_proxy,
                     WaylandSurfaceFactory* factory)
-      : connection_(connection), factory_(factory) {}
+      : connection_(connection),
+        connection_proxy_(connection_proxy),
+        factory_(factory) {}
   ~GLOzoneEGLWayland() override {}
 
   scoped_refptr<gl::GLSurface> CreateViewGLSurface(
@@ -47,7 +51,8 @@
   bool LoadGLES2Bindings(gl::GLImplementation impl) override;
 
  private:
-  WaylandConnectionProxy* const connection_;
+  WaylandConnection* const connection_;
+  WaylandConnectionProxy* const connection_proxy_;
   WaylandSurfaceFactory* const factory_;
 
   DISALLOW_COPY_AND_ASSIGN(GLOzoneEGLWayland);
@@ -56,10 +61,10 @@
 scoped_refptr<gl::GLSurface> GLOzoneEGLWayland::CreateViewGLSurface(
     gfx::AcceleratedWidget widget) {
   // Only EGLGLES2 is supported with surfaceless view gl.
-  if (gl::GetGLImplementation() != gl::kGLImplementationEGLGLES2)
+  if ((gl::GetGLImplementation() != gl::kGLImplementationEGLGLES2) ||
+      !connection_)
     return nullptr;
 
-  DCHECK(connection_);
   WaylandWindow* window = connection_->GetWindow(widget);
   if (!window)
     return nullptr;
@@ -82,10 +87,10 @@
 
 #if defined(WAYLAND_GBM)
   // If there is a gbm device available, use surfaceless gl surface.
-  if (!connection_->gbm_device())
+  if (!connection_proxy_->gbm_device())
     return nullptr;
   return gl::InitializeGLSurface(
-      new GbmSurfacelessWayland(factory_, connection_, window));
+      new GbmSurfacelessWayland(factory_, connection_proxy_, window));
 #else
   return nullptr;
 #endif
@@ -102,7 +107,9 @@
 }
 
 intptr_t GLOzoneEGLWayland::GetNativeDisplay() {
-  return connection_->Display();
+  if (connection_)
+    return reinterpret_cast<intptr_t>(connection_->display());
+  return EGL_DEFAULT_DISPLAY;
 }
 
 bool GLOzoneEGLWayland::LoadGLES2Bindings(gl::GLImplementation impl) {
@@ -114,15 +121,17 @@
 
 }  // namespace
 
-WaylandSurfaceFactory::WaylandSurfaceFactory() {}
+WaylandSurfaceFactory::WaylandSurfaceFactory(WaylandConnection* connection)
+    : connection_(connection) {}
 
-WaylandSurfaceFactory::~WaylandSurfaceFactory() {}
+WaylandSurfaceFactory::~WaylandSurfaceFactory() = default;
 
 void WaylandSurfaceFactory::SetProxy(WaylandConnectionProxy* proxy) {
-  DCHECK(!connection_ && proxy);
-  connection_ = proxy;
+  DCHECK(!connection_proxy_ && proxy);
+  connection_proxy_ = proxy;
 
-  egl_implementation_ = std::make_unique<GLOzoneEGLWayland>(connection_, this);
+  egl_implementation_ =
+      std::make_unique<GLOzoneEGLWayland>(connection_, connection_proxy_, this);
 }
 
 void WaylandSurfaceFactory::RegisterSurface(gfx::AcceleratedWidget widget,
@@ -146,7 +155,7 @@
 
 std::unique_ptr<SurfaceOzoneCanvas>
 WaylandSurfaceFactory::CreateCanvasForWidget(gfx::AcceleratedWidget widget) {
-  return std::make_unique<WaylandCanvasSurface>(connection_, widget);
+  return std::make_unique<WaylandCanvasSurface>(connection_proxy_, widget);
 }
 
 std::vector<gl::GLImplementation>
@@ -178,7 +187,7 @@
     gfx::BufferUsage usage) {
 #if defined(WAYLAND_GBM)
   scoped_refptr<GbmPixmapWayland> pixmap =
-      base::MakeRefCounted<GbmPixmapWayland>(this, connection_, widget);
+      base::MakeRefCounted<GbmPixmapWayland>(this, connection_proxy_, widget);
   if (!pixmap->InitializeBuffer(size, format, usage))
     return nullptr;
   return pixmap;
diff --git a/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h b/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h
index 0153886..9afa77e 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h
+++ b/ui/ozone/platform/wayland/gpu/wayland_surface_factory.h
@@ -18,11 +18,12 @@
 namespace ui {
 
 class GbmSurfacelessWayland;
+class WaylandConnection;
 class WaylandConnectionProxy;
 
 class WaylandSurfaceFactory : public SurfaceFactoryOzone {
  public:
-  WaylandSurfaceFactory();
+  explicit WaylandSurfaceFactory(WaylandConnection* connection);
   ~WaylandSurfaceFactory() override;
 
   void SetProxy(WaylandConnectionProxy* proxy);
@@ -51,7 +52,8 @@
       gfx::NativePixmapHandle handle) override;
 
  private:
-  WaylandConnectionProxy* connection_ = nullptr;
+  WaylandConnection* const connection_;
+  WaylandConnectionProxy* connection_proxy_ = nullptr;
   std::unique_ptr<GLOzone> egl_implementation_;
 
   std::map<gfx::AcceleratedWidget, GbmSurfacelessWayland*>
diff --git a/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc b/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
index 970e031f..1230e1a 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
@@ -89,11 +89,8 @@
 
 class WaylandSurfaceFactoryTest : public WaylandTest {
  public:
-  WaylandSurfaceFactoryTest() {
-    surface_factory_.SetProxy(connection_proxy_.get());
-  }
-
-  ~WaylandSurfaceFactoryTest() override {}
+  WaylandSurfaceFactoryTest() = default;
+  ~WaylandSurfaceFactoryTest() override = default;
 
   void SetUp() override {
     WaylandTest::SetUp();
@@ -114,14 +111,12 @@
  protected:
   std::unique_ptr<SurfaceOzoneCanvas> CreateCanvas(
       gfx::AcceleratedWidget widget) {
-    auto canvas = surface_factory_.CreateCanvasForWidget(widget_);
+    auto canvas = surface_factory_->CreateCanvasForWidget(widget_);
     base::RunLoop().RunUntilIdle();
 
     return canvas;
   }
 
-  WaylandSurfaceFactory surface_factory_;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(WaylandSurfaceFactoryTest);
 };
@@ -137,7 +132,7 @@
   // Wait until the mojo calls are done.
   base::RunLoop().RunUntilIdle();
 
-  Expectation damage = EXPECT_CALL(*surface_, Damage(5, 10, 20, 15));
+  Expectation damage = EXPECT_CALL(*surface_, DamageBuffer(5, 10, 20, 15));
   wl_resource* buffer_resource = nullptr;
   Expectation attach = EXPECT_CALL(*surface_, Attach(_, 0, 0))
                            .WillOnce(SaveArg<0>(&buffer_resource));
@@ -167,7 +162,7 @@
 
   base::RunLoop().RunUntilIdle();
 
-  Expectation damage = EXPECT_CALL(*surface_, Damage(0, 0, 100, 50));
+  Expectation damage = EXPECT_CALL(*surface_, DamageBuffer(0, 0, 100, 50));
   wl_resource* buffer_resource = nullptr;
   Expectation attach = EXPECT_CALL(*surface_, Attach(_, 0, 0))
                            .WillOnce(SaveArg<0>(&buffer_resource));
@@ -189,7 +184,7 @@
   // used.
   EXPECT_FALSE(connection_proxy_->gbm_device());
 
-  auto* gl_ozone = surface_factory_.GetGLOzone(gl::kGLImplementationEGLGLES2);
+  auto* gl_ozone = surface_factory_->GetGLOzone(gl::kGLImplementationEGLGLES2);
   EXPECT_TRUE(gl_ozone);
   auto gl_surface = gl_ozone->CreateSurfacelessViewGLSurface(widget_);
   EXPECT_FALSE(gl_surface);
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager.cc b/ui/ozone/platform/wayland/host/wayland_buffer_manager.cc
index 3818efd..9190b76 100644
--- a/ui/ozone/platform/wayland/host/wayland_buffer_manager.cc
+++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager.cc
@@ -12,6 +12,7 @@
 #include "base/trace_event/trace_event.h"
 #include "ui/ozone/common/linux/drm_util_linux.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_shm.h"
 #include "ui/ozone/platform/wayland/host/wayland_window.h"
 #include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h"
 
@@ -447,50 +448,67 @@
   DCHECK(surfaces_.erase(window->GetWidget()));
 }
 
-bool WaylandBufferManager::CreateBuffer(gfx::AcceleratedWidget widget,
-                                        base::File file,
-                                        const gfx::Size& size,
-                                        const std::vector<uint32_t>& strides,
-                                        const std::vector<uint32_t>& offsets,
-                                        const std::vector<uint64_t>& modifiers,
-                                        uint32_t format,
-                                        uint32_t planes_count,
-                                        uint32_t buffer_id) {
-  TRACE_EVENT2("wayland", "WaylandBufferManager::CreateZwpLinuxDmabuf",
+bool WaylandBufferManager::CreateDmabufBasedBuffer(
+    gfx::AcceleratedWidget widget,
+    base::ScopedFD dmabuf_fd,
+    const gfx::Size& size,
+    const std::vector<uint32_t>& strides,
+    const std::vector<uint32_t>& offsets,
+    const std::vector<uint64_t>& modifiers,
+    uint32_t format,
+    uint32_t planes_count,
+    uint32_t buffer_id) {
+  TRACE_EVENT2("wayland", "WaylandBufferManager::CreateDmabufBasedBuffer",
                "Format", format, "Buffer id", buffer_id);
 
-  if (!ValidateDataFromGpu(widget, file, size, strides, offsets, modifiers,
-                           format, planes_count, buffer_id)) {
-    // base::File::Close() has an assertion that checks if blocking operations
-    // are allowed. Thus, manually close the fd here.
-    base::ScopedFD deleter(file.TakePlatformFile());
+  DCHECK(error_message_.empty());
+  // Validate data and ask surface to create a buffer associated with the
+  // |buffer_id|.
+  if (!ValidateDataFromGpu(widget, dmabuf_fd, size, strides, offsets, modifiers,
+                           format, planes_count, buffer_id) ||
+      !CreateBuffer(widget, size, buffer_id))
     return false;
-  }
-
-  WaylandBufferManager::Surface* surface = GetSurface(widget);
-  DCHECK(surface);
-
-  if (!surface->CreateBuffer(size, buffer_id)) {
-    error_message_ =
-        "A buffer with id= " + NumberToString(buffer_id) + " already exists";
-    return false;
-  }
 
   // Create wl_buffer associated with the internal Buffer.
   auto callback = base::BindOnce(&WaylandBufferManager::OnCreateBufferComplete,
                                  weak_factory_.GetWeakPtr(), widget, buffer_id);
-  connection_->zwp_dmabuf()->CreateBuffer(std::move(file), size, strides,
+  connection_->zwp_dmabuf()->CreateBuffer(std::move(dmabuf_fd), size, strides,
                                           offsets, modifiers, format,
                                           planes_count, std::move(callback));
   return true;
 }
 
+bool WaylandBufferManager::CreateShmBasedBuffer(gfx::AcceleratedWidget widget,
+                                                base::ScopedFD shm_fd,
+                                                size_t length,
+                                                const gfx::Size& size,
+                                                uint32_t buffer_id) {
+  TRACE_EVENT1("wayland", "WaylandBufferManager::CreateShmBasedBuffer",
+               "Buffer id", buffer_id);
+
+  DCHECK(error_message_.empty());
+  // Validate data and create a buffer associated with the |buffer_id|.
+  if (!ValidateDataFromGpu(widget, shm_fd, length, size, buffer_id) ||
+      !CreateBuffer(widget, size, buffer_id))
+    return false;
+
+  // Create a shm based wl_buffer and attach it to the created buffer.
+  auto buffer =
+      connection_->shm()->CreateBuffer(std::move(shm_fd), length, size);
+  OnCreateBufferComplete(widget, buffer_id, std::move(buffer));
+
+  connection_->ScheduleFlush();
+  return true;
+}
+
 bool WaylandBufferManager::CommitBuffer(gfx::AcceleratedWidget widget,
                                         uint32_t buffer_id,
                                         const gfx::Rect& damage_region) {
   TRACE_EVENT1("wayland", "WaylandBufferManager::ScheduleSwapBuffer",
                "Buffer id", buffer_id);
 
+  DCHECK(error_message_.empty());
+
   if (ValidateDataFromGpu(widget, buffer_id)) {
     Surface* surface = GetSurface(widget);
     if (!surface) {
@@ -508,6 +526,8 @@
   TRACE_EVENT1("wayland", "WaylandBufferManager::DestroyZwpLinuxDmabuf",
                "Buffer id", buffer_id);
 
+  DCHECK(error_message_.empty());
+
   Surface* surface = GetSurface(widget);
   // On browser shutdown, the surface might have already been destroyed.
   if (!surface)
@@ -528,6 +548,19 @@
     surface_pair.second->ClearState();
 }
 
+bool WaylandBufferManager::CreateBuffer(gfx::AcceleratedWidget& widget,
+                                        const gfx::Size& size,
+                                        uint32_t buffer_id) {
+  WaylandBufferManager::Surface* surface = GetSurface(widget);
+  DCHECK(surface);
+
+  if (!surface->CreateBuffer(size, buffer_id)) {
+    error_message_ =
+        "A buffer with id= " + NumberToString(buffer_id) + " already exists";
+  }
+  return error_message_.empty();
+}
+
 WaylandBufferManager::Surface* WaylandBufferManager::GetSurface(
     gfx::AcceleratedWidget widget) const {
   auto it = surfaces_.find(widget);
@@ -536,7 +569,7 @@
 
 bool WaylandBufferManager::ValidateDataFromGpu(
     const gfx::AcceleratedWidget& widget,
-    const base::File& file,
+    const base::ScopedFD& fd,
     const gfx::Size& size,
     const std::vector<uint32_t>& strides,
     const std::vector<uint32_t>& offsets,
@@ -548,7 +581,7 @@
     return false;
 
   std::string reason;
-  if (!file.IsValid())
+  if (!fd.is_valid())
     reason = "Buffer fd is invalid";
 
   if (size.IsEmpty())
@@ -599,6 +632,33 @@
   return true;
 }
 
+bool WaylandBufferManager::ValidateDataFromGpu(
+    const gfx::AcceleratedWidget& widget,
+    const base::ScopedFD& fd,
+    size_t length,
+    const gfx::Size& size,
+    uint32_t buffer_id) {
+  if (!ValidateDataFromGpu(widget, buffer_id))
+    return false;
+
+  std::string reason;
+  if (!fd.is_valid())
+    reason = "Buffer fd is invalid";
+
+  if (length == 0)
+    reason = "The shm pool length cannot be less than 1";
+
+  if (size.IsEmpty())
+    reason = "Buffer size is invalid";
+
+  if (!reason.empty()) {
+    error_message_ = std::move(reason);
+    return false;
+  }
+
+  return true;
+}
+
 void WaylandBufferManager::OnCreateBufferComplete(
     gfx::AcceleratedWidget widget,
     uint32_t buffer_id,
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager.h b/ui/ozone/platform/wayland/host/wayland_buffer_manager.h
index e96a146..c1613d70 100644
--- a/ui/ozone/platform/wayland/host/wayland_buffer_manager.h
+++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager.h
@@ -10,7 +10,7 @@
 #include <vector>
 
 #include "base/containers/flat_map.h"
-#include "base/files/file.h"
+#include "base/files/scoped_file.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -26,9 +26,10 @@
 class WaylandConnection;
 class WaylandWindow;
 
-// The manager uses zwp_linux_dmabuf protocol to create wl_buffers from added
-// dmabuf buffers, and uses internal representation of surfaces, which store
-// buffers associated with the WaylandWindow.
+// This is the buffer manager, which creates wl_buffers based on dmabuf (hw
+// accelerated compositing) or shared memory (software compositing) and uses
+// internal representation of surfaces, which are used to store buffers
+// associated with the WaylandWindow.
 class WaylandBufferManager {
  public:
   explicit WaylandBufferManager(WaylandConnection* connection);
@@ -41,15 +42,23 @@
 
   // Creates a wl_buffer based on the dmabuf |file| descriptor. On error, false
   // is returned and |error_message_| is set.
-  bool CreateBuffer(gfx::AcceleratedWidget widget,
-                    base::File file,
-                    const gfx::Size& size,
-                    const std::vector<uint32_t>& strides,
-                    const std::vector<uint32_t>& offsets,
-                    const std::vector<uint64_t>& modifiers,
-                    uint32_t format,
-                    uint32_t planes_count,
-                    uint32_t buffer_id);
+  bool CreateDmabufBasedBuffer(gfx::AcceleratedWidget widget,
+                               base::ScopedFD dmabuf_fd,
+                               const gfx::Size& size,
+                               const std::vector<uint32_t>& strides,
+                               const std::vector<uint32_t>& offsets,
+                               const std::vector<uint64_t>& modifiers,
+                               uint32_t format,
+                               uint32_t planes_count,
+                               uint32_t buffer_id);
+
+  // Create a wl_buffer based on the |file| descriptor to a shared memory. On
+  // error, false is returned and |error_message_| is set.
+  bool CreateShmBasedBuffer(gfx::AcceleratedWidget widget,
+                            base::ScopedFD shm_fd,
+                            size_t length,
+                            const gfx::Size& size,
+                            uint32_t buffer_id);
 
   // Assigns a wl_buffer with |buffer_id| to a window with the same |widget|. On
   // error, false is returned and |error_message_| is set. A |damage_region|
@@ -75,12 +84,16 @@
   // presentation callbacks for that window's surface.
   class Surface;
 
+  bool CreateBuffer(gfx::AcceleratedWidget& widget,
+                    const gfx::Size& size,
+                    uint32_t buffer_id);
+
   Surface* GetSurface(gfx::AcceleratedWidget widget) const;
 
   // Validates data sent from GPU. If invalid, returns false and sets an error
   // message to |error_message_|.
   bool ValidateDataFromGpu(const gfx::AcceleratedWidget& widget,
-                           const base::File& file,
+                           const base::ScopedFD& file,
                            const gfx::Size& size,
                            const std::vector<uint32_t>& strides,
                            const std::vector<uint32_t>& offsets,
@@ -90,6 +103,11 @@
                            uint32_t buffer_id);
   bool ValidateDataFromGpu(const gfx::AcceleratedWidget& widget,
                            uint32_t buffer_id);
+  bool ValidateDataFromGpu(const gfx::AcceleratedWidget& widget,
+                           const base::ScopedFD& file,
+                           size_t length,
+                           const gfx::Size& size,
+                           uint32_t buffer_id);
 
   // Callback method. Receives a result for the request to create a wl_buffer
   // backend by dmabuf file descriptor from ::CreateBuffer call.
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager_unittest.cc b/ui/ozone/platform/wayland/host/wayland_buffer_manager_unittest.cc
index e8ff222..0d817c0 100644
--- a/ui/ozone/platform/wayland/host/wayland_buffer_manager_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager_unittest.cc
@@ -30,18 +30,50 @@
   ~WaylandBufferManagerTest() override = default;
 
  protected:
-  base::File MakeTempFile() {
+  base::ScopedFD MakeFD() {
     base::FilePath temp_path;
     EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
-    return base::File(temp_path, base::File::FLAG_READ |
-                                     base::File::FLAG_WRITE |
-                                     base::File::FLAG_CREATE_ALWAYS);
+    auto file =
+        base::File(temp_path, base::File::FLAG_READ | base::File::FLAG_WRITE |
+                                  base::File::FLAG_CREATE_ALWAYS);
+    return base::ScopedFD(file.TakePlatformFile());
   }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(WaylandBufferManagerTest);
 };
 
+TEST_P(WaylandBufferManagerTest, CreateDmabufBasedBuffers) {
+  WaylandBufferManager* manager = connection_->buffer_manager();
+  ASSERT_TRUE(manager);
+
+  constexpr uint32_t kDmabufBufferId = 1;
+
+  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
+  const gfx::AcceleratedWidget widget = window_->GetWidget();
+  EXPECT_TRUE(manager->CreateDmabufBasedBuffer(widget, MakeFD(), kDefaultSize,
+                                               {1}, {2}, {3}, DRM_FORMAT_R8, 1,
+                                               kDmabufBufferId));
+  EXPECT_TRUE(manager->error_message().empty());
+  EXPECT_TRUE(manager->DestroyBuffer(widget, kDmabufBufferId));
+  EXPECT_TRUE(manager->error_message().empty());
+}
+
+TEST_P(WaylandBufferManagerTest, CreateShmBasedBuffers) {
+  WaylandBufferManager* manager = connection_->buffer_manager();
+  ASSERT_TRUE(manager);
+
+  constexpr uint32_t kShmBufferId = 1;
+
+  const gfx::AcceleratedWidget widget = window_->GetWidget();
+  size_t length = kDefaultSize.width() * kDefaultSize.height() * 4;
+  EXPECT_TRUE(manager->CreateShmBasedBuffer(widget, MakeFD(), length,
+                                            kDefaultSize, kShmBufferId));
+  EXPECT_TRUE(manager->error_message().empty());
+  EXPECT_TRUE(manager->DestroyBuffer(widget, kShmBufferId));
+  EXPECT_TRUE(manager->error_message().empty());
+}
+
 TEST_P(WaylandBufferManagerTest, ValidateDataFromGpu) {
   struct InputData {
     bool has_file = false;
@@ -64,8 +96,8 @@
   // This must be the only buffer that is asked to be created.
   EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
   const gfx::AcceleratedWidget widget = window_->GetWidget();
-  manager->CreateBuffer(widget, MakeTempFile(), kDefaultSize, {1}, {2}, {3},
-                        DRM_FORMAT_R8, 1, kExistingBufferId);
+  manager->CreateDmabufBasedBuffer(widget, MakeFD(), kDefaultSize, {1}, {2},
+                                   {3}, DRM_FORMAT_R8, 1, kExistingBufferId);
   Sync();
 
   const InputData kBadInputs[] = {
@@ -92,21 +124,24 @@
 
   for (const auto& bad : kBadInputs) {
     EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(0);
-    base::File dummy;
-    EXPECT_FALSE(manager->CreateBuffer(
-        widget, bad.has_file ? MakeTempFile() : std::move(dummy), bad.size,
+    base::ScopedFD dummy;
+    EXPECT_FALSE(manager->CreateDmabufBasedBuffer(
+        widget, bad.has_file ? MakeFD() : std::move(dummy), bad.size,
         bad.strides, bad.offsets, bad.modifiers, bad.format, bad.planes_count,
         bad.buffer_id));
     EXPECT_FALSE(manager->error_message().empty());
   }
 
   EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
-  EXPECT_TRUE(manager->CreateBuffer(widget, MakeTempFile(), kDefaultSize, {1},
-                                    {2}, {3}, DRM_FORMAT_R8, 1,
-                                    kNonExistingBufferId));
+  EXPECT_TRUE(manager->CreateDmabufBasedBuffer(widget, MakeFD(), kDefaultSize,
+                                               {1}, {2}, {3}, DRM_FORMAT_R8, 1,
+                                               kNonExistingBufferId));
+  EXPECT_TRUE(manager->error_message().empty());
 
   EXPECT_TRUE(manager->DestroyBuffer(widget, kNonExistingBufferId));
+  EXPECT_TRUE(manager->error_message().empty());
   EXPECT_TRUE(manager->DestroyBuffer(widget, kExistingBufferId));
+  EXPECT_TRUE(manager->error_message().empty());
 }
 
 TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) {
@@ -120,18 +155,35 @@
 
   const gfx::AcceleratedWidget widget = window_->GetWidget();
 
-  EXPECT_TRUE(manager->CreateBuffer(widget, MakeTempFile(), kDefaultSize, {1},
-                                    {2}, {3}, DRM_FORMAT_R8, 1, kBufferId1));
-  EXPECT_FALSE(manager->CreateBuffer(widget, MakeTempFile(), kDefaultSize, {1},
-                                     {2}, {3}, DRM_FORMAT_R8, 1, kBufferId1));
+  EXPECT_TRUE(manager->CreateDmabufBasedBuffer(widget, MakeFD(), kDefaultSize,
+                                               {1}, {2}, {3}, DRM_FORMAT_R8, 1,
+                                               kBufferId1));
+  EXPECT_TRUE(manager->error_message().empty());
+
+  EXPECT_FALSE(manager->CreateDmabufBasedBuffer(widget, MakeFD(), kDefaultSize,
+                                                {1}, {2}, {3}, DRM_FORMAT_R8, 1,
+                                                kBufferId1));
+  EXPECT_FALSE(manager->error_message().empty());
+
   EXPECT_FALSE(manager->DestroyBuffer(widget, kBufferId2));
-  EXPECT_TRUE(manager->CreateBuffer(widget, MakeTempFile(), kDefaultSize, {1},
-                                    {2}, {3}, DRM_FORMAT_R8, 1, kBufferId2));
+  EXPECT_FALSE(manager->error_message().empty());
+
+  EXPECT_TRUE(manager->CreateDmabufBasedBuffer(widget, MakeFD(), kDefaultSize,
+                                               {1}, {2}, {3}, DRM_FORMAT_R8, 1,
+                                               kBufferId2));
+  EXPECT_TRUE(manager->error_message().empty());
 
   EXPECT_TRUE(manager->DestroyBuffer(widget, kBufferId1));
+  EXPECT_TRUE(manager->error_message().empty());
+
   EXPECT_FALSE(manager->DestroyBuffer(widget, kBufferId1));
+  EXPECT_FALSE(manager->error_message().empty());
+
   EXPECT_TRUE(manager->DestroyBuffer(widget, kBufferId2));
+  EXPECT_TRUE(manager->error_message().empty());
+
   EXPECT_FALSE(manager->DestroyBuffer(widget, kBufferId2));
+  EXPECT_FALSE(manager->error_message().empty());
 }
 
 INSTANTIATE_TEST_SUITE_P(XdgVersionV5Test,
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index e0bbcee..4eb6405 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -18,13 +18,13 @@
 #include "base/message_loop/message_loop_current.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/system/platform_handle.h"
 #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
 #include "ui/gfx/swap_result.h"
 #include "ui/ozone/platform/wayland/common/wayland_object.h"
 #include "ui/ozone/platform/wayland/host/wayland_buffer_manager.h"
 #include "ui/ozone/platform/wayland/host/wayland_input_method_context.h"
 #include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
-#include "ui/ozone/platform/wayland/host/wayland_shared_memory_buffer_manager.h"
 #include "ui/ozone/platform/wayland/host/wayland_shm.h"
 #include "ui/ozone/platform/wayland/host/wayland_window.h"
 #include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h"
@@ -198,9 +198,9 @@
   client_associated_ptr_.Bind(std::move(client));
 }
 
-void WaylandConnection::CreateZwpLinuxDmabuf(
+void WaylandConnection::CreateDmabufBasedBuffer(
     gfx::AcceleratedWidget widget,
-    base::File file,
+    mojo::ScopedHandle dmabuf_fd,
     const gfx::Size& size,
     const std::vector<uint32_t>& strides,
     const std::vector<uint32_t>& offsets,
@@ -211,15 +211,27 @@
   DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
   DCHECK(buffer_manager_);
-  if (!buffer_manager_->CreateBuffer(widget, std::move(file), size, strides,
-                                     offsets, modifiers, format, planes_count,
-                                     buffer_id)) {
+  if (!buffer_manager_->CreateDmabufBasedBuffer(
+          widget, mojo::UnwrapPlatformHandle(std::move(dmabuf_fd)).TakeFD(),
+          size, strides, offsets, modifiers, format, planes_count, buffer_id)) {
     TerminateGpuProcess(buffer_manager_->error_message());
   }
 }
 
-void WaylandConnection::DestroyZwpLinuxDmabuf(gfx::AcceleratedWidget widget,
-                                              uint32_t buffer_id) {
+void WaylandConnection::CreateShmBasedBuffer(gfx::AcceleratedWidget widget,
+                                             mojo::ScopedHandle shm_fd,
+                                             uint64_t length,
+                                             const gfx::Size& size,
+                                             uint32_t buffer_id) {
+  DCHECK(buffer_manager_);
+  if (!buffer_manager_->CreateShmBasedBuffer(
+          widget, mojo::UnwrapPlatformHandle(std::move(shm_fd)).TakeFD(),
+          length, size, buffer_id))
+    TerminateGpuProcess(buffer_manager_->error_message());
+}
+
+void WaylandConnection::DestroyBuffer(gfx::AcceleratedWidget widget,
+                                      uint32_t buffer_id) {
   DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
   DCHECK(buffer_manager_);
@@ -238,32 +250,6 @@
     TerminateGpuProcess(buffer_manager_->error_message());
 }
 
-void WaylandConnection::CreateShmBufferForWidget(gfx::AcceleratedWidget widget,
-                                                 base::File file,
-                                                 uint64_t length,
-                                                 const gfx::Size& size,
-                                                 uint32_t buffer_id) {
-  DCHECK(shm_buffer_manager_);
-  if (!shm_buffer_manager_->CreateBufferForWidget(widget, std::move(file),
-                                                  length, size, buffer_id))
-    TerminateGpuProcess("Failed to create SHM buffer.");
-}
-
-void WaylandConnection::PresentShmBufferForWidget(gfx::AcceleratedWidget widget,
-                                                  const gfx::Rect& damage,
-                                                  uint32_t buffer_id) {
-  DCHECK(shm_buffer_manager_);
-  if (!shm_buffer_manager_->PresentBufferForWidget(widget, damage, buffer_id))
-    TerminateGpuProcess("Failed to present SHM buffer.");
-}
-
-void WaylandConnection::DestroyShmBuffer(gfx::AcceleratedWidget widget,
-                                         uint32_t buffer_id) {
-  DCHECK(shm_buffer_manager_);
-  if (!shm_buffer_manager_->DestroyBuffer(widget, buffer_id))
-    TerminateGpuProcess("Failed to destroy SHM buffer.");
-}
-
 void WaylandConnection::OnSubmission(gfx::AcceleratedWidget widget,
                                      uint32_t buffer_id,
                                      const gfx::SwapResult& swap_result) {
@@ -412,8 +398,10 @@
     connection->shm_ = std::make_unique<WaylandShm>(shm.release(), connection);
     if (!connection->shm_)
       LOG(ERROR) << "Failed to bind to wl_shm global";
-    connection->shm_buffer_manager_ =
-        std::make_unique<WaylandShmBufferManager>(connection);
+    if (!connection->buffer_manager_) {
+      connection->buffer_manager_ =
+          std::make_unique<WaylandBufferManager>(connection);
+    }
   } else if (!connection->seat_ && strcmp(interface, "wl_seat") == 0) {
     connection->seat_ =
         wl::Bind<wl_seat>(registry, name, std::min(version, kMaxSeatVersion));
@@ -486,8 +474,10 @@
             registry, name, std::min(version, kMaxLinuxDmabufVersion));
     connection->zwp_dmabuf_ = std::make_unique<WaylandZwpLinuxDmabuf>(
         zwp_linux_dmabuf.release(), connection);
-    connection->buffer_manager_ =
-        std::make_unique<WaylandBufferManager>(connection);
+    if (!connection->buffer_manager_) {
+      connection->buffer_manager_ =
+          std::make_unique<WaylandBufferManager>(connection);
+    }
   } else if (!connection->presentation_ &&
              (strcmp(interface, "wp_presentation") == 0)) {
     connection->presentation_ =
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h
index e55c817..ebbda81 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.h
+++ b/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -31,7 +31,6 @@
 namespace ui {
 
 class WaylandBufferManager;
-class WaylandShmBufferManager;
 class WaylandOutputManager;
 class WaylandWindow;
 class WaylandZwpLinuxDmabuf;
@@ -55,19 +54,28 @@
       ozone::mojom::WaylandConnectionClientAssociatedPtrInfo client) override;
   //
   // Called by the GPU and asks to import a wl_buffer based on a gbm file
-  // descriptor.
-  void CreateZwpLinuxDmabuf(gfx::AcceleratedWidget widget,
-                            base::File file,
+  // descriptor using zwp_linux_dmabuf protocol. Check comments in the
+  // ui/ozone/public/interfaces/wayland/wayland_connection.mojom.
+  void CreateDmabufBasedBuffer(gfx::AcceleratedWidget widget,
+                               mojo::ScopedHandle dmabuf_fd,
+                               const gfx::Size& size,
+                               const std::vector<uint32_t>& strides,
+                               const std::vector<uint32_t>& offsets,
+                               const std::vector<uint64_t>& modifiers,
+                               uint32_t format,
+                               uint32_t planes_count,
+                               uint32_t buffer_id) override;
+  // Called by the GPU and asks to import a wl_buffer based on a shared memory
+  // file descriptor using wl_shm protocol. Check comments in the
+  // ui/ozone/public/interfaces/wayland/wayland_connection.mojom.
+  void CreateShmBasedBuffer(gfx::AcceleratedWidget widget,
+                            mojo::ScopedHandle shm_fd,
+                            uint64_t length,
                             const gfx::Size& size,
-                            const std::vector<uint32_t>& strides,
-                            const std::vector<uint32_t>& offsets,
-                            const std::vector<uint64_t>& modifiers,
-                            uint32_t format,
-                            uint32_t planes_count,
                             uint32_t buffer_id) override;
   // Called by the GPU to destroy the imported wl_buffer with a |buffer_id|.
-  void DestroyZwpLinuxDmabuf(gfx::AcceleratedWidget widget,
-                             uint32_t buffer_id) override;
+  void DestroyBuffer(gfx::AcceleratedWidget widget,
+                     uint32_t buffer_id) override;
   // Called by the GPU and asks to attach a wl_buffer with a |buffer_id| to a
   // WaylandWindow with the specified |widget|.
   // Calls OnSubmission and OnPresentation on successful swap and pixels
@@ -75,19 +83,6 @@
   void CommitBuffer(gfx::AcceleratedWidget widget,
                     uint32_t buffer_id,
                     const gfx::Rect& damage_region) override;
-  // These overridden methods below are invoked by the GPU when hardware
-  // accelerated rendering is not used. Check comments in the
-  // ui/ozone/public/interfaces/wayland/wayland_connection.mojom.
-  void CreateShmBufferForWidget(gfx::AcceleratedWidget widget,
-                                base::File file,
-                                uint64_t length,
-                                const gfx::Size& size,
-                                uint32_t buffer_id) override;
-  void PresentShmBufferForWidget(gfx::AcceleratedWidget widget,
-                                 const gfx::Rect& damage,
-                                 uint32_t buffer_id) override;
-  void DestroyShmBuffer(gfx::AcceleratedWidget widget,
-                        uint32_t buffer_id) override;
 
   // These methods are exclusively used by the WaylandBufferManager to notify
   // the |client_associated_ptr_| about buffer swaps' results.
@@ -256,7 +251,6 @@
   std::unique_ptr<WaylandPointer> pointer_;
   std::unique_ptr<WaylandTouch> touch_;
   std::unique_ptr<WaylandCursorPosition> wayland_cursor_position_;
-  std::unique_ptr<WaylandShmBufferManager> shm_buffer_manager_;
   std::unique_ptr<WaylandZwpLinuxDmabuf> zwp_dmabuf_;
   std::unique_ptr<WaylandShm> shm_;
 
diff --git a/ui/ozone/platform/wayland/host/wayland_shared_memory_buffer_manager.cc b/ui/ozone/platform/wayland/host/wayland_shared_memory_buffer_manager.cc
deleted file mode 100644
index a198a02..0000000
--- a/ui/ozone/platform/wayland/host/wayland_shared_memory_buffer_manager.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2019 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 "ui/ozone/platform/wayland/host/wayland_shared_memory_buffer_manager.h"
-
-#include <utility>
-
-#include "base/trace_event/trace_event.h"
-#include "ui/ozone/platform/wayland/host/wayland_connection.h"
-#include "ui/ozone/platform/wayland/host/wayland_shm.h"
-#include "ui/ozone/platform/wayland/host/wayland_window.h"
-
-namespace ui {
-
-WaylandShmBufferManager::Buffer::Buffer(gfx::AcceleratedWidget widget,
-                                        wl::Object<wl_buffer> buffer)
-    : widget(widget), buffer(std::move(buffer)) {}
-
-WaylandShmBufferManager::Buffer::~Buffer() = default;
-
-WaylandShmBufferManager::WaylandShmBufferManager(WaylandConnection* connection)
-    : connection_(connection) {}
-
-WaylandShmBufferManager::~WaylandShmBufferManager() {
-  DCHECK(shm_buffers_.empty());
-}
-
-bool WaylandShmBufferManager::CreateBufferForWidget(
-    gfx::AcceleratedWidget widget,
-    base::File file,
-    size_t length,
-    const gfx::Size& size,
-    uint32_t buffer_id) {
-  base::ScopedFD fd(file.TakePlatformFile());
-  if (!fd.is_valid() || length == 0 || size.IsEmpty() ||
-      widget == gfx::kNullAcceleratedWidget) {
-    return false;
-  }
-
-  auto it = shm_buffers_.find(buffer_id);
-  if (it != shm_buffers_.end())
-    return false;
-
-  auto wl_buffer =
-      connection_->shm()->CreateBuffer(std::move(fd), length, size);
-
-  if (!wl_buffer)
-    return false;
-
-  std::unique_ptr<Buffer> buffer =
-      std::make_unique<Buffer>(widget, std::move(wl_buffer));
-  shm_buffers_.insert(std::make_pair(buffer_id, std::move(buffer)));
-
-  connection_->ScheduleFlush();
-  return true;
-}
-
-bool WaylandShmBufferManager::PresentBufferForWidget(
-    gfx::AcceleratedWidget widget,
-    const gfx::Rect& damage,
-    uint32_t buffer_id) {
-  auto it = shm_buffers_.find(buffer_id);
-  if (it == shm_buffers_.end())
-    return false;
-
-  DCHECK_EQ(it->second->widget, widget);
-
-  // TODO(https://crbug.com/930662): This is just a naive implementation that
-  // allows chromium to draw to the buffer at any time, even if it is being used
-  // by the Wayland compositor. Instead, we should track buffer releases and
-  // frame callbacks from Wayland to ensure perfect frames (while minimizing
-  // copies).
-  wl_surface* surface = connection_->GetWindow(widget)->surface();
-  DCHECK(surface);
-  wl_surface_damage(surface, damage.x(), damage.y(), damage.width(),
-                    damage.height());
-  wl_surface_attach(surface, it->second->buffer.get(), 0, 0);
-  wl_surface_commit(surface);
-  connection_->ScheduleFlush();
-  return true;
-}
-
-bool WaylandShmBufferManager::DestroyBuffer(gfx::AcceleratedWidget widget,
-                                            uint32_t buffer_id) {
-  auto it = shm_buffers_.find(buffer_id);
-  if (it == shm_buffers_.end())
-    return false;
-
-  DCHECK_EQ(it->second->widget, widget);
-
-  shm_buffers_.erase(it);
-  connection_->ScheduleFlush();
-  return true;
-}
-
-}  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_shared_memory_buffer_manager.h b/ui/ozone/platform/wayland/host/wayland_shared_memory_buffer_manager.h
deleted file mode 100644
index 98f0f7e5..0000000
--- a/ui/ozone/platform/wayland/host/wayland_shared_memory_buffer_manager.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2019 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 UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SHARED_MEMORY_BUFFER_MANAGER_H_
-#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SHARED_MEMORY_BUFFER_MANAGER_H_
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include "base/containers/flat_map.h"
-#include "base/files/file.h"
-#include "base/macros.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/ozone/platform/wayland/common/wayland_object.h"
-#include "ui/ozone/platform/wayland/common/wayland_util.h"
-
-namespace ui {
-
-class WaylandConnection;
-
-// Manages shared memory buffers, which are created by the GPU on the GPU
-// process/thread side, when software rendering is used.
-class WaylandShmBufferManager {
- public:
-  explicit WaylandShmBufferManager(WaylandConnection* connection);
-  ~WaylandShmBufferManager();
-
-  // Creates a wl_buffer based on shared memory handle for the specified
-  // |widget|.
-  bool CreateBufferForWidget(gfx::AcceleratedWidget widget,
-                             base::File file,
-                             size_t length,
-                             const gfx::Size& size,
-                             uint32_t buffer_id);
-
-  // Attaches and commits a |wl_buffer| with |buffer_id| to a surface with the
-  // |widget|.
-  bool PresentBufferForWidget(gfx::AcceleratedWidget widget,
-                              const gfx::Rect& damage,
-                              uint32_t buffer_id);
-
-  // Destroyes a |wl_buffer| with the |buffer_id| for a surface with the
-  // |widget|.
-  bool DestroyBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id);
-
- private:
-  struct Buffer {
-    Buffer() = delete;
-    Buffer(gfx::AcceleratedWidget widget, wl::Object<wl_buffer> buffer);
-    ~Buffer();
-
-    // Widget this buffer is created for.
-    gfx::AcceleratedWidget widget;
-
-    // Actual wayland buffer object.
-    wl::Object<wl_buffer> buffer;
-  };
-
-  // A container of created buffers.
-  base::flat_map<uint32_t, std::unique_ptr<Buffer>> shm_buffers_;
-
-  // Non-owned pointer to the main connection.
-  WaylandConnection* connection_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(WaylandShmBufferManager);
-};
-
-}  // namespace ui
-
-#endif  // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SHARED_MEMORY_BUFFER_MANAGER_H_
diff --git a/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc b/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc
index 8e99460..859b8f5d52 100644
--- a/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc
+++ b/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.cc
@@ -34,7 +34,7 @@
 
 WaylandZwpLinuxDmabuf::~WaylandZwpLinuxDmabuf() = default;
 
-void WaylandZwpLinuxDmabuf::CreateBuffer(base::File file,
+void WaylandZwpLinuxDmabuf::CreateBuffer(base::ScopedFD fd,
                                          const gfx::Size& size,
                                          const std::vector<uint32_t>& strides,
                                          const std::vector<uint32_t>& offsets,
@@ -49,8 +49,6 @@
   struct zwp_linux_buffer_params_v1* params =
       zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf_.get());
 
-  base::ScopedFD fd(file.TakePlatformFile());
-
   for (size_t i = 0; i < planes_count; i++) {
     uint32_t modifier_lo = 0;
     uint32_t modifier_hi = 0;
diff --git a/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h b/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h
index 00cc223d..0716d86 100644
--- a/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h
+++ b/ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "base/containers/flat_map.h"
-#include "base/files/file.h"
+#include "base/files/scoped_file.h"
 #include "base/macros.h"
 #include "ui/ozone/platform/wayland/common/wayland_object.h"
 #include "ui/ozone/platform/wayland/common/wayland_util.h"
@@ -36,7 +36,7 @@
   // Requests to create a wl_buffer backed by the |file| descriptor. The result
   // is sent back via the |callback|. If buffer creation failed, nullptr is sent
   // back via the callback. Otherwise, a pointer to the |wl_buffer| is sent.
-  void CreateBuffer(base::File file,
+  void CreateBuffer(base::ScopedFD fd,
                     const gfx::Size& size,
                     const std::vector<uint32_t>& strides,
                     const std::vector<uint32_t>& offsets,
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index d4f26d8b..a4d262a 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -167,9 +167,9 @@
   }
 
   void InitializeGPU(const InitParams& args) override {
-    surface_factory_ = std::make_unique<WaylandSurfaceFactory>();
-    proxy_ = std::make_unique<WaylandConnectionProxy>(connection_.get(),
-                                                      surface_factory_.get());
+    surface_factory_ =
+        std::make_unique<WaylandSurfaceFactory>(connection_.get());
+    proxy_ = std::make_unique<WaylandConnectionProxy>(surface_factory_.get());
     surface_factory_->SetProxy(proxy_.get());
 #if defined(WAYLAND_GBM)
     const base::FilePath drm_node_path = path_finder_.GetDrmRenderNodePath();
diff --git a/ui/ozone/platform/wayland/test/mock_surface.cc b/ui/ozone/platform/wayland/test/mock_surface.cc
index bab5b77..9d23336 100644
--- a/ui/ozone/platform/wayland/test/mock_surface.cc
+++ b/ui/ozone/platform/wayland/test/mock_surface.cc
@@ -37,23 +37,38 @@
   GetUserDataAs<MockSurface>(resource)->Damage(x, y, width, height);
 }
 
+void Frame(struct wl_client* client,
+           struct wl_resource* resource,
+           uint32_t callback) {
+  GetUserDataAs<MockSurface>(resource)->Frame(callback);
+}
+
 void Commit(wl_client* client, wl_resource* resource) {
   GetUserDataAs<MockSurface>(resource)->Commit();
 }
 
+void DamageBuffer(struct wl_client* client,
+                  struct wl_resource* resource,
+                  int32_t x,
+                  int32_t y,
+                  int32_t width,
+                  int32_t height) {
+  GetUserDataAs<MockSurface>(resource)->DamageBuffer(x, y, width, height);
+}
+
 }  // namespace
 
 const struct wl_surface_interface kMockSurfaceImpl = {
     DestroyResource,  // destroy
     Attach,           // attach
     Damage,           // damage
-    nullptr,          // frame
+    Frame,            // frame
     SetOpaqueRegion,  // set_opaque_region
     SetInputRegion,   // set_input_region
     Commit,           // commit
     nullptr,          // set_buffer_transform
     nullptr,          // set_buffer_scale
-    nullptr,          // damage_buffer
+    DamageBuffer,     // damage_buffer
 };
 
 MockSurface::MockSurface(wl_resource* resource) : ServerObject(resource) {}
diff --git a/ui/ozone/platform/wayland/test/mock_surface.h b/ui/ozone/platform/wayland/test/mock_surface.h
index 0f83878b..1ea9c52 100644
--- a/ui/ozone/platform/wayland/test/mock_surface.h
+++ b/ui/ozone/platform/wayland/test/mock_surface.h
@@ -33,9 +33,12 @@
   MOCK_METHOD3(Attach, void(wl_resource* buffer, int32_t x, int32_t y));
   MOCK_METHOD1(SetOpaqueRegion, void(wl_resource* region));
   MOCK_METHOD1(SetInputRegion, void(wl_resource* region));
+  MOCK_METHOD1(Frame, void(uint32_t callback));
   MOCK_METHOD4(Damage,
                void(int32_t x, int32_t y, int32_t width, int32_t height));
   MOCK_METHOD0(Commit, void());
+  MOCK_METHOD4(DamageBuffer,
+               void(int32_t x, int32_t y, int32_t width, int32_t height));
 
   void set_xdg_surface(std::unique_ptr<MockXdgSurface> xdg_surface) {
     xdg_surface_ = std::move(xdg_surface);
diff --git a/ui/ozone/platform/wayland/test/wayland_test.cc b/ui/ozone/platform/wayland/test/wayland_test.cc
index e96443b..66726ead 100644
--- a/ui/ozone/platform/wayland/test/wayland_test.cc
+++ b/ui/ozone/platform/wayland/test/wayland_test.cc
@@ -6,6 +6,7 @@
 
 #include "base/run_loop.h"
 #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
+#include "ui/ozone/platform/wayland/gpu/wayland_surface_factory.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 
@@ -30,9 +31,11 @@
   KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
       std::make_unique<StubKeyboardLayoutEngine>());
 #endif
-  connection_.reset(new WaylandConnection);
-  connection_proxy_.reset(
-      new WaylandConnectionProxy(connection_.get(), nullptr));
+  connection_ = std::make_unique<WaylandConnection>();
+  surface_factory_ = std::make_unique<WaylandSurfaceFactory>(connection_.get());
+  connection_proxy_ =
+      std::make_unique<WaylandConnectionProxy>(surface_factory_.get());
+  surface_factory_->SetProxy(connection_proxy_.get());
   window_ = std::make_unique<WaylandWindow>(&delegate_, connection_.get());
 }
 
diff --git a/ui/ozone/platform/wayland/test/wayland_test.h b/ui/ozone/platform/wayland/test/wayland_test.h
index 461d279..69f3439e 100644
--- a/ui/ozone/platform/wayland/test/wayland_test.h
+++ b/ui/ozone/platform/wayland/test/wayland_test.h
@@ -30,6 +30,8 @@
 const uint32_t kXdgShellV5 = 5;
 const uint32_t kXdgShellV6 = 6;
 
+class WaylandSurfaceFactory;
+
 // WaylandTest is a base class that sets up a display, window, and test server,
 // and allows easy synchronization between them.
 class WaylandTest : public ::testing::TestWithParam<uint32_t> {
@@ -49,6 +51,7 @@
   wl::MockSurface* surface_;
 
   MockPlatformWindowDelegate delegate_;
+  std::unique_ptr<WaylandSurfaceFactory> surface_factory_;
   std::unique_ptr<WaylandConnectionProxy> connection_proxy_;
   std::unique_ptr<WaylandConnection> connection_;
   std::unique_ptr<WaylandWindow> window_;
diff --git a/ui/ozone/public/interfaces/wayland/wayland_connection.mojom b/ui/ozone/public/interfaces/wayland/wayland_connection.mojom
index dd343f7..b1dc000 100644
--- a/ui/ozone/public/interfaces/wayland/wayland_connection.mojom
+++ b/ui/ozone/public/interfaces/wayland/wayland_connection.mojom
@@ -4,7 +4,6 @@
 
 module ui.ozone.mojom;
 
-import "mojo/public/mojom/base/file.mojom";
 import "mojo/public/mojom/base/file_path.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "ui/gfx/mojo/accelerated_widget.mojom";
@@ -17,9 +16,11 @@
   // Sets up an associated pipe between the Client and Host.
   SetWaylandConnectionClient(associated WaylandConnectionClient client);
 
-  // Methods used for hardware accelerated rendering:
+  // The following two methods are used either for hardware accelerated
+  // rendering or for the software rendering.
   //
-  // Asks Wayland to create a wl_buffer based on the dmabuf |file| descriptor
+  // If hardware accelerated rendering path is taken, this methnod can be used
+  // to ask Wayland to create a wl_buffer based on the |dmabuf_fd| descriptor
   // for the WaylandWindow, which has the following |widget|. The |size|
   // is the size of the buffer, the |strides|, |offsets| and |modifiers|
   // are the descriptions of the drm buffer object. The |format| describes
@@ -28,21 +29,33 @@
   // descriptor has. And the |buffer_id| is a unique id for the buffer, which
   // is used to identify imported wl_buffers on the browser process side and
   // map them with the buffer objects on the gpu process side.
-  CreateZwpLinuxDmabuf(gfx.mojom.AcceleratedWidget widget,
-                       mojo_base.mojom.File file,
+  CreateDmabufBasedBuffer(gfx.mojom.AcceleratedWidget widget,
+                          handle dmabuf_fd,
+                          gfx.mojom.Size size,
+                          array<uint32> strides,
+                          array<uint32> offsets,
+                          array<uint64> modifiers,
+                          uint32 format,
+                          uint32 planes_count,
+                          uint32 buffer_id);
+
+  // If software rendering path is used, this method can be used to ask
+  // Wayland to create a wl_buffer based on the |shm_fd| descriptor.
+  // The |length| is the length of the shared memory, |size|
+  // is the size of buffer and |buffer_id| is the id of the buffer.
+  CreateShmBasedBuffer(gfx.mojom.AcceleratedWidget widget,
+                       handle shm_fd,
+                       uint64 length,
                        gfx.mojom.Size size,
-                       array<uint32> strides,
-                       array<uint32> offsets,
-                       array<uint64> modifiers,
-                       uint32 format,
-                       uint32 planes_count,
                        uint32 buffer_id);
 
+  // These two methods are independent from the type of rendering.
+  //
   // Destroys a wl_buffer created by WaylandConnection based on the |buffer_id|
   // for the WaylandWindow, which has the following |widget|. The |buffer_id|
   // is the unique id of the buffer objects being destroyed on the browser
   // process side.
-  DestroyZwpLinuxDmabuf(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id);
+  DestroyBuffer(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id);
 
   // Attaches a wl_buffer to a WaylandWindow's surface with the following
   // |widget|. The |damage_region| describes changed the region of the buffer.
@@ -51,29 +64,6 @@
   // the ones on the gpu process.
   CommitBuffer(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id,
                gfx.mojom.Rect damage_region);
-
-  // Methods used for software rendering:
-  //
-  // Asks Wayland to create a wl_buffer based on the shared memory |file|
-  // descriptor. The |length| is the length of the shared memory, |size|
-  // is the size of buffer in bytes and |buffer_id| is the id of the buffer.
-  CreateShmBufferForWidget(gfx.mojom.AcceleratedWidget widget,
-                           mojo_base.mojom.File file,
-                           uint64 length,
-                           gfx.mojom.Size size,
-                           uint32 buffer_id);
-
-  // Attaches a wl_buffer to a WaylandWindow's surface with the following
-  // |widget|. The |damage| describes changed the region of the buffer.
-  // The |buffer_id| is a unique id for the buffer, which is used to
-  // identify imported wl_buffers on the browser process side mapped with
-  // the ones on the gpu process.
-  PresentShmBufferForWidget(gfx.mojom.AcceleratedWidget widget,
-                            gfx.mojom.Rect damage,
-                            uint32 buffer_id);
-
-  // Destroys the buffer with |buffer_id| for the |widget|.
-  DestroyShmBuffer(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id);
 };
 
 
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index d2863a2..f515839c 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -218,12 +218,21 @@
 
 base::TimeDelta GetPasswordRevealDuration(const ui::KeyEvent& event) {
   // The key event may carries the property that indicates it was from the
-  // virtual keyboard.
-  // In that case, reveals the password characters for 1 second.
+  // virtual keyboard and mirroring is not occurring
+  // In that case, reveal the password characters for 1 second.
   auto* properties = event.properties();
   bool from_vk =
       properties && properties->find(ui::kPropertyFromVK) != properties->end();
-  return from_vk ? base::TimeDelta::FromSeconds(1) : base::TimeDelta();
+  if (from_vk) {
+    std::vector<uint8_t> fromVKPropertyArray =
+        properties->find(ui::kPropertyFromVK)->second;
+    DCHECK_GT(fromVKPropertyArray.size(), ui::kPropertyFromVKIsMirroringIndex);
+    uint8_t is_mirroring =
+        fromVKPropertyArray[ui::kPropertyFromVKIsMirroringIndex];
+    if (!is_mirroring)
+      return base::TimeDelta::FromSeconds(1);
+  }
+  return base::TimeDelta();
 }
 
 bool IsControlKeyModifier(int flags) {
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index 00bc39c0..997bcdf 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -567,7 +567,8 @@
       ui::KeyEvent event(ch, ui::VKEY_UNKNOWN, ui::DomCode::NONE, flags);
       if (from_vk) {
         ui::Event::Properties properties;
-        properties[ui::kPropertyFromVK] = std::vector<uint8_t>();
+        properties[ui::kPropertyFromVK] =
+            std::vector<uint8_t>(ui::kPropertyFromVKSize);
         event.SetProperties(properties);
       }
 #if defined(OS_MACOSX)