diff --git a/DEPS b/DEPS
index 9b8bbcec..82f1db1 100644
--- a/DEPS
+++ b/DEPS
@@ -121,7 +121,7 @@
   # 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': '1345366c21ea285b4225ce9bf0de4ef401d76d72',
+  'skia_revision': '88bfed46ab6e0e4aec5416092a349e394c72ba59',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -133,7 +133,7 @@
   # 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': '794364ebd51c1c7d113fc4208857a15cd557b4db',
+  'angle_revision': '48d040e8c5241d22ba04746de1fb920268a25aeb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -145,7 +145,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'a2e2d7f291cfa232c3e5ef48d086c0002b0e06df',
+  'pdfium_revision': '7a629886aed968d8bfcabfe48cd898465e4ef469',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -185,7 +185,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '2061050c4d27b3b27bc3cfc7cf20fc99644f66f7',
+  'catapult_revision': 'b6cc5a6baf93cfa6feeb240eea75c454506b0c3c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -233,7 +233,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': '464111eaef80c4951e63473bc1c9e072f8d33588',
+  'spv_tools_revision': '453b7c85c8fde99251028125a19cd3a20a1a758b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -694,7 +694,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '6e669b46b4792ae79198377d36757053c9b445aa',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '2e5e7db63cd9aaa4d7052b10fe18f54f4acbfa26',
       'condition': 'checkout_linux',
   },
 
@@ -719,7 +719,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '50b187ca83716db937923d9ed1ef031f6e071d2a',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '39b0b8e32a4ed0675a38d97799e8a219cc549910',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -948,7 +948,7 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '9ecc0e779a29281e5698451bfd1b3ebe8f053bfd',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'cde3da57b9a18636026531694ca76671894d1dce',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + 'e4931ebc0a816458c18a6734e91a4d1b5acd5c56',
@@ -1258,7 +1258,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@26fd840339f85a6169bbbc0cd0d4cd2d847ea0e5',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2cde328881f0aaec7d59c81f2b861d4fbace3d10',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTest.java
index 3057d796..6f4f333 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTest.java
@@ -36,28 +36,35 @@
     public void
     testModeBrowser() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(
-                shim.runTestForMode("browser", false, "native-include-thread-names", false, false));
+        Assert.assertTrue(shim.runTestForMode(
+                "browser", false, "native-include-thread-names", true, false, false));
     }
 
     @Test
     @MediumTest
     public void testModeBrowserDynamicPseudo() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", false, false));
+        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, false, false));
     }
 
     @Test
     @MediumTest
     public void testModeBrowserDynamicPseudoSampleEverything() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true));
+        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true, true));
     }
 
     @Test
     @MediumTest
     public void testModeBrowserDynamicPseudoSamplePartial() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, false));
+        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true, false));
+    }
+
+    @Test
+    @MediumTest
+    public void testModeBrowserDynamicPseudoSamplePartialNonStreaming() throws Exception {
+        HeapProfilingTestShim shim = new HeapProfilingTestShim();
+        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", false, true, false));
     }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTestShim.java b/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTestShim.java
index 7403f6b5..6a3f052 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTestShim.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTestShim.java
@@ -24,9 +24,9 @@
      *  rather than native stacks.
      */
     public boolean runTestForMode(String mode, boolean dynamicallyStartProfiling, String stackMode,
-            boolean shouldSample, boolean sampleEverything) {
+            boolean streamSamples, boolean shouldSample, boolean sampleEverything) {
         return nativeRunTestForMode(mNativeHeapProfilingTestShim, mode, dynamicallyStartProfiling,
-                stackMode, shouldSample, sampleEverything);
+                stackMode, streamSamples, shouldSample, sampleEverything);
     }
 
     /**
@@ -44,6 +44,6 @@
     private native long nativeInit();
     private native void nativeDestroy(long nativeHeapProfilingTestShim);
     private native boolean nativeRunTestForMode(long nativeHeapProfilingTestShim, String mode,
-            boolean dynamicallyStartProfiling, String stackMode, boolean shouldSample,
-            boolean sampleEverything);
+            boolean dynamicallyStartProfiling, String stackMode, boolean streamSamples,
+            boolean shouldSample, boolean sampleEverything);
 }
diff --git a/ash/login/ui/login_user_menu_view.cc b/ash/login/ui/login_user_menu_view.cc
index 5ab0776..8fed571 100644
--- a/ash/login/ui/login_user_menu_view.cc
+++ b/ash/login/ui/login_user_menu_view.cc
@@ -10,6 +10,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "ui/views/controls/separator.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
@@ -252,6 +254,13 @@
 
 LoginUserMenuView::~LoginUserMenuView() = default;
 
+void LoginUserMenuView::ResetState() {
+  if (remove_user_confirm_data_) {
+    remove_user_confirm_data_->SetVisible(false);
+    remove_user_label_->SetEnabledColor(kRemoveUserInitialColor);
+  }
+}
+
 LoginButton* LoginUserMenuView::GetBubbleOpener() const {
   return bubble_opener_;
 }
@@ -264,7 +273,6 @@
     remove_user_confirm_data_->SetVisible(true);
     remove_user_label_->SetEnabledColor(kRemoveUserConfirmColor);
 
-    SetSize(GetPreferredSize());
     Layout();
 
     // Fire an accessibility alert to make ChromeVox read the warning message
@@ -294,6 +302,24 @@
   if (GetAnchorView())
     position.set_y(position.y() + kAnchorViewUserMenuVerticalSpacingDp);
 
+  gfx::Rect screen_bounds =
+      display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
+
+  // In handling the cases where the bubble could go off screen, we assume that
+  // the bubble can go either off the right side or off the bottom side.
+  if (position.x() + width() > screen_bounds.right() && GetAnchorView()) {
+    // If bubble would go off the right side of the screen, go left instead
+    position.set_x(position.x() + GetAnchorView()->width() - width());
+  } else if (position.y() + height() > screen_bounds.bottom() &&
+             GetAnchorView()) {
+    // If bubble would go off the bottom of the screen, go to the right of the
+    // anchor and upward.
+    position.set_x(position.x() + kAnchorViewUserMenuVerticalSpacingDp +
+                   GetAnchorView()->width());
+    position.set_y(position.y() +
+                   (screen_bounds.bottom() - (position.y() + height())));
+  }
+
   return position;
 }
 
diff --git a/ash/login/ui/login_user_menu_view.h b/ash/login/ui/login_user_menu_view.h
index d14a1fe..2bd29a3 100644
--- a/ash/login/ui/login_user_menu_view.h
+++ b/ash/login/ui/login_user_menu_view.h
@@ -45,6 +45,9 @@
 
   ~LoginUserMenuView() override;
 
+  // Resets the user menu to the state where Remove User has not been pressed.
+  void ResetState();
+
   // LoginBaseBubbleView:
   LoginButton* GetBubbleOpener() const override;
   gfx::Point CalculatePosition() override;
diff --git a/ash/login/ui/login_user_menu_view_unittest.cc b/ash/login/ui/login_user_menu_view_unittest.cc
index 81427f1..016b836 100644
--- a/ash/login/ui/login_user_menu_view_unittest.cc
+++ b/ash/login/ui/login_user_menu_view_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 <memory>
+
 #include "ash/login/ui/login_user_menu_view.h"
 #include "ash/login/ui/login_button.h"
 #include "ash/login/ui/login_test_base.h"
@@ -141,4 +143,29 @@
   EXPECT_FALSE(ink_drop_api.GetInkDrop()->IsHighlightFadingInOrVisible());
 }
 
+TEST_F(LoginUserMenuViewTest, ResetStateHidesConfirmData) {
+  auto* container = new views::View;
+  container->SetLayoutManager(
+      std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
+  SetWidget(CreateWidgetWithContent(container));
+
+  auto* bubble = new LoginUserMenuView(
+      base::string16(), base::string16(), user_manager::USER_TYPE_REGULAR,
+      false /*is_owner*/, nullptr /*anchor*/, nullptr /*bubble_opener*/,
+      true /*show_remove_user*/, base::DoNothing(), base::DoNothing());
+  container->AddChildView(bubble);
+
+  bubble->Show();
+
+  LoginUserMenuView::TestApi test_api(bubble);
+  EXPECT_FALSE(test_api.remove_user_confirm_data()->visible());
+
+  test_api.remove_user_button()->RequestFocus();
+  GetEventGenerator()->PressKey(ui::KeyboardCode::VKEY_RETURN, 0);
+  EXPECT_TRUE(test_api.remove_user_confirm_data()->visible());
+
+  bubble->ResetState();
+  EXPECT_FALSE(test_api.remove_user_confirm_data()->visible());
+}
+
 }  // namespace ash
diff --git a/ash/login/ui/login_user_view.cc b/ash/login/ui/login_user_view.cc
index 4c4b6f7..e34cb5d 100644
--- a/ash/login/ui/login_user_view.cc
+++ b/ash/login/ui/login_user_view.cc
@@ -552,6 +552,8 @@
     if (!menu_->parent())
       login_views_utils::GetTopLevelParentView(this)->AddChildView(menu_);
 
+    // Reset state in case the remove-user button was clicked once previously.
+    menu_->ResetState();
     menu_->Show();
 
     // If the menu was opened by pressing Enter on the focused dropdown, focus
diff --git a/ash/login/ui/login_user_view.h b/ash/login/ui/login_user_view.h
index 61300eb..34d04b4 100644
--- a/ash/login/ui/login_user_view.h
+++ b/ash/login/ui/login_user_view.h
@@ -123,7 +123,7 @@
   // Bubble used for displaying the user dropdown menu. Its parent is the top
   // level view, either LockContentsView or LockDebugView. This allows the menu
   // to be clicked outside the bounds of the user view.
-  LoginBaseBubbleView* menu_ = nullptr;
+  LoginUserMenuView* menu_ = nullptr;
 
   // Show the domain information for public account user.
   UserDomainInfoView* user_domain_ = nullptr;
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 3b7c95a..8082a87c 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -207,7 +207,6 @@
 
 // See content::RunAllTasksUntilIdle().
 void RunAllTasksUntilIdle() {
-  LOG(ERROR) << "RunAllTasksUntilIdle - before";
   while (true) {
     TaskObserver task_observer;
     base::MessageLoopCurrent::Get()->AddTaskObserver(&task_observer);
@@ -220,7 +219,6 @@
     if (!task_observer.processed())
       break;
   }
-  LOG(ERROR) << "RunAllTasksUntilIdle - after";
 }
 
 // A test implementation of the WallpaperObserver mojo interface.
diff --git a/base/base_paths_fuchsia.cc b/base/base_paths_fuchsia.cc
index 72179a9..5e4741d 100644
--- a/base/base_paths_fuchsia.cc
+++ b/base/base_paths_fuchsia.cc
@@ -17,7 +17,7 @@
 bool PathProviderFuchsia(int key, FilePath* result) {
   switch (key) {
     case FILE_MODULE:
-      NOTIMPLEMENTED();
+      NOTIMPLEMENTED() << " for FILE_MODULE.";
       return false;
     case FILE_EXE:
       *result = CommandLine::ForCurrentProcess()->GetProgram();
diff --git a/base/process/process_metrics_fuchsia.cc b/base/process/process_metrics_fuchsia.cc
index 417f7ccf..7e4e504 100644
--- a/base/process/process_metrics_fuchsia.cc
+++ b/base/process/process_metrics_fuchsia.cc
@@ -13,25 +13,25 @@
 }
 
 size_t GetSystemCommitCharge() {
-  // Not available, doesn't seem likely that it will be (for the whole system).
-  NOTIMPLEMENTED();
+  // TODO(https://crbug.com/926581): Fuchsia does not support this.
+  NOTIMPLEMENTED_LOG_ONCE();
   return 0;
 }
 
 // static
 std::unique_ptr<ProcessMetrics> ProcessMetrics::CreateProcessMetrics(
     ProcessHandle process) {
-  NOTIMPLEMENTED();  // TODO(fuchsia): https://crbug.com/706592.
+  NOTIMPLEMENTED_LOG_ONCE();  // TODO(https://crbug.com/926581).
   return nullptr;
 }
 
 TimeDelta ProcessMetrics::GetCumulativeCPUUsage() {
-  NOTIMPLEMENTED();  // TODO(fuchsia): https://crbug.com/706592.
+  NOTIMPLEMENTED_LOG_ONCE();  // TODO(https://crbug.com/926581).
   return TimeDelta();
 }
 
 bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
-  NOTIMPLEMENTED();  // TODO(fuchsia): https://crbug.com/706592.
+  NOTIMPLEMENTED_LOG_ONCE();  // TODO(https://crbug.com/926581).
   return false;
 }
 
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
index eac17086..14f20a5 100644
--- a/base/sampling_heap_profiler/poisson_allocation_sampler.cc
+++ b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
@@ -325,6 +325,11 @@
   TLSSetValue(g_internal_reentry_guard, false);
 }
 
+// static
+bool PoissonAllocationSampler::ScopedMuteThreadSamples::IsMuted() {
+  return TLSGetValue(g_internal_reentry_guard);
+}
+
 PoissonAllocationSampler* PoissonAllocationSampler::instance_;
 
 PoissonAllocationSampler::PoissonAllocationSampler() {
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.h b/base/sampling_heap_profiler/poisson_allocation_sampler.h
index a55fdea..0dfb8b2a8 100644
--- a/base/sampling_heap_profiler/poisson_allocation_sampler.h
+++ b/base/sampling_heap_profiler/poisson_allocation_sampler.h
@@ -54,6 +54,8 @@
    public:
     ScopedMuteThreadSamples();
     ~ScopedMuteThreadSamples();
+
+    static bool IsMuted();
   };
 
   // Must be called early during the process initialization. It creates and
diff --git a/base/threading/platform_thread_fuchsia.cc b/base/threading/platform_thread_fuchsia.cc
index e1e41a4..b732be7c 100644
--- a/base/threading/platform_thread_fuchsia.cc
+++ b/base/threading/platform_thread_fuchsia.cc
@@ -38,7 +38,8 @@
 // static
 void PlatformThread::SetCurrentThreadPriorityImpl(ThreadPriority priority) {
   if (priority != ThreadPriority::NORMAL) {
-    NOTIMPLEMENTED() << "setting ThreadPriority " << static_cast<int>(priority);
+    // TODO(https://crbug.com/926583): Fuchsia does not currently support this.
+    NOTIMPLEMENTED_LOG_ONCE() << " to non-NORMAL priority.";
   }
 }
 
diff --git a/base/threading/sequence_bound.h b/base/threading/sequence_bound.h
index c5fb9d0..683bc42 100644
--- a/base/threading/sequence_bound.h
+++ b/base/threading/sequence_bound.h
@@ -214,6 +214,9 @@
   // might still be pending destruction on the impl thread.
   bool is_null() const { return !t_; }
 
+  // True if and only if we have an object, with the same caveats as is_null().
+  explicit operator bool() const { return !is_null(); }
+
  private:
   // Pointer to the object,  Pointer may be modified on the owning thread.
   T* t_ = nullptr;
diff --git a/base/threading/sequence_bound_unittest.cc b/base/threading/sequence_bound_unittest.cc
index d468aa32..61e2b7b 100644
--- a/base/threading/sequence_bound_unittest.cc
+++ b/base/threading/sequence_bound_unittest.cc
@@ -80,6 +80,7 @@
 TEST_F(SequenceBoundTest, MAYBE_ConstructThenPostThenReset) {
   auto derived = SequenceBound<Derived>(task_runner_, &value);
   EXPECT_FALSE(derived.is_null());
+  EXPECT_TRUE(derived);
 
   // Nothing should happen until we run the message loop.
   EXPECT_EQ(value, kInitialValue);
@@ -96,6 +97,7 @@
   // report that it is null immediately.
   derived.Reset();
   EXPECT_TRUE(derived.is_null());
+  EXPECT_FALSE(derived);
   EXPECT_EQ(value, kDifferentValue);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(value, kDerivedDtorValue);
diff --git a/base/trace_event/trace_config.cc b/base/trace_event/trace_config.cc
index d5aca59..6b208d4 100644
--- a/base/trace_event/trace_config.cc
+++ b/base/trace_event/trace_config.cc
@@ -55,6 +55,8 @@
 // String parameter used to parse process filter.
 const char kIncludedProcessesParam[] = "included_process_ids";
 
+const char kHistogramNamesParam[] = "histogram_names";
+
 class ConvertableTraceConfigToTraceFormat
     : public base::trace_event::ConvertableToTraceFormat {
  public:
@@ -295,6 +297,7 @@
   process_filter_config_ = rhs.process_filter_config_;
   memory_dump_config_ = rhs.memory_dump_config_;
   event_filters_ = rhs.event_filters_;
+  histogram_names_ = rhs.histogram_names_;
   return *this;
 }
 
@@ -337,6 +340,8 @@
 
   event_filters_.insert(event_filters_.end(), config.event_filters().begin(),
                         config.event_filters().end());
+  histogram_names_.insert(config.histogram_names().begin(),
+                          config.histogram_names().end());
 }
 
 void TraceConfig::Clear() {
@@ -348,6 +353,7 @@
   memory_dump_config_.Clear();
   process_filter_config_.Clear();
   event_filters_.clear();
+  histogram_names_.clear();
 }
 
 void TraceConfig::InitializeDefault() {
@@ -386,6 +392,9 @@
   const base::ListValue* category_event_filters = nullptr;
   if (dict.GetList(kEventFiltersParam, &category_event_filters))
     SetEventFiltersFromConfigList(*category_event_filters);
+  const base::ListValue* histogram_names = nullptr;
+  if (dict.GetList(kHistogramNamesParam, &histogram_names))
+    SetHistogramNamesFromConfigList(*histogram_names);
 
   if (category_filter_.IsCategoryEnabled(MemoryDumpManager::kTraceCategory)) {
     // If dump triggers not set, the client is using the legacy with just
@@ -517,6 +526,13 @@
   process_filter_config_ = config;
 }
 
+void TraceConfig::SetHistogramNamesFromConfigList(
+    const base::ListValue& histogram_names) {
+  histogram_names_.clear();
+  for (const Value& value : histogram_names.GetList())
+    histogram_names_.insert(value.GetString());
+}
+
 void TraceConfig::SetEventFiltersFromConfigList(
     const base::ListValue& category_event_filters) {
   event_filters_.clear();
@@ -598,9 +614,21 @@
     }
     dict->Set(kMemoryDumpConfigParam, std::move(memory_dump_config));
   }
+
+  if (!histogram_names_.empty()) {
+    std::unique_ptr<base::ListValue> histogram_names(new base::ListValue());
+    for (const std::string& histogram_name : histogram_names_)
+      histogram_names->AppendString(histogram_name);
+    dict->Set(kHistogramNamesParam, std::move(histogram_names));
+  }
+
   return dict;
 }
 
+void TraceConfig::EnableHistogram(const std::string& histogram_name) {
+  histogram_names_.insert(histogram_name);
+}
+
 std::string TraceConfig::ToTraceOptionsString() const {
   std::string ret;
   switch (record_mode_) {
diff --git a/base/trace_event/trace_config.h b/base/trace_event/trace_config.h
index e3be39d..09bdd60 100644
--- a/base/trace_event/trace_config.h
+++ b/base/trace_event/trace_config.h
@@ -238,6 +238,7 @@
   }
   void EnableSystrace() { enable_systrace_ = true; }
   void EnableArgumentFilter() { enable_argument_filter_ = true; }
+  void EnableHistogram(const std::string& histogram_name);
 
   // Writes the string representation of the TraceConfig. The string is JSON
   // formatted.
@@ -280,6 +281,10 @@
     event_filters_ = filter_configs;
   }
 
+  const std::unordered_set<std::string>& histogram_names() const {
+    return histogram_names_;
+  }
+
  private:
   FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromValidLegacyFormat);
   FRIEND_TEST_ALL_PREFIXES(TraceConfigTest,
@@ -304,6 +309,7 @@
       const DictionaryValue& memory_dump_config);
   void SetDefaultMemoryDumpConfig();
 
+  void SetHistogramNamesFromConfigList(const base::ListValue& histogram_names);
   void SetEventFiltersFromConfigList(const base::ListValue& event_filters);
   std::unique_ptr<DictionaryValue> ToDict() const;
 
@@ -320,6 +326,7 @@
   ProcessFilterConfig process_filter_config_;
 
   EventFilters event_filters_;
+  std::unordered_set<std::string> histogram_names_;
 };
 
 }  // namespace trace_event
diff --git a/base/trace_event/trace_config_unittest.cc b/base/trace_event/trace_config_unittest.cc
index 9fb3753..5cfd4e5 100644
--- a/base/trace_event/trace_config_unittest.cc
+++ b/base/trace_event/trace_config_unittest.cc
@@ -37,6 +37,7 @@
     "}"
     "],"
     "\"excluded_categories\":[\"excluded\",\"exc_pattern*\"],"
+    "\"histogram_names\":[\"uma1\",\"uma2\"],"
     "\"included_categories\":["
     "\"included\","
     "\"inc_pattern*\","
@@ -81,6 +82,21 @@
       "disabled-by-default-cc,disabled-by-default-cc2"));
 }
 
+// Returns an string in which word1 and word2 are swapped. word1 and word2 must
+// be non-overlapping substrings of the input string and word1 must be before
+// word2.
+std::string SwapWords(const std::string& in_str,
+                      const std::string& word1,
+                      const std::string& word2) {
+  size_t pos1 = in_str.find(word1);
+  size_t len1 = word1.size();
+  size_t pos2 = in_str.find(word2);
+  size_t len2 = word2.size();
+  return in_str.substr(0, pos1) + word2 +
+         in_str.substr(pos1 + len1, pos2 - pos1 - len1) + word1 +
+         in_str.substr(pos2 + len2);
+}
+
 }  // namespace
 
 TEST(TraceConfigTest, TraceConfigFromValidLegacyFormat) {
@@ -345,7 +361,10 @@
   is_dict = custom_value->GetAsDictionary(&custom_dict);
   DCHECK(is_dict);
   TraceConfig custom_tc(*custom_dict);
-  EXPECT_STREQ(kCustomTraceConfigString, custom_tc.ToString().c_str());
+  std::string custom_tc_str = custom_tc.ToString();
+  EXPECT_TRUE(custom_tc_str == kCustomTraceConfigString ||
+              custom_tc_str ==
+                  SwapWords(kCustomTraceConfigString, "uma1", "uma2"));
   EXPECT_EQ(RECORD_CONTINUOUSLY, custom_tc.GetTraceRecordMode());
   EXPECT_TRUE(custom_tc.IsSystraceEnabled());
   EXPECT_TRUE(custom_tc.IsArgumentFilterEnabled());
diff --git a/build/config/fuchsia/testing_sandbox_policy b/build/config/fuchsia/testing_sandbox_policy
index 03fe2bb..dd1c7f9 100644
--- a/build/config/fuchsia/testing_sandbox_policy
+++ b/build/config/fuchsia/testing_sandbox_policy
@@ -8,6 +8,7 @@
       "fuchsia.media.Audio",
       "fuchsia.mediacodec.CodecFactory",
       "fuchsia.net.LegacySocketProvider",
+      "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
       "fuchsia.process.Launcher",
       "fuchsia.sys.Launcher",
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn
index 6b92f3e..f4c94fd20 100644
--- a/build/config/sanitizers/BUILD.gn
+++ b/build/config/sanitizers/BUILD.gn
@@ -259,7 +259,6 @@
       # Incremental linking causes padding that messes up SanitizerCoverage.
       # Don't do it.
       ldflags = [ "/INCREMENTAL:NO" ]
-      libs = [ "clang_rt.fuzzer_no_main-x86_64.lib" ]
     }
   }
 }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 2c96613..38c0182 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-f8f8ce7b42e9377b18a457804a6928665004e5b5
\ No newline at end of file
+092aef2aaad3c9ceb2b9771170055371b987fe69
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index be4b1c9..20d2e7f 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-66374af5ae391a2a5152a90e097e991acf9c8d68
\ No newline at end of file
+793fd71e247d7f737d5466cd70c34f32773d5a9e
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 96708ac5..16ea670d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -372,14 +372,15 @@
     }
 
     /**
-     * Return whether the passed in class name matches any of the supported tabbed mode activities.
+     * Return whether the passed in component name matches any of the supported tabbed mode
+     * activities.
      */
     public static boolean isTabbedModeComponentName(String componentName) {
         return TextUtils.equals(componentName, ChromeTabbedActivity.class.getName())
                 || TextUtils.equals(
                         componentName, MultiInstanceChromeTabbedActivity.class.getName())
                 || TextUtils.equals(componentName, ChromeTabbedActivity2.class.getName())
-                || TextUtils.equals(componentName, BuildInfo.getInstance().packageName + ".Main");
+                || TextUtils.equals(componentName, "com.google.android.apps.chrome.Main");
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/profiling_host/ProfilingProcessHostAndroidTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/profiling_host/ProfilingProcessHostAndroidTest.java
index f229514..d5c59aa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/profiling_host/ProfilingProcessHostAndroidTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/profiling_host/ProfilingProcessHostAndroidTest.java
@@ -43,22 +43,36 @@
     public void
     testModeBrowser() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(
-                shim.runTestForMode("browser", false, "native-include-thread-names", false, false));
+        Assert.assertTrue(shim.runTestForMode(
+                "browser", false, "native-include-thread-names", true, false, false));
     }
 
     @Test
     @MediumTest
     public void testModeBrowserDynamic() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(shim.runTestForMode("browser", true, "native", false, false));
+        Assert.assertTrue(shim.runTestForMode("browser", true, "native", true, false, false));
+    }
+
+    @Test
+    @MediumTest
+    public void testModeBrowserDynamicNonStreaming() throws Exception {
+        HeapProfilingTestShim shim = new HeapProfilingTestShim();
+        Assert.assertTrue(shim.runTestForMode("browser", true, "native", false, false, false));
     }
 
     @Test
     @MediumTest
     public void testModeBrowserDynamicPseudo() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", false, false));
+        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, false, false));
+    }
+
+    @Test
+    @MediumTest
+    public void testModeBrowserDynamicPseudoNonStreaming() throws Exception {
+        HeapProfilingTestShim shim = new HeapProfilingTestShim();
+        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", false, false, false));
     }
 
     // Non-browser processes must be profiled with a command line flag, since
@@ -73,7 +87,8 @@
     Add({"memlog=all-renderers", "memlog-stack-mode=pseudo", "memlog-sampling-rate=1"})
     public void testModeRendererPseudo() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(shim.runTestForMode("all-renderers", false, "pseudo", false, false));
+        Assert.assertTrue(
+                shim.runTestForMode("all-renderers", false, "pseudo", true, false, false));
     }
 
     @Test
@@ -81,27 +96,28 @@
     @CommandLineFlags.Add({"memlog=gpu", "memlog-stack-mode=pseudo", "memlog-sampling-rate=1"})
     public void testModeGpuPseudo() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(shim.runTestForMode("gpu", false, "native", false, false));
+        Assert.assertTrue(shim.runTestForMode("gpu", false, "native", true, false, false));
     }
 
     @Test
     @MediumTest
     public void testModeBrowserDynamicPseudoSampleEverything() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true));
+        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true, true));
     }
 
     @Test
     @MediumTest
     public void testModeBrowserDynamicPseudoSamplePartial() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, false));
+        Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true, false));
     }
 
     @Test
     @MediumTest
     public void testModeBrowserAndAllUtility() throws Exception {
         HeapProfilingTestShim shim = new HeapProfilingTestShim();
-        Assert.assertTrue(shim.runTestForMode("utility-and-browser", true, "pseudo", true, false));
+        Assert.assertTrue(
+                shim.runTestForMode("utility-and-browser", true, "pseudo", true, true, false));
     }
 }
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index e0f586f..3727f79 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3336,7 +3336,6 @@
     }
   } else {  # Non-ChromeOS.
     sources += [
-      "badging/badge_service_delegate.h",
       "fullscreen.h",
       "policy/browser_signin_policy_handler.cc",
       "policy/browser_signin_policy_handler.h",
@@ -3353,6 +3352,8 @@
 
   if (is_win) {
     sources += [
+      "badging/badge_manager_delegate_win.cc",
+      "badging/badge_manager_delegate_win.h",
       "downgrade/user_data_downgrade.cc",
       "downgrade/user_data_downgrade.h",
       "first_run/upgrade_util.cc",
@@ -3463,7 +3464,11 @@
 
   if (is_mac) {
     allow_circular_includes_from += [ "//chrome/browser/apps/app_shim" ]
-    sources += [ "download/drag_download_item_mac.mm" ]
+    sources += [
+      "badging/badge_manager_delegate_mac.cc",
+      "badging/badge_manager_delegate_mac.h",
+      "download/drag_download_item_mac.mm",
+    ]
     deps += [
       "//chrome/app_shim",
       "//chrome/browser/apps/app_shim",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 610017f..dfc7893 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -48,6 +48,7 @@
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/autofill/core/common/autofill_util.h"
+#include "components/autofill_assistant/browser/features.h"
 #include "components/browser_sync/browser_sync_switches.h"
 #include "components/cloud_devices/common/cloud_devices_switches.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
@@ -2698,10 +2699,6 @@
     {"fill-on-account-select", flag_descriptions::kFillOnAccountSelectName,
      flag_descriptions::kFillOnAccountSelectDescription, kOsAll,
      FEATURE_VALUE_TYPE(password_manager::features::kFillOnAccountSelect)},
-    {"enable-new-remote-playback-pipeline",
-     flag_descriptions::kNewRemotePlaybackPipelineName,
-     flag_descriptions::kNewRemotePlaybackPipelineDescription, kOsAll,
-     FEATURE_VALUE_TYPE(media::kNewRemotePlaybackPipeline)},
     {"enable-surfaces-for-videos",
      flag_descriptions::kUseSurfaceLayerForVideoName,
      flag_descriptions::kUseSurfaceLayerForVideoDescription, kOsAll,
@@ -2976,6 +2973,10 @@
 #endif  // defined(OS_ANDROID)
 
 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
+    {"omnibox-experimental-keyword-mode",
+     flag_descriptions::kOmniboxExperimentalKeywordModeName,
+     flag_descriptions::kOmniboxExperimentalKeywordModeDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(omnibox::kExperimentalKeywordMode)},
     {"omnibox-reverse-answers", flag_descriptions::kOmniboxReverseAnswersName,
      flag_descriptions::kOmniboxReverseAnswersDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(omnibox::kOmniboxReverseAnswers)},
@@ -4290,6 +4291,14 @@
      FEATURE_VALUE_TYPE(media::kD3D11VideoDecoder)},
 #endif
 
+#if defined(OS_ANDROID)
+    {"autofill-assistant-chrome-entry",
+     flag_descriptions::kAutofillAssistantChromeEntryName,
+     flag_descriptions::kAutofillAssistantChromeEntryDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(
+         autofill_assistant::features::kAutofillAssistantChromeEntry)},
+#endif  // defined(OS_ANDROID)
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index c3344b8..1ba4a81 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -21,6 +21,7 @@
 #include "chrome/common/channel_info.h"
 #include "components/autofill_assistant/browser/access_token_fetcher.h"
 #include "components/autofill_assistant/browser/controller.h"
+#include "components/autofill_assistant/browser/features.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/version_info/channel.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -43,7 +44,7 @@
 namespace {
 
 const base::FeatureParam<std::string> kAutofillAssistantServerUrl{
-    &chrome::android::kAutofillAssistant, "url",
+    &autofill_assistant::features::kAutofillAssistant, "url",
     "https://automate-pa.googleapis.com"};
 
 // Time between two attempts to destroy the controller.
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 72040b0..812c7ec 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -14,6 +14,7 @@
 #include "base/stl_util.h"
 #include "chrome/common/chrome_features.h"
 #include "components/autofill/core/common/autofill_features.h"
+#include "components/autofill_assistant/browser/features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/download/public/common/download_features.h"
 #include "components/feed/feed_feature_list.h"
@@ -56,6 +57,7 @@
     &autofill::features::kAutofillManualFallbackAndroid,
     &autofill::features::kAutofillRefreshStyleAndroid,
     &autofill::features::kAutofillEnableCompanyName,
+    &autofill_assistant::features::kAutofillAssistant,
     &contextual_suggestions::kContextualSuggestionsButton,
     &contextual_suggestions::kContextualSuggestionsIPHReverseScroll,
     &contextual_suggestions::kContextualSuggestionsOptOut,
@@ -89,7 +91,6 @@
     &kAndroidPayIntegrationV2,
     &kAndroidPaymentApps,
     &kAndroidSiteSettingsUIRefresh,
-    &kAutofillAssistant,
     &kCastDeviceFilter,
     &kCCTBackgroundTab,
     &kCCTExternalLinkHandling,
@@ -222,9 +223,6 @@
 const base::Feature kAndroidSiteSettingsUIRefresh{
     "AndroidSiteSettingsUIRefresh", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kAutofillAssistant{"AutofillAssistant",
-                                       base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kBackgroundTaskComponentUpdate{
     "BackgroundTaskComponentUpdate", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 2bd790c..997c16c0 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -19,7 +19,6 @@
 extern const base::Feature kAndroidPayIntegrationV2;
 extern const base::Feature kAndroidPaymentApps;
 extern const base::Feature kAndroidSiteSettingsUIRefresh;
-extern const base::Feature kAutofillAssistant;
 extern const base::Feature kBackgroundTaskComponentUpdate;
 extern const base::Feature kCastDeviceFilter;
 extern const base::Feature kCCTBackgroundTab;
diff --git a/chrome/browser/badging/badge_manager.cc b/chrome/browser/badging/badge_manager.cc
index 9715dd4..f7c5ed4 100644
--- a/chrome/browser/badging/badge_manager.cc
+++ b/chrome/browser/badging/badge_manager.cc
@@ -4,21 +4,50 @@
 
 #include "chrome/browser/badging/badge_manager.h"
 
+#include <utility>
+
+#include "base/i18n/number_formatting.h"
 #include "base/logging.h"
-#include "base/optional.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/strings/grit/ui_strings.h"
+
+#if defined(OS_MACOSX)
+#include "chrome/browser/badging/badge_manager_delegate_mac.h"
+#elif defined(OS_WIN)
+#include "chrome/browser/badging/badge_manager_delegate_win.h"
+#endif
 
 namespace badging {
 
-BadgeManager::BadgeManager() = default;
+std::string GetBadgeString(base::Optional<uint64_t> badge_content) {
+  if (!badge_content)
+    return "•";
+
+  if (badge_content > 99u) {
+    return base::UTF16ToUTF8(l10n_util::GetStringFUTF16(
+        IDS_SATURATED_BADGE_CONTENT, base::FormatNumber(99)));
+  }
+
+  return base::UTF16ToUTF8(base::FormatNumber(badge_content.value()));
+}
+
+BadgeManager::BadgeManager(Profile* profile) {
+#if defined(OS_MACOSX)
+  SetDelegate(std::make_unique<BadgeManagerDelegateMac>(profile));
+#elif defined(OS_WIN)
+  SetDelegate(std::make_unique<BadgeManagerDelegateWin>(profile));
+#endif
+}
 
 BadgeManager::~BadgeManager() = default;
 
 void BadgeManager::UpdateBadge(const extensions::ExtensionId& extension_id,
-                               base::Optional<int> contents) {
-  auto it = badged_apps_.find(extension_id);
-  if (it != badged_apps_.end() && it->second == contents)
-    return;
-
+                               base::Optional<uint64_t> contents) {
   badged_apps_[extension_id] = contents;
 
   if (!delegate_)
@@ -28,16 +57,15 @@
 }
 
 void BadgeManager::ClearBadge(const extensions::ExtensionId& extension_id) {
-  auto removed = badged_apps_.erase(extension_id);
-  if (!removed || !delegate_)
+  badged_apps_.erase(extension_id);
+  if (!delegate_)
     return;
 
   delegate_->OnBadgeCleared(extension_id);
 }
 
-void BadgeManager::SetDelegate(BadgeManagerDelegate* badge_manager_delegate) {
-  DCHECK(!delegate_);
-  delegate_ = badge_manager_delegate;
+void BadgeManager::SetDelegate(std::unique_ptr<BadgeManagerDelegate> delegate) {
+  delegate_ = std::move(delegate);
 }
 
 }  // namespace badging
diff --git a/chrome/browser/badging/badge_manager.h b/chrome/browser/badging/badge_manager.h
index 83dd78e8..940cff3 100644
--- a/chrome/browser/badging/badge_manager.h
+++ b/chrome/browser/badging/badge_manager.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_BADGING_BADGE_MANAGER_H_
 
 #include <map>
+#include <memory>
 #include <string>
 
 #include "base/macros.h"
@@ -15,30 +16,35 @@
 #include "extensions/common/extension_id.h"
 
 class KeyedService;
+class Profile;
 
 namespace badging {
 
+// Determines the text to put on the badge based on some badge_content.
+std::string GetBadgeString(base::Optional<uint64_t> badge_content);
+
 // Maintains a record of badge contents and dispatches badge changes to a
 // delegate.
 class BadgeManager : public KeyedService {
  public:
-  BadgeManager();
+  explicit BadgeManager(Profile* profile);
   ~BadgeManager() override;
 
   // Records badge contents for an app and notifies the delegate if the badge
   // contents have changed.
-  void UpdateBadge(const extensions::ExtensionId&, base::Optional<int>);
+  void UpdateBadge(const extensions::ExtensionId&, base::Optional<uint64_t>);
 
   // Clears badge contents for an app (if existing) and notifies the delegate.
   void ClearBadge(const extensions::ExtensionId&);
 
-  void SetDelegate(BadgeManagerDelegate*);
+  // Sets the delegate used for setting/clearing badges.
+  void SetDelegate(std::unique_ptr<BadgeManagerDelegate> delegate);
 
  private:
-  BadgeManagerDelegate* delegate_ = nullptr;
+  std::unique_ptr<BadgeManagerDelegate> delegate_;
 
   // Maps extension id to badge contents.
-  std::map<extensions::ExtensionId, base::Optional<int>> badged_apps_;
+  std::map<extensions::ExtensionId, base::Optional<uint64_t>> badged_apps_;
 
   DISALLOW_COPY_AND_ASSIGN(BadgeManager);
 };
diff --git a/chrome/browser/badging/badge_manager_delegate.h b/chrome/browser/badging/badge_manager_delegate.h
index 07b0e14..0a6d86c4 100644
--- a/chrome/browser/badging/badge_manager_delegate.h
+++ b/chrome/browser/badging/badge_manager_delegate.h
@@ -9,20 +9,28 @@
 
 #include "base/optional.h"
 
+class Profile;
+
 namespace badging {
 
 // BadgeManagerDelegate is responsible for dispatching badge events that should
 // be handled and reflected in the UI.
 class BadgeManagerDelegate {
  public:
+  explicit BadgeManagerDelegate(Profile* profile) : profile_(profile) {}
+
   virtual ~BadgeManagerDelegate() {}
 
   // Called when an app's badge has changed.
   virtual void OnBadgeSet(const std::string& app_id,
-                          base::Optional<int> contents) = 0;
+                          base::Optional<uint64_t> contents) = 0;
 
   // Called when a app's badge has been cleared.
   virtual void OnBadgeCleared(const std::string& app_id) = 0;
+
+ protected:
+  // The profile the badge manager delegate is associated with.
+  Profile* profile_;
 };
 
 }  // namespace badging
diff --git a/chrome/browser/badging/badge_manager_delegate_mac.cc b/chrome/browser/badging/badge_manager_delegate_mac.cc
new file mode 100644
index 0000000..3c2489b
--- /dev/null
+++ b/chrome/browser/badging/badge_manager_delegate_mac.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 "chrome/browser/badging/badge_manager_delegate_mac.h"
+
+#include "chrome/browser/apps/app_shim/app_shim_host_mac.h"
+#include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h"
+#include "chrome/browser/badging/badge_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/common/mac/app_shim.mojom.h"
+
+namespace {}  // namespace
+
+namespace badging {
+
+BadgeManagerDelegateMac::BadgeManagerDelegateMac(Profile* profile)
+    : BadgeManagerDelegate(profile) {}
+
+void BadgeManagerDelegateMac::OnBadgeSet(const std::string& app_id,
+                                         base::Optional<uint64_t> contents) {
+  SetAppBadgeLabel(app_id, badging::GetBadgeString(contents));
+}
+
+void BadgeManagerDelegateMac::OnBadgeCleared(const std::string& app_id) {
+  SetAppBadgeLabel(app_id, "");
+}
+
+void BadgeManagerDelegateMac::SetAppBadgeLabel(const std::string& app_id,
+                                               const std::string& badge_label) {
+  auto* shim_handler = apps::ExtensionAppShimHandler::Get();
+  if (!shim_handler)
+    return;
+
+  // On OSX all app instances share a dock icon, so we only need to set the
+  // badge label once.
+  AppShimHost* shim_host = shim_handler->FindHost(profile_, app_id);
+  if (!shim_host)
+    return;
+
+  chrome::mojom::AppShim* shim = shim_host->GetAppShim();
+  if (!shim)
+    return;
+
+  shim->SetBadgeLabel(badge_label);
+}
+
+}  // namespace badging
\ No newline at end of file
diff --git a/chrome/browser/badging/badge_manager_delegate_mac.h b/chrome/browser/badging/badge_manager_delegate_mac.h
new file mode 100644
index 0000000..54ad591
--- /dev/null
+++ b/chrome/browser/badging/badge_manager_delegate_mac.h
@@ -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.
+
+#ifndef CHROME_BROWSER_BADGING_BADGE_MANAGER_DELEGATE_MAC_H_
+#define CHROME_BROWSER_BADGING_BADGE_MANAGER_DELEGATE_MAC_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "chrome/browser/badging/badge_manager_delegate.h"
+
+class Profile;
+
+namespace badging {
+
+// OSX specific implementation of the BadgeManagerDelegate.
+class BadgeManagerDelegateMac : public BadgeManagerDelegate {
+ public:
+  explicit BadgeManagerDelegateMac(Profile* profile);
+
+  void OnBadgeSet(const std::string& app_id,
+                  base::Optional<uint64_t> contents) override;
+
+  void OnBadgeCleared(const std::string& app_id) override;
+
+ private:
+  void SetAppBadgeLabel(const std::string& app_id,
+                        const std::string& badge_label);
+};
+
+}  // namespace badging
+
+#endif  // CHROME_BROWSER_BADGING_BADGE_MANAGER_DELEGATE_MAC_H_
diff --git a/chrome/browser/badging/badge_manager_delegate_win.cc b/chrome/browser/badging/badge_manager_delegate_win.cc
new file mode 100644
index 0000000..16a10f0
--- /dev/null
+++ b/chrome/browser/badging/badge_manager_delegate_win.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 "chrome/browser/badging/badge_manager_delegate_win.h"
+
+#include "chrome/browser/badging/badge_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/taskbar/taskbar_decorator_win.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+
+namespace badging {
+
+BadgeManagerDelegateWin::BadgeManagerDelegateWin(Profile* profile)
+    : BadgeManagerDelegate(profile) {}
+
+void BadgeManagerDelegateWin::OnBadgeSet(const std::string& app_id,
+                                         base::Optional<uint64_t> contents) {
+  for (Browser* browser : *BrowserList::GetInstance()) {
+    if (!IsAppBrowser(browser, app_id))
+      continue;
+
+    auto* window = browser->window()->GetNativeWindow();
+    taskbar::DrawTaskbarDecorationString(window,
+                                         badging::GetBadgeString(contents));
+  }
+}
+
+void BadgeManagerDelegateWin::OnBadgeCleared(const std::string& app_id) {
+  for (Browser* browser : *BrowserList::GetInstance()) {
+    if (!IsAppBrowser(browser, app_id))
+      continue;
+
+    // Restore the decoration to whatever it is naturally (either nothing or a
+    // profile picture badge).
+    taskbar::UpdateTaskbarDecoration(browser->profile(),
+                                     browser->window()->GetNativeWindow());
+  }
+}
+
+bool BadgeManagerDelegateWin::IsAppBrowser(Browser* browser,
+                                           const std::string& app_id) {
+  return browser->hosted_app_controller() &&
+         browser->hosted_app_controller()->GetExtensionId() == app_id &&
+         browser->profile() == profile_;
+}
+
+}  // namespace badging
diff --git a/chrome/browser/badging/badge_manager_delegate_win.h b/chrome/browser/badging/badge_manager_delegate_win.h
new file mode 100644
index 0000000..efe38265
--- /dev/null
+++ b/chrome/browser/badging/badge_manager_delegate_win.h
@@ -0,0 +1,36 @@
+// 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_BADGING_BADGE_MANAGER_DELEGATE_WIN_H_
+#define CHROME_BROWSER_BADGING_BADGE_MANAGER_DELEGATE_WIN_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "chrome/browser/badging/badge_manager_delegate.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
+
+class Profile;
+
+namespace badging {
+
+// Windows specific implementation of the BadgeManagerDelegate.
+class BadgeManagerDelegateWin : public BadgeManagerDelegate {
+ public:
+  explicit BadgeManagerDelegateWin(Profile* profile);
+
+  void OnBadgeSet(const std::string& app_id,
+                  base::Optional<uint64_t> contents) override;
+
+  void OnBadgeCleared(const std::string& app_id) override;
+
+ private:
+  // Determines if a browser is for a specific hosted app, on this profile.
+  bool IsAppBrowser(Browser* browser, const std::string& app_id);
+};
+
+}  // namespace badging
+
+#endif  // CHROME_BROWSER_BADGING_BADGE_MANAGER_DELEGATE_WIN_H_
diff --git a/chrome/browser/badging/badge_manager_factory.cc b/chrome/browser/badging/badge_manager_factory.cc
index 526eade..9d61589 100644
--- a/chrome/browser/badging/badge_manager_factory.cc
+++ b/chrome/browser/badging/badge_manager_factory.cc
@@ -36,10 +36,7 @@
 
 KeyedService* BadgeManagerFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  // We don't actually need to use the BrowserContext - we just use the
-  // BrowserContextKeyedFactory contract to to keep BadgeManagers separate
-  // across profiles.
-  return new BadgeManager();
+  return new BadgeManager(Profile::FromBrowserContext(context));
 }
 
 }  // namespace badging
diff --git a/chrome/browser/badging/badge_manager_unittest.cc b/chrome/browser/badging/badge_manager_unittest.cc
index d1a504f9b..9c1db5ee 100644
--- a/chrome/browser/badging/badge_manager_unittest.cc
+++ b/chrome/browser/badging/badge_manager_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/badging/badge_manager.h"
 
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -36,11 +37,12 @@
 // Testing delegate that records badge changes.
 class TestBadgeManagerDelegate : public BadgeManagerDelegate {
  public:
-  TestBadgeManagerDelegate() = default;
+  TestBadgeManagerDelegate() : BadgeManagerDelegate(nullptr) {}
+
   ~TestBadgeManagerDelegate() override = default;
 
   void OnBadgeSet(const std::string& app_id,
-                  base::Optional<int> contents) override {
+                  base::Optional<uint64_t> contents) override {
     set_badges_.push_back(std::make_pair(app_id, contents));
   }
 
@@ -63,20 +65,23 @@
 
   void SetUp() override {
     profile_.reset(new TestingProfile());
-    delegate_ = std::make_unique<TestBadgeManagerDelegate>();
+
+    // Delegate lifetime is managed by BadgeManager
+    auto owned_delegate = std::make_unique<TestBadgeManagerDelegate>();
+    delegate_ = owned_delegate.get();
     badge_manager_ =
         BadgeManagerFactory::GetInstance()->GetForProfile(profile_.get());
-    badge_manager_->SetDelegate(delegate_.get());
+    badge_manager_->SetDelegate(std::move(owned_delegate));
   }
 
   void TearDown() override { profile_.reset(); }
 
-  TestBadgeManagerDelegate* delegate() const { return delegate_.get(); }
+  TestBadgeManagerDelegate* delegate() { return delegate_; }
 
   BadgeManager* badge_manager() const { return badge_manager_; }
 
  private:
-  std::unique_ptr<TestBadgeManagerDelegate> delegate_;
+  TestBadgeManagerDelegate* delegate_;
   BadgeManager* badge_manager_;
   content::TestBrowserThreadBundle thread_bundle_;
   std::unique_ptr<TestingProfile> profile_;
@@ -100,15 +105,6 @@
   EXPECT_EQ(kBadgeContents, delegate()->set_badges().front().second);
 }
 
-TEST_F(BadgeManagerUnittest, SetBadgeForAppWithNoBadgeChange) {
-  badge_manager()->UpdateBadge(kExtensionId, kBadgeContents);
-  badge_manager()->UpdateBadge(kExtensionId, kBadgeContents);
-
-  EXPECT_EQ(1UL, delegate()->set_badges().size());
-  EXPECT_EQ(kExtensionId, delegate()->set_badges().front().first);
-  EXPECT_EQ(kBadgeContents, delegate()->set_badges().front().second);
-}
-
 TEST_F(BadgeManagerUnittest, SetBadgeForMultipleApps) {
   const extensions::ExtensionId otherId("other");
   int otherContents = 2;
@@ -148,17 +144,14 @@
   EXPECT_EQ(kExtensionId, delegate()->cleared_badges().front());
 }
 
-TEST_F(BadgeManagerUnittest, ClearBadgeForNonBadgedApp) {
-  badge_manager()->ClearBadge(kExtensionId);
-  EXPECT_EQ(0UL, delegate()->cleared_badges().size());
-}
-
 TEST_F(BadgeManagerUnittest, BadgingMultipleProfiles) {
   std::unique_ptr<Profile> other_profile = std::make_unique<TestingProfile>();
   auto* other_badge_manager =
       BadgeManagerFactory::GetInstance()->GetForProfile(other_profile.get());
-  auto other_delegate = std::make_unique<TestBadgeManagerDelegate>();
-  other_badge_manager->SetDelegate(other_delegate.get());
+
+  auto owned_other_delegate = std::make_unique<TestBadgeManagerDelegate>();
+  auto* other_delegate = owned_other_delegate.get();
+  other_badge_manager->SetDelegate(std::move(owned_other_delegate));
 
   other_badge_manager->UpdateBadge(kExtensionId, base::nullopt);
   other_badge_manager->UpdateBadge(kExtensionId, kBadgeContents);
@@ -171,7 +164,7 @@
   EXPECT_EQ(0UL, delegate()->set_badges().size());
 
   EXPECT_EQ(1UL, other_delegate->cleared_badges().size());
-  EXPECT_EQ(0UL, delegate()->cleared_badges().size());
+  EXPECT_EQ(1UL, delegate()->cleared_badges().size());
 
   EXPECT_EQ(kExtensionId, other_delegate->set_badges().back().first);
   EXPECT_EQ(base::nullopt, other_delegate->set_badges().back().second);
diff --git a/chrome/browser/badging/badge_service_delegate.h b/chrome/browser/badging/badge_service_delegate.h
deleted file mode 100644
index 4c01c82..0000000
--- a/chrome/browser/badging/badge_service_delegate.h
+++ /dev/null
@@ -1,44 +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 CHROME_BROWSER_BADGING_BADGE_SERVICE_DELEGATE_H_
-#define CHROME_BROWSER_BADGING_BADGE_SERVICE_DELEGATE_H_
-
-#include "base/callback.h"
-#include "base/optional.h"
-
-namespace content {
-class WebContents;
-}
-
-// Delegate for setting and clearing app badges. This is implemented at a
-// UI-layer, allowing for a platform specific implementation of badging.
-class BadgeServiceDelegate {
- public:
-  using SetBadgeCallback =
-      base::RepeatingCallback<void(content::WebContents*,
-                                   base::Optional<uint64_t>)>;
-  using ClearBadgeCallback =
-      base::RepeatingCallback<void(content::WebContents*)>;
-
-  BadgeServiceDelegate();
-  ~BadgeServiceDelegate();
-
-  // Sets the Badge for |web_contents|.
-  void SetBadge(content::WebContents* web_contents,
-                base::Optional<uint64_t> badge_content);
-
-  // Clears the badge for |web_contents|.
-  void ClearBadge(content::WebContents* web_contents);
-
-  // Swaps out the implementation of |SetBadge| and |ClearBadge| for testing.
-  void SetImplForTesting(SetBadgeCallback on_set_badge,
-                         ClearBadgeCallback on_clear_badge);
-
- private:
-  SetBadgeCallback on_set_badge_;
-  ClearBadgeCallback on_clear_badge_;
-};
-
-#endif  // CHROME_BROWSER_BADGING_BADGE_SERVICE_DELEGATE_H_
diff --git a/chrome/browser/badging/badge_service_impl.cc b/chrome/browser/badging/badge_service_impl.cc
index 3f6c5035..d75aa6c 100644
--- a/chrome/browser/badging/badge_service_impl.cc
+++ b/chrome/browser/badging/badge_service_impl.cc
@@ -9,7 +9,6 @@
 #include "base/logging.h"
 #include "chrome/browser/badging/badge_manager.h"
 #include "chrome/browser/badging/badge_manager_factory.h"
-#include "chrome/browser/badging/badge_service_delegate.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -19,18 +18,6 @@
 #include "content/public/browser/web_contents.h"
 #include "extensions/common/extension.h"
 
-namespace {
-
-#if !defined(OS_CHROMEOS)
-BadgeServiceDelegate* GetDelegate(content::WebContents* web_contents) {
-  return chrome::FindBrowserWithWebContents(web_contents)
-      ->window()
-      ->GetBadgeServiceDelegate();
-}
-#endif
-
-}  // namespace
-
 // static
 void BadgeServiceImpl::Create(blink::mojom::BadgeServiceRequest request,
                               content::RenderFrameHost* render_frame_host) {
@@ -49,35 +36,27 @@
 }
 
 void BadgeServiceImpl::SetBadge(base::Optional<uint64_t> content) {
-#if defined(OS_CHROMEOS)
+  if (!IsInApp())
+    return;
+
   const extensions::Extension* extension = ExtensionFromLastUrl();
 
   if (!extension)
     return;
 
-  badge_manager_->UpdateBadge(extension->id(), base::nullopt);
-#else
-  if (!IsInApp())
-    return;
-
-  GetDelegate(web_contents_)->SetBadge(web_contents_, content);
-#endif
+  badge_manager_->UpdateBadge(extension->id(), content);
 }
 
 void BadgeServiceImpl::ClearBadge() {
-#if defined(OS_CHROMEOS)
+  if (!IsInApp())
+    return;
+
   const extensions::Extension* extension = ExtensionFromLastUrl();
 
   if (!extension)
     return;
 
   badge_manager_->ClearBadge(extension->id());
-#else
-  if (!IsInApp())
-    return;
-
-  GetDelegate(web_contents_)->ClearBadge(web_contents_);
-#endif
 }
 
 BadgeServiceImpl::BadgeServiceImpl(content::RenderFrameHost* render_frame_host,
@@ -87,10 +66,8 @@
       render_frame_host_(render_frame_host) {
   web_contents_ = content::WebContents::FromRenderFrameHost(render_frame_host_);
   browser_context_ = web_contents_->GetBrowserContext();
-#if defined(OS_CHROMEOS)
   badge_manager_ = badging::BadgeManagerFactory::GetInstance()->GetForProfile(
       Profile::FromBrowserContext(browser_context_));
-#endif
 }
 
 BadgeServiceImpl::~BadgeServiceImpl() = default;
diff --git a/chrome/browser/badging/badge_service_impl.h b/chrome/browser/badging/badge_service_impl.h
index 5f815c8..e5483f7 100644
--- a/chrome/browser/badging/badge_service_impl.h
+++ b/chrome/browser/badging/badge_service_impl.h
@@ -19,11 +19,9 @@
 class Extension;
 }
 
-#if defined(OS_CHROMEOS)
 namespace badging {
 class BadgeManager;
 }
-#endif
 
 // Desktop implementation of the BadgeService mojo service.
 class BadgeServiceImpl
@@ -49,9 +47,7 @@
   content::RenderFrameHost* render_frame_host_;
   content::BrowserContext* browser_context_;
   content::WebContents* web_contents_;
-#if defined(OS_CHROMEOS)
   badging::BadgeManager* badge_manager_;
-#endif
 };
 
 #endif  // CHROME_BROWSER_BADGING_BADGE_SERVICE_IMPL_H_
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_manager.h b/chrome/browser/chromeos/android_sms/android_sms_app_manager.h
index 1e30871..99952007 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_manager.h
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_manager.h
@@ -36,7 +36,7 @@
   ~AndroidSmsAppManager() override;
 
   // If no app is installed, null is returned.
-  virtual base::Optional<GURL> GetInstalledAppUrl() = 0;
+  virtual base::Optional<GURL> GetCurrentAppUrl() = 0;
 
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
index 57732ffb..53d13b8 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.cc
@@ -48,7 +48,7 @@
     : profile_(profile),
       setup_controller_(setup_controller),
       app_list_syncable_service_(app_list_syncable_service),
-      installed_url_at_last_notify_(GetInstalledAppUrl()),
+      installed_url_at_last_notify_(GetCurrentAppInstallUrl()),
       pwa_delegate_(std::make_unique<PwaDelegate>()),
       weak_ptr_factory_(this) {
   // Post a task to complete initialization. This portion of the flow must be
@@ -62,12 +62,30 @@
 
 AndroidSmsAppManagerImpl::~AndroidSmsAppManagerImpl() = default;
 
-base::Optional<GURL> AndroidSmsAppManagerImpl::GetInstalledAppUrl() {
-  if (setup_controller_->GetPwa(GetAndroidMessagesURL()))
+base::Optional<GURL> AndroidSmsAppManagerImpl::GetCurrentAppUrl() {
+  if (setup_controller_->GetPwa(
+          GetAndroidMessagesURL(true /* use_install_url */))) {
     return GetAndroidMessagesURL();
+  }
 
-  if (setup_controller_->GetPwa(GetAndroidMessagesURLOld()))
+  if (setup_controller_->GetPwa(
+          GetAndroidMessagesURLOld(true /* use_install_url */))) {
     return GetAndroidMessagesURLOld();
+  }
+
+  return base::nullopt;
+}
+
+base::Optional<GURL> AndroidSmsAppManagerImpl::GetCurrentAppInstallUrl() {
+  if (setup_controller_->GetPwa(
+          GetAndroidMessagesURL(true /* use_install_url */))) {
+    return GetAndroidMessagesURL(true /* use_install_url */);
+  }
+
+  if (setup_controller_->GetPwa(
+          GetAndroidMessagesURLOld(true /* use_install_url */))) {
+    return GetAndroidMessagesURLOld(true /* use_install_url */);
+  }
 
   return base::nullopt;
 }
@@ -79,7 +97,8 @@
 
   is_new_app_setup_in_progress_ = true;
   setup_controller_->SetUpApp(
-      GetAndroidMessagesURL(),
+      GetAndroidMessagesURL() /* app_url */,
+      GetAndroidMessagesURL(true /* use_install_url */) /* install_url */,
       base::BindOnce(&AndroidSmsAppManagerImpl::OnSetUpNewAppResult,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -90,7 +109,7 @@
 }
 
 void AndroidSmsAppManagerImpl::TearDownAndroidSmsApp() {
-  base::Optional<GURL> installed_app_url = GetInstalledAppUrl();
+  base::Optional<GURL> installed_app_url = GetCurrentAppUrl();
   if (!installed_app_url)
     return;
 
@@ -101,12 +120,12 @@
 void AndroidSmsAppManagerImpl::CompleteAsyncInitialization() {
   // If the kUseMessagesGoogleComDomain flag has been flipped and the installed
   // PWA is at the old URL, set up the new app.
-  if (GetInstalledAppUrl() == GetAndroidMessagesURLOld())
+  if (GetCurrentAppUrl() == GetAndroidMessagesURLOld())
     SetUpAndroidSmsApp();
 }
 
 void AndroidSmsAppManagerImpl::NotifyInstalledAppUrlChangedIfNecessary() {
-  base::Optional<GURL> installed_app_url = GetInstalledAppUrl();
+  base::Optional<GURL> installed_app_url = GetCurrentAppInstallUrl();
   if (installed_url_at_last_notify_ == installed_app_url)
     return;
 
@@ -117,8 +136,8 @@
 void AndroidSmsAppManagerImpl::OnSetUpNewAppResult(bool success) {
   is_new_app_setup_in_progress_ = false;
 
-  const extensions::Extension* new_pwa =
-      setup_controller_->GetPwa(GetAndroidMessagesURL());
+  const extensions::Extension* new_pwa = setup_controller_->GetPwa(
+      GetAndroidMessagesURL(true /* use_install_url */));
 
   // If the installation succeeded, a PWA should exist at the new URL.
   DCHECK_EQ(success, new_pwa != nullptr);
@@ -129,8 +148,8 @@
     return;
   }
 
-  const extensions::Extension* old_pwa =
-      setup_controller_->GetPwa(GetAndroidMessagesURLOld());
+  const extensions::Extension* old_pwa = setup_controller_->GetPwa(
+      GetAndroidMessagesURLOld(true /* use_install_url */));
 
   // If there is no PWA installed at the old URL, no migration is needed and
   // setup is finished.
@@ -147,13 +166,15 @@
   if (!transfer_attributes_success) {
     PA_LOG(ERROR) << "AndroidSmsAppManagerImpl::OnSetUpNewAppResult(): Failed "
                   << "to transfer item attributes from "
-                  << GetAndroidMessagesURLOld() << " to "
-                  << GetAndroidMessagesURL() << ".";
+                  << GetAndroidMessagesURLOld(true /* use_install_url */)
+                  << " to " << GetAndroidMessagesURL(true /* use_install_url */)
+                  << ".";
   }
 
   // Finish the migration by removing the old app now that it has been replaced.
   setup_controller_->RemoveApp(
       GetAndroidMessagesURLOld(),
+      GetAndroidMessagesURLOld(true /* use_install_url */),
       base::BindOnce(&AndroidSmsAppManagerImpl::OnRemoveOldAppResult,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -163,7 +184,8 @@
   // should still be notified of the URL change.
   if (!success) {
     PA_LOG(ERROR) << "AndroidSmsAppManagerImpl::OnRemoveOldAppResult(): Failed "
-                  << "to remove PWA at old URL " << GetAndroidMessagesURLOld()
+                  << "to remove PWA at old URL "
+                  << GetAndroidMessagesURLOld(true /* use_install_url */)
                   << ".";
   }
 
@@ -180,7 +202,7 @@
   is_app_launch_pending_ = false;
 
   // If launch was requested but setup failed, there is no app to launch.
-  base::Optional<GURL> installed_app_url = GetInstalledAppUrl();
+  base::Optional<GURL> installed_app_url = GetCurrentAppInstallUrl();
   if (!installed_app_url)
     return;
 
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.h b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.h
index b8a179f4..0dba6ed 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.h
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl.h
@@ -62,13 +62,14 @@
   };
 
   // AndroidSmsAppManager:
-  base::Optional<GURL> GetInstalledAppUrl() override;
+  base::Optional<GURL> GetCurrentAppUrl() override;
 
   // AndroidSmsAppHelperDelegate:
   void SetUpAndroidSmsApp() override;
   void SetUpAndLaunchAndroidSmsApp() override;
   void TearDownAndroidSmsApp() override;
 
+  base::Optional<GURL> GetCurrentAppInstallUrl();
   void CompleteAsyncInitialization();
   void NotifyInstalledAppUrlChangedIfNecessary();
   void OnSetUpNewAppResult(bool success);
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc
index 2e3d5a5..d8fcd9c 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_manager_impl_unittest.cc
@@ -148,13 +148,15 @@
 
   android_sms_app_manager()->SetUpAndroidSmsApp();
   fake_android_sms_app_setup_controller()->CompletePendingSetUpAppRequest(
-      GetAndroidMessagesURL() /* expected_url */,
+      GetAndroidMessagesURL() /* expected_app_url */,
+      GetAndroidMessagesURL(
+          true /* use_install_url */) /* expected_install_url */,
       base::nullopt /* id_for_app */);
 
   // Verify that no installed app exists and no observers were notified.
   EXPECT_FALSE(fake_android_sms_app_setup_controller()->GetAppMetadataAtUrl(
-      GetAndroidMessagesURL()));
-  EXPECT_FALSE(android_sms_app_manager()->GetInstalledAppUrl());
+      GetAndroidMessagesURL(true /* use_install_url */)));
+  EXPECT_FALSE(android_sms_app_manager()->GetCurrentAppUrl());
   EXPECT_EQ(0u, test_observer()->num_installed_app_url_changed_events());
 }
 
@@ -164,25 +166,33 @@
 
   android_sms_app_manager()->SetUpAndroidSmsApp();
   fake_android_sms_app_setup_controller()->CompletePendingSetUpAppRequest(
-      GetAndroidMessagesURL() /* expected_url */, kNewAppId);
+      GetAndroidMessagesURL() /* expected_app_url */,
+      GetAndroidMessagesURL(
+          true /* use_install_url */) /* expected_install_url */,
+      kNewAppId);
 
   // Verify that the app was installed and observers were notified.
   EXPECT_EQ(kNewAppId, fake_android_sms_app_setup_controller()
-                           ->GetAppMetadataAtUrl(GetAndroidMessagesURL())
+                           ->GetAppMetadataAtUrl(GetAndroidMessagesURL(
+                               true /* use_install_url */))
                            ->pwa->id());
   EXPECT_TRUE(fake_android_sms_app_setup_controller()
-                  ->GetAppMetadataAtUrl(GetAndroidMessagesURL())
+                  ->GetAppMetadataAtUrl(
+                      GetAndroidMessagesURL(true /* use_install_url */))
                   ->is_cookie_present);
   EXPECT_EQ(GetAndroidMessagesURL(),
-            *android_sms_app_manager()->GetInstalledAppUrl());
+            *android_sms_app_manager()->GetCurrentAppUrl());
   EXPECT_EQ(1u, test_observer()->num_installed_app_url_changed_events());
 
   // Now, tear down the app, which should remove the DefaultToPersist cookie.
   android_sms_app_manager()->TearDownAndroidSmsApp();
   fake_android_sms_app_setup_controller()->CompletePendingDeleteCookieRequest(
-      GetAndroidMessagesURL() /* expected_url */);
+      GetAndroidMessagesURL() /* expected_app_url */,
+      GetAndroidMessagesURL(
+          true /* use_install_url */) /* expected_install_url */);
   EXPECT_FALSE(fake_android_sms_app_setup_controller()
-                   ->GetAppMetadataAtUrl(GetAndroidMessagesURL())
+                   ->GetAppMetadataAtUrl(
+                       GetAndroidMessagesURL(true /* use_install_url */))
                    ->is_cookie_present);
 }
 
@@ -191,17 +201,22 @@
 
   android_sms_app_manager()->SetUpAndLaunchAndroidSmsApp();
   fake_android_sms_app_setup_controller()->CompletePendingSetUpAppRequest(
-      GetAndroidMessagesURL() /* expected_url */, kNewAppId);
+      GetAndroidMessagesURL() /* expected_app_url */,
+      GetAndroidMessagesURL(
+          true /* use_install_url */) /* expected_install_url */,
+      kNewAppId);
 
   // Verify that the app was installed and observers were notified.
   EXPECT_EQ(kNewAppId, fake_android_sms_app_setup_controller()
-                           ->GetAppMetadataAtUrl(GetAndroidMessagesURL())
+                           ->GetAppMetadataAtUrl(GetAndroidMessagesURL(
+                               true /* use_install_url */))
                            ->pwa->id());
   EXPECT_TRUE(fake_android_sms_app_setup_controller()
-                  ->GetAppMetadataAtUrl(GetAndroidMessagesURL())
+                  ->GetAppMetadataAtUrl(
+                      GetAndroidMessagesURL(true /* use_install_url */))
                   ->is_cookie_present);
   EXPECT_EQ(GetAndroidMessagesURL(),
-            *android_sms_app_manager()->GetInstalledAppUrl());
+            *android_sms_app_manager()->GetCurrentAppUrl());
   EXPECT_EQ(1u, test_observer()->num_installed_app_url_changed_events());
 
   // The app should have been launched.
@@ -212,29 +227,33 @@
        TestSetUpMessages_PreviousAppExists_Fails) {
   // Before completing initialization, install the old app.
   fake_android_sms_app_setup_controller()->SetAppAtUrl(
-      GetAndroidMessagesURLOld(), kOldAppId);
+      GetAndroidMessagesURLOld(true /* use_install_url */), kOldAppId);
   CompleteAsyncInitialization();
 
   // This should trigger the new app to be installed; fail this installation.
   // This simulates a situation which could occur if the user signs in with the
   // flag enabled but is offline and thus unable to install the new PWA.
   fake_android_sms_app_setup_controller()->CompletePendingSetUpAppRequest(
-      GetAndroidMessagesURL() /* expected_url */,
+      GetAndroidMessagesURL() /* expected_app_url */,
+      GetAndroidMessagesURL(
+          true /* use_install_url */) /* expected_install_url */,
       base::nullopt /* id_for_app */);
 
   // Verify that the new app was not installed and no observers were notified.
   EXPECT_FALSE(fake_android_sms_app_setup_controller()->GetAppMetadataAtUrl(
-      GetAndroidMessagesURL()));
+      GetAndroidMessagesURL(true /* use_install_url */)));
   EXPECT_EQ(0u, test_observer()->num_installed_app_url_changed_events());
 
-  // The old app should still be active.
+  // The old app should still be true.
   EXPECT_EQ(GetAndroidMessagesURLOld(),
-            *android_sms_app_manager()->GetInstalledAppUrl());
+            *android_sms_app_manager()->GetCurrentAppUrl());
   EXPECT_EQ(kOldAppId, fake_android_sms_app_setup_controller()
-                           ->GetAppMetadataAtUrl(GetAndroidMessagesURLOld())
+                           ->GetAppMetadataAtUrl(GetAndroidMessagesURLOld(
+                               true /* use_install_url */))
                            ->pwa->id());
   EXPECT_TRUE(fake_android_sms_app_setup_controller()
-                  ->GetAppMetadataAtUrl(GetAndroidMessagesURLOld())
+                  ->GetAppMetadataAtUrl(
+                      GetAndroidMessagesURLOld(true /* use_install_url */))
                   ->is_cookie_present);
 }
 
@@ -242,24 +261,29 @@
        TestSetUpMessages_ThenTearDown_PreviousAppExists) {
   // Before completing initialization, install the old app.
   fake_android_sms_app_setup_controller()->SetAppAtUrl(
-      GetAndroidMessagesURLOld(), kOldAppId);
+      GetAndroidMessagesURLOld(true /* use_install_url */), kOldAppId);
   CompleteAsyncInitialization();
 
   // This should trigger the new app to be installed.
   fake_android_sms_app_setup_controller()->CompletePendingSetUpAppRequest(
-      GetAndroidMessagesURL() /* expected_url */, kNewAppId /* id_for_app */);
+      GetAndroidMessagesURL() /* expected_app_url */,
+      GetAndroidMessagesURL(
+          true /* use_install_url */) /* expected_install_url */,
+      kNewAppId /* id_for_app */);
 
   // Verify that the app was installed and attributes were transferred. By this
   // point, observers should not have been notified yet since the old app was
   // not yet installed.
   EXPECT_EQ(kNewAppId, fake_android_sms_app_setup_controller()
-                           ->GetAppMetadataAtUrl(GetAndroidMessagesURL())
+                           ->GetAppMetadataAtUrl(GetAndroidMessagesURL(
+                               true /* use_install_url */))
                            ->pwa->id());
   EXPECT_TRUE(fake_android_sms_app_setup_controller()
-                  ->GetAppMetadataAtUrl(GetAndroidMessagesURL())
+                  ->GetAppMetadataAtUrl(
+                      GetAndroidMessagesURL(true /* use_install_url */))
                   ->is_cookie_present);
   EXPECT_EQ(GetAndroidMessagesURL(),
-            *android_sms_app_manager()->GetInstalledAppUrl());
+            *android_sms_app_manager()->GetCurrentAppUrl());
   EXPECT_EQ(std::make_pair(std::string(kOldAppId), std::string(kNewAppId)),
             test_pwa_delegate()->transfer_item_attribute_params()[0]);
   EXPECT_EQ(0u, test_observer()->num_installed_app_url_changed_events());
@@ -267,7 +291,10 @@
   // Now, complete uninstallation of the old app; this should trigger observers
   // to be notified.
   fake_android_sms_app_setup_controller()->CompleteRemoveAppRequest(
-      GetAndroidMessagesURLOld() /* expected_url */, true /* success */);
+      GetAndroidMessagesURLOld() /* expected_app_url */,
+      GetAndroidMessagesURLOld(
+          true /* use_install_url */) /* expected_install_url */,
+      true /* success */);
   EXPECT_EQ(1u, test_observer()->num_installed_app_url_changed_events());
 }
 
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller.h b/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller.h
index 94e2c13..ef6049d 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller.h
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller.h
@@ -29,23 +29,31 @@
   //   (1) Installing the PWA,
   //   (2) Granting permission for the PWA to show notifications, and
   //   (3) Setting a cookie which defaults the PWA to remember this computer.
-  virtual void SetUpApp(const GURL& url, SuccessCallback callback) = 0;
+  // The |app_url|  parameter should have the root URL of the app to install
+  // and should be the same as the service worker scope
+  // The |install_url| parameter is the url to install the app from and cannot
+  // redirect.
+  virtual void SetUpApp(const GURL& app_url,
+                        const GURL& install_url,
+                        SuccessCallback callback) = 0;
 
   // Returns the extension for the PWA at |url|; if no PWA exists, null is
   // returned.
-  virtual const extensions::Extension* GetPwa(const GURL& url) = 0;
+  virtual const extensions::Extension* GetPwa(const GURL& install_url) = 0;
 
   // Deletes the cookie which causes the PWA to remember this computer by
   // default. Note that this does not actually stop the PWA from remembering
   // this computer; rather, it stops the PWA from *defaulting* to remember the
   // computer in the case that the user has not gone through the PWA's setup.
   virtual void DeleteRememberDeviceByDefaultCookie(
-      const GURL& url,
+      const GURL& app_url,
       SuccessCallback callback) = 0;
 
   // Uninstalls the app at |url| and deletes relevant cookies from the setup
   // process.
-  virtual void RemoveApp(const GURL& url, SuccessCallback callback) = 0;
+  virtual void RemoveApp(const GURL& app_url,
+                         const GURL& install_url,
+                         SuccessCallback callback) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AndroidSmsAppSetupController);
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl.cc b/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl.cc
index 9f15b19..1e6be1e 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl.h"
 
+#include <string>
+#include <vector>
+
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
@@ -38,20 +41,21 @@
 AndroidSmsAppSetupControllerImpl::PwaDelegate::~PwaDelegate() = default;
 
 const extensions::Extension*
-AndroidSmsAppSetupControllerImpl::PwaDelegate::GetPwaForUrl(const GURL& url,
-                                                            Profile* profile) {
+AndroidSmsAppSetupControllerImpl::PwaDelegate::GetPwaForUrl(
+    const GURL& install_url,
+    Profile* profile) {
   // PWA windowing is disabled for some browser tests.
   if (!base::FeatureList::IsEnabled(features::kDesktopPWAWindowing))
     return nullptr;
 
-  return extensions::util::GetInstalledPwaForUrl(profile, url);
+  return extensions::util::GetInstalledPwaForUrl(profile, install_url);
 }
 
 network::mojom::CookieManager*
 AndroidSmsAppSetupControllerImpl::PwaDelegate::GetCookieManager(
-    const GURL& url,
+    const GURL& app_url,
     Profile* profile) {
-  return content::BrowserContext::GetStoragePartitionForSite(profile, url)
+  return content::BrowserContext::GetStoragePartitionForSite(profile, app_url)
       ->GetCookieManagerForBrowserProcess();
 }
 
@@ -67,63 +71,66 @@
 
 AndroidSmsAppSetupControllerImpl::~AndroidSmsAppSetupControllerImpl() = default;
 
-void AndroidSmsAppSetupControllerImpl::SetUpApp(const GURL& url,
+void AndroidSmsAppSetupControllerImpl::SetUpApp(const GURL& app_url,
+                                                const GURL& install_url,
                                                 SuccessCallback callback) {
   PA_LOG(VERBOSE) << "AndroidSmsAppSetupControllerImpl::SetUpApp(): Setting "
-                  << "DefaultToPersist cookie at " << url << " before PWA "
+                  << "DefaultToPersist cookie at " << app_url << " before PWA "
                   << "installation.";
-  pwa_delegate_->GetCookieManager(url, profile_)
+  pwa_delegate_->GetCookieManager(app_url, profile_)
       ->SetCanonicalCookie(
           *net::CanonicalCookie::CreateSanitizedCookie(
-              url, kDefaultToPersistCookieName, kDefaultToPersistCookieValue,
-              std::string() /* domain */, std::string() /* path */,
-              base::Time::Now() /* creation_time */,
+              app_url, kDefaultToPersistCookieName,
+              kDefaultToPersistCookieValue, std::string() /* domain */,
+              std::string() /* path */, base::Time::Now() /* creation_time */,
               base::Time() /* expiration_time */,
               base::Time::Now() /* last_access_time */, true /* secure */,
               false /* http_only */, net::CookieSameSite::STRICT_MODE,
               net::COOKIE_PRIORITY_DEFAULT),
           true /* secure_source */, false /* modify_http_only */,
           base::BindOnce(&AndroidSmsAppSetupControllerImpl::OnSetCookieResult,
-                         weak_ptr_factory_.GetWeakPtr(), url,
+                         weak_ptr_factory_.GetWeakPtr(), app_url, install_url,
                          std::move(callback)));
 }
 
 const extensions::Extension* AndroidSmsAppSetupControllerImpl::GetPwa(
-    const GURL& url) {
-  return pwa_delegate_->GetPwaForUrl(url, profile_);
+    const GURL& install_url) {
+  return pwa_delegate_->GetPwaForUrl(install_url, profile_);
 }
 
 void AndroidSmsAppSetupControllerImpl::DeleteRememberDeviceByDefaultCookie(
-    const GURL& url,
+    const GURL& app_url,
     SuccessCallback callback) {
   PA_LOG(INFO) << "AndroidSmsAppSetupControllerImpl::"
                << "DeleteRememberDeviceByDefaultCookie(): Deleting "
-               << "DefaultToPersist cookie at " << url << ".";
+               << "DefaultToPersist cookie at " << app_url << ".";
   network::mojom::CookieDeletionFilterPtr filter(
       network::mojom::CookieDeletionFilter::New());
-  filter->url = url;
+  filter->url = app_url;
   filter->cookie_name = kDefaultToPersistCookieName;
-  pwa_delegate_->GetCookieManager(url, profile_)
+  pwa_delegate_->GetCookieManager(app_url, profile_)
       ->DeleteCookies(
           std::move(filter),
           base::BindOnce(
               &AndroidSmsAppSetupControllerImpl::OnDeleteCookiesResult,
-              weak_ptr_factory_.GetWeakPtr(), url, std::move(callback)));
+              weak_ptr_factory_.GetWeakPtr(), app_url, std::move(callback)));
 }
 
-void AndroidSmsAppSetupControllerImpl::RemoveApp(const GURL& url,
+void AndroidSmsAppSetupControllerImpl::RemoveApp(const GURL& app_url,
+                                                 const GURL& install_url,
                                                  SuccessCallback callback) {
   // If there is no app installed at |url|, there is nothing more to do.
-  if (!pwa_delegate_->GetPwaForUrl(url, profile_)) {
+  if (!pwa_delegate_->GetPwaForUrl(install_url, profile_)) {
     PA_LOG(VERBOSE) << "AndroidSmsAppSetupControllerImpl::RemoveApp(): No app "
-                    << "is installed at " << url << "; skipping removal "
+                    << "is installed at " << install_url
+                    << "; skipping removal "
                     << "process.";
     std::move(callback).Run(true /* success */);
     return;
   }
 
   PA_LOG(INFO) << "AndroidSmsAppSetupControllerImpl::RemoveApp(): "
-               << "Uninstalling app at " << url << ".";
+               << "Uninstalling app at " << install_url << ".";
   // UninstallApps() takes a base::RepeatedCallback, but |callback| is a
   // base::OnceCallback; thus, |callback| cannot be included in the closure
   // because it has move-only semantics. Assign this uninstall attempt an ID
@@ -132,33 +139,34 @@
   auto id = base::UnguessableToken::Create();
   uninstall_id_to_callback_map_.emplace(id, std::move(callback));
   pending_app_manager_->UninstallApps(
-      std::vector<GURL>{url},
+      std::vector<GURL>{install_url},
       base::BindRepeating(
           &AndroidSmsAppSetupControllerImpl::OnAppUninstallResult,
-          weak_ptr_factory_.GetWeakPtr(), id));
+          weak_ptr_factory_.GetWeakPtr(), id, app_url));
 }
 
 void AndroidSmsAppSetupControllerImpl::OnSetCookieResult(
-    const GURL& url,
+    const GURL& app_url,
+    const GURL& install_url,
     SuccessCallback callback,
     bool succeeded) {
   if (!succeeded) {
     PA_LOG(WARNING) << "AndroidSmsAppSetupControllerImpl::"
                     << "OnSetCookieResult(): Failed to set "
-                    << "DefaultToPersist cookie at " << url << ". Proceeding "
-                    << "with installation request.";
+                    << "DefaultToPersist cookie at " << app_url
+                    << ". Proceeding with installation request.";
   }
 
   // If the app is already installed at |url|, there is nothing more to do.
-  if (pwa_delegate_->GetPwaForUrl(url, profile_)) {
+  if (pwa_delegate_->GetPwaForUrl(install_url, profile_)) {
     PA_LOG(VERBOSE) << "AndroidSmsAppSetupControllerImpl::OnSetCookieResult(): "
-                    << "App is already installed at " << url << "; skipping "
-                    << "setup process.";
+                    << "App is already installed at " << install_url
+                    << "; skipping setup process.";
     std::move(callback).Run(true /* success */);
     return;
   }
 
-  web_app::PendingAppManager::AppInfo info(url,
+  web_app::PendingAppManager::AppInfo info(install_url,
                                            web_app::LaunchContainer::kWindow,
                                            web_app::InstallSource::kInternal);
   info.override_previous_user_uninstall = true;
@@ -168,34 +176,36 @@
   info.require_manifest = true;
 
   PA_LOG(VERBOSE) << "AndroidSmsAppSetupControllerImpl::OnSetCookieResult(): "
-                  << "Installing PWA for " << url << ".";
+                  << "Installing PWA for " << install_url << ".";
   pending_app_manager_->Install(
       std::move(info),
       base::BindOnce(&AndroidSmsAppSetupControllerImpl::OnAppInstallResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     app_url));
 }
 
 void AndroidSmsAppSetupControllerImpl::OnAppInstallResult(
     SuccessCallback callback,
-    const GURL& url,
+    const GURL& app_url,
+    const GURL& install_url,
     web_app::InstallResultCode code) {
   UMA_HISTOGRAM_ENUMERATION("AndroidSms.PWAInstallationResult", code);
 
   if (code != web_app::InstallResultCode::kSuccess) {
     PA_LOG(WARNING)
         << "AndroidSmsAppSetupControllerImpl::OnAppInstallResult(): "
-        << "PWA for " << url << " failed to install. "
+        << "PWA for " << install_url << " failed to install. "
         << "InstallResultCode: " << static_cast<int>(code);
     std::move(callback).Run(false /* success */);
     return;
   }
 
   PA_LOG(INFO) << "AndroidSmsAppSetupControllerImpl::OnAppInstallResult(): "
-               << "PWA for " << url << " was installed successfully.";
+               << "PWA for " << install_url << " was installed successfully.";
 
   // Grant notification permission for the PWA.
   host_content_settings_map_->SetWebsiteSettingDefaultScope(
-      url, GURL() /* top_level_url */,
+      app_url, GURL() /* top_level_url */,
       ContentSettingsType::CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
       content_settings::ResourceIdentifier(),
       std::make_unique<base::Value>(ContentSetting::CONTENT_SETTING_ALLOW));
@@ -205,7 +215,8 @@
 
 void AndroidSmsAppSetupControllerImpl::OnAppUninstallResult(
     const base::UnguessableToken& id,
-    const GURL& url,
+    const GURL& app_url,
+    const GURL& install_url,
     bool succeeded) {
   UMA_HISTOGRAM_BOOLEAN("AndroidSms.PWAUninstallationResult", succeeded);
 
@@ -218,22 +229,22 @@
   if (!succeeded) {
     PA_LOG(ERROR)
         << "AndroidSmsAppSetupControllerImpl::OnAppUninstallResult(): "
-        << "PWA for " << url << " failed to uninstall.";
+        << "PWA for " << install_url << " failed to uninstall.";
     std::move(callback).Run(false /* success */);
     return;
   }
 
-  DeleteRememberDeviceByDefaultCookie(url, std::move(callback));
+  DeleteRememberDeviceByDefaultCookie(app_url, std::move(callback));
 }
 
 void AndroidSmsAppSetupControllerImpl::OnDeleteCookiesResult(
-    const GURL& url,
+    const GURL& app_url,
     SuccessCallback callback,
     uint32_t num_deleted) {
   if (num_deleted != 1u) {
     PA_LOG(WARNING) << "AndroidSmsAppSetupControllerImpl::"
                     << "OnDeleteCookiesResult(): Tried to delete a single "
-                    << "cookie at " << url << ", but " << num_deleted << " "
+                    << "cookie at " << app_url << ", but " << num_deleted << " "
                     << "cookies were deleted.";
   }
 
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl.h b/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl.h
index 95036f4c..bbf8fbc 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl.h
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl.h
@@ -47,29 +47,36 @@
     PwaDelegate();
     virtual ~PwaDelegate();
 
-    virtual const extensions::Extension* GetPwaForUrl(const GURL& url,
+    virtual const extensions::Extension* GetPwaForUrl(const GURL& install_url,
                                                       Profile* profile);
-    virtual network::mojom::CookieManager* GetCookieManager(const GURL& url,
+    virtual network::mojom::CookieManager* GetCookieManager(const GURL& app_url,
                                                             Profile* profile);
   };
 
   // AndroidSmsAppSetupController:
-  void SetUpApp(const GURL& url, SuccessCallback callback) override;
-  const extensions::Extension* GetPwa(const GURL& url) override;
-  void DeleteRememberDeviceByDefaultCookie(const GURL& url,
+  void SetUpApp(const GURL& app_url,
+                const GURL& install_url,
+                SuccessCallback callback) override;
+  const extensions::Extension* GetPwa(const GURL& install_url) override;
+  void DeleteRememberDeviceByDefaultCookie(const GURL& app_url,
                                            SuccessCallback callback) override;
-  void RemoveApp(const GURL& url, SuccessCallback callback) override;
+  void RemoveApp(const GURL& app_url,
+                 const GURL& install_url,
+                 SuccessCallback callback) override;
 
-  void OnSetCookieResult(const GURL& url,
+  void OnSetCookieResult(const GURL& app_url,
+                         const GURL& install_url,
                          SuccessCallback callback,
                          bool succeeded);
   void OnAppInstallResult(SuccessCallback callback,
-                          const GURL& url,
+                          const GURL& app_url,
+                          const GURL& install_url,
                           web_app::InstallResultCode code);
   void OnAppUninstallResult(const base::UnguessableToken& id,
-                            const GURL& url,
+                            const GURL& app_url,
+                            const GURL& install_url,
                             bool succeeded);
-  void OnDeleteCookiesResult(const GURL& url,
+  void OnDeleteCookiesResult(const GURL& app_url,
                              SuccessCallback callback,
                              uint32_t num_deleted);
 
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl_unittest.cc b/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl_unittest.cc
index b2e3f79..9acda9ef 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl_unittest.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl_unittest.cc
@@ -36,6 +36,7 @@
 namespace {
 
 const char kTestUrl1[] = "https://test-url-1.com/";
+const char kTestInstallUrl1[] = "https://test-url-1.com/install";
 const char kTestUrl2[] = "https://test-url-2.com/";
 
 web_app::PendingAppManager::AppInfo GetAppInfoForUrl(const GURL& url) {
@@ -159,15 +160,15 @@
     }
 
     // AndroidSmsAppSetupControllerImpl::PwaDelegate:
-    const extensions::Extension* GetPwaForUrl(const GURL& url,
+    const extensions::Extension* GetPwaForUrl(const GURL& install_url,
                                               Profile* profile) override {
-      if (!base::ContainsKey(url_to_pwa_map_, url))
+      if (!base::ContainsKey(url_to_pwa_map_, install_url))
         return nullptr;
 
-      return url_to_pwa_map_[url].get();
+      return url_to_pwa_map_[install_url].get();
     }
 
-    network::mojom::CookieManager* GetCookieManager(const GURL& url,
+    network::mojom::CookieManager* GetCookieManager(const GURL& app_url,
                                                     Profile* profile) override {
       return fake_cookie_manager_;
     }
@@ -205,7 +206,9 @@
         ->SetPwaDelegateForTesting(std::move(base_delegate));
   }
 
-  void CallSetUpApp(const GURL url, size_t num_expected_app_installs) {
+  void CallSetUpApp(const GURL& app_url,
+                    const GURL& install_url,
+                    size_t num_expected_app_installs) {
     const auto& install_requests =
         test_pending_app_manager_->install_requests();
     size_t num_install_requests_before_call = install_requests.size();
@@ -214,7 +217,7 @@
     base::HistogramTester histogram_tester;
 
     setup_controller_->SetUpApp(
-        url,
+        app_url, install_url,
         base::BindOnce(&AndroidSmsAppSetupControllerImplTest::OnSetUpAppResult,
                        base::Unretained(this), run_loop.QuitClosure()));
 
@@ -225,12 +228,12 @@
 
     // If the PWA was not already installed at the URL, SetUpApp() should
     // install it.
-    if (!test_pwa_delegate_->GetPwaForUrl(url, &profile_)) {
+    if (!test_pwa_delegate_->GetPwaForUrl(install_url, &profile_)) {
       EXPECT_EQ(num_install_requests_before_call + 1u, install_requests.size());
-      EXPECT_EQ(GetAppInfoForUrl(url), install_requests.back());
+      EXPECT_EQ(GetAppInfoForUrl(install_url), install_requests.back());
 
       EXPECT_EQ(ContentSetting::CONTENT_SETTING_ALLOW,
-                GetNotificationSetting(url));
+                GetNotificationSetting(app_url));
     }
 
     if (num_expected_app_installs) {
@@ -244,16 +247,17 @@
     last_set_up_app_result_.reset();
   }
 
-  void CallDeleteRememberDeviceByDefaultCookie(const GURL url) {
+  void CallDeleteRememberDeviceByDefaultCookie(const GURL& app_url) {
     base::RunLoop run_loop;
 
     setup_controller_->DeleteRememberDeviceByDefaultCookie(
-        url, base::BindOnce(&AndroidSmsAppSetupControllerImplTest::
-                                OnDeleteRememberDeviceByDefaultCookieResult,
-                            base::Unretained(this), run_loop.QuitClosure()));
+        app_url,
+        base::BindOnce(&AndroidSmsAppSetupControllerImplTest::
+                           OnDeleteRememberDeviceByDefaultCookieResult,
+                       base::Unretained(this), run_loop.QuitClosure()));
 
     fake_cookie_manager_->InvokePendingDeleteCookiesCallback(
-        url, "default_to_persist" /* expected_cookie_name */,
+        app_url, "default_to_persist" /* expected_cookie_name */,
         true /* success */);
 
     run_loop.Run();
@@ -261,7 +265,9 @@
     last_delete_cookie_result_.reset();
   }
 
-  void CallRemoveApp(const GURL url, size_t num_expected_app_uninstalls) {
+  void CallRemoveApp(const GURL& app_url,
+                     const GURL& install_url,
+                     size_t num_expected_app_uninstalls) {
     const auto& uninstall_requests =
         test_pending_app_manager_->uninstall_requests();
     size_t num_uninstall_requests_before_call = uninstall_requests.size();
@@ -270,19 +276,19 @@
     base::HistogramTester histogram_tester;
 
     setup_controller_->RemoveApp(
-        url,
+        app_url, install_url,
         base::BindOnce(&AndroidSmsAppSetupControllerImplTest::OnRemoveAppResult,
                        base::Unretained(this), run_loop.QuitClosure()));
 
     // If the PWA was already installed at the URL, RemoveApp() should uninstall
     // the it.
-    if (test_pwa_delegate_->GetPwaForUrl(url, &profile_)) {
+    if (test_pwa_delegate_->GetPwaForUrl(install_url, &profile_)) {
       EXPECT_EQ(num_uninstall_requests_before_call + 1u,
                 uninstall_requests.size());
-      EXPECT_EQ(url, uninstall_requests.back());
+      EXPECT_EQ(install_url, uninstall_requests.back());
 
       fake_cookie_manager_->InvokePendingDeleteCookiesCallback(
-          url, "default_to_persist" /* expected_cookie_name */,
+          app_url, "default_to_persist" /* expected_cookie_name */,
           true /* success */);
     }
 
@@ -345,43 +351,52 @@
 };
 
 TEST_F(AndroidSmsAppSetupControllerImplTest, SetUpApp_NoPreviousApp) {
-  CallSetUpApp(GURL(kTestUrl1), 1u /* num_expected_app_installs */);
+  CallSetUpApp(GURL(kTestUrl1), GURL(kTestInstallUrl1),
+               1u /* num_expected_app_installs */);
 }
 
 TEST_F(AndroidSmsAppSetupControllerImplTest, SetUpApp_AppAlreadyInstalled) {
   // Start with a PWA already installed at the URL.
-  test_pwa_delegate()->SetHasPwa(GURL(kTestUrl1), true);
-  CallSetUpApp(GURL(kTestUrl1), 0u /* num_expected_app_installs */);
+  test_pwa_delegate()->SetHasPwa(GURL(kTestInstallUrl1), true);
+  CallSetUpApp(GURL(kTestUrl1), GURL(kTestInstallUrl1),
+               0u /* num_expected_app_installs */);
 }
 
 TEST_F(AndroidSmsAppSetupControllerImplTest, SetUpApp_OtherPwaInstalled) {
   // Start with a PWA already installed at a different URL.
   test_pwa_delegate()->SetHasPwa(GURL(kTestUrl2), true);
-  CallSetUpApp(GURL(kTestUrl1), 1u /* num_expected_app_installs */);
+  CallSetUpApp(GURL(kTestUrl1), GURL(kTestInstallUrl1),
+               1u /* num_expected_app_installs */);
 }
 
 TEST_F(AndroidSmsAppSetupControllerImplTest, SetUpAppThenDeleteCookie) {
-  CallSetUpApp(GURL(kTestUrl1), 1u /* num_expected_app_installs */);
+  CallSetUpApp(GURL(kTestUrl1), GURL(kTestInstallUrl1),
+               1u /* num_expected_app_installs */);
   CallDeleteRememberDeviceByDefaultCookie(GURL(kTestUrl1));
 }
 
 TEST_F(AndroidSmsAppSetupControllerImplTest, SetUpAppThenRemove) {
   // Install and remove.
-  CallSetUpApp(GURL(kTestUrl1), 1u /* num_expected_app_installs */);
-  test_pwa_delegate()->SetHasPwa(GURL(kTestUrl1), true);
-  CallRemoveApp(GURL(kTestUrl1), 1u /* num_expected_app_uninstalls */);
-  test_pwa_delegate()->SetHasPwa(GURL(kTestUrl1), false);
+  CallSetUpApp(GURL(kTestUrl1), GURL(kTestInstallUrl1),
+               1u /* num_expected_app_installs */);
+  test_pwa_delegate()->SetHasPwa(GURL(kTestInstallUrl1), true);
+  CallRemoveApp(GURL(kTestUrl1), GURL(kTestInstallUrl1),
+                1u /* num_expected_app_uninstalls */);
+  test_pwa_delegate()->SetHasPwa(GURL(kTestInstallUrl1), false);
 
   // Repeat once more.
-  CallSetUpApp(GURL(kTestUrl1), 1u /* num_expected_app_installs */);
-  test_pwa_delegate()->SetHasPwa(GURL(kTestUrl1), true);
-  CallRemoveApp(GURL(kTestUrl1), 1u /* num_expected_app_uninstalls */);
-  test_pwa_delegate()->SetHasPwa(GURL(kTestUrl1), false);
+  CallSetUpApp(GURL(kTestUrl1), GURL(kTestInstallUrl1),
+               1u /* num_expected_app_installs */);
+  test_pwa_delegate()->SetHasPwa(GURL(kTestInstallUrl1), true);
+  CallRemoveApp(GURL(kTestUrl1), GURL(kTestInstallUrl1),
+                1u /* num_expected_app_uninstalls */);
+  test_pwa_delegate()->SetHasPwa(GURL(kTestInstallUrl1), false);
 }
 
 TEST_F(AndroidSmsAppSetupControllerImplTest, RemoveApp_NoInstalledApp) {
   // Do not have an installed app before attempting to remove it.
-  CallRemoveApp(GURL(kTestUrl1), 0u /* num_expected_app_uninstalls */);
+  CallRemoveApp(GURL(kTestUrl1), GURL(kTestInstallUrl1),
+                0u /* num_expected_app_uninstalls */);
 }
 
 }  // namespace android_sms
diff --git a/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.cc b/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.cc
index 8e47f19..a5d1640 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.cc
@@ -94,7 +94,7 @@
 }
 
 GURL AndroidSmsPairingStateTrackerImpl::GetPairingUrl() {
-  base::Optional<GURL> app_url = android_sms_app_manager_->GetInstalledAppUrl();
+  base::Optional<GURL> app_url = android_sms_app_manager_->GetCurrentAppUrl();
   if (app_url)
     return *app_url;
 
diff --git a/chrome/browser/chromeos/android_sms/android_sms_urls.cc b/chrome/browser/chromeos/android_sms/android_sms_urls.cc
index 3fb6cc0..242bd47e 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_urls.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_urls.cc
@@ -17,10 +17,13 @@
 
 namespace {
 
+const char kGoogleMessagesInstallUrl[] =
+    "https://messages.google.com/web/authentication";
+const char kGoogleMessagesUrl[] = "https://messages.google.com/web/";
 const char kAndroidMessagesUrl[] = "https://messages.android.com/";
-const char kGoogleMessagesUrl[] = "https://messages.google.com/";
 
-GURL GetAndroidMessagesURL(bool use_google_url_if_applicable) {
+GURL GetAndroidMessagesURL(bool use_google_url_if_applicable,
+                           bool use_install_url) {
   // If a custom URL was passed via a command line argument, use it.
   std::string url_from_command_line_arg =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
@@ -28,20 +31,25 @@
   if (!url_from_command_line_arg.empty())
     return GURL(url_from_command_line_arg);
 
-  return use_google_url_if_applicable ? GURL(kGoogleMessagesUrl)
-                                      : GURL(kAndroidMessagesUrl);
+  if (use_google_url_if_applicable) {
+    return use_install_url ? GURL(kGoogleMessagesInstallUrl)
+                           : GURL(kGoogleMessagesUrl);
+  }
+  return GURL(kAndroidMessagesUrl);
 }
 
 }  // namespace
 
-GURL GetAndroidMessagesURL() {
+GURL GetAndroidMessagesURL(bool use_install_url) {
   return GetAndroidMessagesURL(
-      base::FeatureList::IsEnabled(features::kUseMessagesGoogleComDomain));
+      base::FeatureList::IsEnabled(features::kUseMessagesGoogleComDomain),
+      use_install_url);
 }
 
-GURL GetAndroidMessagesURLOld() {
+GURL GetAndroidMessagesURLOld(bool use_install_url) {
   return GetAndroidMessagesURL(
-      !base::FeatureList::IsEnabled(features::kUseMessagesGoogleComDomain));
+      !base::FeatureList::IsEnabled(features::kUseMessagesGoogleComDomain),
+      use_install_url);
 }
 
 }  // namespace android_sms
diff --git a/chrome/browser/chromeos/android_sms/android_sms_urls.h b/chrome/browser/chromeos/android_sms/android_sms_urls.h
index b1292fe6..2f887c2 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_urls.h
+++ b/chrome/browser/chromeos/android_sms/android_sms_urls.h
@@ -12,14 +12,19 @@
 namespace android_sms {
 
 // Returns URL to Android Messages for Web page used by AndroidSmsService.
-GURL GetAndroidMessagesURL();
+// If |use_install_url| is true, the URL used only for installation and
+// uninstallation of the PWA is returned; otherwise, the URL for the service
+// worker is returned.
+GURL GetAndroidMessagesURL(bool use_install_url = false);
 
 // Returns the old URL used for Android Messages. In this context, the "old" URL
 // refers to the URL used before the last change to the
 // kUseMessagesGoogleComDomain flag. See go/awm-cros-domain for details.
 // TODO(https://crbug.com/917855): Remove this function when migration is
-// complete.
-GURL GetAndroidMessagesURLOld();
+// complete.  If |use_install_url| is true, the URL used only for installation
+// and uninstallation of the PWA is returned; otherwise, the URL for the service
+// worker is returned.
+GURL GetAndroidMessagesURLOld(bool use_install_url = false);
 
 }  // namespace android_sms
 
diff --git a/chrome/browser/chromeos/android_sms/connection_manager.cc b/chrome/browser/chromeos/android_sms/connection_manager.cc
index d7e4357b..34b8039 100644
--- a/chrome/browser/chromeos/android_sms/connection_manager.cc
+++ b/chrome/browser/chromeos/android_sms/connection_manager.cc
@@ -163,7 +163,7 @@
 
   // Return the installed app URL if the PWA is installed.
   base::Optional<GURL> installed_url =
-      android_sms_app_manager_->GetInstalledAppUrl();
+      android_sms_app_manager_->GetCurrentAppUrl();
   if (installed_url)
     return installed_url;
 
diff --git a/chrome/browser/chromeos/android_sms/fake_android_sms_app_manager.cc b/chrome/browser/chromeos/android_sms/fake_android_sms_app_manager.cc
index 32d00f0..75e6a72 100644
--- a/chrome/browser/chromeos/android_sms/fake_android_sms_app_manager.cc
+++ b/chrome/browser/chromeos/android_sms/fake_android_sms_app_manager.cc
@@ -23,7 +23,7 @@
   NotifyInstalledAppUrlChanged();
 }
 
-base::Optional<GURL> FakeAndroidSmsAppManager::GetInstalledAppUrl() {
+base::Optional<GURL> FakeAndroidSmsAppManager::GetCurrentAppUrl() {
   return url_;
 }
 
diff --git a/chrome/browser/chromeos/android_sms/fake_android_sms_app_manager.h b/chrome/browser/chromeos/android_sms/fake_android_sms_app_manager.h
index 5405716..ba290b4b 100644
--- a/chrome/browser/chromeos/android_sms/fake_android_sms_app_manager.h
+++ b/chrome/browser/chromeos/android_sms/fake_android_sms_app_manager.h
@@ -31,7 +31,7 @@
 
  private:
   // AndroidSmsAppManager:
-  base::Optional<GURL> GetInstalledAppUrl() override;
+  base::Optional<GURL> GetCurrentAppUrl() override;
 
   base::Optional<GURL> url_;
 
diff --git a/chrome/browser/chromeos/android_sms/fake_android_sms_app_setup_controller.cc b/chrome/browser/chromeos/android_sms/fake_android_sms_app_setup_controller.cc
index 0e56d0a..e81d3be8 100644
--- a/chrome/browser/chromeos/android_sms/fake_android_sms_app_setup_controller.cc
+++ b/chrome/browser/chromeos/android_sms/fake_android_sms_app_setup_controller.cc
@@ -27,62 +27,66 @@
 FakeAndroidSmsAppSetupController::~FakeAndroidSmsAppSetupController() = default;
 
 const FakeAndroidSmsAppSetupController::AppMetadata*
-FakeAndroidSmsAppSetupController::GetAppMetadataAtUrl(const GURL& url) const {
-  const auto it = url_to_metadata_map_.find(url);
-  if (it == url_to_metadata_map_.end())
+FakeAndroidSmsAppSetupController::GetAppMetadataAtUrl(
+    const GURL& install_url) const {
+  const auto it = install_url_to_metadata_map_.find(install_url);
+  if (it == install_url_to_metadata_map_.end())
     return nullptr;
   return std::addressof(it->second);
 }
 
 void FakeAndroidSmsAppSetupController::SetAppAtUrl(
-    const GURL& url,
+    const GURL& install_url,
     const base::Optional<extensions::ExtensionId>& id_for_app) {
   if (!id_for_app) {
-    url_to_metadata_map_.erase(url);
+    install_url_to_metadata_map_.erase(install_url);
     return;
   }
 
-  // Create a test Extension and add it to |url_to_metadata_map_|.
+  // Create a test Extension and add it to |install_url_to_metadata_map_|.
   base::FilePath path;
   base::PathService::Get(extensions::DIR_TEST_DATA, &path);
-  url_to_metadata_map_[url].pwa =
+  install_url_to_metadata_map_[install_url].pwa =
       extensions::ExtensionBuilder(
-          url.spec(), extensions::ExtensionBuilder::Type::PLATFORM_APP)
-          .SetPath(path.AppendASCII(url.spec()))
+          install_url.spec(), extensions::ExtensionBuilder::Type::PLATFORM_APP)
+          .SetPath(path.AppendASCII(install_url.spec()))
           .SetID(*id_for_app)
           .Build();
 }
 
 void FakeAndroidSmsAppSetupController::CompletePendingSetUpAppRequest(
-    const GURL& expected_url,
+    const GURL& expected_app_url,
+    const GURL& expected_install_url,
     const base::Optional<extensions::ExtensionId>& id_for_app) {
   DCHECK(!pending_set_up_app_requests_.empty());
 
   auto request = std::move(pending_set_up_app_requests_.front());
   pending_set_up_app_requests_.erase(pending_set_up_app_requests_.begin());
-  DCHECK_EQ(expected_url, request->first);
+  DCHECK_EQ(expected_app_url, std::get<0>(*request));
+  DCHECK_EQ(expected_install_url, std::get<1>(*request));
 
   if (!id_for_app) {
-    std::move(request->second).Run(false /* success */);
+    std::move(std::get<2>(*request)).Run(false /* success */);
     return;
   }
 
-  SetAppAtUrl(expected_url, *id_for_app);
-  std::move(request->second).Run(true /* success */);
+  SetAppAtUrl(expected_install_url, *id_for_app);
+  std::move(std::get<2>(*request)).Run(true /* success */);
 }
 
 void FakeAndroidSmsAppSetupController::CompletePendingDeleteCookieRequest(
-    const GURL& expected_url) {
+    const GURL& expected_app_url,
+    const GURL& expected_install_url) {
   DCHECK(!pending_delete_cookie_requests_.empty());
 
   auto request = std::move(pending_delete_cookie_requests_.front());
   pending_delete_cookie_requests_.erase(
       pending_delete_cookie_requests_.begin());
-  DCHECK_EQ(expected_url, request->first);
+  DCHECK_EQ(expected_app_url, request->first);
 
   // The app must exist before the cookie is deleted.
-  auto it = url_to_metadata_map_.find(expected_url);
-  DCHECK(it != url_to_metadata_map_.end());
+  auto it = install_url_to_metadata_map_.find(expected_install_url);
+  DCHECK(it != install_url_to_metadata_map_.end());
 
   it->second.is_cookie_present = false;
 
@@ -90,45 +94,49 @@
 }
 
 void FakeAndroidSmsAppSetupController::CompleteRemoveAppRequest(
-    const GURL& expected_url,
+    const GURL& expected_app_url,
+    const GURL& expected_install_url,
     bool should_succeed) {
   DCHECK(!pending_remove_app_requests_.empty());
 
   auto request = std::move(pending_remove_app_requests_.front());
   pending_remove_app_requests_.erase(pending_remove_app_requests_.begin());
-  DCHECK_EQ(expected_url, request->first);
+  DCHECK_EQ(expected_app_url, std::get<0>(*request));
+  DCHECK_EQ(expected_install_url, std::get<1>(*request));
 
   if (should_succeed)
-    SetAppAtUrl(expected_url, base::nullopt /* id_for_app */);
+    SetAppAtUrl(expected_install_url, base::nullopt /* id_for_app */);
 
-  std::move(request->second).Run(should_succeed);
+  std::move(std::get<2>(*request)).Run(should_succeed);
 }
 
-void FakeAndroidSmsAppSetupController::SetUpApp(const GURL& url,
+void FakeAndroidSmsAppSetupController::SetUpApp(const GURL& app_url,
+                                                const GURL& install_url,
                                                 SuccessCallback callback) {
-  pending_set_up_app_requests_.push_back(
-      std::make_unique<RequestData>(url, std::move(callback)));
+  pending_set_up_app_requests_.push_back(std::make_unique<AppRequestData>(
+      app_url, install_url, std::move(callback)));
 }
 
 const extensions::Extension* FakeAndroidSmsAppSetupController::GetPwa(
-    const GURL& url) {
-  auto it = url_to_metadata_map_.find(url);
-  if (it == url_to_metadata_map_.end())
+    const GURL& install_url) {
+  auto it = install_url_to_metadata_map_.find(install_url);
+  if (it == install_url_to_metadata_map_.end())
     return nullptr;
   return it->second.pwa.get();
 }
 
 void FakeAndroidSmsAppSetupController::DeleteRememberDeviceByDefaultCookie(
-    const GURL& url,
+    const GURL& app_url,
     SuccessCallback callback) {
   pending_delete_cookie_requests_.push_back(
-      std::make_unique<RequestData>(url, std::move(callback)));
+      std::make_unique<RequestData>(app_url, std::move(callback)));
 }
 
-void FakeAndroidSmsAppSetupController::RemoveApp(const GURL& url,
+void FakeAndroidSmsAppSetupController::RemoveApp(const GURL& app_url,
+                                                 const GURL& install_url,
                                                  SuccessCallback callback) {
-  pending_remove_app_requests_.push_back(
-      std::make_unique<RequestData>(url, std::move(callback)));
+  pending_remove_app_requests_.push_back(std::make_unique<AppRequestData>(
+      app_url, install_url, std::move(callback)));
 }
 
 }  // namespace android_sms
diff --git a/chrome/browser/chromeos/android_sms/fake_android_sms_app_setup_controller.h b/chrome/browser/chromeos/android_sms/fake_android_sms_app_setup_controller.h
index 05306384..9e9e7ba 100644
--- a/chrome/browser/chromeos/android_sms/fake_android_sms_app_setup_controller.h
+++ b/chrome/browser/chromeos/android_sms/fake_android_sms_app_setup_controller.h
@@ -7,6 +7,7 @@
 
 #include <list>
 #include <memory>
+#include <tuple>
 #include <utility>
 
 #include "base/containers/flat_map.h"
@@ -39,45 +40,55 @@
     bool is_cookie_present = true;
   };
 
-  // Returns null if no app has been installed at |url|.
-  const AppMetadata* GetAppMetadataAtUrl(const GURL& url) const;
+  // Returns null if no app has been installed at |install_url|.
+  const AppMetadata* GetAppMetadataAtUrl(const GURL& install_url) const;
 
   // If |id_for_app| is provided, this function installs an app with the given
-  // ID at |ur|. Otherwise, this function removes any existing app at that URL.
-  void SetAppAtUrl(const GURL& url,
+  // ID at |install_url|. Otherwise, this function removes any existing app at
+  // that URL.
+  void SetAppAtUrl(const GURL& install_url,
                    const base::Optional<extensions::ExtensionId>& id_for_app);
 
   // Completes a pending setup request (i.e., a previous call to SetUpApp()).
   // If |id_for_app| is set, the request is successful and the installed app
   // will have the provided ID; if |id_for_app| is null, the request fails.
   void CompletePendingSetUpAppRequest(
-      const GURL& expected_url,
+      const GURL& expected_app_url,
+      const GURL& expected_install_url,
       const base::Optional<extensions::ExtensionId>& id_for_app);
 
   // Completes a pending cookie deletion request (i.e., a previous call to
   // DeleteRememberDeviceByDefaultCookie()).
-  void CompletePendingDeleteCookieRequest(const GURL& expected_url);
+  void CompletePendingDeleteCookieRequest(const GURL& expected_app_url,
+                                          const GURL& expected_install_url);
 
   // Completes a pending app removal request (i.e., a previous call to
   // RemoveApp()). If |success| is true, the app will be removed; otherwise, the
   // app will remain in place.
-  void CompleteRemoveAppRequest(const GURL& expected_url, bool success);
+  void CompleteRemoveAppRequest(const GURL& expected_app_url,
+                                const GURL& expected_install_url,
+                                bool success);
 
  private:
   // AndroidSmsAppSetupController:
-  void SetUpApp(const GURL& url, SuccessCallback callback) override;
-  const extensions::Extension* GetPwa(const GURL& url) override;
-  void DeleteRememberDeviceByDefaultCookie(const GURL& url,
+  void SetUpApp(const GURL& app_url,
+                const GURL& install_url,
+                SuccessCallback callback) override;
+  const extensions::Extension* GetPwa(const GURL& install_url) override;
+  void DeleteRememberDeviceByDefaultCookie(const GURL& app_url,
                                            SuccessCallback callback) override;
-  void RemoveApp(const GURL& url, SuccessCallback callback) override;
+  void RemoveApp(const GURL& app_url,
+                 const GURL& install_url,
+                 SuccessCallback callback) override;
 
+  using AppRequestData = std::tuple<GURL, GURL, SuccessCallback>;
   using RequestData = std::pair<GURL, SuccessCallback>;
 
-  std::list<std::unique_ptr<RequestData>> pending_set_up_app_requests_;
   std::list<std::unique_ptr<RequestData>> pending_delete_cookie_requests_;
-  std::list<std::unique_ptr<RequestData>> pending_remove_app_requests_;
+  std::list<std::unique_ptr<AppRequestData>> pending_set_up_app_requests_;
+  std::list<std::unique_ptr<AppRequestData>> pending_remove_app_requests_;
 
-  base::flat_map<GURL, AppMetadata> url_to_metadata_map_;
+  base::flat_map<GURL, AppMetadata> install_url_to_metadata_map_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeAndroidSmsAppSetupController);
 };
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.cc
index 367e485..a56d3a4 100644
--- a/chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.cc
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.cc
@@ -36,9 +36,14 @@
 constexpr char kAcquireBufferQuery[] =
     "android:onMessageReceived/android:handleMessageInvalidate/"
     "android:latchBuffer/android:updateTexImage/android:acquireBuffer";
-constexpr char kReleaseBufferQuery[] =
+// Android PI+
+constexpr char kReleaseBufferQueryP[] =
     "android:onMessageReceived/android:handleMessageRefresh/"
     "android:postComposition/android:releaseBuffer";
+// Android NYC
+constexpr char kReleaseBufferQueryN[] =
+    "android:onMessageReceived/android:handleMessageRefresh/"
+    "android:releaseBuffer";
 constexpr char kDequeueBufferQuery[] = "android:dequeueBuffer";
 constexpr char kQueueBufferQuery[] = "android:queueBuffer";
 
@@ -304,7 +309,9 @@
   BufferToEvents per_buffer_surface_flinger_events;
   ProcessSurfaceFlingerEvents(common_model, kAcquireBufferQuery,
                               &per_buffer_surface_flinger_events);
-  ProcessSurfaceFlingerEvents(common_model, kReleaseBufferQuery,
+  ProcessSurfaceFlingerEvents(common_model, kReleaseBufferQueryP,
+                              &per_buffer_surface_flinger_events);
+  ProcessSurfaceFlingerEvents(common_model, kReleaseBufferQueryN,
                               &per_buffer_surface_flinger_events);
   ProcessSurfaceFlingerEvents(common_model, kQueueBufferQuery,
                               &per_buffer_surface_flinger_events);
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc
index 9b12d6c8..13048d4 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -83,7 +83,6 @@
     kDeviceNativePrintersWhitelist,
     kDeviceQuirksDownloadEnabled,
     kDeviceUnaffiliatedCrostiniAllowed,
-    kDeviceWallpaperImage,
     kDeviceDisplayResolution,
     kDisplayRotationDefault,
     kExtensionCacheSize,
@@ -630,14 +629,6 @@
         policy.quirks_download_enabled().quirks_download_enabled());
   }
 
-  if (policy.has_device_wallpaper_image() &&
-      policy.device_wallpaper_image().has_device_wallpaper_image()) {
-    SetJsonDeviceSetting(
-        kDeviceWallpaperImage, policy::key::kDeviceWallpaperImage,
-        policy.device_wallpaper_image().device_wallpaper_image(),
-        new_values_cache);
-  }
-
   if (policy.has_device_off_hours()) {
     auto off_hours_policy = policy::off_hours::ConvertOffHoursProtoToValue(
         policy.device_off_hours());
diff --git a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
index 7ed620c..17e9c94 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider_unittest.cc
@@ -607,20 +607,6 @@
   VerifyLogUploadSettings(false);
 }
 
-TEST_F(DeviceSettingsProviderTest, SetWallpaperSettings) {
-  // Invalid format should be ignored.
-  const std::string invalid_format = "\\\\invalid\\format";
-  SetWallpaperSettings(invalid_format);
-  EXPECT_EQ(nullptr, provider_->Get(kDeviceWallpaperImage));
-
-  // Set with valid json format.
-  const std::string valid_format(R"({"url":"foo", "hash": "bar"})");
-  SetWallpaperSettings(valid_format);
-  std::unique_ptr<base::DictionaryValue> expected_value =
-      base::DictionaryValue::From(base::JSONReader::Read(valid_format));
-  EXPECT_EQ(*expected_value, *provider_->Get(kDeviceWallpaperImage));
-}
-
 TEST_F(DeviceSettingsProviderTest, SamlLoginAuthenticationType) {
   using PolicyProto = em::SamlLoginAuthenticationTypeProto;
 
diff --git a/chrome/browser/devtools/protocol/cast_handler_unittest.cc b/chrome/browser/devtools/protocol/cast_handler_unittest.cc
index 7e9ec8a8..a13f11e 100644
--- a/chrome/browser/devtools/protocol/cast_handler_unittest.cc
+++ b/chrome/browser/devtools/protocol/cast_handler_unittest.cc
@@ -78,7 +78,12 @@
     EXPECT_CALL(*router_, RegisterMediaRoutesObserver(_))
         .WillOnce(SaveArg<0>(&routes_observer_));
     EXPECT_CALL(*router_, RegisterMediaSinksObserver(_))
-        .WillOnce(DoAll(SaveArg<0>(&sinks_observer_), Return(true)));
+        .WillRepeatedly(
+            WithArg<0>([this](media_router::MediaSinksObserver* observer) {
+              if (observer->source())
+                sinks_observer_ = observer;
+              return true;
+            }));
     handler_->Enable(protocol::Maybe<std::string>());
   }
 
@@ -99,7 +104,7 @@
   EXPECT_CALL(*router_, RegisterMediaSinksObserver(_))
       .WillOnce(WithArg<0>(
           [presentation_url](media_router::MediaSinksObserver* observer) {
-            EXPECT_EQ(presentation_url, observer->source().id());
+            EXPECT_EQ(presentation_url, observer->source()->id());
             observer->OnSinksUpdated({sink1, sink2}, {});
             return true;
           }));
diff --git a/chrome/browser/extensions/extension_bindings_apitest.cc b/chrome/browser/extensions/extension_bindings_apitest.cc
index c21541c..aa3d6a5 100644
--- a/chrome/browser/extensions/extension_bindings_apitest.cc
+++ b/chrome/browser/extensions/extension_bindings_apitest.cc
@@ -18,6 +18,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/process_manager.h"
@@ -637,6 +638,78 @@
   }
 }
 
+// Tests that bindings are properly instantiated for a window navigated to an
+// extension URL after being opened with an undefined URL.
+// Regression test for https://crbug.com/925118.
+IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest,
+                       TestBindingsAvailableWithNavigatedBlankWindow) {
+  constexpr char kManifest[] =
+      R"({
+           "name": "chrome.runtime bug checker",
+           "description": "test case for crbug.com/925118",
+           "version": "0",
+           "manifest_version": 2
+         })";
+  constexpr char kOpenerHTML[] =
+      R"(<!DOCTYPE html>
+         <html>
+           <head>
+             <script src='opener.js'></script>
+           </head>
+           <body>
+           </body>
+         </html>)";
+  // opener.js opens a blank window and then navigates it to an extension URL
+  // (where extension APIs should be available).
+  constexpr char kOpenerJS[] =
+      R"(const url = chrome.runtime.getURL('/page.html');
+         const win = window.open(undefined, '');
+         win.location = url;
+         chrome.test.notifyPass())";
+  constexpr char kPageHTML[] =
+      R"(<!DOCTYPE html>
+         <html>
+           This space intentionally left blank.
+         </html>)";
+  TestExtensionDir extension_dir;
+  extension_dir.WriteManifest(kManifest);
+  extension_dir.WriteFile(FILE_PATH_LITERAL("opener.html"), kOpenerHTML);
+  extension_dir.WriteFile(FILE_PATH_LITERAL("opener.js"), kOpenerJS);
+  extension_dir.WriteFile(FILE_PATH_LITERAL("page.html"), kPageHTML);
+
+  const Extension* extension = LoadExtension(extension_dir.UnpackedPath());
+  const GURL target_url = extension->GetResourceURL("page.html");
+
+  ResultCatcher catcher;
+  content::TestNavigationObserver observer(target_url);
+  observer.StartWatchingNewWebContents();
+  ui_test_utils::NavigateToURL(browser(),
+                               extension->GetResourceURL("opener.html"));
+  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+  observer.Wait();
+  EXPECT_TRUE(observer.last_navigation_succeeded());
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_EQ(target_url, web_contents->GetLastCommittedURL());
+
+  // Check whether bindings are available. They should be.
+  constexpr char kScript[] =
+      R"(let message;
+         if (!chrome.runtime)
+           message = 'Runtime not defined';
+         else if (!chrome.tabs)
+           message = 'Tabs not defined';
+         else
+           message = 'success';
+         domAutomationController.send(message);)";
+  std::string result;
+  // Note: Can't use EvalJs() because of CSP in extension pages.
+  EXPECT_TRUE(
+      content::ExecuteScriptAndExtractString(web_contents, kScript, &result));
+  EXPECT_EQ("success", result);
+}
+
 // Run core bindings API tests with both native and JS-based bindings. This
 // ensures we have some minimum level of coverage while in the experimental
 // phase, when native bindings may be enabled on trunk but not at 100% stable.
diff --git a/chrome/browser/first_run/upgrade_util_win.cc b/chrome/browser/first_run/upgrade_util_win.cc
index 9c30b37..8d77b39 100644
--- a/chrome/browser/first_run/upgrade_util_win.cc
+++ b/chrome/browser/first_run/upgrade_util_win.cc
@@ -11,6 +11,7 @@
 #include <wrl/client.h>
 
 #include <algorithm>
+#include <ios>
 #include <string>
 
 #include "base/base_paths.h"
@@ -53,24 +54,41 @@
 bool InvokeGoogleUpdateForRename() {
 #if defined(GOOGLE_CHROME_BUILD)
   Microsoft::WRL::ComPtr<IProcessLauncher> ipl;
-  if (!FAILED(::CoCreateInstance(__uuidof(ProcessLauncherClass), nullptr,
-                                 CLSCTX_ALL, IID_PPV_ARGS(&ipl)))) {
-    ULONG_PTR phandle = NULL;
-    DWORD id = GetCurrentProcessId();
-    if (!FAILED(ipl->LaunchCmdElevated(install_static::GetAppGuid(),
-                                       google_update::kRegRenameCmdField, id,
-                                       &phandle))) {
-      HANDLE handle = HANDLE(phandle);
-      WaitForSingleObject(handle, INFINITE);
-      DWORD exit_code;
-      ::GetExitCodeProcess(handle, &exit_code);
-      ::CloseHandle(handle);
-      if (exit_code == installer::RENAME_SUCCESSFUL)
-        return true;
-    }
+  HRESULT hr = ::CoCreateInstance(__uuidof(ProcessLauncherClass), nullptr,
+                                  CLSCTX_ALL, IID_PPV_ARGS(&ipl));
+  if (FAILED(hr)) {
+    LOG(ERROR) << "CoCreate ProcessLauncherClass failed; hr = " << std::hex
+               << hr;
+    return false;
   }
-#endif  // GOOGLE_CHROME_BUILD
+
+  ULONG_PTR process_handle;
+  hr = ipl->LaunchCmdElevated(install_static::GetAppGuid(),
+                              google_update::kRegRenameCmdField,
+                              ::GetCurrentProcessId(), &process_handle);
+  if (FAILED(hr)) {
+    LOG(ERROR) << "IProcessLauncher::LaunchCmdElevated failed; hr = "
+               << std::hex << hr;
+    return false;
+  }
+
+  base::Process rename_process(
+      reinterpret_cast<base::ProcessHandle>(process_handle));
+  int exit_code;
+  if (!rename_process.WaitForExit(&exit_code)) {
+    PLOG(ERROR) << "WaitForExit of rename process failed";
+    return false;
+  }
+
+  if (exit_code != installer::RENAME_SUCCESSFUL) {
+    LOG(ERROR) << "Rename process failed with exit code " << exit_code;
+    return false;
+  }
+
+  return true;
+#else   // GOOGLE_CHROME_BUILD
   return false;
+#endif  // GOOGLE_CHROME_BUILD
 }
 
 }  // namespace
@@ -118,25 +136,45 @@
   // If this is a user-level install, directly launch a process to rename Chrome
   // executables. Obtain the command to launch the process from the registry.
   base::win::RegKey key;
-  if (key.Open(HKEY_CURRENT_USER, install_static::GetClientsKeyPath().c_str(),
-               KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS) {
-    std::wstring rename_cmd;
-    if (key.ReadValue(google_update::kRegRenameCmdField,
-                      &rename_cmd) == ERROR_SUCCESS) {
-      base::LaunchOptions options;
-      options.wait = true;
-      options.start_hidden = true;
-      base::Process process = base::LaunchProcess(rename_cmd, options);
-      if (process.IsValid()) {
-        DWORD exit_code;
-        ::GetExitCodeProcess(process.Handle(), &exit_code);
-        if (exit_code == installer::RENAME_SUCCESSFUL)
-          return true;
-      }
-    }
+  auto result =
+      key.Open(HKEY_CURRENT_USER, install_static::GetClientsKeyPath().c_str(),
+               KEY_QUERY_VALUE | KEY_WOW64_32KEY);
+  if (result != ERROR_SUCCESS) {
+    ::SetLastError(result);
+    PLOG(ERROR) << "Open Clients key failed";
+    return false;
   }
 
-  return false;
+  std::wstring rename_cmd;
+  result = key.ReadValue(google_update::kRegRenameCmdField, &rename_cmd);
+  if (result != ERROR_SUCCESS) {
+    ::SetLastError(result);
+    PLOG(ERROR) << "Read rename command failed";
+    return false;
+  }
+
+  base::LaunchOptions options;
+  options.wait = true;
+  options.start_hidden = true;
+  ::SetLastError(ERROR_SUCCESS);
+  base::Process process = base::LaunchProcess(rename_cmd, options);
+  if (!process.IsValid()) {
+    PLOG(ERROR) << "Launch rename process failed";
+    return false;
+  }
+
+  DWORD exit_code;
+  if (!::GetExitCodeProcess(process.Handle(), &exit_code)) {
+    PLOG(ERROR) << "GetExitCodeProcess of rename process failed";
+    return false;
+  }
+
+  if (exit_code != installer::RENAME_SUCCESSFUL) {
+    LOG(ERROR) << "Rename process failed with exit code " << exit_code;
+    return false;
+  }
+
+  return true;
 }
 
 bool IsRunningOldChrome() {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index d624898..fcf85e21 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -221,6 +221,11 @@
     "expiry_milestone": 74
   },
   {
+    "name": "autofill-assistant-chrome-entry",
+    "owners": [ "gogerald,wuandy" ],
+    "expiry_milestone": 79
+  },
+  {
     "name": "autofill-cache-query-responses",
     "owners": [ "rogerm" ],
     "expiry_milestone": 72
@@ -1729,7 +1734,7 @@
   },
   {
     "name": "enable-query-in-omnibox",
-    // "owners": [ "your-team" ],
+    "owners": [ "tommycli", "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
@@ -2713,77 +2718,82 @@
   },
   {
     "name": "omnibox-display-title-for-current-url",
-    // "owners": [ "your-team" ],
+    "owners": [ "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-drive-suggestions",
-    // "owners": [ "your-team" ],
+    "owners": [ "skare", "chrome-omnibox-team" ],
+    "expiry_milestone": 76
+  },
+  {
+    "name": "omnibox-experimental-keyword-mode",
+    "owners": [ "krb", "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-new-answer-layout",
-    // "owners": [ "your-team" ],
+    "owners": [ "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-pedal-suggestions",
-    // "owners": [ "your-team" ],
+    "owners": [ "orinj", "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-reverse-answers",
-    // "owners": [ "your-team" ],
+    "owners": [ "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-rich-entity-suggestions",
-    // "owners": [ "your-team" ],
+    "owners": [ "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-spare-renderer",
-    // "owners": [ "your-team" ],
+    "owners": [ "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-tab-switch-suggestions",
-    // "owners": [ "your-team" ],
+    "owners": [ "krb", "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-tail-suggestions",
-    // "owners": [ "your-team" ],
+    "owners": [ "krb", "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-ui-hide-steady-state-url-path-query-and-ref",
-    // "owners": [ "your-team" ],
+    "owners": [ "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-ui-hide-steady-state-url-scheme",
-    // "owners": [ "your-team" ],
+    "owners": [ "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-ui-hide-steady-state-url-trivial-subdomains",
-    // "owners": [ "your-team" ],
+    "owners": [ "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-ui-max-autocomplete-matches",
-    // "owners": [ "your-team" ],
+    "owners": [ "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-ui-one-click-unelide",
-    "owners": [ "tommycli" ],
+    "owners": [ "tommycli", "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
     "name": "omnibox-ui-swap-title-and-url",
-    // "owners": [ "your-team" ],
+    "owners": [ "chrome-omnibox-team" ],
     "expiry_milestone": 76
   },
   {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index b57c0ec..c98b39a4 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -103,6 +103,11 @@
 extern const char kAutofillAlwaysShowServerCardsInSyncTransportDescription[] =
     "Always show server cards when in sync transport mode for wallet data";
 
+extern const char kAutofillAssistantChromeEntryName[] =
+    "AutofillAssistantChromeEntry";
+extern const char kAutofillAssistantChromeEntryDescription[] =
+    "Initiate autofill assistant from within Chrome.";
+
 const char kAutofillCacheQueryResponsesName[] =
     "Cache Autofill Query Responses";
 const char kAutofillCacheQueryResponsesDescription[] =
@@ -1267,11 +1272,6 @@
     "version, currently under development. WARNING: when enabled, Password "
     "Manager might stop working";
 
-const char kNewRemotePlaybackPipelineName[] =
-    "Enable the new remote playback pipeline.";
-const char kNewRemotePlaybackPipelineDescription[] =
-    "Enable the new pipeline for playing media element remotely via "
-    "RemotePlayback API or native controls.";
 const char kUseSurfaceLayerForVideoName[] =
     "Enable the use of SurfaceLayer objects for videos.";
 const char kUseSurfaceLayerForVideoDescription[] =
@@ -2836,6 +2836,12 @@
     "Display suggestions for Google Drive documents in the omnibox when Google "
     "is the default search engine.";
 
+const char kOmniboxExperimentalKeywordModeName[] =
+    "Omnibox Experimental Keyword Mode";
+const char kOmniboxExperimentalKeywordModeDescription[] =
+    "Enables various experimental features related to keyword mode, its "
+    "suggestions and layout";
+
 const char kOmniboxPedalSuggestionsName[] = "Omnibox Pedal suggestions";
 const char kOmniboxPedalSuggestionsDescription[] =
     "Enable omnibox Pedal suggestions using either a side button in suggestion "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index d21bc7b..dbfbb030 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -94,6 +94,9 @@
 extern const char kAutofillAlwaysShowServerCardsInSyncTransportName[];
 extern const char kAutofillAlwaysShowServerCardsInSyncTransportDescription[];
 
+extern const char kAutofillAssistantChromeEntryName[];
+extern const char kAutofillAssistantChromeEntryDescription[];
+
 extern const char kAutofillCacheQueryResponsesName[];
 extern const char kAutofillCacheQueryResponsesDescription[];
 
@@ -766,9 +769,6 @@
 extern const char kNewPasswordFormParsingForSavingName[];
 extern const char kNewPasswordFormParsingForSavingDescription[];
 
-extern const char kNewRemotePlaybackPipelineName[];
-extern const char kNewRemotePlaybackPipelineDescription[];
-
 extern const char kOnlyNewPasswordFormParsingName[];
 extern const char kOnlyNewPasswordFormParsingDescription[];
 
@@ -1681,6 +1681,9 @@
 extern const char kOmniboxDriveSuggestionsName[];
 extern const char kOmniboxDriveSuggestionsDescriptions[];
 
+extern const char kOmniboxExperimentalKeywordModeName[];
+extern const char kOmniboxExperimentalKeywordModeDescription[];
+
 extern const char kOmniboxPedalSuggestionsName[];
 extern const char kOmniboxPedalSuggestionsDescription[];
 
diff --git a/chrome/browser/generic_sensor/sensor_permission_context.cc b/chrome/browser/generic_sensor/sensor_permission_context.cc
index 9d0df83..b946135 100644
--- a/chrome/browser/generic_sensor/sensor_permission_context.cc
+++ b/chrome/browser/generic_sensor/sensor_permission_context.cc
@@ -39,29 +39,6 @@
     content_settings->OnContentBlocked(CONTENT_SETTINGS_TYPE_SENSORS);
 }
 
-ContentSetting SensorPermissionContext::GetPermissionStatusInternal(
-    content::RenderFrameHost* render_frame_host,
-    const GURL& requesting_origin,
-    const GURL& embedding_origin) const {
-  // TODO(juncai): We may need to add cross-origin iframes check here when we
-  // can grant permission for certain sensor types. Currently this function
-  // doesn't have any information of which sensor type requests permission.
-  // The Generic Sensor API is not allowed in cross-origin iframes and
-  // this is enforced by the renderer.
-  // https://crbug.com/787019
-
-  // This is to allow DeviceMotion and DeviceOrientation Event to be
-  // able to access sensors (which are provided by generic sensor) in
-  // cross-origin iframes. The Generic Sensor API is not allowed in
-  // cross-origin iframes and this is enforced by the renderer.
-
-  // Sensors are allowed by default in content_settings_registry.cc.
-  // If cross-origin access check is required, comparison between requesting
-  // and embedding origin must be performed.
-  return PermissionContextBase::GetPermissionStatusInternal(
-      render_frame_host, requesting_origin, embedding_origin);
-}
-
 bool SensorPermissionContext::IsRestrictedToSecureOrigins() const {
   // This is to allow non-secure origins that use DeviceMotion and
   // DeviceOrientation Event to be able to access sensors that are provided
diff --git a/chrome/browser/generic_sensor/sensor_permission_context.h b/chrome/browser/generic_sensor/sensor_permission_context.h
index dcf6d81..10ae0f77 100644
--- a/chrome/browser/generic_sensor/sensor_permission_context.h
+++ b/chrome/browser/generic_sensor/sensor_permission_context.h
@@ -19,10 +19,6 @@
   void UpdateTabContext(const PermissionRequestID& id,
                         const GURL& requesting_frame,
                         bool allowed) override;
-  ContentSetting GetPermissionStatusInternal(
-      content::RenderFrameHost* render_frame_host,
-      const GURL& requesting_origin,
-      const GURL& embedding_origin) const override;
   bool IsRestrictedToSecureOrigins() const override;
 
   DISALLOW_COPY_AND_ASSIGN(SensorPermissionContext);
diff --git a/chrome/browser/media/android/router/media_router_android.cc b/chrome/browser/media/android/router/media_router_android.cc
index 173f750..6f0c1d9 100644
--- a/chrome/browser/media/android/router/media_router_android.cc
+++ b/chrome/browser/media/android/router/media_router_android.cc
@@ -205,7 +205,7 @@
 
 bool MediaRouterAndroid::RegisterMediaSinksObserver(
     MediaSinksObserver* observer) {
-  const std::string& source_id = observer->source().id();
+  const std::string& source_id = observer->source()->id();
   auto& observer_list = sinks_observers_[source_id];
   if (!observer_list) {
     observer_list = std::make_unique<MediaSinksObserverList>();
@@ -219,7 +219,7 @@
 
 void MediaRouterAndroid::UnregisterMediaSinksObserver(
     MediaSinksObserver* observer) {
-  const std::string& source_id = observer->source().id();
+  const std::string& source_id = observer->source()->id();
   auto it = sinks_observers_.find(source_id);
   if (it == sinks_observers_.end() || !it->second->HasObserver(observer))
     return;
diff --git a/chrome/browser/media/router/media_sinks_observer.cc b/chrome/browser/media/router/media_sinks_observer.cc
index e7276fe5..47f91ae 100644
--- a/chrome/browser/media/router/media_sinks_observer.cc
+++ b/chrome/browser/media/router/media_sinks_observer.cc
@@ -21,6 +21,11 @@
   DCHECK(router_);
 }
 
+MediaSinksObserver::MediaSinksObserver(MediaRouter* router)
+    : router_(router), initialized_(false) {
+  DCHECK(router_);
+}
+
 MediaSinksObserver::~MediaSinksObserver() {
 #if DCHECK_IS_ON()
   DCHECK(!in_on_sinks_updated_);
diff --git a/chrome/browser/media/router/media_sinks_observer.h b/chrome/browser/media/router/media_sinks_observer.h
index 8740520..0c95c035 100644
--- a/chrome/browser/media/router/media_sinks_observer.h
+++ b/chrome/browser/media/router/media_sinks_observer.h
@@ -29,6 +29,8 @@
   MediaSinksObserver(MediaRouter* router,
                      const MediaSource& source,
                      const url::Origin& origin);
+  // Constructs an observer for all sinks known to |router|.
+  explicit MediaSinksObserver(MediaRouter* router);
   virtual ~MediaSinksObserver();
 
   // Registers with MediaRouter to start observing. Must be called before the
@@ -37,7 +39,8 @@
   bool Init();
 
   // This function is invoked when the list of sinks compatible with |source_|
-  // has been updated. The result also contains the list of valid origins.
+  // has been updated, unless |source_| is nullopt, in which case it is invoked
+  // with all sinks. The result also contains the list of valid origins.
   // If |origins| is empty or contains |origin_|, then |OnSinksReceived(sinks)|
   // will be invoked with |sinks|. Otherwise, it will be invoked with an empty
   // list.
@@ -45,7 +48,7 @@
   virtual void OnSinksUpdated(const std::vector<MediaSink>& sinks,
                               const std::vector<url::Origin>& origins);
 
-  const MediaSource& source() const { return source_; }
+  const base::Optional<const MediaSource>& source() const { return source_; }
 
  protected:
   // This function is invoked from |OnSinksUpdated(sinks, origins)|.
@@ -55,7 +58,7 @@
   virtual void OnSinksReceived(const std::vector<MediaSink>& sinks) = 0;
 
  private:
-  const MediaSource source_;
+  const base::Optional<const MediaSource> source_;
   const url::Origin origin_;
   MediaRouter* const router_;
   bool initialized_;
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index ad85828..41e15ad8 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -666,7 +666,8 @@
 
   // Create an observer list for the media source and add |observer|
   // to it. Fail if |observer| is already registered.
-  const std::string& source_id = observer->source().id();
+  const std::string& source_id =
+      observer->source() ? observer->source()->id() : "";
   std::unique_ptr<MediaSinksQuery>& sinks_query = sinks_queries_[source_id];
   bool is_new_query = false;
   if (!sinks_query) {
@@ -693,7 +694,8 @@
     MediaSinksObserver* observer) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  const MediaSource::Id& source_id = observer->source().id();
+  const std::string& source_id =
+      observer->source() ? observer->source()->id() : "";
   auto it = sinks_queries_.find(source_id);
   if (it == sinks_queries_.end() || !it->second->HasObserver(observer))
     return;
diff --git a/chrome/browser/media/router/presentation/presentation_media_sinks_observer.cc b/chrome/browser/media/router/presentation/presentation_media_sinks_observer.cc
index 625e769..bc70c0b 100644
--- a/chrome/browser/media/router/presentation/presentation_media_sinks_observer.cc
+++ b/chrome/browser/media/router/presentation/presentation_media_sinks_observer.cc
@@ -30,8 +30,9 @@
       result.empty() ? blink::mojom::ScreenAvailability::UNAVAILABLE
                      : blink::mojom::ScreenAvailability::AVAILABLE;
 
-  DVLOG(1) << "PresentationMediaSinksObserver::OnSinksReceived: " << source()
-           << " " << (result.empty() ? "unavailable" : "available");
+  DVLOG(1) << "PresentationMediaSinksObserver::OnSinksReceived: "
+           << (source() ? source()->id() : "Any source") << " "
+           << (result.empty() ? "unavailable" : "available");
 
   // Don't send if new result is same as previous.
   if (previous_availability_ == current_availability)
diff --git a/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc b/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc
index d5f56216..2086ca9 100644
--- a/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc
+++ b/chrome/browser/media/router/providers/dial/dial_media_route_provider.cc
@@ -63,6 +63,7 @@
 
   binding_.Bind(std::move(request));
   media_router_.Bind(std::move(media_router));
+  media_sink_service_->AddObserver(this);
 
   // |activity_manager_| might have already been set in tests.
   if (!activity_manager_)
@@ -81,6 +82,7 @@
 DialMediaRouteProvider::~DialMediaRouteProvider() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(media_sink_queries_.empty());
+  media_sink_service_->RemoveObserver(this);
 }
 
 void DialMediaRouteProvider::CreateRoute(const std::string& media_source,
@@ -397,6 +399,13 @@
 void DialMediaRouteProvider::StartObservingMediaSinks(
     const std::string& media_source) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (media_source.empty()) {
+    std::vector<MediaSinkInternal> sinks;
+    for (const auto& sink_it : media_sink_service_->GetSinks())
+      sinks.push_back(sink_it.second);
+    OnSinksDiscovered(sinks);
+    return;
+  }
 
   MediaSource dial_source(media_source);
   if (!IsDialMediaSource(dial_source))
@@ -429,7 +438,7 @@
 
   MediaSource dial_source(media_source);
   std::string app_name = AppNameFromDialMediaSource(dial_source);
-  if (app_name.empty())
+  if (!dial_source.id().empty() && app_name.empty())
     return;
 
   const auto& sink_query_it = media_sink_queries_.find(app_name);
@@ -508,6 +517,13 @@
   activity_manager_ = std::move(activity_manager);
 }
 
+void DialMediaRouteProvider::OnSinksDiscovered(
+    const std::vector<MediaSinkInternal>& sinks) {
+  // Send a list of all available sinks to Media Router as sinks not associated
+  // with any particular source.
+  NotifyOnSinksReceived(MediaSource::Id(), sinks, {});
+}
+
 void DialMediaRouteProvider::OnAvailableSinksUpdated(
     const std::string& app_name) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chrome/browser/media/router/providers/dial/dial_media_route_provider.h b/chrome/browser/media/router/providers/dial/dial_media_route_provider.h
index d79afb9..9540136 100644
--- a/chrome/browser/media/router/providers/dial/dial_media_route_provider.h
+++ b/chrome/browser/media/router/providers/dial/dial_media_route_provider.h
@@ -45,7 +45,8 @@
 //    DialMediaRouteProvider will initiate the app launch on the device.
 // 4) Once the app is launched, the workflow is complete. The webpage will then
 //    communicate with the app on the device via its own mechanism.
-class DialMediaRouteProvider : public mojom::MediaRouteProvider {
+class DialMediaRouteProvider : public mojom::MediaRouteProvider,
+                               public MediaSinkServiceBase::Observer {
  public:
   // |request|: Request to bind to |this|.
   // |media_router|: Pointer to MediaRouter.
@@ -138,6 +139,9 @@
     DISALLOW_COPY_AND_ASSIGN(MediaSinkQuery);
   };
 
+  // MediaSinkServiceBase::Observer:
+  void OnSinksDiscovered(const std::vector<MediaSinkInternal>& sinks) override;
+
   // Binds the message pipes |request| and |media_router| to |this|.
   void Init(mojom::MediaRouteProviderRequest request,
             mojom::MediaRouterPtrInfo media_router);
diff --git a/chrome/browser/no_best_effort_tasks_browsertest.cc b/chrome/browser/no_best_effort_tasks_browsertest.cc
index 35696c8..882aa79 100644
--- a/chrome/browser/no_best_effort_tasks_browsertest.cc
+++ b/chrome/browser/no_best_effort_tasks_browsertest.cc
@@ -134,6 +134,9 @@
 
 // Verify that an extension can be loaded and perform basic messaging without
 // running BEST_EFFORT tasks. Regression test for http://crbug.com/177163#c112.
+//
+// NOTE: If this test times out, it might help to look at how
+// http://crbug.com/924416 was resolved.
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 IN_PROC_BROWSER_TEST_F(NoBestEffortTasksTest, LoadExtensionAndSendMessages) {
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc
index 92645b8..e8821960 100644
--- a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.cc
@@ -41,7 +41,7 @@
 ForegroundDurationUKMObserver::FlushMetricsOnAppEnterBackground(
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const page_load_metrics::PageLoadExtraInfo& info) {
-  RecordUkmIfInForeground();
+  RecordUkmIfInForeground(base::TimeTicks::Now());
   return CONTINUE_OBSERVING;
 }
 
@@ -49,7 +49,7 @@
 ForegroundDurationUKMObserver::OnHidden(
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const page_load_metrics::PageLoadExtraInfo& info) {
-  RecordUkmIfInForeground();
+  RecordUkmIfInForeground(base::TimeTicks::Now());
   return CONTINUE_OBSERVING;
 }
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
@@ -64,14 +64,22 @@
 void ForegroundDurationUKMObserver::OnComplete(
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const page_load_metrics::PageLoadExtraInfo& info) {
-  RecordUkmIfInForeground();
+  // If we have a page_end_time, use it as our end time, else fall back to the
+  // current time. Note that we expect page_end_time.has_value() to always be
+  // true in OnComplete (the PageLoadTracker destructor is supposed to guarantee
+  // it), but we use Now() as a graceful fallback just in case.
+  base::TimeTicks end_time =
+      info.page_end_time.has_value()
+          ? info.navigation_start + info.page_end_time.value()
+          : base::TimeTicks::Now();
+  RecordUkmIfInForeground(end_time);
 }
 
-void ForegroundDurationUKMObserver::RecordUkmIfInForeground() {
+void ForegroundDurationUKMObserver::RecordUkmIfInForeground(
+    base::TimeTicks end_time) {
   if (!currently_in_foreground_)
     return;
-  base::TimeDelta foreground_duration =
-      base::TimeTicks::Now() - last_time_shown_;
+  base::TimeDelta foreground_duration = end_time - last_time_shown_;
   ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
   ukm::builders::PageForegroundSession(source_id_)
       .SetForegroundDuration(foreground_duration.InMilliseconds())
diff --git a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h
index 63b9c85..2ab8c48 100644
--- a/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h
+++ b/chrome/browser/page_load_metrics/observers/foreground_duration_ukm_observer.h
@@ -37,7 +37,7 @@
   bool currently_in_foreground_ = false;
   base::TimeTicks last_time_shown_;
   ukm::SourceId source_id_ = ukm::kInvalidSourceId;
-  void RecordUkmIfInForeground();
+  void RecordUkmIfInForeground(base::TimeTicks end_time);
 
   DISALLOW_COPY_AND_ASSIGN(ForegroundDurationUKMObserver);
 };
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
index 730676e..a5c48e0 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
@@ -288,7 +288,11 @@
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
       test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
-  EXPECT_EQ(0ul, merged_entries.size());
+  for (const auto& kv : merged_entries) {
+    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+        kv.second.get(),
+        PageLoad::kExperimental_PaintTiming_NavigationToLargestImagePaintName));
+  }
 }
 
 TEST_F(UkmPageLoadMetricsObserverTest, LastImagePaint) {
@@ -342,7 +346,11 @@
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
       test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
-  EXPECT_EQ(0ul, merged_entries.size());
+  for (const auto& kv : merged_entries) {
+    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+        kv.second.get(),
+        PageLoad::kExperimental_PaintTiming_NavigationToLargestImagePaintName));
+  }
 }
 
 TEST_F(UkmPageLoadMetricsObserverTest, FCPPlusPlus_ReportLastCandidate) {
diff --git a/chrome/browser/payments/chrome_payment_request_delegate.cc b/chrome/browser/payments/chrome_payment_request_delegate.cc
index 6f90c7b..56e3421a 100644
--- a/chrome/browser/payments/chrome_payment_request_delegate.cc
+++ b/chrome/browser/payments/chrome_payment_request_delegate.cc
@@ -190,4 +190,8 @@
   }
 }
 
+bool ChromePaymentRequestDelegate::IsInteractive() const {
+  return shown_dialog_ && shown_dialog_->IsInteractive();
+}
+
 }  // namespace payments
diff --git a/chrome/browser/payments/chrome_payment_request_delegate.h b/chrome/browser/payments/chrome_payment_request_delegate.h
index 4940660..c76e7a1 100644
--- a/chrome/browser/payments/chrome_payment_request_delegate.h
+++ b/chrome/browser/payments/chrome_payment_request_delegate.h
@@ -45,12 +45,15 @@
   std::string GetAuthenticatedEmail() const override;
   PrefService* GetPrefService() override;
   bool IsBrowserWindowActive() const override;
+
+  // ContentPaymentRequestDelegate:
   scoped_refptr<PaymentManifestWebDataService>
   GetPaymentManifestWebDataService() const override;
   PaymentRequestDisplayManager* GetDisplayManager() override;
   void EmbedPaymentHandlerWindow(
       const GURL& url,
       PaymentHandlerOpenWindowCallback callback) override;
+  bool IsInteractive() const override;
 
  protected:
   // Reference to the dialog so that we can satisfy calls to CloseDialog(). This
diff --git a/chrome/browser/printing/cloud_print/privet_traffic_detector.cc b/chrome/browser/printing/cloud_print/privet_traffic_detector.cc
index ca2850c..481743a 100644
--- a/chrome/browser/printing/cloud_print/privet_traffic_detector.cc
+++ b/chrome/browser/printing/cloud_print/privet_traffic_detector.cc
@@ -29,18 +29,15 @@
 
 const int kMaxRestartAttempts = 10;
 
-void GetNetworkListInBackground(
-    base::OnceCallback<void(net::NetworkInterfaceList)> callback) {
-  net::NetworkInterfaceList networks;
-  {
-    base::ScopedBlockingCall scoped_blocking_call(
-        base::BlockingType::MAY_BLOCK);
-    if (!GetNetworkList(&networks, net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES))
-      return;
-  }
+void OnGetNetworkList(
+    base::OnceCallback<void(net::NetworkInterfaceList)> callback,
+    const base::Optional<net::NetworkInterfaceList>& networks) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!networks.has_value())
+    return;
 
   net::NetworkInterfaceList ip4_networks;
-  for (const auto& network : networks) {
+  for (const auto& network : networks.value()) {
     net::AddressFamily address_family = net::GetAddressFamily(network.address);
     if (address_family == net::ADDRESS_FAMILY_IPV4 &&
         network.prefix_length >= 24) {
@@ -49,19 +46,23 @@
   }
 
   net::IPAddress localhost_prefix(127, 0, 0, 0);
-  ip4_networks.push_back(
-      net::NetworkInterface("lo",
-                            "lo",
-                            0,
-                            net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
-                            localhost_prefix,
-                            8,
-                            net::IP_ADDRESS_ATTRIBUTE_NONE));
+  ip4_networks.push_back(net::NetworkInterface(
+      "lo", "lo", 0, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
+      localhost_prefix, 8, net::IP_ADDRESS_ATTRIBUTE_NONE));
+
   base::PostTaskWithTraits(
       FROM_HERE, {content::BrowserThread::IO},
       base::BindOnce(std::move(callback), std::move(ip4_networks)));
 }
 
+void GetNetworkListOnUIThread(
+    base::OnceCallback<void(net::NetworkInterfaceList)> callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  content::GetNetworkService()->GetNetworkList(
+      net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+      base::BindOnce(&OnGetNetworkList, std::move(callback)));
+}
+
 void CreateUDPSocketOnUIThread(
     content::BrowserContext* profile,
     network::mojom::UDPSocketRequest request,
@@ -134,9 +135,9 @@
   ResetConnection();
   weak_ptr_factory_.InvalidateWeakPtrs();
   base::PostDelayedTaskWithTraits(
-      FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+      FROM_HERE, {content::BrowserThread::UI},
       base::BindOnce(
-          &GetNetworkListInBackground,
+          &GetNetworkListOnUIThread,
           base::BindOnce(&Helper::Restart, weak_ptr_factory_.GetWeakPtr())),
       base::TimeDelta::FromSeconds(3));
 }
diff --git a/chrome/browser/profiling_host/memlog_browsertest.cc b/chrome/browser/profiling_host/memlog_browsertest.cc
index 09f81a1..a4a9bd9c 100644
--- a/chrome/browser/profiling_host/memlog_browsertest.cc
+++ b/chrome/browser/profiling_host/memlog_browsertest.cc
@@ -34,6 +34,7 @@
 struct TestParam {
   Mode mode;
   mojom::StackMode stack_mode;
+  bool stream_samples;
   bool start_profiling_with_command_line_flag;
   bool should_sample;
   bool sample_everything;
@@ -83,15 +84,10 @@
 
 // Ensure invocations via TracingController can generate a valid JSON file with
 // expected data.
-// TODO(crbug.com/921036): Test is flaky.
-#if defined(OS_LINUX)
-#define MAYBE_EndToEnd DISABLED_EndToEnd
-#else
-#define MAYBE_EndToEnd EndToEnd
-#endif
-IN_PROC_BROWSER_TEST_P(MemlogBrowserTest, MAYBE_EndToEnd) {
+IN_PROC_BROWSER_TEST_P(MemlogBrowserTest, EndToEnd) {
   LOG(INFO) << "Memlog mode: " << static_cast<int>(GetParam().mode);
   LOG(INFO) << "Memlog stack mode: " << static_cast<int>(GetParam().stack_mode);
+  LOG(INFO) << "Stream samples: " << GetParam().stream_samples;
   LOG(INFO) << "Started via command line flag: "
             << GetParam().start_profiling_with_command_line_flag;
   LOG(INFO) << "Should sample: " << GetParam().should_sample;
@@ -100,6 +96,7 @@
   TestDriver::Options options;
   options.mode = GetParam().mode;
   options.stack_mode = GetParam().stack_mode;
+  options.stream_samples = GetParam().stream_samples;
   options.profiling_already_started =
       GetParam().start_profiling_with_command_line_flag;
   options.should_sample = GetParam().should_sample;
@@ -123,54 +120,38 @@
   stack_modes.push_back(mojom::StackMode::NATIVE_WITHOUT_THREAD_NAMES);
   stack_modes.push_back(mojom::StackMode::PSEUDO);
 
-  for (const auto& mode : dynamic_start_modes) {
-    for (const auto& stack_mode : stack_modes) {
-      params.push_back(
-          {mode, stack_mode, false /* start_profiling_with_command_line_flag */,
-           false /* should_sample */, false /* sample_everything*/});
+  for (bool stream_samples : (bool[]){true, false}) {
+    for (const auto& mode : dynamic_start_modes) {
+      for (const auto& stack_mode : stack_modes) {
+        params.push_back({mode, stack_mode, stream_samples,
+                          false /* start_profiling_with_command_line_flag */,
+                          true /* should_sample */,
+                          false /* sample_everything*/});
+      }
     }
-  }
 
 // For unknown reasons, renderer profiling has become flaky on ChromeOS. This is
 // likely happening because the renderers are never being given the signal to
 // start profiling. It's unclear why this happens. https://crbug.com/843843.
 // https://crbug.com/843467.
 #if !defined(OS_CHROMEOS)
-  // Non-browser processes must be profiled with a command line flag, since
-  // otherwise, profiling will start after the relevant processes have been
-  // created, thus that process will be not be profiled.
-  std::vector<Mode> command_line_start_modes;
-  command_line_start_modes.push_back(Mode::kAll);
-  command_line_start_modes.push_back(Mode::kAllRenderers);
-  for (const auto& mode : command_line_start_modes) {
-    for (const auto& stack_mode : stack_modes) {
-      params.push_back(
-          {mode, stack_mode, true /* start_profiling_with_command_line_flag */,
-           false /* should_sample */, false /* sample_everything*/});
+    // Non-browser processes must be profiled with a command line flag, since
+    // otherwise, profiling will start after the relevant processes have been
+    // created, thus that process will be not be profiled.
+    std::vector<Mode> command_line_start_modes;
+    command_line_start_modes.push_back(Mode::kAll);
+    command_line_start_modes.push_back(Mode::kAllRenderers);
+    for (const auto& mode : command_line_start_modes) {
+      for (const auto& stack_mode : stack_modes) {
+        params.push_back({mode, stack_mode, stream_samples,
+                          true /* start_profiling_with_command_line_flag */,
+                          true /* should_sample */,
+                          false /* sample_everything*/});
+      }
     }
-  }
 #endif  // defined(OS_CHROMEOS)
+  }
 
-  // Test sampling all allocations.
-  params.push_back({Mode::kBrowser, mojom::StackMode::NATIVE_WITH_THREAD_NAMES,
-                    false /* start_profiling_with_command_line_flag */,
-                    true /* should_sample */, true /* sample_everything*/});
-
-  // Test sampling some allocations.
-  params.push_back({Mode::kBrowser, mojom::StackMode::PSEUDO,
-                    false /* start_profiling_with_command_line_flag */,
-                    true /* should_sample */, false /* sample_everything*/});
-
-  // Test thread names for native profiling.
-  params.push_back(
-      {Mode::kBrowser, mojom::StackMode::NATIVE_WITH_THREAD_NAMES, false});
-
-  // Profile all utility processes and the browser process. The main goal is to
-  // check that there is no deadlock in the profiling process.
-  params.push_back({Mode::kUtilityAndBrowser,
-                    mojom::StackMode::NATIVE_WITH_THREAD_NAMES,
-                    false /* start_profiling_with_command_line_flag */,
-                    true /* should_sample */, false /* sample_everything*/});
   return params;
 }
 
diff --git a/chrome/browser/push_messaging/push_messaging_notification_manager.cc b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
index fd0a4890..f505b2a 100644
--- a/chrome/browser/push_messaging/push_messaging_notification_manager.cc
+++ b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
@@ -334,7 +334,7 @@
   }
 
   // Check if origin matches current messages url
-  base::Optional<GURL> app_url = android_sms_app_manager->GetInstalledAppUrl();
+  base::Optional<GURL> app_url = android_sms_app_manager->GetCurrentAppUrl();
   if (!app_url)
     app_url = chromeos::android_sms::GetAndroidMessagesURL();
 
diff --git a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h
index 0741de0..f925c61 100644
--- a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h
+++ b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.h
@@ -166,9 +166,9 @@
   // This variables defaults to NO for new gestures.
   BOOL firstScrollUnconsumed_;
 
-  // Whether the renderer disables the overscroll effect, e.g. by
-  // CSSOverscrollBehavior.
-  BOOL rendererDisabledOverscroll_;
+  // Whether the overscroll has been triggered by renderer and is not disabled
+  // by CSSOverscrollBehavior.
+  BOOL overscrollTriggeredByRenderer_;
 
   // Whether we have received a gesture scroll begin and are awiting on the
   // first gesture scroll update to deteremine of the event was consumed by
diff --git a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm
index d1f8812..d31c14f0 100644
--- a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm
+++ b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper.mm
@@ -153,9 +153,10 @@
 }
 
 - (void)onOverscrolled:(const ui::DidOverscrollParams&)params {
-  rendererDisabledOverscroll_ = params.overscroll_behavior.x !=
-                                cc::OverscrollBehavior::OverscrollBehaviorType::
-                                    kOverscrollBehaviorTypeAuto;
+  overscrollTriggeredByRenderer_ =
+      params.overscroll_behavior.x ==
+      cc::OverscrollBehavior::OverscrollBehaviorType::
+          kOverscrollBehaviorTypeAuto;
 }
 
 - (void)beginGestureWithEvent:(NSEvent*)event {
@@ -217,7 +218,7 @@
   gestureStartPointValid_ = NO;
   gestureTotalY_ = 0;
   firstScrollUnconsumed_ = NO;
-  rendererDisabledOverscroll_ = NO;
+  overscrollTriggeredByRenderer_ = NO;
   waitingForFirstGestureScroll_ = NO;
   recognitionState_ = history_swiper::kPending;
 }
@@ -545,8 +546,8 @@
   if (!firstScrollUnconsumed_)
     return NO;
 
-  // History swiping should be prevented if the renderer disables it.
-  if (rendererDisabledOverscroll_)
+  // History swiping should be prevented if the renderer hasn't triggered it.
+  if (!overscrollTriggeredByRenderer_)
     return NO;
 
   // Magic mouse and touchpad swipe events are identical except magic mouse
diff --git a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm
index b27004c..2ad19db0 100644
--- a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm
+++ b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm
@@ -172,6 +172,12 @@
         andReturnValue:OCMOCK_VALUE(timestamp)] timestamp];
     [(NSEvent*)[[mock_event stub] andReturnValue:OCMOCK_VALUE(type)] type];
 
+    // We need to assign a locationInWindow for the event so that the wheel
+    // event happens inside the page.
+    NSPoint locationInWindow = NSMakePoint(400, 400);
+    [(NSEvent*)[[mock_event stub] andReturnValue:OCMOCK_VALUE(locationInWindow)]
+        locationInWindow];
+
     return mock_event;
   }
 
@@ -788,3 +794,25 @@
   RunQueuedEvents();
   ExpectUrlAndOffset(url1_, 0);
 }
+
+IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest,
+                       InnerScrollersOverscrollBehaviorPreventsNavigation) {
+  if (!IsHistorySwipingSupported())
+    return;
+
+  const base::FilePath base_path(FILE_PATH_LITERAL("scroll"));
+  GURL url_overscroll_behavior = ui_test_utils::GetTestUrl(
+      base_path, base::FilePath(FILE_PATH_LITERAL("overscroll_behavior.html")));
+  ui_test_utils::NavigateToURL(browser(), url_overscroll_behavior);
+  ASSERT_EQ(url_overscroll_behavior, GetWebContents()->GetURL());
+
+  QueueBeginningEvents(1, 0);
+  for (int i = 0; i < 10; ++i) {
+    QueueScrollAndTouchMoved(10, 0);
+  }
+
+  QueueEndEvents();
+  RunQueuedEvents();
+  // If navigation was to occur, the URL would be url2_.
+  ExpectUrlAndOffset(url_overscroll_behavior, 0);
+}
diff --git a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm
index 8147a37..1af449d 100644
--- a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm
+++ b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_unit_test.mm
@@ -233,6 +233,8 @@
 
   startGestureInMiddle();
   moveGestureInMiddle();
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
 
   EXPECT_EQ(begin_count_, 0);
   EXPECT_EQ(end_count_, 0);
@@ -259,6 +261,8 @@
 
   startGestureInMiddle();
   moveGestureInMiddle();
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
 
   EXPECT_EQ(begin_count_, 0);
   EXPECT_EQ(end_count_, 0);
@@ -286,6 +290,8 @@
 
   startGestureInMiddle();
   moveGestureInMiddle();
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
   moveGestureAtPoint(makePoint(0.45, 0.5));
   endGestureAtPoint(makePoint(0.45, 0.5));
   EXPECT_EQ(begin_count_, 1);
@@ -304,6 +310,8 @@
 
   startGestureInMiddle();
   moveGestureInMiddle();
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
   moveGestureInMiddle();
   moveGestureAtPoint(makePoint(0.6, 0.59));
   endGestureAtPoint(makePoint(0.6, 0.59));
@@ -324,6 +332,8 @@
 
   startGestureInMiddle();
   moveGestureInMiddle();
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
   moveGestureAtPoint(makePoint(0.4, 0.5));
   moveGestureAtPoint(makePoint(0.4, 0.3));
   endGestureAtPoint(makePoint(0.2, 0.2));
@@ -345,6 +355,8 @@
 
   // Send a momentum move gesture.
   momentumMoveGestureAtPoint(makePoint(0.5, 0.5));
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
   EXPECT_EQ(begin_count_, 0);
   EXPECT_EQ(end_count_, 0);
 
@@ -371,6 +383,8 @@
   // Magic mouse events don't generate 'touches*' callbacks.
   NSEvent* event = mockEventWithPoint(makePoint(0.5, 0.5), NSEventTypeGesture);
   [historySwiper_ beginGestureWithEvent:event];
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
   NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseBegan);
   [historySwiper_ handleEvent:scrollEvent];
 
@@ -394,6 +408,8 @@
 
   startGestureInMiddle();
   moveGestureInMiddle();
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
 
   // Starts the gesture.
   moveGestureAtPoint(makePoint(0.44, 0.44));
@@ -418,6 +434,8 @@
   // Successfully pass through a gesture.
   startGestureInMiddle();
   moveGestureInMiddle();
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
   moveGestureAtPoint(makePoint(0.8, 0.5));
   endGestureAtPoint(makePoint(0.8, 0.5));
   EXPECT_TRUE(navigated_right_);
@@ -444,6 +462,8 @@
   NSEvent* scrollEvent = scrollWheelEventWithPhase(NSEventPhaseBegan);
   NSEvent* event = mockEventWithPoint(makePoint(0.5, 0.5), NSEventTypeGesture);
   [historySwiper_ touchesBeganWithEvent:event];
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
   [historySwiper_ handleEvent:scrollEvent];
   rendererACKForBeganEvent();
 
@@ -483,6 +503,8 @@
 
   startGestureInMiddle();
   moveGestureInMiddle();
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
 
   // Move up, then move down.
   for (CGFloat y = 0.51; y < 0.6; y += 0.01)
@@ -518,6 +540,8 @@
   //  - endGesture
   sendBeginGestureEventInMiddle();
   [historySwiper_ handleEvent:scrollWheelEventWithPhase(NSEventPhaseBegan)];
+  onOverscrolled(cc::OverscrollBehavior::OverscrollBehaviorType::
+                     kOverscrollBehaviorTypeAuto);
 
   // Callback from Blink to set the relevant state for history swiping.
   rendererACKForBeganEvent();
@@ -556,7 +580,7 @@
   EXPECT_FALSE(magic_mouse_history_swipe_);
 }
 
-// With scroll-boundary-behavior value as contain, the page should not navigate,
+// With overscroll-behavior value as contain, the page should not navigate,
 // nor should the history overlay appear.
 TEST_F(MacHistorySwiperTest, OverscrollBehaviorContainPreventsNavigation) {
   // These tests require 10.7+ APIs.
@@ -585,7 +609,7 @@
   EXPECT_FALSE(navigated_left_);
 }
 
-// With scroll-boundary-behavior value as none, the page should not navigate,
+// With overscroll-behavior value as none, the page should not navigate,
 // nor should the history overlay appear.
 TEST_F(MacHistorySwiperTest, OverscrollBehaviorNonePreventsNavigation) {
   startGestureInMiddle();
@@ -608,3 +632,25 @@
   EXPECT_FALSE(navigated_right_);
   EXPECT_FALSE(navigated_left_);
 }
+
+// Without overscroll msg from renderer, the page should not navigate, nor
+// should the history overlay appear.
+TEST_F(MacHistorySwiperTest, NoNavigationWithoutOverscrollMsg) {
+  startGestureInMiddle();
+  moveGestureInMiddle();
+
+  EXPECT_EQ(begin_count_, 0);
+  EXPECT_EQ(end_count_, 0);
+
+  moveGestureAtPoint(makePoint(0.2, 0.5));
+  EXPECT_EQ(begin_count_, 0);
+  EXPECT_EQ(end_count_, 0);
+  EXPECT_FALSE(navigated_right_);
+  EXPECT_FALSE(navigated_left_);
+
+  endGestureAtPoint(makePoint(0.2, 0.5));
+  EXPECT_EQ(begin_count_, 0);
+  EXPECT_EQ(end_count_, 0);
+  EXPECT_FALSE(navigated_right_);
+  EXPECT_FALSE(navigated_left_);
+}
diff --git a/chrome/browser/resources/bookmarks/bookmarks.html b/chrome/browser/resources/bookmarks/bookmarks.html
index 80747bd..6b7581da 100644
--- a/chrome/browser/resources/bookmarks/bookmarks.html
+++ b/chrome/browser/resources/bookmarks/bookmarks.html
@@ -7,8 +7,6 @@
   <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
   <style>
     html {
-      /* Reconcile with cr_toolbar.html and shared_vars_css.html */
-      --toolbar-height: 56px;
       /* Remove 300ms delay for 'click' event, when using touch interface. */
       touch-action: manipulation;
     }
@@ -21,13 +19,8 @@
       overflow: hidden;
     }
 
-    html:not([dark]).loading {
-      border-top: var(--toolbar-height) solid var(--md-toolbar-color);
-    }
-
-    html[dark].loading {
-      border-top: 1px solid var(--md-toolbar-border-color);
-      margin-top: var(--toolbar-height);
+    html.loading {
+      border-top: 56px solid var(--md-toolbar-color);
     }
   </style>
 </head>
diff --git a/chrome/browser/resources/bookmarks/toolbar.html b/chrome/browser/resources/bookmarks/toolbar.html
index df03299..846d53c 100644
--- a/chrome/browser/resources/bookmarks/toolbar.html
+++ b/chrome/browser/resources/bookmarks/toolbar.html
@@ -18,12 +18,7 @@
         margin: 4px;
       }
 
-      paper-icon-button-light.more-vert-button div {
-        border-color: white;
-      }
-
       cr-toolbar {
-        color: #fff;
         flex: 1;
         --cr-toolbar-field-margin:
             calc(var(--sidebar-width) + var(--splitter-width));
diff --git a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css
index 222ca75..59a7eeac 100644
--- a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css
+++ b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing.css
@@ -34,19 +34,32 @@
 }
 
 .arc-events-band-title {
+  align-items: center;
   border: none;
-  display: block;
+  display: flex;
+  flex-direction: row;
   left: 0;
   outline: none;
   position: sticky;
   text-align: start;
 }
 
+.arc-events-band-title img {
+  height: 24px;
+  width: 24px;
+}
+
+.arc-events-band-title span {
+  flex: 1;
+  padding-top: 4px;
+}
+
 .arc-events-band-title::before {
   content: '\2212';
   float: left;
   font-weight: bold;
   margin-inline-end: 4px;
+  padding-top: 4px;
 }
 
 .hidden.arc-events-band-title::before {
diff --git a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js
index 691d955..17c80a4 100644
--- a/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js
+++ b/chrome/browser/resources/chromeos/arc_graphics_tracing/arc_graphics_tracing_ui.js
@@ -144,10 +144,17 @@
  * content.
  */
 class EventBandTitle {
-  constructor(title) {
+  constructor(title, opt_iconContent) {
     this.div = document.createElement('div');
     this.div.classList.add('arc-events-band-title');
-    this.div.appendChild(document.createTextNode(title));
+    if (opt_iconContent) {
+      var icon = document.createElement('img');
+      icon.src = 'data:image/png;base64,' + opt_iconContent;
+      this.div.appendChild(icon);
+    }
+    var span = document.createElement('span');
+    span.appendChild(document.createTextNode(title));
+    this.div.appendChild(span);
     this.controlledItems = [];
     this.div.onclick = this.onClick_.bind(this);
     var parent = $('arc-event-bands');
@@ -416,8 +423,16 @@
 
   for (i = 0; i < model.views.length; i++) {
     var view = model.views[i];
-    var activityTitleText = 'Task #' + view.task_id + ' - ' + view.activity;
-    var activityTitle = new EventBandTitle(activityTitleText);
+    var activityTitleText;
+    var icon;
+    if (view.task_id in model.tasks) {
+      activityTitleText =
+          model.tasks[view.task_id].title + ' - ' + view.activity;
+      icon = model.tasks[view.task_id].icon;
+    } else {
+      activityTitleText = 'Task #' + view.task_id + ' - ' + view.activity;
+    }
+    var activityTitle = new EventBandTitle(activityTitleText, icon);
     for (j = 0; j < view.buffers.length; j++) {
       var androidBand = new EventBand(
           activityTitle, 'arc-events-inner-band', model.duration, 14);
diff --git a/chrome/browser/resources/downloads/downloads.html b/chrome/browser/resources/downloads/downloads.html
index 19caed5f..2397587 100644
--- a/chrome/browser/resources/downloads/downloads.html
+++ b/chrome/browser/resources/downloads/downloads.html
@@ -4,26 +4,16 @@
 <head>
   <meta charset="utf-8">
   <title>$i18n{title}</title>
+  <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
   <style>
     html {
-      --toolbar-height: 56px;
-      background: rgb(248, 249, 250);
+      background: var(--md-background-color);
       /* Remove 300ms delay for 'click' event, when using touch interface. */
       touch-action: manipulation;
     }
 
-    html[dark] {
-      background: rgb(32, 33, 36);  /* --google-grey-900 */
-    }
-
-    html[dark].loading {
-      border-top: 1px solid rgb(95, 99, 104);  /* --google-grey-refresh-700 */
-      margin-top: var(--toolbar-height);
-    }
-
-    html:not([dark]).loading {
-      /* --google-blue-700 disguised. Replaced when downloads-toolbar loads. */
-      border-top: var(--toolbar-height) solid rgb(51, 103, 214);
+    html.loading {
+      border-top: 56px solid var(--md-toolbar-color);
     }
 
     html:not(.loading),
@@ -47,7 +37,6 @@
   <command id="clear-all-command" shortcut="Alt|c">
   <command id="undo-command" shortcut="Ctrl|z">
 </if>
-  <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <link rel="import" href="chrome://resources/html/cr.html">
   <link rel="import" href="chrome://resources/html/polymer.html">
diff --git a/chrome/browser/resources/downloads/toolbar.html b/chrome/browser/resources/downloads/toolbar.html
index 9f25666..f9b804f 100644
--- a/chrome/browser/resources/downloads/toolbar.html
+++ b/chrome/browser/resources/downloads/toolbar.html
@@ -20,7 +20,6 @@
       :host {
         align-items: center;
         background: var(--md-toolbar-color);
-        color: white;
         display: flex;
         min-height: 56px;
       }
@@ -29,10 +28,6 @@
         flex: 1;
       }
 
-      :host-context([dark]) #toolbar {
-        color: var(--cr-primary-text-color);
-      }
-
       #moreActionsContainer {
         --iron-icon-height: 20px;
         --iron-icon-width: 20px;
diff --git a/chrome/browser/resources/history/history.html b/chrome/browser/resources/history/history.html
index 748d431..ae99c88 100644
--- a/chrome/browser/resources/history/history.html
+++ b/chrome/browser/resources/history/history.html
@@ -62,7 +62,6 @@
     }
 
     html[dark] #loading-toolbar {
-      border-bottom: 1px solid var(--md-toolbar-border-color);
       color: rgb(232, 234, 237);  /* --google-grey-200 */
     }
 
diff --git a/chrome/browser/resources/history/history_toolbar.html b/chrome/browser/resources/history/history_toolbar.html
index 2b7df411..c28d0ce 100644
--- a/chrome/browser/resources/history/history_toolbar.html
+++ b/chrome/browser/resources/history/history_toolbar.html
@@ -13,7 +13,6 @@
   <template>
     <style include="shared-style">
       :host {
-        color: #fff;
         display: flex;
         position: relative;
       }
diff --git a/chrome/browser/resources/md_extensions/extensions.html b/chrome/browser/resources/md_extensions/extensions.html
index 7b1d039..e02a07a 100644
--- a/chrome/browser/resources/md_extensions/extensions.html
+++ b/chrome/browser/resources/md_extensions/extensions.html
@@ -7,51 +7,41 @@
 <if expr="not optimize_webui">
   <base href="chrome://extensions">
 </if>
+  <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
   <style>
     html {
-      /* All colors are inlined as the equivalent variables live in stylesheets,
-       * which can slow down the shell by being pasted or loaded externally. */
-      --dev-mode-toolbar-height: 52px;
-      --toolbar-light-color: rgb(51, 103, 214); /* --google-blue-700 */
-      --toolbar-height: 56px;
-
-      /* --md-background-color in disguise. */
-      background-color: rgb(248, 249, 250);
-
+      background: var(--md-background-color);
       /* Remove 300ms delay for 'click' event, when using touch interface. */
       touch-action: manipulation;
     }
 
-    html[dark] {
-      background-color: rgb(32, 33, 36);  /* --google-grey-900 */
-    }
-
-    html:not([dark]).loading {
-      /* Replaced when manager.html loads. */
-      border-top: var(--toolbar-height) solid var(--toolbar-light-color);
-    }
-
-    html[dark].loading {
-      border-top: 1px solid rgb(95, 99, 104);  /* --google-grey-refresh-700 */
-      margin-top: var(--toolbar-height);
-    }
-
-    /* Mimics the developer mode toolbar until the real one loads. */
-    html[dark].loading.in-dev-mode::before {
-      background-color: rgb(60, 64, 67);  /* --google-grey-800 */
+    html.loading::before,
+    html.loading body::before {
+      box-sizing: border-box;
       content: '';
       display: block;
-      height: var(--dev-mode-toolbar-height);
     }
 
-    /* Note: .in-dev-mode is applied by i18n{loadTimeClasses}. */
-    html:not([dark]).loading.in-dev-mode {
-      border-image: linear-gradient(to bottom,
-          var(--toolbar-light-color) var(--toolbar-height),
-          #fff var(--toolbar-height),
-          #fff 107px, #e0e0e0 /* --google-grey-300 */ 107px) 108;
-      border-top: calc(var(--toolbar-height) + var(--dev-mode-toolbar-height))
-          solid;
+    html.loading::before {
+      background: var(--md-toolbar-color);
+      height: 56px;
+    }
+
+    /* Mimics the developer mode toolbar until the real one loads. Note:
+     * .in-dev-mode is applied by i18n{loadTimeClasses}. */
+    html.loading.in-dev-mode body::before {
+      background-color: #fff;
+      border-bottom: 1px solid #e0e0e0;  /* --google-grey-300 */
+      height: 52px;
+    }
+
+    html[dark].loading.in-dev-mode body::before {
+      background: none;
+    }
+
+    html[dark].loading.in-dev-mode::before,
+    html[dark].loading.in-dev-mode body::before {
+      border-bottom: 1px solid rgba(255, 255, 255, .1);
     }
 
     html,
@@ -66,7 +56,6 @@
 </head>
 <body>
   <extensions-manager></extensions-manager>
-  <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <link rel="import" href="chrome://extensions/manager.html">
   <link rel="import" href="chrome://resources/html/dark_mode.html">
diff --git a/chrome/browser/resources/md_extensions/manager.html b/chrome/browser/resources/md_extensions/manager.html
index 797b13e..30bfe07 100644
--- a/chrome/browser/resources/md_extensions/manager.html
+++ b/chrome/browser/resources/md_extensions/manager.html
@@ -51,10 +51,6 @@
       extensions-item {
         display: inline-block;
       }
-
-      extensions-toolbar {
-        background-color: var(--md-toolbar-color);
-      }
     </style>
     <extensions-drop-overlay drag-enabled="[[inDevMode]]">
     </extensions-drop-overlay>
diff --git a/chrome/browser/resources/md_extensions/toolbar.html b/chrome/browser/resources/md_extensions/toolbar.html
index 9d44487..8458178c 100644
--- a/chrome/browser/resources/md_extensions/toolbar.html
+++ b/chrome/browser/resources/md_extensions/toolbar.html
@@ -20,20 +20,22 @@
   <template>
     <style include="cr-hidden-style paper-button-style">
       :host {
-        /* The constant is the height of the tallest control. */
-        --button-row-height: calc(2 * var(--padding-top-bottom) + 36px);
-        --dev-drawer-background-color: #fff;
+        --border-bottom-height: 1px;
+        --button-row-height: calc(2 * var(--padding-top-bottom) +
+            var(--cr-button-height));
         --drawer-transition: 0.3s cubic-bezier(.25, .1, .25, 1);
-        --padding-top-bottom: 8px;
-        --toolbar-color: var(--md-toolbar-color);
-      }
-
-      :host-context([dark]) {
-        --dev-drawer-background-color: var(--google-grey-800);
+        --padding-top-bottom: 10px;
       }
 
       cr-toolbar {
-        background: var(--toolbar-color);
+        background: var(--md-toolbar-color);
+        border-bottom: var(--border-bottom-height) solid transparent;
+        box-sizing: border-box;
+        transition: border-bottom-color var(--drawer-transition);
+      }
+
+      :host-context([dark]):host([in-dev-mode]) cr-toolbar {
+        border-bottom-color: var(--cr-separator-color);
       }
 
       /* This toggle needs special styling because it's on blue background. */
@@ -54,7 +56,8 @@
       }
 
       #devDrawer {
-        background: var(--dev-drawer-background-color);
+        background: #fff;
+        border-bottom: 1px solid var(--google-grey-300);
         box-sizing: border-box;
         height: 0;
         overflow-x: hidden;
@@ -63,12 +66,13 @@
         transition: height var(--drawer-transition);
       }
 
-      :host-context(html:not([dark])) #devDrawer {
-        border-bottom: 1px solid var(--google-grey-300);
+      :host-context([dark]) #devDrawer {
+        background: none;
+        border-bottom-color: var(--cr-separator-color);
       }
 
       #devDrawer[expanded] {
-        height: var(--button-row-height);
+        height: calc(var(--button-row-height) + var(--border-bottom-height));
       }
 
       #buttonStrip {
diff --git a/chrome/browser/resources/md_extensions/toolbar.js b/chrome/browser/resources/md_extensions/toolbar.js
index c9ee828..c231c5a 100644
--- a/chrome/browser/resources/md_extensions/toolbar.js
+++ b/chrome/browser/resources/md_extensions/toolbar.js
@@ -37,6 +37,7 @@
         type: Boolean,
         value: false,
         observer: 'onInDevModeChanged_',
+        reflectToAttribute: true,
       },
 
       devModeControlledByPolicy: Boolean,
diff --git a/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js b/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js
index e01a388..68735f1 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js
+++ b/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.js
@@ -94,18 +94,33 @@
     this.selectedColor = e.target.value;
   },
 
+  /** @private */
   toggleExpanded_: function() {
     this.expanded_ = !this.expanded_;
     this.updateExpandedState_();
   },
 
-  attached() {
+  /** @private */
+  updateExpandedStateAndFinishAnimations_: function() {
     this.updateExpandedState_();
     for (const animation of this.expandAnimations_) {
       animation.finish();
     }
   },
 
+  /** @override */
+  attached: function() {
+    // TODO (rbpotter): Remove this conditional when the migration to Polymer 2
+    // is completed.
+    if (Polymer.DomIf) {
+      Polymer.RenderStatus.beforeNextRender(this, () => {
+        this.updateExpandedStateAndFinishAnimations_();
+      });
+    } else {
+      this.updateExpandedStateAndFinishAnimations_();
+    }
+  },
+
   /**
    * Updates the state of the UI to reflect the current value of `expanded`.
    * Starts or reverses animations and enables/disable controls.
@@ -169,6 +184,6 @@
    * @return {string}
    */
   lookup_: function(strings, name) {
-    return strings[name];
+    return strings ? strings[name] : '';
   }
 });
\ No newline at end of file
diff --git a/chrome/browser/resources/reset_password/reset_password.html b/chrome/browser/resources/reset_password/reset_password.html
index ebc277e..b68848f 100644
--- a/chrome/browser/resources/reset_password/reset_password.html
+++ b/chrome/browser/resources/reset_password/reset_password.html
@@ -4,7 +4,6 @@
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width">
   <title>$i18n{title}</title>
-  <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <link rel="import" href="chrome://resources/html/polymer.html">
   <link rel="import" href="chrome://resources/cr_elements/icons.html">
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
index 0a19b70a..89be0f3 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
@@ -29,16 +29,21 @@
       }
 
       #clearBrowsingDataDialog {
+        --border-top-color: var(--paper-grey-300);
         --cr-dialog-top-container-min-height: 42px;
         --cr-dialog-title: {
           padding-bottom: 8px;
         };
         --cr-dialog-body-container: {
-          border-top: 1px solid var(--paper-grey-300);
+          border-top: 1px solid var(--border-top-color);
           height: var(--body-container-height);
         };
       }
 
+      :host-context([dark]) #clearBrowsingDataDialog {
+        --border-top-color: var(--cr-separator-color);
+      }
+
       #clearBrowsingDataDialog:not(.fully-rendered) {
         visibility: hidden;
       }
@@ -53,6 +58,10 @@
         padding: 0;
       }
 
+      :host-context([dark]) #clearBrowsingDataDialog [slot=footer] {
+        background: rgb(50, 54, 57);  /* Custom color from Namrata. */
+      }
+
       .row {
         align-items: center;
         display: flex;
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.html b/chrome/browser/resources/settings/people_page/sync_account_control.html
index 51754fd..cd22d868 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.html
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.html
@@ -2,6 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
@@ -17,9 +18,9 @@
   <template>
     <style include="settings-shared">
       :host {
-        --sync-icon-size: 16px;
-        --sync-icon-border-size: 2px;
         --shown-avatar-size: 40px;
+        --sync-icon-border-size: 2px;
+        --sync-icon-size: 16px;
       }
 
       setting-box.middle {
@@ -77,7 +78,13 @@
         right: initial;
       }
 
-      #sync-icon-container.sync-problem {
+      :host-context([dark]) #sync-icon-container {
+        background: var(--google-green-refresh-300);
+        border-color: var(--google-grey-900);
+      }
+
+      #sync-icon-container.sync-problem,
+      :host-context([dark]) #sync-icon-container.sync-problem {
         background: var(--settings-error-color);
       }
 
@@ -85,12 +92,20 @@
         background: var(--google-blue-500);
       }
 
+      :host-context([dark]) #sync-icon-container.sync-paused {
+        background: var(--google-blue-refresh-300);
+      }
+
       #sync-icon-container.sync-disabled {
         background: var(--google-grey-400);
       }
 
+      :host-context([dark]) #sync-icon-container.sync-disabled {
+        background: var(--google-grey-refresh-500);
+      }
+
       #sync-icon-container iron-icon {
-        fill: white;
+        fill: white;  /* Same in light and dark modes. */
         height: 12px;
         margin: auto;
         width: 12px;
diff --git a/chrome/browser/resources/settings/settings.html b/chrome/browser/resources/settings/settings.html
index 75e1fc2..60fc7482 100644
--- a/chrome/browser/resources/settings/settings.html
+++ b/chrome/browser/resources/settings/settings.html
@@ -7,33 +7,22 @@
 <if expr="not optimize_webui">
   <base href="chrome://settings">
 </if>
+  <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
   <style>
     html {
-      --toolbar-height: 56px;
-      background-color: rgb(248, 249, 250);
+      background: var(--md-background-color);
       overflow: hidden;
       /* Remove 300ms delay for 'click' event, when using touch interface. */
       touch-action: manipulation;
     }
 
-    html[dark] {
-      background: rgb(32, 33, 36);  /* --google-grey-900 */
-    }
-
-    html[dark].loading {
-      border-top: 1px solid rgb(95, 99, 104);  /* --google-grey-refresh-700 */
-      margin-top: var(--toolbar-height);
-    }
-
-    html:not([dark]).loading {
-      /* --google-blue-700 in disguise. Replaced when settings-ui loads. */
-      border-top: var(--toolbar-height) solid rgb(51, 103, 214);
+    html.loading {
+      border-top: 56px solid var(--md-toolbar-color);
     }
   </style>
 </head>
 <body>
   <settings-ui></settings-ui>
-  <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <link rel="import" href="chrome://resources/html/polymer.html">
   <link rel="import" href="settings_ui/settings_ui.html">
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html
index cf4382d..dad18a07 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -38,13 +38,15 @@
 
       cr-toolbar {
         @apply --layout-center;
-        --iron-icon-fill-color: white;
         background-color: var(--md-toolbar-color);
-        color: white;
         min-height: 56px;
         z-index: 2;
       }
 
+      :host-context(html:not([dark])) cr-toolbar {
+        --iron-icon-fill-color: white;
+      }
+
       #container {
         flex: 1;
         overflow: overlay;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 21ea97e..e2d9585 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2807,6 +2807,8 @@
       "views/tabs/tab_controller.h",
       "views/tabs/tab_drag_controller.cc",
       "views/tabs/tab_drag_controller.h",
+      "views/tabs/tab_group_header.cc",
+      "views/tabs/tab_group_header.h",
       "views/tabs/tab_hover_card_bubble_view.cc",
       "views/tabs/tab_hover_card_bubble_view.h",
       "views/tabs/tab_icon.cc",
@@ -3006,7 +3008,6 @@
       ]
     } else {
       sources += [
-        "views/badge_service_delegate_impl.cc",
         "views/frame/opaque_browser_frame_view.cc",
         "views/frame/opaque_browser_frame_view.h",
         "views/frame/opaque_browser_frame_view_layout.cc",
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 05bd4ae7..1a7e770d 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -89,10 +89,6 @@
   EDITABLE_FIELD_IS_ACTIVE,
 };
 
-#if !defined(OS_CHROMEOS)
-class BadgeServiceDelegate;
-#endif
-
 ////////////////////////////////////////////////////////////////////////////////
 // BrowserWindow interface
 //  An interface implemented by the "view" of the Browser window.
@@ -316,9 +312,6 @@
       bool disable_stay_in_chrome,
       IntentPickerResponse callback) = 0;
   virtual void SetIntentPickerViewVisibility(bool visible) = 0;
-#else   // !defined(OS_CHROMEOS)
-  // Returns the badge service delegate.
-  virtual BadgeServiceDelegate* GetBadgeServiceDelegate() const = 0;
 #endif  // defined(OS_CHROMEOS)
 
   // Shows the Bookmark bubble. |url| is the URL being bookmarked,
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
index 996d8ce8..5b49549 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -331,6 +331,10 @@
   return GetExtension()->short_name();
 }
 
+std::string HostedAppBrowserController::GetExtensionId() const {
+  return extension_id_;
+}
+
 base::string16 HostedAppBrowserController::GetFormattedUrlOrigin() const {
   return FormatUrlOrigin(AppLaunchInfo::GetLaunchWebURL(GetExtension()));
 }
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.h b/chrome/browser/ui/extensions/hosted_app_browser_controller.h
index b7b2275..3a6645d 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.h
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.h
@@ -92,6 +92,9 @@
   // Gets the short name of the app.
   std::string GetAppShortName() const;
 
+  // Returns the extension id for the app.
+  std::string GetExtensionId() const;
+
   // Gets the origin of the app start url suitable for display (e.g
   // example.com.au).
   base::string16 GetFormattedUrlOrigin() const;
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index b9df8d2..22185ef 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -20,7 +20,9 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/badging/badge_service_delegate.h"
+#include "chrome/browser/badging/badge_manager.h"
+#include "chrome/browser/badging/badge_manager_delegate.h"
+#include "chrome/browser/badging/badge_manager_factory.h"
 #include "chrome/browser/banners/app_banner_manager_desktop.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
@@ -978,29 +980,44 @@
             app_browser->GetWindowTitleForCurrentTab(false));
 }
 
-#if !defined(OS_CHROMEOS)
+#if !defined(OS_ANDROID)
 class HostedAppBadgingTest : public HostedAppTest {
  public:
+  // Listens to BadgeManager events and forwards them to the test class.
+  class TestBadgeManagerDelegate : public badging::BadgeManagerDelegate {
+   public:
+    using SetBadgeCallback =
+        base::RepeatingCallback<void(const std::string&,
+                                     base::Optional<uint64_t>)>;
+    using ClearBadgeCallback =
+        base::RepeatingCallback<void(const std::string&)>;
+
+    TestBadgeManagerDelegate(Profile* profile,
+                             SetBadgeCallback on_set_badge,
+                             ClearBadgeCallback on_clear_badge)
+        : badging::BadgeManagerDelegate(profile),
+          on_set_badge_(on_set_badge),
+          on_clear_badge_(on_clear_badge) {}
+
+    void OnBadgeSet(const std::string& app_id,
+                    base::Optional<uint64_t> contents) override {
+      on_set_badge_.Run(app_id, contents);
+    }
+
+    void OnBadgeCleared(const std::string& app_id) override {
+      on_clear_badge_.Run(app_id);
+    }
+
+   private:
+    SetBadgeCallback on_set_badge_;
+    ClearBadgeCallback on_clear_badge_;
+  };
+
   void SetUpCommandLine(base::CommandLine* command_line) override {
     HostedAppTest::SetUpCommandLine(command_line);
     command_line->AppendSwitchASCII("enable-blink-features", "Badging");
   }
 
-  void OnBadgeSet(content::WebContents* web_contents,
-                  base::Optional<uint64_t> badge_content) {
-    if (badge_content.has_value())
-      last_badge_content_ = badge_content;
-    else
-      was_flagged_ = true;
-
-    awaiter_->Quit();
-  }
-
-  void OnBadgeCleared(content::WebContents* web_contents) {
-    was_cleared_ = true;
-    awaiter_->Quit();
-  }
-
   void SetUpOnMainThread() override {
     HostedAppTest::SetUpOnMainThread();
 
@@ -1010,12 +1027,34 @@
     InstallSecurePWA();
 
     awaiter_ = std::make_unique<base::RunLoop>();
-    badge_service_delegate_ = app_browser_->window()->GetBadgeServiceDelegate();
-    badge_service_delegate_->SetImplForTesting(
-        base::BindRepeating(&HostedAppBadgingTest::OnBadgeSet,
-                            base::Unretained(this)),
-        base::BindRepeating(&HostedAppBadgingTest::OnBadgeCleared,
-                            base::Unretained(this)));
+
+    Profile* profile = app_browser_->profile();
+    std::unique_ptr<badging::BadgeManagerDelegate> delegate =
+        std::make_unique<TestBadgeManagerDelegate>(
+            profile,
+            base::BindRepeating(&HostedAppBadgingTest::OnBadgeSet,
+                                base::Unretained(this)),
+            base::BindRepeating(&HostedAppBadgingTest::OnBadgeCleared,
+                                base::Unretained(this)));
+    badging::BadgeManagerFactory::GetInstance()
+        ->GetForProfile(profile)
+        ->SetDelegate(std::move(delegate));
+  }
+
+  // BadgeManagerDelegate:
+  void OnBadgeSet(const std::string& app_id,
+                  base::Optional<uint64_t> badge_content) {
+    if (badge_content.has_value())
+      last_badge_content_ = badge_content;
+    else
+      was_flagged_ = true;
+
+    awaiter_->Quit();
+  }
+
+  void OnBadgeCleared(const std::string& app_id) {
+    was_cleared_ = true;
+    awaiter_->Quit();
   }
 
  protected:
@@ -1035,8 +1074,6 @@
     awaiter_->Run();
   }
 
-  BadgeServiceDelegate* badge_service_delegate_;
-
   bool was_cleared_ = false;
   bool was_flagged_ = false;
   base::Optional<uint64_t> last_badge_content_ = base::nullopt;
diff --git a/chrome/browser/ui/login/login_handler.cc b/chrome/browser/ui/login/login_handler.cc
index 8f928ad6..5c6b5432 100644
--- a/chrome/browser/ui/login/login_handler.cc
+++ b/chrome/browser/ui/login/login_handler.cc
@@ -171,7 +171,7 @@
     handler_ = nullptr;
   }
 
-  LoginHandler* handler_;  // owned on the UI thread
+  LoginHandler* handler_ = nullptr;  // owned on the UI thread
   LoginAuthRequiredCallback callback_;
 };
 
diff --git a/chrome/browser/ui/media_router/query_result_manager.cc b/chrome/browser/ui/media_router/query_result_manager.cc
index d24c989..ff6209c 100644
--- a/chrome/browser/ui/media_router/query_result_manager.cc
+++ b/chrome/browser/ui/media_router/query_result_manager.cc
@@ -34,7 +34,7 @@
 
   ~MediaSourceMediaSinksObserver() override {}
 
-  // MediaSinksObserver
+  // MediaSinksObserver:
   void OnSinksReceived(const std::vector<MediaSink>& result) override {
     latest_sink_ids_.clear();
     for (const MediaSink& sink : result)
@@ -44,7 +44,7 @@
     result_manager_->NotifyOnResultsUpdated();
   }
 
-  // Returns the most recent sink IDs that were passed to |OnSinksReceived|.
+  // Returns the most recent sink IDs that were passed to |OnSinksReceived()|.
   void GetLatestSinkIds(std::vector<MediaSink::Id>* sink_ids) const {
     DCHECK(sink_ids);
     *sink_ids = latest_sink_ids_;
@@ -59,8 +59,29 @@
   QueryResultManager* const result_manager_;
 };
 
+// Observes for all the available sinks.
+class QueryResultManager::AnyMediaSinksObserver : public MediaSinksObserver {
+ public:
+  AnyMediaSinksObserver(MediaRouter* router, QueryResultManager* result_manager)
+      : MediaSinksObserver(router), result_manager_(result_manager) {}
+
+  ~AnyMediaSinksObserver() override {}
+
+  // MediaSinksObserver:
+  void OnSinksReceived(const std::vector<MediaSink>& sinks) override {
+    result_manager_->UpdateSinkList(sinks);
+    result_manager_->NotifyOnResultsUpdated();
+  }
+
+ private:
+  QueryResultManager* const result_manager_;
+};
+
 QueryResultManager::QueryResultManager(MediaRouter* router) : router_(router) {
   DCHECK(router_);
+  auto observer = std::make_unique<AnyMediaSinksObserver>(router_, this);
+  observer->Init();
+  sinks_observers_[MediaSource()] = std::move(observer);
 }
 
 QueryResultManager::~QueryResultManager() {
@@ -175,15 +196,11 @@
 
   // (1) Iterate through current sink set, remove cast mode from those that
   // do not appear in latest result.
-  for (auto it = all_sinks_.begin(); it != all_sinks_.end(); /*no-op*/) {
+  for (auto it = all_sinks_.begin(); it != all_sinks_.end(); ++it) {
     const MediaSink::Id& sink_id = it->first;
     CastModesWithMediaSources& sources_for_sink = it->second;
     if (!base::ContainsKey(new_sink_ids, sink_id))
       sources_for_sink.RemoveSource(cast_mode, source);
-    if (sources_for_sink.IsEmpty())
-      all_sinks_.erase(it++);
-    else
-      ++it;
   }
 
   // (2) Add / update sinks with latest result.
@@ -200,6 +217,25 @@
   }
 }
 
+// A sink is added to |all_sinks_| in SetSinksCompatibleWithSource() when
+// MediaSourceMediaSinksObserver receives it, or in UpdateSinkList() when
+// AnyMediaSinksObserver receives it, whichever comes first.
+// A sink is removed from |all_sinks_| in UpdateSinkList() when
+// AnyMediaSinksObserver receives an update that does not contain it.
+void QueryResultManager::UpdateSinkList(const std::vector<MediaSink>& sinks) {
+  // Erase sinks in |all_sinks_| that do not appear in |sinks|.
+  base::EraseIf(all_sinks_, [&sinks](const auto& sink_pair) {
+    return std::find_if(sinks.begin(), sinks.end(),
+                        [&sink_pair](const MediaSink& sink) {
+                          return sink.id() == sink_pair.first;
+                        }) == sinks.end();
+  });
+  for (const MediaSink& sink : sinks) {
+    if (!base::ContainsKey(all_sinks_, sink.id()))
+      all_sinks_.emplace(sink.id(), sink);
+  }
+}
+
 std::unique_ptr<MediaSource>
 QueryResultManager::GetHighestPrioritySourceForCastModeAndSink(
     MediaCastMode cast_mode,
diff --git a/chrome/browser/ui/media_router/query_result_manager.h b/chrome/browser/ui/media_router/query_result_manager.h
index ea341d5d..8f19d56 100644
--- a/chrome/browser/ui/media_router/query_result_manager.h
+++ b/chrome/browser/ui/media_router/query_result_manager.h
@@ -112,6 +112,7 @@
 
  private:
   class MediaSourceMediaSinksObserver;
+  class AnyMediaSinksObserver;
 
   FRIEND_TEST_ALL_PREFIXES(QueryResultManagerTest, Observers);
   FRIEND_TEST_ALL_PREFIXES(QueryResultManagerTest, StartRoutesDiscovery);
@@ -137,6 +138,9 @@
                                     const MediaSource& source,
                                     const std::vector<MediaSink>& new_sinks);
 
+  // Updates the overall list of sinks to match |sinks|.
+  void UpdateSinkList(const std::vector<MediaSink>& sinks);
+
   // Returns the highest-priority source for |cast_mode| contained in
   // |sources_for_sink|. Returns an empty unique_ptr if none exists.
   std::unique_ptr<MediaSource> GetHighestPrioritySourceForCastModeAndSink(
diff --git a/chrome/browser/ui/media_router/query_result_manager_unittest.cc b/chrome/browser/ui/media_router/query_result_manager_unittest.cc
index d87520a..9cd1d44d 100644
--- a/chrome/browser/ui/media_router/query_result_manager_unittest.cc
+++ b/chrome/browser/ui/media_router/query_result_manager_unittest.cc
@@ -15,12 +15,11 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+using testing::_;
 using testing::Eq;
 using testing::IsEmpty;
-using testing::Eq;
 using testing::Mock;
 using testing::Return;
-using testing::_;
 
 namespace media_router {
 
@@ -44,7 +43,7 @@
   void DiscoverSinks(MediaCastMode cast_mode, const MediaSource& source) {
     EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
         .WillOnce(Return(true));
-    EXPECT_CALL(mock_observer_, OnResultsUpdated(_)).Times(1);
+    EXPECT_CALL(mock_observer_, OnResultsUpdated(_));
     query_result_manager_.SetSourcesForCastMode(
         cast_mode, {source}, url::Origin::Create(GURL(kOrigin)));
   }
@@ -94,12 +93,12 @@
   query_result_manager_.AddObserver(&ob1);
   query_result_manager_.AddObserver(&ob2);
 
-  EXPECT_CALL(ob1, OnResultsUpdated(_)).Times(1);
-  EXPECT_CALL(ob2, OnResultsUpdated(_)).Times(1);
+  EXPECT_CALL(ob1, OnResultsUpdated(_));
+  EXPECT_CALL(ob2, OnResultsUpdated(_));
   query_result_manager_.NotifyOnResultsUpdated();
 
   query_result_manager_.RemoveObserver(&ob2);
-  EXPECT_CALL(ob1, OnResultsUpdated(_)).Times(1);
+  EXPECT_CALL(ob1, OnResultsUpdated(_));
   query_result_manager_.NotifyOnResultsUpdated();
 
   query_result_manager_.RemoveObserver(&ob1);
@@ -131,7 +130,7 @@
   // Register a different set of sources for the same cast mode.
   MediaSource another_source(
       MediaSourceForPresentationUrl(GURL("http://bar.com")));
-  EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(1);
+  EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_));
   EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
       .WillOnce(Return(true));
   query_result_manager_.SetSourcesForCastMode(
@@ -146,7 +145,7 @@
   EXPECT_EQ(1u, actual_sources.size());
   EXPECT_EQ(another_source, actual_sources[0]);
 
-  EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(1);
+  EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_));
   query_result_manager_.RemoveSourcesForCastMode(MediaCastMode::PRESENTATION);
 
   cast_modes = query_result_manager_.GetSupportedCastModes();
@@ -161,7 +160,6 @@
   MediaSink sink2("sinkId2", "Sink 2", SinkIconType::CAST);
   MediaSink sink3("sinkId3", "Sink 3", SinkIconType::CAST);
   MediaSink sink4("sinkId4", "Sink 4", SinkIconType::CAST);
-  MediaSink sink5("sinkId5", "Sink 5", SinkIconType::CAST);
   MediaSource presentation_source1 =
       MediaSourceForPresentationUrl(GURL("http://bar.com"));
   MediaSource presentation_source2 =
@@ -173,30 +171,40 @@
   DiscoverSinks(MediaCastMode::TAB_MIRROR, tab_source);
 
   // Scenario (results in this order):
+  // Action: (all sinks) -> [1, 2, 3, 4]
+  // Expected result:
+  // Sinks: [1 -> {}, 2 -> {}, 3 -> {}, 4 -> {}]
+  std::vector<MediaSinkWithCastModes> expected_sinks;
+  expected_sinks.push_back(MediaSinkWithCastModes(sink1));
+  expected_sinks.push_back(MediaSinkWithCastModes(sink2));
+  expected_sinks.push_back(MediaSinkWithCastModes(sink3));
+  expected_sinks.push_back(MediaSinkWithCastModes(sink4));
+
+  std::vector<MediaSink> sinks_query_result = {sink1, sink2, sink3, sink4};
+
+  const auto& sinks_observers = query_result_manager_.sinks_observers_;
+  auto* any_sink_observer = sinks_observers.find(MediaSource())->second.get();
+  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)));
+  any_sink_observer->OnSinksUpdated(sinks_query_result, {});
+
   // Action: PRESENTATION -> [1, 2, 3]
   // Expected result:
-  // Sinks: [1 -> {PRESENTATION}, 2 -> {PRESENTATION}, 3 -> {PRESENTATION}]
-  std::vector<MediaSinkWithCastModes> expected_sinks;
+  // Sinks: [1 -> {PRESENTATION}, 2 -> {PRESENTATION}, 3 -> {PRESENTATION},
+  //         4 -> {}]
+  expected_sinks.clear();
   expected_sinks.push_back(MediaSinkWithCastModes(sink1));
   expected_sinks.back().cast_modes.insert(MediaCastMode::PRESENTATION);
   expected_sinks.push_back(MediaSinkWithCastModes(sink2));
   expected_sinks.back().cast_modes.insert(MediaCastMode::PRESENTATION);
   expected_sinks.push_back(MediaSinkWithCastModes(sink3));
   expected_sinks.back().cast_modes.insert(MediaCastMode::PRESENTATION);
+  expected_sinks.push_back(MediaSinkWithCastModes(sink4));
 
-  const auto& sinks_observers = query_result_manager_.sinks_observers_;
-  auto sinks_observer_it = sinks_observers.find(presentation_source1);
-  ASSERT_TRUE(sinks_observer_it != sinks_observers.end());
-  ASSERT_TRUE(sinks_observer_it->second.get());
-
-  std::vector<MediaSink> sinks_query_result;
-  sinks_query_result.push_back(sink1);
-  sinks_query_result.push_back(sink2);
-  sinks_query_result.push_back(sink3);
-  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)))
-      .Times(1);
-  sinks_observer_it->second->OnSinksUpdated(sinks_query_result,
-                                            std::vector<url::Origin>());
+  sinks_query_result = {sink1, sink2, sink3};
+  auto* presentation1_sinks_observer =
+      sinks_observers.find(presentation_source1)->second.get();
+  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)));
+  presentation1_sinks_observer->OnSinksUpdated(sinks_query_result, {});
 
   // Action: TAB_MIRROR -> [2, 3, 4]
   // Expected result:
@@ -214,23 +222,17 @@
   expected_sinks.push_back(MediaSinkWithCastModes(sink4));
   expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
 
-  sinks_query_result.clear();
-  sinks_query_result.push_back(sink2);
-  sinks_query_result.push_back(sink3);
-  sinks_query_result.push_back(sink4);
-
-  sinks_observer_it = sinks_observers.find(tab_source);
-  ASSERT_TRUE(sinks_observer_it != sinks_observers.end());
-  ASSERT_TRUE(sinks_observer_it->second.get());
-  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)))
-      .Times(1);
-  sinks_observer_it->second->OnSinksUpdated(
-      sinks_query_result, {url::Origin::Create(GURL(kOrigin))});
+  sinks_query_result = {sink2, sink3, sink4};
+  auto* tab_sinks_observer = sinks_observers.find(tab_source)->second.get();
+  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)));
+  tab_sinks_observer->OnSinksUpdated(sinks_query_result,
+                                     {url::Origin::Create(GURL(kOrigin))});
 
   // Action: Update presentation URL
   // Expected result:
-  // Sinks: [2 -> {TAB_MIRROR}, 3 -> {TAB_MIRROR}, 4 -> {TAB_MIRROR}]
+  // Sinks: [1 -> {}, 2 -> {TAB_MIRROR}, 3 -> {TAB_MIRROR}, 4 -> {TAB_MIRROR}]
   expected_sinks.clear();
+  expected_sinks.push_back(MediaSinkWithCastModes(sink1));
   expected_sinks.push_back(MediaSinkWithCastModes(sink2));
   expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
   expected_sinks.push_back(MediaSinkWithCastModes(sink3));
@@ -239,42 +241,51 @@
   expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
 
   // The observer for the old source will be unregistered.
-  EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(1);
+  EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_));
   // The observer for the new source will be registered.
   EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
       .WillOnce(Return(true));
-  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)))
-      .Times(1);
+  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)));
   query_result_manager_.SetSourcesForCastMode(
       MediaCastMode::PRESENTATION, {presentation_source2},
       url::Origin::Create(GURL(kOrigin)));
 
   // Action: PRESENTATION -> [1], origins don't match
-  // Expected result: [2 -> {TAB_MIRROR}, 3 -> {TAB_MIRROR}, 4 -> {TAB_MIRROR}]
-  // (No change)
-  sinks_query_result.clear();
-  sinks_query_result.push_back(sink1);
-  sinks_observer_it = sinks_observers.find(presentation_source2);
-  ASSERT_TRUE(sinks_observer_it != sinks_observers.end());
-  ASSERT_TRUE(sinks_observer_it->second.get());
-  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)))
-      .Times(1);
-  sinks_observer_it->second->OnSinksUpdated(
+  // Expected result: [1 -> {}, 2 -> {TAB_MIRROR}, 3 -> {TAB_MIRROR},
+  //                   4 -> {TAB_MIRROR}] (No change)
+  sinks_query_result = {sink1};
+  auto* presentation2_sinks_observer =
+      sinks_observers.find(presentation_source2)->second.get();
+  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)));
+  presentation2_sinks_observer->OnSinksUpdated(
       sinks_query_result,
       {url::Origin::Create(GURL("https://differentOrigin.com"))});
 
+  // Action: (all sinks) -> [2, 3]
+  // Expected result: [2 -> {TAB_MIRROR}, 3 -> {TAB_MIRROR}]
+  expected_sinks.clear();
+  expected_sinks.push_back(MediaSinkWithCastModes(sink2));
+  expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
+  expected_sinks.push_back(MediaSinkWithCastModes(sink3));
+  expected_sinks.back().cast_modes.insert(MediaCastMode::TAB_MIRROR);
+
+  sinks_query_result = {sink2, sink3};
+  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)));
+  any_sink_observer->OnSinksUpdated(sinks_query_result, {});
+
   // Action: Remove TAB_MIRROR observer
   // Expected result:
-  // Sinks: []
+  // Sinks: [2 -> {}, 3 -> {}]
   expected_sinks.clear();
-  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)))
-      .Times(1);
-  EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(1);
+  expected_sinks.push_back(MediaSinkWithCastModes(sink2));
+  expected_sinks.push_back(MediaSinkWithCastModes(sink3));
+  EXPECT_CALL(mock_observer_, OnResultsUpdated(VectorEquals(expected_sinks)));
+  EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_));
   query_result_manager_.RemoveSourcesForCastMode(MediaCastMode::TAB_MIRROR);
 
   // Remaining observers: PRESENTATION observer, which will be removed on
   // destruction
-  EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_)).Times(1);
+  EXPECT_CALL(mock_router_, UnregisterMediaSinksObserver(_));
 }
 
 TEST_F(QueryResultManagerTest, MultipleUrls) {
@@ -393,8 +404,7 @@
       MediaSourceForPresentationUrl(GURL("http://url.com")));
 
   EXPECT_CALL(mock_router_, RegisterMediaSinksObserver(_))
-      .Times(1)
-      .WillRepeatedly(Return(true));
+      .WillOnce(Return(true));
   query_result_manager_.SetSourcesForCastMode(
       MediaCastMode::PRESENTATION, {source},
       url::Origin::Create(GURL(kOrigin)));
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc
index 9b24ae0..c737177 100644
--- a/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/save_card_bubble_views_browsertest.cc
@@ -14,7 +14,6 @@
 #include "base/test/metrics/user_action_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
diff --git a/chrome/browser/ui/views/badge_service_delegate_impl.cc b/chrome/browser/ui/views/badge_service_delegate_impl.cc
deleted file mode 100644
index ce72410..0000000
--- a/chrome/browser/ui/views/badge_service_delegate_impl.cc
+++ /dev/null
@@ -1,107 +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 "chrome/browser/badging/badge_service_delegate.h"
-
-#include "base/bind.h"
-#include "base/i18n/number_formatting.h"
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/strings/grit/ui_strings.h"
-
-#if defined(OS_WIN)
-#include "chrome/browser/taskbar/taskbar_decorator_win.h"
-#elif defined(OS_MACOSX)
-#include "chrome/browser/apps/app_shim/app_shim_host_mac.h"
-#include "chrome/browser/apps/app_shim/extension_app_shim_handler_mac.h"
-#include "chrome/common/mac/app_shim.mojom.h"
-#endif
-
-namespace {
-
-#if defined(OS_MACOSX)
-void SetAppShimBadgeLabel(content::WebContents* contents,
-                          const std::string& badge_label) {
-  Browser* browser = chrome::FindBrowserWithWebContents(contents);
-  auto* shim_handler = apps::ExtensionAppShimHandler::Get();
-  if (!shim_handler)
-    return;
-
-  AppShimHost* shim_host = shim_handler->GetHostForBrowser(browser);
-  if (!shim_host)
-    return;
-
-  chrome::mojom::AppShim* shim = shim_host->GetAppShim();
-  if (!shim)
-    return;
-
-  shim->SetBadgeLabel(badge_label);
-}
-#endif
-
-#if defined(OS_WIN) || defined(OS_MACOSX)
-std::string GetBadgeString(base::Optional<uint64_t> badge_content) {
-  if (!badge_content)
-    return "•";
-
-  if (badge_content > 99u) {
-    return base::UTF16ToUTF8(l10n_util::GetStringFUTF16(
-        IDS_SATURATED_BADGE_CONTENT, base::FormatNumber(99)));
-  }
-
-  return base::UTF16ToUTF8(base::FormatNumber(badge_content.value()));
-}
-#endif
-
-void SetBadgeImpl(content::WebContents* contents,
-                  base::Optional<uint64_t> badge_content) {
-#if defined(OS_WIN)
-  Browser* browser = chrome::FindBrowserWithWebContents(contents);
-  auto* window = browser->window()->GetNativeWindow();
-  taskbar::DrawTaskbarDecorationString(window, GetBadgeString(badge_content));
-#elif defined(OS_MACOSX)
-  SetAppShimBadgeLabel(contents, GetBadgeString(badge_content));
-#endif
-}
-
-void ClearBadgeImpl(content::WebContents* contents) {
-#if defined(OS_WIN)
-  Browser* browser = chrome::FindBrowserWithWebContents(contents);
-
-  // Restore the decoration to whatever it is naturally (either nothing or a
-  // profile picture badge).
-  taskbar::UpdateTaskbarDecoration(browser->profile(),
-                                   browser->window()->GetNativeWindow());
-#elif defined(OS_MACOSX)
-  SetAppShimBadgeLabel(contents, "");
-#endif
-}
-
-}  // namespace
-
-BadgeServiceDelegate::BadgeServiceDelegate()
-    : on_set_badge_(base::BindRepeating(&SetBadgeImpl)),
-      on_clear_badge_(base::BindRepeating(&ClearBadgeImpl)) {}
-
-BadgeServiceDelegate::~BadgeServiceDelegate() {}
-
-void BadgeServiceDelegate::SetBadge(content::WebContents* contents,
-                                    base::Optional<uint64_t> badge_content) {
-  on_set_badge_.Run(contents, badge_content);
-}
-
-void BadgeServiceDelegate::ClearBadge(content::WebContents* contents) {
-  on_clear_badge_.Run(contents);
-}
-
-void BadgeServiceDelegate::SetImplForTesting(
-    SetBadgeCallback on_set_badge,
-    ClearBadgeCallback on_clear_badge) {
-  on_set_badge_ = on_set_badge;
-  on_clear_badge_ = on_clear_badge;
-}
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
index 972e9edb..57cccf34 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/bookmarks/bookmark_editor_view.h"
 
+#include <set>
 #include <string>
 
 #include "base/logging.h"
@@ -402,12 +403,6 @@
     layout->AddView(tree_view_->CreateParentIfNecessary());
   }
 
-  if (provider->UseExtraDialogPadding()) {
-    layout->AddPaddingRow(
-        views::GridLayout::kFixedSize,
-        provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
-  }
-
   if (!show_tree_ || bb_model_->loaded())
     Reset();
 }
diff --git a/chrome/browser/ui/views/chrome_layout_provider.cc b/chrome/browser/ui/views/chrome_layout_provider.cc
index b07eac5f..65eccce 100644
--- a/chrome/browser/ui/views/chrome_layout_provider.cc
+++ b/chrome/browser/ui/views/chrome_layout_provider.cc
@@ -186,10 +186,6 @@
   return views::GridLayout::LEADING;
 }
 
-bool ChromeLayoutProvider::UseExtraDialogPadding() const {
-  return false;
-}
-
 bool ChromeLayoutProvider::ShouldShowWindowIcon() const {
   return false;
 }
diff --git a/chrome/browser/ui/views/chrome_layout_provider.h b/chrome/browser/ui/views/chrome_layout_provider.h
index b92ec0d..d023093 100644
--- a/chrome/browser/ui/views/chrome_layout_provider.h
+++ b/chrome/browser/ui/views/chrome_layout_provider.h
@@ -91,10 +91,6 @@
   // This value controls the alignment used for "Label 1" and "Label 2".
   virtual views::GridLayout::Alignment GetControlLabelGridAlignment() const;
 
-  // Returns whether to use extra padding on dialogs. If this is false, content
-  // Views for dialogs should not insert extra padding at their own edges.
-  virtual bool UseExtraDialogPadding() const;
-
   // Returns whether to show the icon next to the title text on a dialog.
   virtual bool ShouldShowWindowIcon() const;
 
diff --git a/chrome/browser/ui/views/collected_cookies_views.cc b/chrome/browser/ui/views/collected_cookies_views.cc
index ce8a862..071696d 100644
--- a/chrome/browser/ui/views/collected_cookies_views.cc
+++ b/chrome/browser/ui/views/collected_cookies_views.cc
@@ -4,7 +4,9 @@
 
 #include "chrome/browser/ui/views/collected_cookies_views.h"
 
+#include <map>
 #include <memory>
+#include <utility>
 
 #include "base/macros.h"
 #include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
@@ -60,10 +62,6 @@
 const int kTreeViewWidth = 400;
 const int kTreeViewHeight = 125;
 
-// Spacing constants used with non-Harmony dialogs.
-const int kTabbedPaneTopPadding = 14;
-const int kCookieInfoBottomPadding = 4;
-
 // Adds a ColumnSet to |layout| to hold two buttons with padding between.
 // Starts a new row with the added ColumnSet.
 void StartNewButtonColumnSet(views::GridLayout* layout,
@@ -401,10 +399,13 @@
   views::GridLayout* layout =
       SetLayoutManager(std::make_unique<views::GridLayout>(this));
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
-  if (provider->UseExtraDialogPadding()) {
-    SetBorder(
-        views::CreateEmptyBorder(gfx::Insets(kTabbedPaneTopPadding, 0, 0, 0)));
-  }
+
+  // Add margin above the content. The left, right, and bottom margins are added
+  // by the content itself.
+  set_margins(
+      gfx::Insets(provider->GetDistanceMetric(
+                      views::DISTANCE_DIALOG_CONTENT_MARGIN_TOP_CONTROL),
+                  0, 0, 0));
 
   const int single_column_layout_id = 0;
   views::ColumnSet* column_set = layout->AddColumnSet(single_column_layout_id);
@@ -424,18 +425,10 @@
   tabbed_pane->AddTab(label_blocked, CreateBlockedPane());
   tabbed_pane->SelectTabAt(0);
   tabbed_pane->set_listener(this);
-  if (ChromeLayoutProvider::Get()->UseExtraDialogPadding()) {
-    layout->AddPaddingRow(
-        views::GridLayout::kFixedSize,
-        provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
-  }
 
   layout->StartRow(views::GridLayout::kFixedSize, single_column_layout_id);
   cookie_info_view_ = new CookieInfoView();
   layout->AddView(cookie_info_view_);
-  if (provider->UseExtraDialogPadding())
-    layout->AddPaddingRow(views::GridLayout::kFixedSize,
-                          kCookieInfoBottomPadding);
 
   layout->StartRow(views::GridLayout::kFixedSize, single_column_layout_id);
   infobar_ = new InfobarView();
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 9299322..7311cb0 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -169,7 +169,6 @@
 #include "chrome/grit/chrome_unscaled_resources.h"
 #include "ui/base/ui_base_features.h"
 #else
-#include "chrome/browser/badging/badge_service_delegate.h"
 #include "chrome/browser/ui/signin_view_controller.h"
 #include "chrome/browser/ui/views/profiles/profile_chooser_view.h"
 #endif  // !defined(OS_CHROMEOS)
@@ -535,9 +534,6 @@
   browser_ = std::move(browser);
   browser_->tab_strip_model()->AddObserver(this);
   immersive_mode_controller_.reset(chrome::CreateImmersiveModeController());
-#if !defined(OS_CHROMEOS)
-  badge_service_delegate_ = std::make_unique<BadgeServiceDelegate>();
-#endif
 }
 
 // static
@@ -1352,10 +1348,6 @@
     location_bar->Layout();
   }
 }
-#else   // !defined(OS_CHROMEOS)
-BadgeServiceDelegate* BrowserView::GetBadgeServiceDelegate() const {
-  return badge_service_delegate_.get();
-}
 #endif  // defined(OS_CHROMEOS)
 
 void BrowserView::ShowBookmarkBubble(const GURL& url, bool already_bookmarked) {
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 4d0878e..bf8bfc65 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -367,8 +367,6 @@
       bool disable_stay_in_chrome,
       IntentPickerResponse callback) override;
   void SetIntentPickerViewVisibility(bool visible) override;
-#else   // !defined(OS_CHROMEOS)
-  BadgeServiceDelegate* GetBadgeServiceDelegate() const override;
 #endif  // defined(OS_CHROMEOS)
   void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) override;
   autofill::SaveCardBubbleView* ShowSaveCreditCardBubble(
@@ -831,11 +829,6 @@
 
   std::unique_ptr<FullscreenControlHost> fullscreen_control_host_;
 
-#if !defined(OS_CHROMEOS)
-  // The badge service delegate for this window.
-  std::unique_ptr<BadgeServiceDelegate> badge_service_delegate_;
-#endif  // !defined(OS_CHROMEOS)
-
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
   std::unique_ptr<ReopenTabPromoController> reopen_tab_promo_controller_;
 #endif
diff --git a/chrome/browser/ui/views/login_view.cc b/chrome/browser/ui/views/login_view.cc
index fe7d447..3f3092e 100644
--- a/chrome/browser/ui/views/login_view.cc
+++ b/chrome/browser/ui/views/login_view.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/login_view.h"
 
+#include <memory>
+
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/textfield_layout.h"
@@ -70,12 +72,6 @@
       kFieldsColumnSetId);
   password_field_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
 
-  if (provider->UseExtraDialogPadding()) {
-    layout->AddPaddingRow(views::GridLayout::kFixedSize,
-                          provider->GetDistanceMetric(
-                              views::DISTANCE_UNRELATED_CONTROL_VERTICAL));
-  }
-
   if (login_model_data) {
     login_model_->AddObserverAndDeliverCredentials(this,
                                                    login_model_data->form);
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index 5f5d5527..3c9c323 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -42,9 +42,7 @@
 #if defined(OS_CHROMEOS)
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/window_properties.h"  // nogncheck
-#include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
-#include "ui/base/ui_base_features.h"
 #endif
 
 // static
@@ -230,12 +228,6 @@
 
 #if defined(OS_CHROMEOS)
   GetNativeWindow()->SetProperty(ash::kWindowPipTypeKey, true);
-  if (features::IsUsingWindowService()) {
-    aura::Window* window = GetNativeWindow()->GetRootWindow();
-    window->SetProperty(aura::client::kMinimumSize,
-                        new gfx::Size(kMinWindowSize));
-    window->SetProperty(aura::client::kMaximumSize, new gfx::Size(max_size_));
-  }
 #endif  // defined(OS_CHROMEOS)
 
   is_initialized_ = true;
@@ -256,12 +248,6 @@
   // Lower bound size of the window is a fixed value to allow for minimal sizes
   // on UI affordances, such as buttons.
   min_size_ = kMinWindowSize;
-#if defined(OS_CHROMEOS)
-  if (features::IsUsingWindowService() && is_initialized_) {
-    GetNativeWindow()->GetRootWindow()->SetProperty(aura::client::kMaximumSize,
-                                                    new gfx::Size(max_size_));
-  }
-#endif
 
   gfx::Size window_size = window_bounds_.size();
   if (!has_been_shown_) {
diff --git a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
index d817e6fe..f4c38cd 100644
--- a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
+++ b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
@@ -311,8 +311,6 @@
     }
     unmask_delegate_->OnUnmaskResponse(response);
   }
-
-  dialog()->ShowProcessingSpinner();
 }
 
 void CvcUnmaskViewController::DisplayError(base::string16 error) {
diff --git a/chrome/browser/ui/views/tabs/tab_group_header.cc b/chrome/browser/ui/views/tabs/tab_group_header.cc
new file mode 100644
index 0000000..0b635160
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_group_header.cc
@@ -0,0 +1,53 @@
+// 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/views/tabs/tab_group_header.h"
+
+#include <memory>
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/button/image_button_factory.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
+
+TabGroupHeader::TabGroupHeader() {
+  // TODO(crbug.com/905491): Call TabStyle::GetContentsInsets.
+  constexpr gfx::Insets kPlaceholderInsets = gfx::Insets(2, 10);
+  SetBorder(views::CreateEmptyBorder(kPlaceholderInsets));
+
+  views::FlexLayout* layout =
+      SetLayoutManager(std::make_unique<views::FlexLayout>());
+  layout->SetOrientation(views::LayoutOrientation::kHorizontal)
+      .SetCollapseMargins(true)
+      .SetMainAxisAlignment(views::LayoutAlignment::kStart)
+      .SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
+
+  // TODO(crbug.com/905491): Get title from TabGroupData::title().
+  auto* title = new views::Label(base::ASCIIToUTF16("Placeholder Title"));
+  title->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
+  title->SetElideBehavior(gfx::FADE_TAIL);
+  AddChildView(title);
+  layout->SetFlexForView(title, views::FlexSpecification::ForSizeRule(
+                                    views::MinimumFlexSizeRule::kScaleToZero,
+                                    views::MaximumFlexSizeRule::kUnbounded));
+
+  auto* group_menu_button =
+      views::CreateVectorImageButton(/*listener*/ nullptr);
+  views::SetImageFromVectorIcon(group_menu_button, kBrowserToolsIcon);
+  AddChildView(group_menu_button);
+}
+
+void TabGroupHeader::OnPaint(gfx::Canvas* canvas) {
+  // TODO(crbug.com/905491): Call TabStyle::PaintTab.
+  constexpr SkColor kPlaceholderColor = SkColorSetRGB(0xAA, 0xBB, 0xCC);
+  canvas->FillRect(GetLocalBounds(), kPlaceholderColor);
+}
diff --git a/chrome/browser/ui/views/tabs/tab_group_header.h b/chrome/browser/ui/views/tabs/tab_group_header.h
new file mode 100644
index 0000000..01d111e9
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_group_header.h
@@ -0,0 +1,28 @@
+// 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_VIEWS_TABS_TAB_GROUP_HEADER_H_
+#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_GROUP_HEADER_H_
+
+#include "ui/views/view.h"
+
+namespace gfx {
+class Canvas;
+}
+
+// View for tab group headers in the tab strip, which are tab-shaped markers of
+// group boundaries. There is one header for each group, which is included in
+// the tab strip flow and positioned left of the leftmost tab in the group.
+class TabGroupHeader : public views::View {
+ public:
+  TabGroupHeader();
+
+  // views::View:
+  void OnPaint(gfx::Canvas* canvas) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TabGroupHeader);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_GROUP_HEADER_H_
diff --git a/chrome/browser/ui/views/touch_selection_menu_runner_chromeos.cc b/chrome/browser/ui/views/touch_selection_menu_runner_chromeos.cc
index e7d67805..7edd923 100644
--- a/chrome/browser/ui/views/touch_selection_menu_runner_chromeos.cc
+++ b/chrome/browser/ui/views/touch_selection_menu_runner_chromeos.cc
@@ -7,6 +7,8 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/views/touch_selection_menu_chromeos.h"
 #include "components/arc/arc_bridge_service.h"
@@ -79,6 +81,7 @@
   const display::Screen* screen = display::Screen::GetScreen();
   DCHECK(screen);
 
+  base::RecordAction(base::UserMetricsAction("Arc.SmartTextSelection.Request"));
   // Fetch actions for selected text and then show quick menu.
   instance->RequestTextSelectionActions(
       converted_text,
diff --git a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc
index 196f4c1e..6aaa395 100644
--- a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc
@@ -4,21 +4,78 @@
 
 #include "chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h"
 
+#include <map>
 #include <string>
 
+#include "ash/public/cpp/shell_window_ids.h"
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/task/post_task.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.h"
 #include "chrome/browser/chromeos/arc/tracing/arc_tracing_model.h"
+#include "components/exo/shell_surface_util.h"
+#include "components/exo/wm_helper_chromeos.h"
 #include "content/public/browser/tracing_controller.h"
 #include "content/public/browser/web_ui.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/gfx/codec/png_codec.h"
 
 namespace chromeos {
 
 namespace {
 
+constexpr char kKeyIcon[] = "icon";
+constexpr char kKeyTitle[] = "title";
+constexpr char kKeyTasks[] = "tasks";
+constexpr char kTaskIdPrefix[] = "org.chromium.arc.";
+
+// Scans for all ARC windows and and extracts the title and optionally icon.
+void CreateTaskMap(aura::Window* window, base::DictionaryValue* tasks) {
+  if (!window->IsVisible())
+    return;
+
+  // ARC window is top level window, all its parents have type not set.
+  if (window->type() == aura::client::WINDOW_TYPE_UNKNOWN) {
+    for (aura::Window* child_window : window->children())
+      CreateTaskMap(child_window, tasks);
+    return;
+  }
+
+  // Verifies if this is top-level ARC window.
+  const std::string* arc_app_id = exo::GetShellApplicationId(window);
+  if (!arc_app_id)
+    return;
+
+  // Root surface may not have task id.
+  const size_t prefix_pos = arc_app_id->find(kTaskIdPrefix);
+  if (prefix_pos)
+    return;
+
+  base::DictionaryValue task_information;
+  task_information.SetKey(kKeyTitle, base::Value(window->GetTitle()));
+
+  const gfx::ImageSkia* app_icon =
+      window->GetProperty(aura::client::kAppIconKey);
+  if (app_icon) {
+    std::vector<unsigned char> png_data;
+    if (gfx::PNGCodec::EncodeBGRASkBitmap(
+            app_icon->GetRepresentation(1.0f).GetBitmap(),
+            false /* discard_transparency */, &png_data)) {
+      const std::string png_data_as_string(
+          reinterpret_cast<const char*>(&png_data[0]), png_data.size());
+      std::string icon_content;
+      base::Base64Encode(png_data_as_string, &icon_content);
+      task_information.SetKey(kKeyIcon, base::Value(icon_content));
+    }
+  }
+
+  tasks->SetKey(arc_app_id->c_str() + strlen(kTaskIdPrefix),
+                std::move(task_information));
+}
+
 std::unique_ptr<base::Value> BuildGraphicsModel(const std::string& data) {
   arc::ArcTracingModel common_model;
   if (!common_model.Build(data)) {
@@ -32,7 +89,19 @@
     return nullptr;
   }
 
-  return graphics_model.Serialize();
+  std::unique_ptr<base::DictionaryValue> model = graphics_model.Serialize();
+  base::DictionaryValue tasks;
+  // Scan for ARC++ windows
+  // TODO(https://crbug.com/887156): Fix the mash.
+  if (!features::IsMultiProcessMash()) {
+    CreateTaskMap(
+        exo::WMHelperChromeOS::GetInstance()->GetPrimaryDisplayContainer(
+            ash::kShellWindowId_DefaultContainer),
+        &tasks);
+  }
+  model->SetKey(kKeyTasks, std::move(tasks));
+
+  return model;
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
index 00d7313..c2fa4ac 100644
--- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h"
 #include "chrome/common/chrome_constants.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
index a4f0faf..31a5128 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
@@ -294,7 +294,7 @@
 MultideviceHandler::GenerateAndroidSmsInfo() {
   base::Optional<GURL> app_url;
   if (android_sms_app_manager_)
-    app_url = android_sms_app_manager_->GetInstalledAppUrl();
+    app_url = android_sms_app_manager_->GetCurrentAppUrl();
   if (!app_url)
     app_url = android_sms::GetAndroidMessagesURL();
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
index bec01db..e889966 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
@@ -382,9 +382,9 @@
 
 TEST_F(MultideviceHandlerTest, GetAndroidSmsInfo) {
   // Check that getAndroidSmsInfo returns correct value.
-  CallGetAndroidSmsInfo(
-      false /* expected_enabled */,
-      android_sms::GetAndroidMessagesURL() /* expected_url */);
+  CallGetAndroidSmsInfo(false /* expected_enabled */,
+                        android_sms::GetAndroidMessagesURL(
+                            true /* use_install_url */) /* expected_url */);
 
   // Change messages feature state and assert that the change
   // callback is fired.
@@ -401,20 +401,19 @@
   EXPECT_EQ("settings.onAndroidSmsInfoChange", call_data_1.arg1()->GetString());
 
   // Check that getAndroidSmsInfo returns update value.
-  CallGetAndroidSmsInfo(
-      true /* enabled */,
-      android_sms::GetAndroidMessagesURL() /* expected_url */);
+  CallGetAndroidSmsInfo(true /* enabled */, android_sms::GetAndroidMessagesURL(
+                                                true) /* expected_url */);
 
   // Now, update the installed URL. This should have resulted in another call.
   fake_android_sms_app_manager()->SetInstalledAppUrl(
-      android_sms::GetAndroidMessagesURLOld());
+      android_sms::GetAndroidMessagesURLOld(true));
   const content::TestWebUI::CallData& call_data_2 =
       CallDataAtIndex(call_data_count_before_call + 4);
   EXPECT_EQ("cr.webUIListenerCallback", call_data_2.function_name());
   EXPECT_EQ("settings.onAndroidSmsInfoChange", call_data_2.arg1()->GetString());
   CallGetAndroidSmsInfo(
       true /* enabled */,
-      android_sms::GetAndroidMessagesURLOld() /* expected_url */);
+      android_sms::GetAndroidMessagesURLOld(true) /* expected_url */);
 }
 
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc b/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
index db57f7a..c25759c 100644
--- a/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
+++ b/chrome/browser/ui/webui/signin/signin_create_profile_handler_unittest.cc
@@ -10,9 +10,7 @@
 #include "chrome/browser/profiles/profile_attributes_entry.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
-#include "chrome/browser/signin/fake_signin_manager_builder.h"
 #include "chrome/browser/signin/signin_error_controller_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/signin/signin_util.h"
 #include "chrome/browser/ui/webui/signin/signin_utils.h"
 #include "chrome/common/buildflags.h"
@@ -120,13 +118,8 @@
     return handler_.get();
   }
 
-  FakeSigninManagerForTesting* signin_manager() {
-    return fake_signin_manager_;
-  }
-
  private:
   std::unique_ptr<content::TestWebUI> web_ui_;
-  FakeSigninManagerForTesting* fake_signin_manager_;
   std::unique_ptr<TestSigninCreateProfileHandler> handler_;
 };
 
diff --git a/chrome/common/media_router/discovery/media_sink_service_base.cc b/chrome/common/media_router/discovery/media_sink_service_base.cc
index 1f87e73..009517d7 100644
--- a/chrome/common/media_router/discovery/media_sink_service_base.cc
+++ b/chrome/common/media_router/discovery/media_sink_service_base.cc
@@ -116,6 +116,8 @@
   for (const auto& sink_it : sinks_)
     sinks.push_back(sink_it.second);
 
+  for (auto& observer : observers_)
+    observer.OnSinksDiscovered(sinks);
   on_sinks_discovered_cb_.Run(std::move(sinks));
   previous_sinks_ = sinks_;
 }
diff --git a/chrome/common/media_router/discovery/media_sink_service_base.h b/chrome/common/media_router/discovery/media_sink_service_base.h
index 571d682..df9547f 100644
--- a/chrome/common/media_router/discovery/media_sink_service_base.h
+++ b/chrome/common/media_router/discovery/media_sink_service_base.h
@@ -6,6 +6,7 @@
 #define CHROME_COMMON_MEDIA_ROUTER_DISCOVERY_MEDIA_SINK_SERVICE_BASE_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/containers/flat_map.h"
 #include "base/gtest_prod_util.h"
@@ -38,15 +39,19 @@
    public:
     virtual ~Observer() = default;
 
+    // Invoked when the list of discovered sinks changes.
+    virtual void OnSinksDiscovered(
+        const std::vector<MediaSinkInternal>& sinks) {}
+
     // Invoked when |sink| is added or updated.
-    virtual void OnSinkAddedOrUpdated(const MediaSinkInternal& sink) = 0;
+    virtual void OnSinkAddedOrUpdated(const MediaSinkInternal& sink) {}
 
     // Invoked when |sink| is removed.
-    virtual void OnSinkRemoved(const MediaSinkInternal& sink) = 0;
+    virtual void OnSinkRemoved(const MediaSinkInternal& sink) {}
   };
 
-  // |callback|: Callback to invoke inform MediaRouter of discovered sinks
-  // updates.
+  // |callback|: Callback to inform the MediaRouter extension of discovered
+  // sinks updates. Other uses should implement Observer::OnSinksDiscovered().
   explicit MediaSinkServiceBase(const OnSinksDiscoveredCallback& callback);
   virtual ~MediaSinkServiceBase();
 
diff --git a/chrome/common/media_router/mojo/media_router.mojom b/chrome/common/media_router/mojo/media_router.mojom
index 405d374..de758c6 100644
--- a/chrome/common/media_router/mojo/media_router.mojom
+++ b/chrome/common/media_router/mojo/media_router.mojom
@@ -395,7 +395,8 @@
   // If the operation was successful, |sent| is true; otherwise it is false.
   SendRouteBinaryMessage(string media_route_id, array<uint8> data);
 
-  // Starts querying for sinks capable of displaying |media_source|.
+  // Starts querying for sinks capable of displaying |media_source|. If
+  // |media_source| is empty, queries for all available sinks.
   StartObservingMediaSinks(string media_source);
 
   // Stops querying sinks for |media_source|.
diff --git a/chrome/test/base/in_process_browser_test_browsertest.cc b/chrome/test/base/in_process_browser_test_browsertest.cc
index 79e3260..83dc373 100644
--- a/chrome/test/base/in_process_browser_test_browsertest.cc
+++ b/chrome/test/base/in_process_browser_test_browsertest.cc
@@ -97,7 +97,9 @@
 
 // On Mac this crashes inside cc::SingleThreadProxy::SetNeedsCommit. See
 // https://ci.chromium.org/b/8923336499994443392
-#if !defined(OS_MACOSX)
+// On ChromeOS this crashes because ProfileIOData and NetworkContext both try
+// to set up NSS on different threads, which it doesn't like.
+#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
 class SingleProcessBrowserTest : public InProcessBrowserTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/test/base/test_browser_window.cc b/chrome/test/base/test_browser_window.cc
index 9320c7dc..568caec 100644
--- a/chrome/test/base/test_browser_window.cc
+++ b/chrome/test/base/test_browser_window.cc
@@ -175,12 +175,6 @@
   return false;
 }
 
-#if !defined(OS_CHROMEOS)
-BadgeServiceDelegate* TestBrowserWindow::GetBadgeServiceDelegate() const {
-  return nullptr;
-}
-#endif
-
 ShowTranslateBubbleResult TestBrowserWindow::ShowTranslateBubble(
     content::WebContents* contents,
     translate::TranslateStep step,
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index 37a129b..e07533a 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -118,8 +118,6 @@
       bool disable_stay_in_chrome,
       IntentPickerResponse callback) override {}
   void SetIntentPickerViewVisibility(bool visible) override {}
-#else  // !defined(OS_CHROMEOS)
-  BadgeServiceDelegate* GetBadgeServiceDelegate() const override;
 #endif
   autofill::SaveCardBubbleView* ShowSaveCreditCardBubble(
       content::WebContents* contents,
diff --git a/chrome/test/chromedriver/test/test_expectations b/chrome/test/chromedriver/test/test_expectations
index 41d864ab..cf2d415 100644
--- a/chrome/test/chromedriver/test/test_expectations
+++ b/chrome/test/chromedriver/test/test_expectations
@@ -41,7 +41,13 @@
 ]
 _OS_NEGATIVE_FILTER['mac'] = [
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2663
-    'WindowTest.testSetsThePositionOfTheCurrentWindow',
+    'WindowTest.canFullscreenTheWindow',
+    'WindowTest.testSetsTheSizeOfTheCurrentWindowFromIframe',
+    'WindowTest.testSetsTheSizeOfTheCurrentWindowFromFrame',
+    'WindowTest.canFullscreenTheWindowFromFrame',
+    'WindowTest.canFullscreenTheWindowFromIframe',
+    'WindowTest.testSetsTheSizeOfTheCurrentWindow',
+    'BasicMouseInterfaceTest.testMovingMouseByRelativeOffset',
 ]
 
 _SPECIFIC_OS_REVISION_NEGATIVE_FILTER = {}
diff --git a/chrome/test/data/extensions/api_test/mime_handler_view/test_page.html b/chrome/test/data/extensions/api_test/mime_handler_view/test_page.html
index 3dbe2d5..ef0a3d2 100644
--- a/chrome/test/data/extensions/api_test/mime_handler_view/test_page.html
+++ b/chrome/test/data/extensions/api_test/mime_handler_view/test_page.html
@@ -7,6 +7,7 @@
     const token = "?next=";
     if (!query.startsWith(token))
       return false;
+    let query_index = query.indexOf(token);
     window.location.href = query.substr(query_index + token.length);
     return true;
   }
diff --git a/chrome/test/data/extensions/no_best_effort_tasks_test_extension/background.js b/chrome/test/data/extensions/no_best_effort_tasks_test_extension/background.js
index ee7ee7d..de7e188 100644
--- a/chrome/test/data/extensions/no_best_effort_tasks_test_extension/background.js
+++ b/chrome/test/data/extensions/no_best_effort_tasks_test_extension/background.js
@@ -8,3 +8,1099 @@
     sendResponse({pong: true});
   }
 });
+
+/*
+What follows is garbage to make the total size of this file more than 64
+kilobytes. This is to test the case where the NetworkService has to schedule
+multiple tasks to feed this file through a mojo data pipe (when the extension
+loads this background.js file resource).
+
+See http://crbug.com/924416 for further details.
+
+
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+| _       _                                                                  |
+|( )  _  ( )                                         _                       |
+|| | ( ) | |   __      _ _    _ __   _     ___ ___  (_)  ___    __           |
+|| | | | | | /'__`\   ( '_`\ ( '__)/'_`\ /' _ ` _ `\| |/',__) /'__`\         |
+|| (_/ \_) |(  ___/   | (_) )| |  ( (_) )| ( ) ( ) || |\__, \(  ___/         |
+|`\___x___/'`\____)   | ,__/'(_)  `\___/'(_) (_) (_)(_)(____/`\____)         |
+|                     | |                                                    |
+|                     (_)                                                    |
+| _                    _                                                     |
+|( )_                 ( )_                                                   |
+|| ,_)   _        ___ | ,_)   _    _ _                                       |
+|| |   /'_`\    /',__)| |   /'_`\ ( '_`\                                     |
+|| |_ ( (_) )   \__, \| |_ ( (_) )| (_) )                                    |
+|`\__)`\___/'   (____/`\__)`\___/'| ,__/'                                    |
+|                                 | |                                        |
+|                                 (_)                                        |
+|                     _                        ___      _        _           |
+|                  _ ( )_  _                  (  _`\   ( )      ( )          |
+| _   _   _  _ __ (_)| ,_)(_)  ___     __     | ( (_)__| |__  __| |__        |
+|( ) ( ) ( )( '__)| || |  | |/' _ `\ /'_ `\   | |  _(__   __)(__   __)       |
+|| \_/ \_/ || |   | || |_ | || ( ) |( (_) |   | (_( )  | |      | |          |
+|`\___x___/'(_)   (_)`\__)(_)(_) (_)`\__  |   (____/'  (_)      (_)          |
+|                                   ( )_) |                                  |
+|                                    \___/'                                  |
+| _       _                _____                                             |
+|(_ )  _ ( )              (___  )                                            |
+| | | (_)| |/')    __         | |   _ _  _   _    _ _                        |
+| | | | || , <   /'__`\    _  | | /'_` )( ) ( ) /'_` )                       |
+| | | | || |\`\ (  ___/   ( )_| |( (_| || \_/ |( (_| | _                     |
+|(___)(_)(_) (_)`\____)   `\___/'`\__,_)`\___/'`\__,_)(_)                    |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+|                                                                            |
+*/
diff --git a/chrome/test/data/pdf/annotations_feature_enabled_test.js b/chrome/test/data/pdf/annotations_feature_enabled_test.js
index 181765b..1413030 100644
--- a/chrome/test/data/pdf/annotations_feature_enabled_test.js
+++ b/chrome/test/data/pdf/annotations_feature_enabled_test.js
@@ -84,17 +84,18 @@
       inkHost.ink_.setAnnotationTool = value => tool = value;
 
       // Pen defaults.
-      document.querySelector('* /deep/ #pen').click();
+      const viewerPdfToolbar = document.querySelector('viewer-pdf-toolbar');
+      const pen = viewerPdfToolbar.$$('#pen');
+      pen.click();
       chrome.test.assertEq('pen', tool.tool);
       chrome.test.assertEq(0.1429, tool.size);
       chrome.test.assertEq('#000000', tool.color);
 
 
       // Selected size and color.
-      document.querySelector(
-          '* /deep/ #pen /deep/ #sizes [value="1"]').click();
-      document.querySelector(
-          '* /deep/ #pen /deep/ #colors [value="#00b0ff"]').click();
+      const penOptions = viewerPdfToolbar.$$('#pen viewer-pen-options');
+      penOptions.$$('#sizes [value="1"]').click();
+      penOptions.$$('#colors [value="#00b0ff"]').click();
       await animationFrame();
       chrome.test.assertEq('pen', tool.tool);
       chrome.test.assertEq(1, tool.size);
@@ -102,38 +103,36 @@
 
 
       // Eraser defaults.
-      document.querySelector('* /deep/ #eraser').click();
+      viewerPdfToolbar.$$('#eraser').click();
       chrome.test.assertEq('eraser', tool.tool);
       chrome.test.assertEq(1, tool.size);
       chrome.test.assertEq(null, tool.color);
 
 
       // Pen keeps previous settings.
-      document.querySelector('* /deep/ #pen').click();
+      pen.click();
       chrome.test.assertEq('pen', tool.tool);
       chrome.test.assertEq(1, tool.size);
       chrome.test.assertEq('#00b0ff', tool.color);
 
 
       // Highlighter defaults.
-      document.querySelector('* /deep/ #highlighter').click();
+      viewerPdfToolbar.$$('#highlighter').click();
       chrome.test.assertEq('highlighter', tool.tool);
       chrome.test.assertEq(0.7143, tool.size);
       chrome.test.assertEq('#ffbc00', tool.color);
 
 
       // Need to expand to use this color.
-      document.querySelector(
-          '* /deep/ #highlighter /deep/ #colors [value="#d1c4e9"]').click();
+      const highlighterOptions =
+          viewerPdfToolbar.$$('#highlighter viewer-pen-options');
+      highlighterOptions.$$('#colors [value="#d1c4e9"]').click();
       chrome.test.assertEq('#ffbc00', tool.color);
 
       // Selected size and expanded color.
-      document.querySelector(
-        '* /deep/ #highlighter /deep/ #sizes [value="1"]').click();
-      document.querySelector(
-        '* /deep/ #highlighter /deep/ #colors #expand').click();
-      document.querySelector(
-        '* /deep/ #highlighter /deep/ #colors [value="#d1c4e9"]').click();
+      highlighterOptions.$$('#sizes [value="1"]').click();
+      highlighterOptions.$$('#colors #expand').click();
+      highlighterOptions.$$('#colors [value="#d1c4e9"]').click();
       chrome.test.assertEq('highlighter', tool.tool);
       chrome.test.assertEq(1, tool.size);
       chrome.test.assertEq('#d1c4e9', tool.color);
diff --git a/chrome/test/data/scroll/overscroll_behavior.html b/chrome/test/data/scroll/overscroll_behavior.html
new file mode 100644
index 0000000..1829fa5
--- /dev/null
+++ b/chrome/test/data/scroll/overscroll_behavior.html
@@ -0,0 +1,18 @@
+<style>
+body {
+  margin: 0px;
+}
+#scroller {
+  overflow: scroll;
+  overscroll-behavior: none;
+  width: 800px;
+  height: 800px;
+}
+#space {
+  height: 800px;
+  width: 2000px;
+}
+</style>
+<div id="scroller">
+  <div id="space"></div>
+</div>
\ No newline at end of file
diff --git a/chromecast/browser/cast_network_contexts.cc b/chromecast/browser/cast_network_contexts.cc
index c2e42eb..e7b0c0ca 100644
--- a/chromecast/browser/cast_network_contexts.cc
+++ b/chromecast/browser/cast_network_contexts.cc
@@ -10,9 +10,15 @@
 
 #include "base/bind.h"
 #include "base/task/post_task.h"
+#include "chromecast/browser/cast_browser_context.h"
+#include "chromecast/browser/cast_browser_process.h"
+#include "chromecast/browser/cast_http_user_agent_settings.h"
 #include "chromecast/browser/url_request_context_factory.h"
+#include "chromecast/common/cast_content_client.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/network_service_instance.h"
+#include "content/public/browser/storage_partition.h"
 #include "services/network/network_context.h"
 #include "services/network/public/cpp/cross_thread_shared_url_loader_factory_info.h"
 #include "services/network/public/cpp/features.h"
@@ -209,11 +215,30 @@
                                         CreateSystemNetworkContextParams());
 }
 
+void CastNetworkContexts::OnLocaleUpdate() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  if (!base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;
+
+  auto accept_language = CastHttpUserAgentSettings::AcceptLanguage();
+
+  GetSystemContext()->SetAcceptLanguage(accept_language);
+
+  auto* browser_context = CastBrowserProcess::GetInstance()->browser_context();
+  content::BrowserContext::GetDefaultStoragePartition(browser_context)
+      ->GetNetworkContext()
+      ->SetAcceptLanguage(accept_language);
+}
+
 network::mojom::NetworkContextParamsPtr
 CastNetworkContexts::CreateDefaultNetworkContextParams() {
   network::mojom::NetworkContextParamsPtr network_context_params =
       network::mojom::NetworkContextParams::New();
 
+  network_context_params->user_agent = GetUserAgent();
+  network_context_params->accept_language =
+      CastHttpUserAgentSettings::AcceptLanguage();
+
   return network_context_params;
 }
 
diff --git a/chromecast/browser/cast_network_contexts.h b/chromecast/browser/cast_network_contexts.h
index de68895..ed3fcba 100644
--- a/chromecast/browser/cast_network_contexts.h
+++ b/chromecast/browser/cast_network_contexts.h
@@ -70,6 +70,9 @@
       bool in_memory,
       const base::FilePath& relative_partition_path);
 
+  // Called when the locale has changed.
+  void OnLocaleUpdate();
+
  private:
   class SystemNetworkContextOwner;
   class URLLoaderFactoryForSystem;
diff --git a/chromecast/cast_shell_sandbox_policy b/chromecast/cast_shell_sandbox_policy
index 2555134..5a31e2bf 100644
--- a/chromecast/cast_shell_sandbox_policy
+++ b/chromecast/cast_shell_sandbox_policy
@@ -9,6 +9,7 @@
       "fuchsia.media.Audio",
       "fuchsia.mediacodec.CodecFactory",
       "fuchsia.net.LegacySocketProvider",
+      "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
       "fuchsia.process.Launcher",
       "fuchsia.sys.Launcher",
diff --git a/chromeos/dbus/update_engine_client.cc b/chromeos/dbus/update_engine_client.cc
index 0bc947d8..d6e05b3 100644
--- a/chromeos/dbus/update_engine_client.cc
+++ b/chromeos/dbus/update_engine_client.cc
@@ -246,7 +246,7 @@
       const std::string& update_version,
       int64_t update_size,
       const UpdateOverCellularOneTimePermissionCallback& callback) override {
-    // TODO(weidongg): Change 'kSetUpdateOverCellularTarget' to
+    // TODO(https://crbug.com/927439): Change 'kSetUpdateOverCellularTarget' to
     // 'kSetUpdateOverCellularOneTimePermission'
     dbus::MethodCall method_call(update_engine::kUpdateEngineInterface,
                                  update_engine::kSetUpdateOverCellularTarget);
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn
index a4b6fa2..b252301f 100644
--- a/chromeos/services/device_sync/BUILD.gn
+++ b/chromeos/services/device_sync/BUILD.gn
@@ -30,8 +30,6 @@
     "cryptauth_enrollment_result.h",
     "cryptauth_enrollment_scheduler.cc",
     "cryptauth_enrollment_scheduler.h",
-    "cryptauth_enrollment_scheduler_impl.cc",
-    "cryptauth_enrollment_scheduler_impl.h",
     "cryptauth_gcm_manager.cc",
     "cryptauth_gcm_manager.h",
     "cryptauth_gcm_manager_impl.cc",
@@ -54,6 +52,8 @@
     "device_sync_type_converters.h",
     "network_request_error.cc",
     "network_request_error.h",
+    "persistent_enrollment_scheduler.cc",
+    "persistent_enrollment_scheduler.h",
     "pref_names.cc",
     "pref_names.h",
     "remote_device_loader.cc",
@@ -155,12 +155,12 @@
     "cryptauth_device_manager_impl_unittest.cc",
     "cryptauth_enroller_impl_unittest.cc",
     "cryptauth_enrollment_manager_impl_unittest.cc",
-    "cryptauth_enrollment_scheduler_impl_unittest.cc",
     "cryptauth_gcm_manager_impl_unittest.cc",
     "cryptauth_key_bundle_unittest.cc",
     "cryptauth_key_registry_impl_unittest.cc",
     "cryptauth_key_unittest.cc",
     "device_sync_service_unittest.cc",
+    "persistent_enrollment_scheduler_unittest.cc",
     "remote_device_loader_unittest.cc",
     "remote_device_provider_impl_unittest.cc",
     "software_feature_manager_impl_unittest.cc",
diff --git a/chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl.h b/chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl.h
deleted file mode 100644
index cff85a2..0000000
--- a/chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl.h
+++ /dev/null
@@ -1,89 +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 CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_ENROLLMENT_SCHEDULER_IMPL_H_
-#define CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_ENROLLMENT_SCHEDULER_IMPL_H_
-
-#include "chromeos/services/device_sync/cryptauth_enrollment_scheduler.h"
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
-
-class PrefRegistrySimple;
-class PrefService;
-
-namespace base {
-class Clock;
-class OneShotTimer;
-}  // namespace base
-
-namespace chromeos {
-
-namespace device_sync {
-
-// Implementation of CryptAuthEnrollmentScheduler.
-class CryptAuthEnrollmentSchedulerImpl : public CryptAuthEnrollmentScheduler {
- public:
-  class Factory {
-   public:
-    static Factory* Get();
-    static void SetFactoryForTesting(Factory* test_factory);
-    virtual std::unique_ptr<CryptAuthEnrollmentScheduler> BuildInstance(
-        Delegate* delegate,
-        PrefService* pref_service,
-        base::Clock* clock,
-        std::unique_ptr<base::OneShotTimer> timer);
-
-   private:
-    static Factory* test_factory_;
-  };
-
-  // Registers the prefs used by this class to the given |registry|.
-  static void RegisterPrefs(PrefRegistrySimple* registry);
-
-  ~CryptAuthEnrollmentSchedulerImpl() override;
-
- private:
-  CryptAuthEnrollmentSchedulerImpl(Delegate* delegate,
-                                   PrefService* pref_service,
-                                   base::Clock* clock,
-                                   std::unique_ptr<base::OneShotTimer> timer);
-
-  // CryptAuthEnrollmentScheduler:
-  void RequestEnrollmentNow() override;
-  void HandleEnrollmentResult(
-      const CryptAuthEnrollmentResult& enrollment_result) override;
-  base::Optional<base::Time> GetLastSuccessfulEnrollmentTime() const override;
-  base::TimeDelta GetRefreshPeriod() const override;
-  base::TimeDelta GetTimeToNextEnrollmentRequest() const override;
-  bool IsWaitingForEnrollmentResult() const override;
-  size_t GetNumConsecutiveFailures() const override;
-
-  // Calculates the time period between the previous enrollment attempt and the
-  // next enrollment attempt, taking failures into consideration.
-  base::TimeDelta CalculateTimeBetweenEnrollmentRequests() const;
-
-  // Starts a new timer that will fire when an enrollment is ready to be
-  // attempted.
-  void ScheduleNextEnrollment();
-
-  // Get the ClientDirective's PolicyReference. If one has not been set, returns
-  // base::nullopt.
-  base::Optional<cryptauthv2::PolicyReference> GetPolicyReference() const;
-
-  PrefService* pref_service_;
-  base::Clock* clock_;
-  std::unique_ptr<base::OneShotTimer> timer_;
-  cryptauthv2::ClientDirective client_directive_;
-
-  DISALLOW_COPY_AND_ASSIGN(CryptAuthEnrollmentSchedulerImpl);
-};
-
-}  // namespace device_sync
-
-}  // namespace chromeos
-
-#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_CRYPTAUTH_ENROLLMENT_SCHEDULER_IMPL_H_
diff --git a/chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl.cc b/chromeos/services/device_sync/persistent_enrollment_scheduler.cc
similarity index 78%
rename from chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl.cc
rename to chromeos/services/device_sync/persistent_enrollment_scheduler.cc
index 1f878b2..dae6082 100644
--- a/chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl.cc
+++ b/chromeos/services/device_sync/persistent_enrollment_scheduler.cc
@@ -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 "chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl.h"
+#include "chromeos/services/device_sync/persistent_enrollment_scheduler.h"
 
 #include <algorithm>
 #include <string>
@@ -12,8 +12,6 @@
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
 #include "base/optional.h"
-#include "base/time/clock.h"
-#include "base/timer/timer.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/services/device_sync/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -107,37 +105,38 @@
 }  // namespace
 
 // static
-CryptAuthEnrollmentSchedulerImpl::Factory*
-    CryptAuthEnrollmentSchedulerImpl::Factory::test_factory_ = nullptr;
+PersistentEnrollmentScheduler::Factory*
+    PersistentEnrollmentScheduler::Factory::test_factory_ = nullptr;
 
 // static
-CryptAuthEnrollmentSchedulerImpl::Factory*
-CryptAuthEnrollmentSchedulerImpl::Factory::Get() {
+PersistentEnrollmentScheduler::Factory*
+PersistentEnrollmentScheduler::Factory::Get() {
   if (test_factory_)
     return test_factory_;
 
-  static base::NoDestructor<CryptAuthEnrollmentSchedulerImpl::Factory> factory;
+  static base::NoDestructor<PersistentEnrollmentScheduler::Factory> factory;
   return factory.get();
 }
 
 // static
-void CryptAuthEnrollmentSchedulerImpl::Factory::SetFactoryForTesting(
+void PersistentEnrollmentScheduler::Factory::SetFactoryForTesting(
     Factory* test_factory) {
   test_factory_ = test_factory;
 }
 
 std::unique_ptr<CryptAuthEnrollmentScheduler>
-CryptAuthEnrollmentSchedulerImpl::Factory::BuildInstance(
+PersistentEnrollmentScheduler::Factory::BuildInstance(
     Delegate* delegate,
     PrefService* pref_service,
     base::Clock* clock,
-    std::unique_ptr<base::OneShotTimer> timer) {
-  return base::WrapUnique(new CryptAuthEnrollmentSchedulerImpl(
-      delegate, pref_service, clock, std::move(timer)));
+    std::unique_ptr<base::OneShotTimer> timer,
+    scoped_refptr<base::TaskRunner> task_runner) {
+  return base::WrapUnique(new PersistentEnrollmentScheduler(
+      delegate, pref_service, clock, std::move(timer), task_runner));
 }
 
 // static
-void CryptAuthEnrollmentSchedulerImpl::RegisterPrefs(
+void PersistentEnrollmentScheduler::RegisterPrefs(
     PrefRegistrySimple* registry) {
   registry->RegisterStringPref(
       prefs::kCryptAuthEnrollmentSchedulerClientDirective, std::string());
@@ -151,16 +150,18 @@
       prefs::kCryptAuthEnrollmentSchedulerNumConsecutiveFailures, 0);
 }
 
-CryptAuthEnrollmentSchedulerImpl::CryptAuthEnrollmentSchedulerImpl(
+PersistentEnrollmentScheduler::PersistentEnrollmentScheduler(
     Delegate* delegate,
     PrefService* pref_service,
     base::Clock* clock,
-    std::unique_ptr<base::OneShotTimer> timer)
+    std::unique_ptr<base::OneShotTimer> timer,
+    scoped_refptr<base::TaskRunner> task_runner)
     : CryptAuthEnrollmentScheduler(delegate),
       pref_service_(pref_service),
       clock_(clock),
       timer_(std::move(timer)),
-      client_directive_(BuildClientDirective(pref_service)) {
+      client_directive_(BuildClientDirective(pref_service)),
+      weak_ptr_factory_(this) {
   DCHECK(pref_service);
   DCHECK(clock);
   DCHECK(IsClientDirectiveValid(client_directive_));
@@ -173,17 +174,23 @@
         prefs::kCryptAuthEnrollmentSchedulerNumConsecutiveFailures, 1);
   }
 
-  ScheduleNextEnrollment();
+  // Schedule the next enrollment as part of a new task. This ensures that the
+  // delegate can complete its initialization before handling an enrollment
+  // request.
+  task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(&PersistentEnrollmentScheduler::ScheduleNextEnrollment,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
-CryptAuthEnrollmentSchedulerImpl::~CryptAuthEnrollmentSchedulerImpl() = default;
+PersistentEnrollmentScheduler::~PersistentEnrollmentScheduler() = default;
 
-void CryptAuthEnrollmentSchedulerImpl::RequestEnrollmentNow() {
+void PersistentEnrollmentScheduler::RequestEnrollmentNow() {
   timer_->Stop();
   NotifyEnrollmentRequested(GetPolicyReference());
 }
 
-void CryptAuthEnrollmentSchedulerImpl::HandleEnrollmentResult(
+void PersistentEnrollmentScheduler::HandleEnrollmentResult(
     const CryptAuthEnrollmentResult& enrollment_result) {
   DCHECK(!timer_->IsRunning());
 
@@ -217,7 +224,7 @@
 }
 
 base::Optional<base::Time>
-CryptAuthEnrollmentSchedulerImpl::GetLastSuccessfulEnrollmentTime() const {
+PersistentEnrollmentScheduler::GetLastSuccessfulEnrollmentTime() const {
   base::Time time = pref_service_->GetTime(
       prefs::kCryptAuthEnrollmentSchedulerLastSuccessfulEnrollmentTime);
   if (time.is_null())
@@ -226,31 +233,30 @@
   return time;
 }
 
-base::TimeDelta CryptAuthEnrollmentSchedulerImpl::GetRefreshPeriod() const {
+base::TimeDelta PersistentEnrollmentScheduler::GetRefreshPeriod() const {
   return base::TimeDelta::FromMilliseconds(
       client_directive_.checkin_delay_millis());
 }
 
-base::TimeDelta
-CryptAuthEnrollmentSchedulerImpl::GetTimeToNextEnrollmentRequest() const {
+base::TimeDelta PersistentEnrollmentScheduler::GetTimeToNextEnrollmentRequest()
+    const {
   if (IsWaitingForEnrollmentResult())
     return base::TimeDelta::FromMilliseconds(0);
 
   return timer_->GetCurrentDelay();
 }
 
-bool CryptAuthEnrollmentSchedulerImpl::IsWaitingForEnrollmentResult() const {
+bool PersistentEnrollmentScheduler::IsWaitingForEnrollmentResult() const {
   return !timer_->IsRunning();
 }
 
-size_t CryptAuthEnrollmentSchedulerImpl::GetNumConsecutiveFailures() const {
+size_t PersistentEnrollmentScheduler::GetNumConsecutiveFailures() const {
   return pref_service_->GetUint64(
       prefs::kCryptAuthEnrollmentSchedulerNumConsecutiveFailures);
 }
 
 base::TimeDelta
-CryptAuthEnrollmentSchedulerImpl::CalculateTimeBetweenEnrollmentRequests()
-    const {
+PersistentEnrollmentScheduler::CalculateTimeBetweenEnrollmentRequests() const {
   size_t num_consecutive_failures = GetNumConsecutiveFailures();
   if (num_consecutive_failures == 0)
     return GetRefreshPeriod();
@@ -262,7 +268,7 @@
       client_directive_.retry_period_millis());
 }
 
-void CryptAuthEnrollmentSchedulerImpl::ScheduleNextEnrollment() {
+void PersistentEnrollmentScheduler::ScheduleNextEnrollment() {
   DCHECK(!timer_->IsRunning());
 
   base::Time last_attempt_time = pref_service_->GetTime(
@@ -279,13 +285,12 @@
 
   timer_->Start(
       FROM_HERE, time_until_next_request,
-      base::BindOnce(
-          &CryptAuthEnrollmentSchedulerImpl::NotifyEnrollmentRequested,
-          base::Unretained(this), GetPolicyReference()));
+      base::BindOnce(&PersistentEnrollmentScheduler::NotifyEnrollmentRequested,
+                     base::Unretained(this), GetPolicyReference()));
 }
 
 base::Optional<cryptauthv2::PolicyReference>
-CryptAuthEnrollmentSchedulerImpl::GetPolicyReference() const {
+PersistentEnrollmentScheduler::GetPolicyReference() const {
   if (client_directive_.has_policy_reference())
     return client_directive_.policy_reference();
 
diff --git a/chromeos/services/device_sync/persistent_enrollment_scheduler.h b/chromeos/services/device_sync/persistent_enrollment_scheduler.h
new file mode 100644
index 0000000..3ea6b36
--- /dev/null
+++ b/chromeos/services/device_sync/persistent_enrollment_scheduler.h
@@ -0,0 +1,98 @@
+// 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 CHROMEOS_SERVICES_DEVICE_SYNC_PERSISTENT_ENROLLMENT_SCHEDULER_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_PERSISTENT_ENROLLMENT_SCHEDULER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/default_clock.h"
+#include "base/timer/timer.h"
+#include "chromeos/services/device_sync/cryptauth_enrollment_scheduler.h"
+#include "chromeos/services/device_sync/proto/cryptauth_directive.pb.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace chromeos {
+
+namespace device_sync {
+
+// CryptAuthEnrollmentScheduler implementation which stores scheduling metadata
+// persistently so that the enrollment schedule is saved across device reboots.
+// If this class is instantiated before at least one enrollment has been
+// completed successfully, it requests enrollment immediately. Once enrollment
+// has been completed successfully, this class schedules the next enrollment
+// attempt at a time provided by the server via a ClientDirective proto.
+class PersistentEnrollmentScheduler : public CryptAuthEnrollmentScheduler {
+ public:
+  class Factory {
+   public:
+    static Factory* Get();
+    static void SetFactoryForTesting(Factory* test_factory);
+    virtual std::unique_ptr<CryptAuthEnrollmentScheduler> BuildInstance(
+        Delegate* delegate,
+        PrefService* pref_service,
+        base::Clock* clock = base::DefaultClock::GetInstance(),
+        std::unique_ptr<base::OneShotTimer> timer =
+            std::make_unique<base::OneShotTimer>(),
+        scoped_refptr<base::TaskRunner> task_runner =
+            base::ThreadTaskRunnerHandle::Get());
+
+   private:
+    static Factory* test_factory_;
+  };
+
+  // Registers the prefs used by this class to the given |registry|.
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+  ~PersistentEnrollmentScheduler() override;
+
+ private:
+  PersistentEnrollmentScheduler(Delegate* delegate,
+                                PrefService* pref_service,
+                                base::Clock* clock,
+                                std::unique_ptr<base::OneShotTimer> timer,
+                                scoped_refptr<base::TaskRunner> task_runner);
+
+  // CryptAuthEnrollmentScheduler:
+  void RequestEnrollmentNow() override;
+  void HandleEnrollmentResult(
+      const CryptAuthEnrollmentResult& enrollment_result) override;
+  base::Optional<base::Time> GetLastSuccessfulEnrollmentTime() const override;
+  base::TimeDelta GetRefreshPeriod() const override;
+  base::TimeDelta GetTimeToNextEnrollmentRequest() const override;
+  bool IsWaitingForEnrollmentResult() const override;
+  size_t GetNumConsecutiveFailures() const override;
+
+  // Calculates the time period between the previous enrollment attempt and the
+  // next enrollment attempt, taking failures into consideration.
+  base::TimeDelta CalculateTimeBetweenEnrollmentRequests() const;
+
+  // Starts a new timer that will fire when an enrollment is ready to be
+  // attempted.
+  void ScheduleNextEnrollment();
+
+  // Get the ClientDirective's PolicyReference. If one has not been set, returns
+  // base::nullopt.
+  base::Optional<cryptauthv2::PolicyReference> GetPolicyReference() const;
+
+  PrefService* pref_service_;
+  base::Clock* clock_;
+  std::unique_ptr<base::OneShotTimer> timer_;
+  cryptauthv2::ClientDirective client_directive_;
+  base::WeakPtrFactory<PersistentEnrollmentScheduler> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PersistentEnrollmentScheduler);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_PERSISTENT_ENROLLMENT_SCHEDULER_H_
diff --git a/chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl_unittest.cc b/chromeos/services/device_sync/persistent_enrollment_scheduler_unittest.cc
similarity index 91%
rename from chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl_unittest.cc
rename to chromeos/services/device_sync/persistent_enrollment_scheduler_unittest.cc
index c81f885..7df7acb 100644
--- a/chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl_unittest.cc
+++ b/chromeos/services/device_sync/persistent_enrollment_scheduler_unittest.cc
@@ -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 "chromeos/services/device_sync/cryptauth_enrollment_scheduler_impl.h"
+#include "chromeos/services/device_sync/persistent_enrollment_scheduler.h"
 
 #include <memory>
 #include <string>
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/simple_test_clock.h"
+#include "base/test/test_simple_task_runner.h"
 #include "base/timer/mock_timer.h"
 #include "chromeos/services/device_sync/pref_names.h"
 #include "components/prefs/testing_pref_service.h"
@@ -93,9 +94,9 @@
 
 }  // namespace
 
-class CryptAuthEnrollmentSchedulerImplTest : public testing::Test {
+class DeviceSyncPersistentEnrollmentSchedulerTest : public testing::Test {
  protected:
-  CryptAuthEnrollmentSchedulerImplTest() {
+  DeviceSyncPersistentEnrollmentSchedulerTest() {
     fake_client_directive_.mutable_policy_reference()->set_name(
         kFakePolicyName);
     fake_client_directive_.mutable_policy_reference()->set_version(
@@ -107,10 +108,10 @@
     fake_client_directive_.set_retry_attempts(kFakeMaxImmediateRetries);
   };
 
-  ~CryptAuthEnrollmentSchedulerImplTest() override = default;
+  ~DeviceSyncPersistentEnrollmentSchedulerTest() override = default;
 
   void SetUp() override {
-    CryptAuthEnrollmentSchedulerImpl::RegisterPrefs(pref_service_.registry());
+    PersistentEnrollmentScheduler::RegisterPrefs(pref_service_.registry());
   }
 
   void CreateScheduler() {
@@ -119,10 +120,14 @@
 
     auto mock_timer = std::make_unique<base::MockOneShotTimer>();
     mock_timer_ = mock_timer.get();
-    scheduler_ =
-        CryptAuthEnrollmentSchedulerImpl::Factory::Get()->BuildInstance(
-            &fake_delegate_, &pref_service_, &test_clock_,
-            std::move(mock_timer));
+
+    auto test_task_runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+
+    scheduler_ = PersistentEnrollmentScheduler::Factory::Get()->BuildInstance(
+        &fake_delegate_, &pref_service_, &test_clock_, std::move(mock_timer),
+        test_task_runner);
+
+    test_task_runner->RunUntilIdle();
   }
 
   FakeDelegate* delegate() { return &fake_delegate_; }
@@ -156,10 +161,11 @@
 
   std::unique_ptr<CryptAuthEnrollmentScheduler> scheduler_;
 
-  DISALLOW_COPY_AND_ASSIGN(CryptAuthEnrollmentSchedulerImplTest);
+  DISALLOW_COPY_AND_ASSIGN(DeviceSyncPersistentEnrollmentSchedulerTest);
 };
 
-TEST_F(CryptAuthEnrollmentSchedulerImplTest, HandleSuccessfulEnrollmentResult) {
+TEST_F(DeviceSyncPersistentEnrollmentSchedulerTest,
+       HandleSuccessfulEnrollmentResult) {
   clock()->SetNow(kFakeTimeNow);
   CreateScheduler();
 
@@ -200,7 +206,7 @@
       2, fake_client_directive().policy_reference());
 }
 
-TEST_F(CryptAuthEnrollmentSchedulerImplTest,
+TEST_F(DeviceSyncPersistentEnrollmentSchedulerTest,
        NotDueForRefresh_RequestImmediateEnrollment) {
   pref_service()->SetString(
       prefs::kCryptAuthEnrollmentSchedulerClientDirective,
@@ -234,7 +240,8 @@
   EXPECT_EQ(1u, delegate()->policy_reference_history().size());
 }
 
-TEST_F(CryptAuthEnrollmentSchedulerImplTest, DueForRefreshBeforeConstructed) {
+TEST_F(DeviceSyncPersistentEnrollmentSchedulerTest,
+       DueForRefreshBeforeConstructed) {
   pref_service()->SetString(
       prefs::kCryptAuthEnrollmentSchedulerClientDirective,
       ClientDirectiveToPrefString(fake_client_directive()));
@@ -255,7 +262,7 @@
             scheduler()->GetTimeToNextEnrollmentRequest());
 }
 
-TEST_F(CryptAuthEnrollmentSchedulerImplTest, HandleFailures) {
+TEST_F(DeviceSyncPersistentEnrollmentSchedulerTest, HandleFailures) {
   clock()->SetNow(kFakeTimeNow);
   CreateScheduler();
 
@@ -325,7 +332,7 @@
   VerifyLastEnrollmentAttemptTimePref(kFakeTimeLaterAfterRetryPeriod);
 }
 
-TEST_F(CryptAuthEnrollmentSchedulerImplTest, HandlePersistedFailures) {
+TEST_F(DeviceSyncPersistentEnrollmentSchedulerTest, HandlePersistedFailures) {
   // Seed the preferences to simulate the previous scheduler using all of its
   // immediate retry attempts and making 10 periodic retry attempts.
   pref_service()->SetString(
diff --git a/chromeos/settings/cros_settings_names.cc b/chromeos/settings/cros_settings_names.cc
index b7ab45f..8eb5d22 100644
--- a/chromeos/settings/cros_settings_names.cc
+++ b/chromeos/settings/cros_settings_names.cc
@@ -268,10 +268,6 @@
 const char kDeviceLoginScreenAppInstallList[] =
     "cros.device.login_screen_app_install_list";
 
-// A string pref storing the url and cryptographic hash of the image in JSON
-// format allowed to set a device-level wallpaper before any user logs in.
-const char kDeviceWallpaperImage[] = "cros.device_wallpaper_image";
-
 // A list pref specifying the locales allowed on the login screen. Currently
 // only the first value is used, as the single locale allowed on the login
 // screen.
diff --git a/chromeos/settings/cros_settings_names.h b/chromeos/settings/cros_settings_names.h
index 583cc24..c9caaf4b 100644
--- a/chromeos/settings/cros_settings_names.h
+++ b/chromeos/settings/cros_settings_names.h
@@ -164,8 +164,6 @@
 COMPONENT_EXPORT(CHROMEOS_SETTINGS)
 extern const char kDeviceLoginScreenAppInstallList[];
 
-COMPONENT_EXPORT(CHROMEOS_SETTINGS) extern const char kDeviceWallpaperImage[];
-
 COMPONENT_EXPORT(CHROMEOS_SETTINGS)
 extern const char kDeviceLoginScreenLocales[];
 COMPONENT_EXPORT(CHROMEOS_SETTINGS)
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index 9e69040..bfbd58c 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -81,6 +81,8 @@
     "details.h",
     "element_area.cc",
     "element_area.h",
+    "features.cc",
+    "features.h",
     "metrics.cc",
     "metrics.h",
     "payment_information.h",
diff --git a/components/autofill_assistant/browser/features.cc b/components/autofill_assistant/browser/features.cc
new file mode 100644
index 0000000..845b393
--- /dev/null
+++ b/components/autofill_assistant/browser/features.cc
@@ -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.
+
+#include "components/autofill_assistant/browser/features.h"
+
+#include "base/feature_list.h"
+
+namespace autofill_assistant {
+namespace features {
+
+const base::Feature kAutofillAssistant{"AutofillAssistant",
+                                       base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Controls whether to query backend service to start any assisted actions.
+const base::Feature kAutofillAssistantChromeEntry{
+    "AutofillAssistantChromeEntry", base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace features
+}  // namespace autofill_assistant
\ No newline at end of file
diff --git a/components/autofill_assistant/browser/features.h b/components/autofill_assistant/browser/features.h
new file mode 100644
index 0000000..eedd49e
--- /dev/null
+++ b/components/autofill_assistant/browser/features.h
@@ -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.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_FEATURES_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_FEATURES_H_
+
+namespace base {
+struct Feature;
+}
+
+namespace autofill_assistant {
+namespace features {
+// All features in alphabetical order.
+extern const base::Feature kAutofillAssistant;
+extern const base::Feature kAutofillAssistantChromeEntry;
+
+}  // namespace features
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_FEATURES_H_
diff --git a/components/crash/android/java/src/org/chromium/components/crash/browser/PackagePaths.java b/components/crash/android/java/src/org/chromium/components/crash/browser/PackagePaths.java
index d43b244..9b621c48 100644
--- a/components/crash/android/java/src/org/chromium/components/crash/browser/PackagePaths.java
+++ b/components/crash/android/java/src/org/chromium/components/crash/browser/PackagePaths.java
@@ -53,13 +53,17 @@
             }
 
             List<String> libPaths = new ArrayList<>(10);
-            libPaths.add(pi.applicationInfo.nativeLibraryDir);
+            File parent = new File(pi.applicationInfo.nativeLibraryDir).getParentFile();
+            if (parent != null) {
+                libPaths.add(new File(parent, arch).getPath());
+            }
             for (String zip : zipPaths) {
                 if (zip.endsWith(".apk")) {
                     libPaths.add(zip + "!/lib/" + arch);
                 }
             }
             libPaths.add(System.getProperty("java.library.path"));
+            libPaths.add(pi.applicationInfo.nativeLibraryDir);
 
             return new String[] {TextUtils.join(File.pathSeparator, zipPaths),
                     TextUtils.join(File.pathSeparator, libPaths)};
diff --git a/components/gwp_asan/client/guarded_page_allocator.cc b/components/gwp_asan/client/guarded_page_allocator.cc
index a50d076..798583809 100644
--- a/components/gwp_asan/client/guarded_page_allocator.cc
+++ b/components/gwp_asan/client/guarded_page_allocator.cc
@@ -17,7 +17,6 @@
 #include "components/crash/core/common/crash_key.h"
 #include "components/gwp_asan/common/allocator_state.h"
 #include "components/gwp_asan/common/crash_key_name.h"
-#include "components/gwp_asan/common/pack_stack_trace.h"
 
 namespace gwp_asan {
 namespace internal {
@@ -175,13 +174,10 @@
   slots_[slot].alloc_size = size;
   slots_[slot].alloc_ptr = reinterpret_cast<uintptr_t>(ptr);
 
-  void* trace[AllocatorState::kMaxStackFrames];
-  size_t len =
-      base::debug::CollectStackTrace(trace, AllocatorState::kMaxStackFrames);
-  slots_[slot].alloc.trace_len = Pack(reinterpret_cast<uintptr_t*>(trace), len,
-                                      slots_[slot].alloc.packed_trace,
-                                      sizeof(slots_[slot].alloc.packed_trace));
   slots_[slot].alloc.tid = base::PlatformThread::CurrentId();
+  slots_[slot].alloc.trace_len = base::debug::CollectStackTrace(
+      reinterpret_cast<void**>(&slots_[slot].alloc.trace),
+      AllocatorState::kMaxStackFrames);
   slots_[slot].alloc.trace_collected = true;
 
   slots_[slot].dealloc.tid = base::kInvalidThreadId;
@@ -191,14 +187,10 @@
 }
 
 void GuardedPageAllocator::RecordDeallocationInSlot(size_t slot) {
-  void* trace[AllocatorState::kMaxStackFrames];
-  size_t len =
-      base::debug::CollectStackTrace(trace, AllocatorState::kMaxStackFrames);
-  slots_[slot].dealloc.trace_len =
-      Pack(reinterpret_cast<uintptr_t*>(trace), len,
-           slots_[slot].dealloc.packed_trace,
-           sizeof(slots_[slot].dealloc.packed_trace));
   slots_[slot].dealloc.tid = base::PlatformThread::CurrentId();
+  slots_[slot].dealloc.trace_len = base::debug::CollectStackTrace(
+      reinterpret_cast<void**>(&slots_[slot].dealloc.trace),
+      AllocatorState::kMaxStackFrames);
   slots_[slot].dealloc.trace_collected = true;
 }
 
diff --git a/components/gwp_asan/common/allocator_state.cc b/components/gwp_asan/common/allocator_state.cc
index 8aa839f..ccd2a7d 100644
--- a/components/gwp_asan/common/allocator_state.cc
+++ b/components/gwp_asan/common/allocator_state.cc
@@ -15,7 +15,6 @@
 // TODO: Delete out-of-line constexpr defininitons once C++17 is in use.
 constexpr size_t AllocatorState::kGpaMaxPages;
 constexpr size_t AllocatorState::kMaxStackFrames;
-constexpr size_t AllocatorState::kMaxPackedTraceLength;
 
 AllocatorState::AllocatorState() {}
 
diff --git a/components/gwp_asan/common/allocator_state.h b/components/gwp_asan/common/allocator_state.h
index 2f1f88b..d393749 100644
--- a/components/gwp_asan/common/allocator_state.h
+++ b/components/gwp_asan/common/allocator_state.h
@@ -40,9 +40,6 @@
   static constexpr size_t kGpaMaxPages = 256;
   // Maximum number of stack trace frames to collect.
   static constexpr size_t kMaxStackFrames = 60;
-  // Number of bytes to allocate for packed stack traces. This can hold
-  // approximately kMaxStackFrames under normal conditions.
-  static constexpr size_t kMaxPackedTraceLength = 200;
 
   enum class ErrorType {
     kUseAfterFree = 0,
@@ -68,9 +65,9 @@
       // (De)allocation thread id or base::kInvalidThreadId if no (de)allocation
       // occurred.
       base::PlatformThreadId tid = base::kInvalidThreadId;
-      // Packed stack trace.
-      uint8_t packed_trace[kMaxPackedTraceLength];
-      // Length used to encode the packed stack trace.
+      // Stack trace contents.
+      uintptr_t trace[kMaxStackFrames];
+      // Stack trace length.
       size_t trace_len = 0;
       // Whether a stack trace has been collected for this (de)allocation.
       bool trace_collected = false;
diff --git a/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc b/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc
index f964385..e2a1da8 100644
--- a/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc
+++ b/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc
@@ -6,6 +6,7 @@
 
 #include <string.h>
 #include <algorithm>
+#include <memory>
 
 // Tests that whatever we give to Pack() is the same as what comes out of
 // Unpack().
@@ -24,16 +25,16 @@
   // We don't need a buffer large than Size*10 as the longest variable length
   // encoding of a 64-bit integer is 10 bytes long.)
   size_t array_size = std::min(Size * 10, packed_max_size);
-  uint8_t packed[array_size];
+  std::unique_ptr<uint8_t[]> packed(new uint8_t[array_size]);
   size_t packed_size =
       gwp_asan::internal::Pack(reinterpret_cast<const uintptr_t*>(Data),
-                               entries, packed, packed_max_size);
-  if (packed_size > sizeof(packed))
+                               entries, packed.get(), packed_max_size);
+  if (packed_size > array_size)
     __builtin_trap();
 
-  uintptr_t unpacked[std::min(unpacked_max_size, Size)];
+  uintptr_t unpacked[std::min(unpacked_max_size, entries)];
   size_t unpacked_size = gwp_asan::internal::Unpack(
-      packed, packed_size, unpacked, unpacked_max_size);
+      packed.get(), packed_size, unpacked, unpacked_max_size);
   // We can only be sure there was enough room to pack the entire input when
   // packed_max_size was larger than Size*10.
   if (packed_max_size > array_size &&
diff --git a/components/gwp_asan/crash_handler/crash_analyzer.cc b/components/gwp_asan/crash_handler/crash_analyzer.cc
index 95f1223..04e70d1 100644
--- a/components/gwp_asan/crash_handler/crash_analyzer.cc
+++ b/components/gwp_asan/crash_handler/crash_analyzer.cc
@@ -14,7 +14,6 @@
 #include "build/build_config.h"
 #include "components/gwp_asan/common/allocator_state.h"
 #include "components/gwp_asan/common/crash_key_name.h"
-#include "components/gwp_asan/common/pack_stack_trace.h"
 #include "components/gwp_asan/crash_handler/crash.pb.h"
 #include "third_party/crashpad/crashpad/client/annotation.h"
 #include "third_party/crashpad/crashpad/snapshot/cpu_context.h"
@@ -159,21 +158,12 @@
     return;
   }
 
-  uintptr_t unpacked_stack_trace[AllocatorState::kMaxPackedTraceLength];
-  size_t unpacked_len =
-      Unpack(slot_info.packed_trace, slot_info.trace_len, unpacked_stack_trace,
-             AllocatorState::kMaxPackedTraceLength);
-  if (!unpacked_len) {
-    DLOG(ERROR) << "Failed to unpack stack trace.";
-    return;
-  }
-
-  // On 32-bit platforms we can't copy directly into
+  // On 32-bit platforms we can't copy directly to
   // proto_info->mutable_stack_trace()->mutable_data().
-  proto_info->mutable_stack_trace()->Resize(unpacked_len, 0);
+  proto_info->mutable_stack_trace()->Resize(slot_info.trace_len, 0);
   uint64_t* output = proto_info->mutable_stack_trace()->mutable_data();
-  for (size_t i = 0; i < unpacked_len; i++)
-    output[i] = unpacked_stack_trace[i];
+  for (size_t i = 0; i < slot_info.trace_len; i++)
+    output[i] = slot_info.trace[i];
 }
 
 }  // namespace internal
diff --git a/components/heap_profiling/heap_profiling_test_shim.cc b/components/heap_profiling/heap_profiling_test_shim.cc
index ca6efeb9..bae0523 100644
--- a/components/heap_profiling/heap_profiling_test_shim.cc
+++ b/components/heap_profiling/heap_profiling_test_shim.cc
@@ -19,8 +19,7 @@
 }
 
 HeapProfilingTestShim::HeapProfilingTestShim(JNIEnv* env, jobject obj) {}
-
-HeapProfilingTestShim::~HeapProfilingTestShim() {}
+HeapProfilingTestShim::~HeapProfilingTestShim() = default;
 
 void HeapProfilingTestShim::Destroy(JNIEnv* env,
                                     const JavaParamRef<jobject>& obj) {
@@ -33,6 +32,7 @@
     const base::android::JavaParamRef<jstring>& mode,
     jboolean dynamically_start_profiling,
     const base::android::JavaParamRef<jstring>& stack_mode,
+    jboolean stream_samples,
     jboolean should_sample,
     jboolean sample_everything) {
   heap_profiling::TestDriver driver;
@@ -42,6 +42,7 @@
   options.stack_mode = heap_profiling::ConvertStringToStackMode(
       base::android::ConvertJavaStringToUTF8(stack_mode));
   options.profiling_already_started = !dynamically_start_profiling;
+  options.stream_samples = stream_samples;
   options.should_sample = should_sample;
   options.sample_everything = sample_everything;
   return driver.RunTest(options);
diff --git a/components/heap_profiling/heap_profiling_test_shim.h b/components/heap_profiling/heap_profiling_test_shim.h
index 134d548..558933c 100644
--- a/components/heap_profiling/heap_profiling_test_shim.h
+++ b/components/heap_profiling/heap_profiling_test_shim.h
@@ -23,6 +23,7 @@
       const base::android::JavaParamRef<jstring>& mode,
       jboolean dynamically_start_profiling,
       const base::android::JavaParamRef<jstring>& stack_mode,
+      jboolean stream_samples,
       jboolean should_sample,
       jboolean sample_everything);
 
diff --git a/components/heap_profiling/javatests/src/org/chromium/components/heap_profiling/HeapProfilingTestShim.java b/components/heap_profiling/javatests/src/org/chromium/components/heap_profiling/HeapProfilingTestShim.java
index f55ceaf8..c7f788e 100644
--- a/components/heap_profiling/javatests/src/org/chromium/components/heap_profiling/HeapProfilingTestShim.java
+++ b/components/heap_profiling/javatests/src/org/chromium/components/heap_profiling/HeapProfilingTestShim.java
@@ -23,9 +23,9 @@
      *  rather than native stacks.
      */
     public boolean runTestForMode(String mode, boolean dynamicallyStartProfiling, String stackMode,
-            boolean shouldSample, boolean sampleEverything) {
+            boolean streamSamples, boolean shouldSample, boolean sampleEverything) {
         return nativeRunTestForMode(mNativeHeapProfilingTestShim, mode, dynamicallyStartProfiling,
-                stackMode, shouldSample, sampleEverything);
+                stackMode, streamSamples, shouldSample, sampleEverything);
     }
 
     /**
@@ -43,6 +43,6 @@
     private native long nativeInit();
     private native void nativeDestroy(long nativeHeapProfilingTestShim);
     private native boolean nativeRunTestForMode(long nativeHeapProfilingTestShim, String mode,
-            boolean dynamicallyStartProfiling, String stackMode, boolean shouldSample,
-            boolean sampleEverything);
+            boolean dynamicallyStartProfiling, String stackMode, boolean streamSamples,
+            boolean shouldSample, boolean sampleEverything);
 }
diff --git a/components/heap_profiling/supervisor.cc b/components/heap_profiling/supervisor.cc
index 70afb5f..987902b3 100644
--- a/components/heap_profiling/supervisor.cc
+++ b/components/heap_profiling/supervisor.cc
@@ -60,24 +60,27 @@
 
 void Supervisor::Start(content::ServiceManagerConnection* connection,
                        base::OnceClosure closure) {
+  // TODO(alph): Obtain stream_samples from the command line / Finch.
   Start(connection, GetModeForStartup(), GetStackModeForStartup(),
-        GetSamplingRateForStartup(), std::move(closure));
+        true /* stream_samples */, GetSamplingRateForStartup(),
+        std::move(closure));
 }
 
 void Supervisor::Start(content::ServiceManagerConnection* connection,
                        Mode mode,
                        mojom::StackMode stack_mode,
+                       bool stream_samples,
                        uint32_t sampling_rate,
                        base::OnceClosure closure) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   DCHECK(!started_);
 
   base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})
-      ->PostTask(FROM_HERE,
-                 base::BindOnce(&Supervisor::StartServiceOnIOThread,
-                                base::Unretained(this),
-                                connection->GetConnector()->Clone(), mode,
-                                stack_mode, sampling_rate, std::move(closure)));
+      ->PostTask(FROM_HERE, base::BindOnce(&Supervisor::StartServiceOnIOThread,
+                                           base::Unretained(this),
+                                           connection->GetConnector()->Clone(),
+                                           mode, stack_mode, stream_samples,
+                                           sampling_rate, std::move(closure)));
 }
 
 Mode Supervisor::GetMode() {
@@ -180,12 +183,13 @@
     std::unique_ptr<service_manager::Connector> connector,
     Mode mode,
     mojom::StackMode stack_mode,
+    bool stream_samples,
     uint32_t sampling_rate,
     base::OnceClosure closure) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
 
-  controller_.reset(
-      new Controller(std::move(connector), stack_mode, sampling_rate));
+  controller_.reset(new Controller(std::move(connector), stack_mode,
+                                   stream_samples, sampling_rate));
   base::WeakPtr<Controller> controller_weak_ptr = controller_->GetWeakPtr();
 
   base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI})
diff --git a/components/heap_profiling/supervisor.h b/components/heap_profiling/supervisor.h
index b4b8515..592b72e 100644
--- a/components/heap_profiling/supervisor.h
+++ b/components/heap_profiling/supervisor.h
@@ -72,6 +72,7 @@
   void Start(content::ServiceManagerConnection* connection,
              Mode mode,
              mojom::StackMode stack_mode,
+             bool stream_samples,
              uint32_t sampling_rate,
              base::OnceClosure callback);
 
@@ -116,6 +117,7 @@
       std::unique_ptr<service_manager::Connector> connector,
       Mode mode,
       mojom::StackMode stack_mode,
+      bool stream_samples,
       uint32_t sampling_rate,
       base::OnceClosure callback);
 
diff --git a/components/heap_profiling/test_driver.cc b/components/heap_profiling/test_driver.cc
index 7a6decc..6799497c 100644
--- a/components/heap_profiling/test_driver.cc
+++ b/components/heap_profiling/test_driver.cc
@@ -559,7 +559,7 @@
                           base::WaitableEvent::InitialState::NOT_SIGNALED) {
   partition_allocator_.init();
 }
-TestDriver::~TestDriver() {}
+TestDriver::~TestDriver() = default;
 
 bool TestDriver::RunTest(const Options& options) {
   options_ = options;
@@ -723,8 +723,8 @@
                                ? (options_.sample_everything ? 2 : kSampleRate)
                                : 1;
   Supervisor::GetInstance()->Start(connection, options_.mode,
-                                   options_.stack_mode, sampling_rate,
-                                   std::move(start_callback));
+                                   options_.stack_mode, options_.stream_samples,
+                                   sampling_rate, std::move(start_callback));
 
   return true;
 }
@@ -776,8 +776,8 @@
                                ? (options_.sample_everything ? 2 : kSampleRate)
                                : 1;
   Supervisor::GetInstance()->Start(connection, options_.mode,
-                                   options_.stack_mode, sampling_rate,
-                                   std::move(start_callback));
+                                   options_.stack_mode, options_.stream_samples,
+                                   sampling_rate, std::move(start_callback));
 
   run_loop->Run();
 
diff --git a/components/heap_profiling/test_driver.h b/components/heap_profiling/test_driver.h
index f897c672..03e447b 100644
--- a/components/heap_profiling/test_driver.h
+++ b/components/heap_profiling/test_driver.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/synchronization/waitable_event.h"
+#include "components/services/heap_profiling/public/cpp/settings.h"
 #include "components/services/heap_profiling/public/mojom/heap_profiling_client.mojom.h"
 
 namespace base {
@@ -19,8 +20,6 @@
 
 namespace heap_profiling {
 
-enum class Mode;
-
 // This class runs tests for the Heap Profiling Service, a cross-platform,
 // multi-process component.
 //
@@ -45,23 +44,28 @@
  public:
   struct Options {
     // The profiling mode to test.
-    Mode mode;
+    Mode mode = Mode::kBrowser;
 
     // The stack profiling mode to test.
-    mojom::StackMode stack_mode;
+    mojom::StackMode stack_mode = mojom::StackMode::NATIVE_WITHOUT_THREAD_NAMES;
+
+    // Whether the client should stream samples as they are collected through
+    // the provided pipe. When false the samples are accumulated on the client
+    // side and can be retrieved later.
+    bool stream_samples = true;
 
     // Whether the caller has already started profiling with the given mode.
     // When false, the test driver is responsible for starting profiling.
-    bool profiling_already_started;
+    bool profiling_already_started = false;
 
     // Whether to test sampling.
-    bool should_sample;
+    bool should_sample = true;
 
     // When set to true, the internal sampling_rate is set to 2. While this
     // doesn't record all allocations, it should record all test allocations
     // made in this file with exponentially high probability.
     // When set to false, the internal sampling rate is set to 10000.
-    bool sample_everything;
+    bool sample_everything = false;
   };
 
   TestDriver();
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index b3ff4f5..7a2f65a6 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -134,7 +134,7 @@
 
 // Feature used to enable various experiments on keyword mode, UI and
 // suggestions.
-const base::Feature kExperimentalKeywordMode{"ExperimentalKeywordMode",
+const base::Feature kExperimentalKeywordMode{"OmniboxExperimentalKeywordMode",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Feature used to enable Pedal suggestions as either in-suggestion side button
diff --git a/components/omnibox/browser/vector_icons/drive_sheets.icon b/components/omnibox/browser/vector_icons/drive_sheets.icon
index 64e369d..4ca6f953 100644
--- a/components/omnibox/browser/vector_icons/drive_sheets.icon
+++ b/components/omnibox/browser/vector_icons/drive_sheets.icon
@@ -2,29 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-CANVAS_DIMENSIONS, 18,
+CANVAS_DIMENSIONS, 16,
 PATH_COLOR_ARGB, 0xFF, 0x0F, 0x9D, 0x58,
-MOVE_TO, 16, 0,
+MOVE_TO, 14, 0,
 H_LINE_TO, 2,
-CUBIC_TO, 0.9f, 0, 0.01f, 0.9f, 0.01f, 2,
-LINE_TO, 0, 5,
-R_V_LINE_TO, 11,
+CUBIC_TO, 0.9f, 0, 0, 0.9f, 0, 2,
+R_V_LINE_TO, 12,
 R_CUBIC_TO, 0, 1.1f, 0.9f, 2, 2, 2,
-R_H_LINE_TO, 14,
+R_H_LINE_TO, 12,
 R_CUBIC_TO, 1.1f, 0, 2, -0.9f, 2, -2,
 V_LINE_TO, 2,
-CUBIC_TO, 18, 0.9f, 17.1f, 0, 16, 0,
+R_CUBIC_TO, 0, -1.1f, -0.9f, -2, -2, -2,
 CLOSE,
-R_MOVE_TO, 0, 8,
-H_LINE_TO, 8,
-R_V_LINE_TO, 8,
-H_LINE_TO, 6,
-V_LINE_TO, 8,
+// The white cross is drawn in one horizontal and two vertical sections to
+// avoid inverting the color in the intersection.
+MOVE_TO, 2, 5,
+H_LINE_TO, 14,
+R_V_LINE_TO, 2,
 H_LINE_TO, 2,
-V_LINE_TO, 6,
-H_LINE_TO, 6,
-V_LINE_TO, 2,
+
+MOVE_TO, 5, 2,
 R_H_LINE_TO, 2,
-R_V_LINE_TO, 4,
-R_H_LINE_TO, 8,
+V_LINE_TO, 5,
+R_H_LINE_TO, -2,
+
+MOVE_TO, 5, 7,
+R_H_LINE_TO, 2,
+V_LINE_TO, 14,
+R_H_LINE_TO, -2,
 CLOSE
diff --git a/components/omnibox/browser/vector_icons/drive_slides.icon b/components/omnibox/browser/vector_icons/drive_slides.icon
index 24c6176..4ee7ffec 100644
--- a/components/omnibox/browser/vector_icons/drive_slides.icon
+++ b/components/omnibox/browser/vector_icons/drive_slides.icon
@@ -2,20 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-CANVAS_DIMENSIONS, 18,
+CANVAS_DIMENSIONS, 16,
 PATH_COLOR_ARGB, 0xFF, 0xFC, 0xB7, 0x00,
-MOVE_TO, 16, 0,
-R_H_LINE_TO, -14,
+MOVE_TO, 14, 0,
+H_LINE_TO, 2,
 CUBIC_TO, 0.9f, 0, 0, 0.9f, 0, 2,
-R_V_LINE_TO, 14,
+R_V_LINE_TO, 12,
 R_CUBIC_TO, 0, 1.1f, 0.9f, 2, 2, 2,
-R_H_LINE_TO, 14,
+R_H_LINE_TO, 12,
 R_CUBIC_TO, 1.1f, 0, 2, -0.9f, 2, -2,
 V_LINE_TO, 2,
 R_CUBIC_TO, 0, -1.1f, -0.9f, -2, -2, -2,
 CLOSE,
-R_MOVE_TO, 0, 13,
-R_H_LINE_TO, -14,
-V_LINE_TO, 5,
-R_H_LINE_TO, 14,
+// Padding for the white 'content' area is {4, 2, 4, 2}
+MOVE_TO, 2, 4,
+H_LINE_TO, 14,
+V_LINE_TO, 12,
+H_LINE_TO, 2,
 CLOSE
diff --git a/components/payments/content/content_payment_request_delegate.h b/components/payments/content/content_payment_request_delegate.h
index efa368a..ac5c967 100644
--- a/components/payments/content/content_payment_request_delegate.h
+++ b/components/payments/content/content_payment_request_delegate.h
@@ -36,6 +36,10 @@
   virtual void EmbedPaymentHandlerWindow(
       const GURL& url,
       PaymentHandlerOpenWindowCallback callback) = 0;
+
+  // Returns whether user interaction is enabled. (False when showing a
+  // spinner.)
+  virtual bool IsInteractive() const = 0;
 };
 
 }  // namespace payments
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index 967a875e..6b853dc 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -456,6 +456,13 @@
     return;
   }
 
+  // If currently interactive, show the processing spinner. Autofill payment
+  // instruments request a CVC, so they are always interactive at this point. A
+  // payment handler may elect to be non-interactive by not showing a
+  // confirmation page to the user.
+  if (delegate_->IsInteractive())
+    delegate_->ShowProcessingSpinner();
+
   client_->OnPaymentResponse(std::move(response));
 }
 
diff --git a/components/payments/content/test_content_payment_request_delegate.cc b/components/payments/content/test_content_payment_request_delegate.cc
index 2b76cd4..aaeabd5 100644
--- a/components/payments/content/test_content_payment_request_delegate.cc
+++ b/components/payments/content/test_content_payment_request_delegate.cc
@@ -103,6 +103,10 @@
     const GURL& url,
     PaymentHandlerOpenWindowCallback callback) {}
 
+bool TestContentPaymentRequestDelegate::IsInteractive() const {
+  return true;
+}
+
 autofill::TestAddressNormalizer*
 TestContentPaymentRequestDelegate::test_address_normalizer() {
   return core_delegate_.test_address_normalizer();
diff --git a/components/payments/content/test_content_payment_request_delegate.h b/components/payments/content/test_content_payment_request_delegate.h
index dae7dbc..10ee9d93 100644
--- a/components/payments/content/test_content_payment_request_delegate.h
+++ b/components/payments/content/test_content_payment_request_delegate.h
@@ -48,6 +48,7 @@
   void EmbedPaymentHandlerWindow(
       const GURL& url,
       PaymentHandlerOpenWindowCallback callback) override;
+  bool IsInteractive() const override;
 
   autofill::TestAddressNormalizer* test_address_normalizer();
   void DelayFullCardRequestCompletion();
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 74f903b4..28f9fb93 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -7545,6 +7545,8 @@
       'tags': [],
       'desc': '''The Safe Browsing service shows a warning page when users navigate to sites that are flagged as potentially malicious. Enabling this setting prevents users from proceeding anyway from the warning page to the malicious site.
 
+      This policy only prevents users from proceeding on Safe Browsing warnings (e.g. malware and phishing) not for SSL certificate related issues like invalid or expired certificates.
+
       If this setting is disabled or not configured then users can choose to proceed to the flagged site after being shown the warning.
 
       See https://developers.google.com/safe-browsing for more info on Safe Browsing.''',
diff --git a/components/services/heap_profiling/connection_manager.cc b/components/services/heap_profiling/connection_manager.cc
index 2a115e1..9fee87a 100644
--- a/components/services/heap_profiling/connection_manager.cc
+++ b/components/services/heap_profiling/connection_manager.cc
@@ -5,6 +5,7 @@
 #include "components/services/heap_profiling/connection_manager.h"
 
 #include "base/bind.h"
+#include "base/json/string_escape.h"
 #include "base/message_loop/message_loop.h"
 #include "base/message_loop/message_loop_current.h"
 #include "base/metrics/histogram_macros.h"
@@ -74,13 +75,15 @@
              scoped_refptr<ReceiverPipe> p,
              mojom::ProcessType process_type,
              uint32_t sampling_rate,
-             mojom::StackMode stack_mode)
+             mojom::StackMode stack_mode,
+             bool stream_samples)
       : thread(base::StringPrintf("Sender %lld thread",
                                   static_cast<long long>(pid))),
         client(std::move(client)),
         pipe(p),
         process_type(process_type),
         stack_mode(stack_mode),
+        stream_samples(stream_samples),
         tracker(std::move(complete_cb), backtrace_storage),
         sampling_rate(sampling_rate) {}
 
@@ -103,6 +106,7 @@
   scoped_refptr<StreamParser> parser;
   mojom::ProcessType process_type;
   mojom::StackMode stack_mode;
+  bool stream_samples;
 
   // Danger: This lives on the |thread| member above. The connection manager
   // lives on the I/O thread, so accesses to the variable must be synchronized.
@@ -159,7 +163,8 @@
 
   auto connection = std::make_unique<Connection>(
       std::move(complete_cb), &backtrace_storage_, pid, std::move(client),
-      new_pipe, process_type, params->sampling_rate, params->stack_mode);
+      new_pipe, process_type, params->sampling_rate, params->stack_mode,
+      params->stream_samples);
 
   base::Thread::Options options;
   options.message_loop_type = base::MessageLoop::TYPE_IO;
@@ -254,6 +259,13 @@
   for (auto& it : connections_) {
     base::ProcessId pid = it.first;
     Connection* connection = it.second.get();
+    if (!connection->stream_samples) {
+      connection->client->RetrieveHeapProfile(base::BindOnce(
+          &ConnectionManager::HeapProfileRetrieved, weak_factory_.GetWeakPtr(),
+          tracking, pid, connection->process_type, keep_small_allocations,
+          strip_path_from_mapped_files, connection->sampling_rate));
+      continue;
+    }
     int barrier_id = next_barrier_id_++;
 
     // Register for callback before requesting the dump so we don't race for the
@@ -270,6 +282,66 @@
   }
 }
 
+void ConnectionManager::HeapProfileRetrieved(
+    scoped_refptr<DumpProcessesForTracingTracking> tracking,
+    base::ProcessId pid,
+    mojom::ProcessType process_type,
+    bool keep_small_allocations,
+    bool strip_path_from_mapped_files,
+    uint32_t sampling_rate,
+    mojom::HeapProfilePtr profile) {
+  AllocationCountMap counts;
+  AllocationTracker::ContextMap context_map;
+  AllocationTracker::AddressToStringMap string_map;
+  BacktraceStorage backtrace_storage;
+  BacktraceStorage::Lock backtrace_storage_lock(&backtrace_storage);
+
+  bool success = true;
+  for (const mojom::HeapProfileSamplePtr& sample : profile->samples) {
+    int context_id = 0;
+    if (sample->context_id) {
+      auto it = profile->strings.find(sample->context_id);
+      if (it == profile->strings.end()) {
+        success = false;
+        break;
+      }
+      const std::string& context = it->second;
+      // Escape the strings early, to simplify exporting a heap dump.
+      std::string escaped_context;
+      base::EscapeJSONString(context, false /* put_in_quotes */,
+                             &escaped_context);
+      context_id = context_map
+                       .emplace(std::move(escaped_context),
+                                static_cast<int>(context_map.size() + 1))
+                       .first->second;
+    }
+    const Backtrace* backtrace = backtrace_storage.Insert(
+        std::vector<Address>(sample->stack.begin(), sample->stack.end()));
+    AllocatorType allocator = static_cast<AllocatorType>(sample->allocator);
+    if (allocator >= AllocatorType::kCount) {
+      success = false;
+      break;
+    }
+    AllocationEvent alloc(allocator, Address(0), sample->size, backtrace,
+                          context_id);
+    ++counts[alloc];
+  }
+
+  for (const auto& str : profile->strings) {
+    std::string quoted_string;
+    // Escape the strings before saving them, to simplify exporting a heap dump.
+    base::EscapeJSONString(str.second, false /* put_in_quotes */,
+                           &quoted_string);
+    string_map.emplace(str.first, std::move(quoted_string));
+  }
+
+  DCHECK(success);
+  DoDumpOneProcessForTracing(
+      tracking, pid, process_type, keep_small_allocations,
+      strip_path_from_mapped_files, sampling_rate, success, std::move(counts),
+      std::move(context_map), std::move(string_map));
+}
+
 void ConnectionManager::DoDumpOneProcessForTracing(
     scoped_refptr<DumpProcessesForTracingTracking> tracking,
     base::ProcessId pid,
diff --git a/components/services/heap_profiling/connection_manager.h b/components/services/heap_profiling/connection_manager.h
index 6c746b3..6571f9f 100644
--- a/components/services/heap_profiling/connection_manager.h
+++ b/components/services/heap_profiling/connection_manager.h
@@ -88,6 +88,15 @@
   struct Connection;
   struct DumpProcessesForTracingTracking;
 
+  void HeapProfileRetrieved(
+      scoped_refptr<DumpProcessesForTracingTracking> tracking,
+      base::ProcessId pid,
+      mojom::ProcessType process_type,
+      bool keep_small_allocations,
+      bool strip_path_from_mapped_files,
+      uint32_t sampling_rate,
+      mojom::HeapProfilePtr profile);
+
   void DoDumpOneProcessForTracing(
       scoped_refptr<DumpProcessesForTracingTracking> tracking,
       base::ProcessId pid,
diff --git a/components/services/heap_profiling/public/cpp/client.cc b/components/services/heap_profiling/public/cpp/client.cc
index 020ef5e..39cd6c1d 100644
--- a/components/services/heap_profiling/public/cpp/client.cc
+++ b/components/services/heap_profiling/public/cpp/client.cc
@@ -128,6 +128,10 @@
   SamplingProfilerWrapper::FlushPipe(barrier_id);
 }
 
+void Client::RetrieveHeapProfile(RetrieveHeapProfileCallback callback) {
+  std::move(callback).Run(sampling_profiler_->RetrieveHeapProfile());
+}
+
 void Client::StartProfilingInternal(mojom::ProfilingParamsPtr params) {
   sampling_profiler_->StartProfiling(sender_pipe_.get(), std::move(params));
 }
diff --git a/components/services/heap_profiling/public/cpp/client.h b/components/services/heap_profiling/public/cpp/client.h
index e06103c7..23abac2 100644
--- a/components/services/heap_profiling/public/cpp/client.h
+++ b/components/services/heap_profiling/public/cpp/client.h
@@ -27,6 +27,7 @@
   // mojom::ProfilingClient overrides:
   void StartProfiling(mojom::ProfilingParamsPtr params) override;
   void FlushMemlogPipe(uint32_t barrier_id) override;
+  void RetrieveHeapProfile(RetrieveHeapProfileCallback callback) override;
 
   void BindToInterface(mojom::ProfilingClientRequest request);
 
diff --git a/components/services/heap_profiling/public/cpp/controller.cc b/components/services/heap_profiling/public/cpp/controller.cc
index afb48330..8ab69c5 100644
--- a/components/services/heap_profiling/public/cpp/controller.cc
+++ b/components/services/heap_profiling/public/cpp/controller.cc
@@ -16,10 +16,12 @@
 
 Controller::Controller(std::unique_ptr<service_manager::Connector> connector,
                        mojom::StackMode stack_mode,
+                       bool stream_samples,
                        uint32_t sampling_rate)
     : connector_(std::move(connector)),
       sampling_rate_(sampling_rate),
       stack_mode_(stack_mode),
+      stream_samples_(stream_samples),
       weak_factory_(this) {
   DCHECK_NE(sampling_rate, 0u);
 
@@ -50,6 +52,7 @@
 
   mojom::ProfilingParamsPtr params = mojom::ProfilingParams::New();
   params->sampling_rate = sampling_rate_;
+  params->stream_samples = stream_samples_;
   params->sender_pipe = mojo::WrapPlatformHandle(pipes.PassSender());
   params->stack_mode = stack_mode_;
   heap_profiling_service_->AddProfilingClient(
diff --git a/components/services/heap_profiling/public/cpp/controller.h b/components/services/heap_profiling/public/cpp/controller.h
index ab077b0d..969f89e 100644
--- a/components/services/heap_profiling/public/cpp/controller.h
+++ b/components/services/heap_profiling/public/cpp/controller.h
@@ -42,6 +42,7 @@
   // named |sampling_interval|.
   Controller(std::unique_ptr<service_manager::Connector> connector,
              mojom::StackMode stack_mode,
+             bool stream_samples,
              uint32_t sampling_rate);
   ~Controller();
 
@@ -71,6 +72,7 @@
   // The same sampling rate and stack mode is used for each client.
   const uint32_t sampling_rate_ = 1;
   const mojom::StackMode stack_mode_;
+  const bool stream_samples_;
 
   SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<Controller> weak_factory_;
diff --git a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc
index b97ec3c5..05b88f1 100644
--- a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc
+++ b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc
@@ -4,6 +4,8 @@
 
 #include "components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h"
 
+#include <utility>
+
 #include "base/allocator/buildflags.h"
 #include "base/atomicops.h"
 #include "base/bind.h"
@@ -424,8 +426,12 @@
     *context = allocation_context.type_name;
 }
 
-void SerializeFramesFromBacktrace(FrameSerializer* serializer,
-                                  const char** context) {
+// Captures up to |max_entries| stack frames using the buffer pointed by
+// |frames|. Puts the number of captured frames into the |count| output
+// parameters. Returns the pointer to the topmost frame.
+const void** CaptureStackTrace(const void** frames,
+                               size_t max_entries,
+                               size_t* count) {
   // Skip 3 top frames related to the profiler itself, e.g.:
   //   base::debug::StackTrace::StackTrace
   //   heap_profiling::RecordAndSendAlloc
@@ -433,26 +439,33 @@
   size_t skip_frames = 3;
 #if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
     defined(OFFICIAL_BUILD)
-  const void* frames[kMaxStackEntries - 1];
   size_t frame_count =
       base::trace_event::CFIBacktraceAndroid::GetInitializedInstance()->Unwind(
-          frames, kMaxStackEntries - 1);
+          frames, max_entries);
 #elif BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
-  const void* frames[kMaxStackEntries - 1];
-  size_t frame_count = base::debug::TraceStackFramePointers(
-      frames, kMaxStackEntries - 1, skip_frames);
+  size_t frame_count =
+      base::debug::TraceStackFramePointers(frames, max_entries, skip_frames);
   skip_frames = 0;
 #else
-  // Fall-back to capturing the stack with base::debug::StackTrace,
+  // Fall-back to capturing the stack with base::debug::CollectStackTrace,
   // which is likely slower, but more reliable.
-  base::debug::StackTrace stack_trace(kMaxStackEntries - 1);
-  size_t frame_count = 0u;
-  const void* const* frames = stack_trace.Addresses(&frame_count);
+  // TODO(alph): Make CollectStackTrace accept const void** pointer.
+  size_t frame_count =
+      base::debug::CollectStackTrace(const_cast<void**>(frames), max_entries);
 #endif
 
   skip_frames = std::min(skip_frames, frame_count);
-  serializer->AddAllInstructionPointers(frame_count - skip_frames,
-                                        frames + skip_frames);
+  *count = frame_count - skip_frames;
+  return frames + skip_frames;
+}
+
+void SerializeFramesFromBacktrace(FrameSerializer* serializer,
+                                  const char** context) {
+  const void* frames[kMaxStackEntries];
+  size_t frames_count;
+  const void** first_frame =
+      CaptureStackTrace(frames, kMaxStackEntries - 1, &frames_count);
+  serializer->AddAllInstructionPointers(frames_count, first_frame);
 
   // Both thread name and task context require access to TLS.
   if (ScopedAllowAlloc::HasTLSBeenDestroyed())
@@ -605,9 +618,14 @@
   base::PoissonAllocationSampler::Get()->RemoveSamplesObserver(this);
 }
 
+SamplingProfilerWrapper::Sample::Sample() = default;
+SamplingProfilerWrapper::Sample::Sample(Sample&&) = default;
+SamplingProfilerWrapper::Sample::~Sample() = default;
+
 void SamplingProfilerWrapper::StartProfiling(SenderPipe* sender_pipe,
                                              mojom::ProfilingParamsPtr params) {
   size_t sampling_rate = params->sampling_rate;
+  stream_samples_ = params->stream_samples;
   InitAllocationRecorder(sender_pipe, std::move(params));
   auto* sampler = base::PoissonAllocationSampler::Get();
   sampler->SetSamplingInterval(sampling_rate);
@@ -619,17 +637,126 @@
   base::PoissonAllocationSampler::Get()->Stop();
 }
 
+mojom::HeapProfilePtr SamplingProfilerWrapper::RetrieveHeapProfile() {
+  base::PoissonAllocationSampler::ScopedMuteThreadSamples no_samples_scope;
+  base::AutoLock lock(mutex_);
+  mojom::HeapProfilePtr profile = mojom::HeapProfile::New();
+  profile->samples.reserve(samples_.size());
+  for (const auto& pair : samples_) {
+    auto mojo_sample = mojom::HeapProfileSample::New();
+    mojo_sample->allocator = static_cast<uint32_t>(pair.second.allocator);
+    mojo_sample->size = pair.second.size;
+    mojo_sample->context_id = reinterpret_cast<uintptr_t>(pair.second.context);
+    mojo_sample->stack = pair.second.stack;
+    profile->samples.push_back(std::move(mojo_sample));
+  }
+  profile->strings.reserve(strings_.size());
+  for (const char* string : strings_)
+    profile->strings.emplace(reinterpret_cast<uintptr_t>(string), string);
+  return profile;
+}
+
+// The PoissonAllocationSampler that invokes this method guarantees
+// non-reentrancy, i.e. no allocations made within the scope of SampleAdded
+// will produce a sample.
 void SamplingProfilerWrapper::SampleAdded(
     void* address,
     size_t size,
     size_t total,
     base::PoissonAllocationSampler::AllocatorType type,
     const char* context) {
-  RecordAndSendAlloc(ConvertType(type), address, size, context);
+  DCHECK(base::PoissonAllocationSampler::ScopedMuteThreadSamples::IsMuted());
+  if (stream_samples_) {
+    RecordAndSendAlloc(ConvertType(type), address, size, context);
+    return;
+  }
+  base::AutoLock lock(mutex_);
+  Sample sample;
+  sample.allocator = ConvertType(type);
+  sample.size = size;
+  CaptureMode capture_mode = AllocationContextTracker::capture_mode();
+  if (capture_mode == CaptureMode::PSEUDO_STACK ||
+      capture_mode == CaptureMode::MIXED_STACK) {
+    CaptureMixedStack(context, &sample);
+  } else {
+    CaptureNativeStack(context, &sample);
+  }
+  RecordString(sample.context);
+  samples_.emplace(address, std::move(sample));
+}
+
+void SamplingProfilerWrapper::CaptureMixedStack(const char* context,
+                                                Sample* sample) {
+  // Allocation context is tracked in TLS. Return nothing if TLS was destroyed.
+  if (ScopedAllowAlloc::HasTLSBeenDestroyed())
+    return;
+  auto* tracker = AllocationContextTracker::GetInstanceForCurrentThread();
+  if (!tracker)
+    return;
+
+  AllocationContext allocation_context;
+  if (!tracker->GetContextSnapshot(&allocation_context))
+    return;
+
+  const base::trace_event::Backtrace& backtrace = allocation_context.backtrace;
+  CHECK_LE(backtrace.frame_count, kMaxStackEntries);
+  std::vector<uint64_t> stack;
+  stack.reserve(backtrace.frame_count);
+  for (int i = base::checked_cast<int>(backtrace.frame_count) - 1; i >= 0;
+       --i) {
+    const base::trace_event::StackFrame& frame = backtrace.frames[i];
+    if (frame.type != base::trace_event::StackFrame::Type::PROGRAM_COUNTER)
+      RecordString(static_cast<const char*>(frame.value));
+    stack.push_back(reinterpret_cast<uintptr_t>(frame.value));
+  }
+  sample->stack = std::move(stack);
+  if (!context)
+    context = allocation_context.type_name;
+  sample->context = context;
+}
+
+void SamplingProfilerWrapper::CaptureNativeStack(const char* context,
+                                                 Sample* sample) {
+  const void* stack[kMaxStackEntries];
+  size_t frame_count;
+  // One frame is reserved for the thread name.
+  const void** first_frame =
+      CaptureStackTrace(stack, kMaxStackEntries - 1, &frame_count);
+  DCHECK_LT(frame_count, kMaxStackEntries);
+  sample->stack.reserve(frame_count + (g_include_thread_names ? 1 : 0));
+  sample->stack.insert(sample->stack.end(),
+                       reinterpret_cast<uintptr_t*>(first_frame),
+                       reinterpret_cast<uintptr_t*>(first_frame + frame_count));
+
+  // Both thread name and task context require access to TLS.
+  if (ScopedAllowAlloc::HasTLSBeenDestroyed())
+    return;
+
+  if (g_include_thread_names) {
+    sample->stack.push_back(
+        reinterpret_cast<uintptr_t>(RecordString(GetOrSetThreadName())));
+  }
+  if (!context) {
+    const auto* tracker =
+        AllocationContextTracker::GetInstanceForCurrentThread();
+    if (tracker)
+      context = tracker->TaskContext();
+  }
+  sample->context = context;
+}
+
+const char* SamplingProfilerWrapper::RecordString(const char* string) {
+  return string ? *strings_.insert(string).first : nullptr;
 }
 
 void SamplingProfilerWrapper::SampleRemoved(void* address) {
-  RecordAndSendFree(address);
+  DCHECK(base::PoissonAllocationSampler::ScopedMuteThreadSamples::IsMuted());
+  if (stream_samples_) {
+    RecordAndSendFree(address);
+    return;
+  }
+  base::AutoLock lock(mutex_);
+  samples_.erase(address);
 }
 
 }  // namespace heap_profiling
diff --git a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h
index bee5c72..19d5a29c 100644
--- a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h
+++ b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h
@@ -5,6 +5,10 @@
 #ifndef COMPONENTS_SERVICES_HEAP_PROFILING_PUBLIC_CPP_SAMPLING_PROFILER_WRAPPER_H_
 #define COMPONENTS_SERVICES_HEAP_PROFILING_PUBLIC_CPP_SAMPLING_PROFILER_WRAPPER_H_
 
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
 #include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
 #include "components/services/heap_profiling/public/cpp/sender_pipe.h"
 #include "components/services/heap_profiling/public/cpp/stream.h"
@@ -44,7 +48,24 @@
   // logging process so it knows when this operation is complete.
   static void FlushPipe(uint32_t barrier_id);
 
+  mojom::HeapProfilePtr RetrieveHeapProfile();
+
  private:
+  struct Sample {
+    Sample();
+    Sample(Sample&& sample);
+    ~Sample();
+
+    Sample& operator=(Sample&&) = default;
+
+    AllocatorType allocator;
+    size_t size;
+    const char* context = nullptr;
+    std::vector<uint64_t> stack;
+
+    DISALLOW_COPY_AND_ASSIGN(Sample);
+  };
+
   // base::PoissonAllocationSampler::SamplesObserver
   void SampleAdded(void* address,
                    size_t size,
@@ -52,6 +73,26 @@
                    base::PoissonAllocationSampler::AllocatorType,
                    const char* context) override;
   void SampleRemoved(void* address) override;
+
+  void CaptureMixedStack(const char* context, Sample* sample);
+  void CaptureNativeStack(const char* context, Sample* sample);
+  const char* RecordString(const char* string);
+
+  bool stream_samples_ = false;
+
+  // Mutex to access |samples_| and |strings_|.
+  base::Lock mutex_;
+
+  // Samples of the currently live allocations.
+  std::unordered_map<void*, Sample> samples_;
+
+  // When CaptureMode::PSEUDO_STACK or CaptureMode::MIXED_STACK is enabled
+  // the call stack contents of samples may contain strings besides
+  // PC addresses.
+  // In this case each string pointer is also added to the |strings_| set.
+  // The set does only contain pointers to static strings that are never
+  // deleted.
+  std::unordered_set<const char*> strings_;
 };
 
 }  // namespace heap_profiling
diff --git a/components/services/heap_profiling/public/mojom/heap_profiling_client.mojom b/components/services/heap_profiling/public/mojom/heap_profiling_client.mojom
index 8b98d691..6a9607776 100644
--- a/components/services/heap_profiling/public/mojom/heap_profiling_client.mojom
+++ b/components/services/heap_profiling/public/mojom/heap_profiling_client.mojom
@@ -23,6 +23,12 @@
 // A wrapper for parameters that affect each client's implementation of
 // profiling.
 struct ProfilingParams {
+  // When |stream_samples| is true the samples are streamed through the
+  // provided |sender_pipe|. Otherwise, the samples are stored on
+  // the client side during recording and can be retrieved using
+  // |ProfilingClient.RetrieveHeapProfile| method.
+  bool stream_samples;
+
   // The client should record allocations into |memlog_sender_pipe|.
   handle sender_pipe;
 
@@ -37,17 +43,48 @@
   uint32 sampling_rate;
 };
 
+// A single memory allocation sample.
+struct HeapProfileSample {
+  // Allocator type.
+  uint32 allocator;
+
+  // The size in bytes accounted for the sample.
+  uint64 size;
+
+  // Id of the context string.
+  uint64 context_id;
+
+  // Program stack in top to bottom order recorded for the allocation.
+  // Each element of the |stack| is either a PC memory address or a string
+  // if it is among the |strings| map items of |HeapProfile|.
+  array<uint64> stack;
+};
+
+// Heap profile data. Can be retrieved from the client with
+// |RetrieveHeapProfile| method.
+struct HeapProfile {
+  // Samples recorded for the profile.
+  array<HeapProfileSample> samples;
+
+  // Strings used within the profile.
+  map<uint64, string> strings;
+};
+
 // This interface is implemented by "memlog clients" (profiled processes that
 // can send memory allocation events to the profiling process). These functions
 // are called by the profiling process to control the senders.
 interface ProfilingClient {
-  // Start recording allocations and sending them to the profiling process via
-  // |params.sender_pipe|. There is currently no mechanism to stop recording
-  // allocations.
+  // Start recording allocations.
+  // Collected allocations are either streamed to the profiling process via
+  // |params.sender_pipe|, or accumulated in the profiled process depending on
+  // the |params.use_in_process_storage|.
+  // There is currently no mechanism to stop recording allocations.
   StartProfiling(ProfilingParams params);
 
   // Flushes the memlog pipe associated with this client. A barrier packet is
   // set over the memlog pipe with the given identifier. This allows the
   // receiver to synchronize with the flush.
   FlushMemlogPipe(uint32 barrier_id);
+
+  RetrieveHeapProfile() => (HeapProfile profile);
 };
diff --git a/components/signin/ios/browser/BUILD.gn b/components/signin/ios/browser/BUILD.gn
index 088e53e..8aafb19 100644
--- a/components/signin/ios/browser/BUILD.gn
+++ b/components/signin/ios/browser/BUILD.gn
@@ -8,8 +8,6 @@
     "account_consistency_service.h",
     "account_consistency_service.mm",
     "manage_accounts_delegate.h",
-    "merge_session_observer_bridge.h",
-    "merge_session_observer_bridge.mm",
     "profile_oauth2_token_service_ios_delegate.h",
     "profile_oauth2_token_service_ios_delegate.mm",
     "profile_oauth2_token_service_ios_provider.h",
diff --git a/components/signin/ios/browser/merge_session_observer_bridge.h b/components/signin/ios/browser/merge_session_observer_bridge.h
deleted file mode 100644
index ec9b104..0000000
--- a/components/signin/ios/browser/merge_session_observer_bridge.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2014 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_SIGNIN_IOS_BROWSER_MERGE_SESSION_OBSERVER_BRIDGE_H_
-#define COMPONENTS_SIGNIN_IOS_BROWSER_MERGE_SESSION_OBSERVER_BRIDGE_H_
-
-#import <Foundation/Foundation.h>
-
-#include <string>
-
-#include "base/macros.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
-
-class GoogleServiceAuthError;
-
-@protocol MergeSessionObserverBridgeDelegate
-
-// Informs the delegate that the merge session operation for |account_id| has
-// finished. If there was an error, it will be described in |error|.
-- (void)onMergeSessionCompleted:(const std::string&)account_id
-                          error:(const GoogleServiceAuthError&)error;
-
-@end
-
-// C++ class to monitor merge session status in Objective C type.
-class MergeSessionObserverBridge : public GaiaCookieManagerService::Observer {
- public:
-  MergeSessionObserverBridge(id<MergeSessionObserverBridgeDelegate> delegate,
-                             GaiaCookieManagerService* cookie_manager_service);
-  ~MergeSessionObserverBridge() override;
-
-  void OnAddAccountToCookieCompleted(
-      const std::string& account_id,
-      const GoogleServiceAuthError& error) override;
-
- private:
-  __weak id<MergeSessionObserverBridgeDelegate> delegate_;
-  GaiaCookieManagerService* cookie_manager_service_;
-
-  DISALLOW_COPY_AND_ASSIGN(MergeSessionObserverBridge);
-};
-
-#endif  // COMPONENTS_SIGNIN_IOS_BROWSER_MERGE_SESSION_OBSERVER_BRIDGE_H_
diff --git a/components/signin/ios/browser/merge_session_observer_bridge.mm b/components/signin/ios/browser/merge_session_observer_bridge.mm
deleted file mode 100644
index ee30039..0000000
--- a/components/signin/ios/browser/merge_session_observer_bridge.mm
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2014 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/signin/ios/browser/merge_session_observer_bridge.h"
-
-#include "base/logging.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-MergeSessionObserverBridge::MergeSessionObserverBridge(
-    id<MergeSessionObserverBridgeDelegate> delegate,
-    GaiaCookieManagerService* cookie_manager_service)
-    : delegate_(delegate), cookie_manager_service_(cookie_manager_service) {
-  DCHECK(delegate);
-  DCHECK(cookie_manager_service);
-  cookie_manager_service_->AddObserver(this);
-}
-
-MergeSessionObserverBridge::~MergeSessionObserverBridge() {
-  cookie_manager_service_->RemoveObserver(this);
-}
-
-void MergeSessionObserverBridge::OnAddAccountToCookieCompleted(
-    const std::string& account_id,
-    const GoogleServiceAuthError& error) {
-  [delegate_ onMergeSessionCompleted:account_id error:error];
-}
diff --git a/components/translate/core/browser/BUILD.gn b/components/translate/core/browser/BUILD.gn
index aea0ed1..5ee75683 100644
--- a/components/translate/core/browser/BUILD.gn
+++ b/components/translate/core/browser/BUILD.gn
@@ -84,12 +84,6 @@
   testonly = true
   sources = [
     "language_state_unittest.cc",
-    "mock_translate_client.cc",
-    "mock_translate_client.h",
-    "mock_translate_driver.cc",
-    "mock_translate_driver.h",
-    "mock_translate_ranker.cc",
-    "mock_translate_ranker.h",
     "translate_accept_languages_unittest.cc",
     "translate_browser_metrics_unittest.cc",
     "translate_language_list_unittest.cc",
@@ -102,6 +96,7 @@
   ]
   deps = [
     ":browser",
+    ":test_support",
     "//base",
     "//components/assist_ranker",
     "//components/assist_ranker/proto",
@@ -120,8 +115,38 @@
     "//services/metrics/public/cpp:ukm_builders",
     "//services/network:test_support",
     "//services/network/public/cpp:cpp",
-    "//testing/gtest",
-    "//third_party/metrics_proto",
     "//ui/base",
   ]
 }
+
+source_set("test_support") {
+  testonly = true
+  sources = [
+    "mock_translate_client.cc",
+    "mock_translate_client.h",
+    "mock_translate_driver.cc",
+    "mock_translate_driver.h",
+    "mock_translate_ranker.cc",
+    "mock_translate_ranker.h",
+  ]
+  deps = [
+    "//base",
+    "//components/infobars/core",
+    "//components/language/core/browser",
+    "//components/sync_preferences",
+    "//components/sync_preferences:test_support",
+    "//components/translate/core/browser",
+    "//components/translate/core/common",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/metrics_proto:metrics_proto",
+    "//url",
+  ]
+
+  if (is_ios || is_android) {
+    sources += [
+      "mock_translate_infobar_delegate.cc",
+      "mock_translate_infobar_delegate.h",
+    ]
+  }
+}
diff --git a/components/translate/core/browser/mock_translate_infobar_delegate.cc b/components/translate/core/browser/mock_translate_infobar_delegate.cc
new file mode 100644
index 0000000..9b67068
--- /dev/null
+++ b/components/translate/core/browser/mock_translate_infobar_delegate.cc
@@ -0,0 +1,59 @@
+// 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/translate/core/browser/mock_translate_infobar_delegate.h"
+
+namespace translate {
+
+namespace testing {
+
+std::vector<MockLanguageModel::LanguageDetails>
+MockLanguageModel::GetLanguages() {
+  return {MockLanguageModel::LanguageDetails("en", 1.0)};
+}
+
+MockTranslateInfoBarDelegate::MockTranslateInfoBarDelegate(
+    const base::WeakPtr<translate::TranslateManager>& translate_manager,
+    bool is_off_the_record,
+    translate::TranslateStep step,
+    const std::string& original_language,
+    const std::string& target_language,
+    translate::TranslateErrors::Type error_type,
+    bool triggered_from_menu)
+    : translate::TranslateInfoBarDelegate(translate_manager,
+                                          is_off_the_record,
+                                          step,
+                                          original_language,
+                                          target_language,
+                                          error_type,
+                                          triggered_from_menu) {}
+
+MockTranslateInfoBarDelegate::~MockTranslateInfoBarDelegate() {}
+
+MockTranslateInfoBarDelegateFactory::MockTranslateInfoBarDelegateFactory(
+    const std::string& original_language,
+    const std::string& target_language) {
+  pref_service_ =
+      std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
+  translate::TranslatePrefs::RegisterProfilePrefs(pref_service_->registry());
+  pref_service_->registry()->RegisterBooleanPref(prefs::kOfferTranslateEnabled,
+                                                 true);
+  client_ =
+      std::make_unique<MockTranslateClient>(&driver_, pref_service_.get());
+  ranker_ = std::make_unique<MockTranslateRanker>();
+  language_model_ = std::make_unique<MockLanguageModel>();
+  manager_ = std::make_unique<translate::TranslateManager>(
+      client_.get(), ranker_.get(), language_model_.get());
+  delegate_ = std::make_unique<MockTranslateInfoBarDelegate>(
+      manager_->GetWeakPtr(), false,
+      translate::TranslateStep::TRANSLATE_STEP_BEFORE_TRANSLATE,
+      original_language, target_language,
+      translate::TranslateErrors::Type::NONE, false);
+}
+
+MockTranslateInfoBarDelegateFactory::~MockTranslateInfoBarDelegateFactory() {}
+
+}  // namespace testing
+
+}  // namespace translate
diff --git a/components/translate/core/browser/mock_translate_infobar_delegate.h b/components/translate/core/browser/mock_translate_infobar_delegate.h
new file mode 100644
index 0000000..3f736f90
--- /dev/null
+++ b/components/translate/core/browser/mock_translate_infobar_delegate.h
@@ -0,0 +1,78 @@
+// 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_TRANSLATE_CORE_BROWSER_MOCK_TRANSLATE_INFOBAR_DELEGATE_H_
+#define COMPONENTS_TRANSLATE_CORE_BROWSER_MOCK_TRANSLATE_INFOBAR_DELEGATE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/language/core/browser/language_model.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/translate/core/browser/mock_translate_client.h"
+#include "components/translate/core/browser/mock_translate_driver.h"
+#include "components/translate/core/browser/mock_translate_ranker.h"
+#include "components/translate/core/browser/translate_infobar_delegate.h"
+#include "components/translate/core/browser/translate_manager.h"
+#include "components/translate/core/browser/translate_pref_names.h"
+#include "components/translate/core/browser/translate_prefs.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::_;
+
+namespace translate {
+
+namespace testing {
+
+class MockLanguageModel : public language::LanguageModel {
+  std::vector<LanguageDetails> GetLanguages() override;
+};
+
+class MockTranslateInfoBarDelegate
+    : public translate::TranslateInfoBarDelegate {
+ public:
+  MockTranslateInfoBarDelegate(
+      const base::WeakPtr<translate::TranslateManager>& translate_manager,
+      bool is_off_the_record,
+      translate::TranslateStep step,
+      const std::string& original_language,
+      const std::string& target_language,
+      translate::TranslateErrors::Type error_type,
+      bool triggered_from_menu);
+  ~MockTranslateInfoBarDelegate() override;
+
+  MOCK_CONST_METHOD0(num_languages, size_t());
+  MOCK_CONST_METHOD1(language_code_at, std::string(size_t index));
+  MOCK_CONST_METHOD1(language_name_at, base::string16(size_t index));
+  MOCK_CONST_METHOD0(original_language_name, base::string16());
+  MOCK_CONST_METHOD0(ShouldAlwaysTranslate, bool());
+  MOCK_METHOD1(SetObserver, void(Observer* observer));
+};
+
+class MockTranslateInfoBarDelegateFactory {
+ public:
+  MockTranslateInfoBarDelegateFactory(const std::string& original_language,
+                                      const std::string& target_language);
+  ~MockTranslateInfoBarDelegateFactory();
+
+  MockTranslateInfoBarDelegate* GetMockTranslateInfoBarDelegate() {
+    return delegate_.get();
+  }
+
+ private:
+  MockTranslateDriver driver_;
+  std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_;
+  std::unique_ptr<MockTranslateClient> client_;
+  std::unique_ptr<MockTranslateRanker> ranker_;
+  std::unique_ptr<MockLanguageModel> language_model_;
+  std::unique_ptr<translate::TranslateManager> manager_;
+  std::unique_ptr<MockTranslateInfoBarDelegate> delegate_;
+};
+
+}  // namespace testing
+
+}  // namespace translate
+
+#endif  // COMPONENTS_TRANSLATE_CORE_BROWSER_MOCK_TRANSLATE_INFOBAR_DELEGATE_H_
diff --git a/components/translate/core/browser/translate_infobar_delegate.cc b/components/translate/core/browser/translate_infobar_delegate.cc
index aa2cb72..68d4a1a 100644
--- a/components/translate/core/browser/translate_infobar_delegate.cc
+++ b/components/translate/core/browser/translate_infobar_delegate.cc
@@ -103,10 +103,26 @@
     infobar_manager->AddInfoBar(std::move(infobar));
 }
 
+size_t TranslateInfoBarDelegate::num_languages() const {
+  return ui_delegate_.GetNumberOfLanguages();
+}
+
+std::string TranslateInfoBarDelegate::language_code_at(size_t index) const {
+  return ui_delegate_.GetLanguageCodeAt(index);
+}
+
+base::string16 TranslateInfoBarDelegate::language_name_at(size_t index) const {
+  return ui_delegate_.GetLanguageNameAt(index);
+}
+
 void TranslateInfoBarDelegate::SetObserver(Observer* observer) {
   observer_ = observer;
 }
 
+base::string16 TranslateInfoBarDelegate::original_language_name() const {
+  return language_name_at(ui_delegate_.GetOriginalLanguageIndex());
+}
+
 void TranslateInfoBarDelegate::UpdateOriginalLanguage(
     const std::string& language_code) {
   ui_delegate_.UpdateOriginalLanguage(language_code);
diff --git a/components/translate/core/browser/translate_infobar_delegate.h b/components/translate/core/browser/translate_infobar_delegate.h
index dc16ed9..47c1c91 100644
--- a/components/translate/core/browser/translate_infobar_delegate.h
+++ b/components/translate/core/browser/translate_infobar_delegate.h
@@ -77,17 +77,13 @@
                      bool triggered_from_menu);
 
   // Returns the number of languages supported.
-  size_t num_languages() const { return ui_delegate_.GetNumberOfLanguages(); }
+  virtual size_t num_languages() const;
 
   // Returns the ISO code for the language at |index|.
-  std::string language_code_at(size_t index) const {
-    return ui_delegate_.GetLanguageCodeAt(index);
-  }
+  virtual std::string language_code_at(size_t index) const;
 
   // Returns the displayable name for the language at |index|.
-  base::string16 language_name_at(size_t index) const {
-    return ui_delegate_.GetLanguageNameAt(index);
-  }
+  virtual base::string16 language_name_at(size_t index) const;
 
   translate::TranslateStep translate_step() const { return step_; }
 
@@ -99,9 +95,7 @@
     return ui_delegate_.GetOriginalLanguageCode();
   }
 
-  base::string16 original_language_name() const {
-    return language_name_at(ui_delegate_.GetOriginalLanguageIndex());
-  }
+  virtual base::string16 original_language_name() const;
 
   void UpdateOriginalLanguage(const std::string& language_code);
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 92632c3..81ecaf42 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -134,7 +134,8 @@
         backend_format, size_.width(), size_.height(), mipmap_,
         kTopLeft_GrSurfaceOrigin /* origin */, color_type, alpha_type_,
         color_space_, PromiseTextureHelper::Fulfill,
-        PromiseTextureHelper::Release, PromiseTextureHelper::Done, this);
+        PromiseTextureHelper::Release, PromiseTextureHelper::Done, this,
+        SkDeferredDisplayListRecorder::DelayReleaseCallback::kYes);
   }
 
   static sk_sp<SkPromiseImageTexture> Fulfill(void* texture_context) {
@@ -267,7 +268,8 @@
         yuv_color_space, formats, yuva_sizes, indices, yuva_sizes[0].width(),
         yuva_sizes[0].height(), kTopLeft_GrSurfaceOrigin,
         nullptr /* color_space */, PromiseTextureHelper::Fulfill,
-        PromiseTextureHelper::Release, PromiseTextureHelper::Done, contexts);
+        PromiseTextureHelper::Release, PromiseTextureHelper::Done, contexts,
+        SkDeferredDisplayListRecorder::DelayReleaseCallback::kYes);
     return image;
   }
 
diff --git a/content/browser/child_process_launcher_helper_fuchsia.cc b/content/browser/child_process_launcher_helper_fuchsia.cc
index 06e1067..413ff43 100644
--- a/content/browser/child_process_launcher_helper_fuchsia.cc
+++ b/content/browser/child_process_launcher_helper_fuchsia.cc
@@ -18,8 +18,8 @@
     base::Process process,
     const ChildProcessLauncherPriority& priority) {
   DCHECK(CurrentlyOnProcessLauncherTaskRunner());
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
+  // TODO(https://crbug.com/926583): Fuchsia does not currently support this.
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 ChildProcessTerminationInfo ChildProcessLauncherHelper::GetTerminationInfo(
@@ -41,14 +41,11 @@
 void ChildProcessLauncherHelper::SetRegisteredFilesForService(
     const std::string& service_name,
     std::map<std::string, base::FilePath> required_files) {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
+  NOTREACHED() << " for service " << service_name;
 }
 
 // static
 void ChildProcessLauncherHelper::ResetRegisteredFilesForTesting() {
-  // TODO(fuchsia): Implement this. (crbug.com/707031)
-  NOTIMPLEMENTED();
 }
 
 void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() {
@@ -85,7 +82,6 @@
   DCHECK(mojo_channel_);
   DCHECK(mojo_channel_->remote_endpoint().is_valid());
 
-  // TODO(750938): Implement sandboxed/isolated subprocess launching.
   Process child_process;
   child_process.process = base::LaunchProcess(*command_line(), options);
   return child_process;
diff --git a/content/browser/frame_host/cross_process_frame_connector.cc b/content/browser/frame_host/cross_process_frame_connector.cc
index ae486a2..1024c39 100644
--- a/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/content/browser/frame_host/cross_process_frame_connector.cc
@@ -150,8 +150,8 @@
     delegate_was_shown_after_crash_ = false;
 
     view_->SetFrameConnectorDelegate(this);
-    if (is_hidden_)
-      OnVisibilityChanged(false);
+    if (visibility_ != blink::mojom::FrameVisibility::kRenderedInViewport)
+      OnVisibilityChanged(visibility_);
     FrameMsg_ViewChanged_Params params;
     if (!features::IsMultiProcessMash())
       params.frame_sink_id = view_->GetFrameSinkId();
@@ -370,8 +370,10 @@
   }
 }
 
-void CrossProcessFrameConnector::OnVisibilityChanged(bool visible) {
-  is_hidden_ = !visible;
+void CrossProcessFrameConnector::OnVisibilityChanged(
+    blink::mojom::FrameVisibility visibility) {
+  bool visible = visibility != blink::mojom::FrameVisibility::kNotRendered;
+  visibility_ = visibility;
   if (IsVisible()) {
     // Record metrics if a crashed subframe became visible as a result of this
     // visibility change.
@@ -380,6 +382,10 @@
   if (!view_)
     return;
 
+  frame_proxy_in_parent_renderer_->frame_tree_node()
+      ->current_frame_host()
+      ->VisibilityChanged(visibility);
+
   // If there is an inner WebContents, it should be notified of the change in
   // the visibility. The Show/Hide methods will not be called if an inner
   // WebContents exists since the corresponding WebContents will itself call
@@ -453,7 +459,7 @@
 }
 
 bool CrossProcessFrameConnector::IsHidden() const {
-  return is_hidden_;
+  return visibility_ == blink::mojom::FrameVisibility::kNotRendered;
 }
 
 #if defined(USE_AURA)
@@ -588,7 +594,7 @@
 }
 
 bool CrossProcessFrameConnector::IsVisible() {
-  if (is_hidden_)
+  if (visibility_ == blink::mojom::FrameVisibility::kNotRendered)
     return false;
   if (viewport_intersection_rect().IsEmpty())
     return false;
diff --git a/content/browser/frame_host/cross_process_frame_connector.h b/content/browser/frame_host/cross_process_frame_connector.h
index d7f7cea..cf9648bf 100644
--- a/content/browser/frame_host/cross_process_frame_connector.h
+++ b/content/browser/frame_host/cross_process_frame_connector.h
@@ -14,6 +14,7 @@
 #include "content/browser/renderer_host/frame_connector_delegate.h"
 #include "content/common/content_export.h"
 #include "content/common/frame_visual_properties.h"
+#include "third_party/blink/public/mojom/frame/lifecycle.mojom.h"
 
 namespace IPC {
 class Message;
@@ -158,6 +159,8 @@
   // became visible.
   void DelegateWasShown();
 
+  blink::mojom::FrameVisibility visibility() const { return visibility_; }
+
  private:
   friend class MockCrossProcessFrameConnector;
 
@@ -181,7 +184,7 @@
   void OnUpdateViewportIntersection(const gfx::Rect& viewport_intersection,
                                     const gfx::Rect& compositor_visible_rect,
                                     bool occluded_or_obscured);
-  void OnVisibilityChanged(bool visible);
+  void OnVisibilityChanged(blink::mojom::FrameVisibility visibility);
   void OnSetIsInert(bool);
   void OnSetInheritedEffectiveTouchAction(cc::TouchAction);
   void OnUpdateRenderThrottlingStatus(bool is_throttled,
@@ -199,8 +202,9 @@
   bool subtree_throttled_ = false;
 
   // Visibility state of the corresponding frame owner element in parent process
-  // which is set through CSS.
-  bool is_hidden_ = false;
+  // which is set through CSS or scrolling.
+  blink::mojom::FrameVisibility visibility_ =
+      blink::mojom::FrameVisibility::kRenderedInViewport;
 
   // Used to make sure we only log UMA once per renderer crash.
   bool is_crash_already_logged_ = false;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index b699b0c2..870875d 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3019,6 +3019,11 @@
 }
 #endif
 
+void RenderFrameHostImpl::VisibilityChanged(
+    blink::mojom::FrameVisibility visibility) {
+  visibility_ = visibility;
+}
+
 void RenderFrameHostImpl::OnDidBlockFramebust(const GURL& url) {
   delegate_->OnDidBlockFramebust(url);
 }
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 57296a8..3dcbda6 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -870,6 +870,11 @@
   // activated.
   void OnPortalActivated();
 
+  // mojom::FrameHost:
+  void VisibilityChanged(blink::mojom::FrameVisibility) override;
+
+  blink::mojom::FrameVisibility visibility() const { return visibility_; }
+
  protected:
   friend class RenderFrameHostFactory;
 
@@ -1957,6 +1962,9 @@
   // BackForwardCache:
   bool is_in_back_forward_cache_ = false;
 
+  blink::mojom::FrameVisibility visibility_ =
+      blink::mojom::FrameVisibility::kRenderedInViewport;
+
   // NOTE: This must be the last member.
   base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_;
 
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index c108626d..510aab1 100644
--- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -2016,4 +2016,43 @@
   EXPECT_EQ(0, process->get_media_stream_count_for_testing());
 }
 
+// Test that a frame is visible/hidden depending on its WebContents visibility
+// state.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
+                       VisibilityScrolledOutOfView) {
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+
+  GURL main_frame(embedded_test_server()->GetURL("/iframe_out_of_view.html"));
+  GURL child_url(embedded_test_server()->GetURL("/hello.html"));
+
+  // This will set up the page frame tree as A(A1()).
+  ASSERT_TRUE(NavigateToURL(shell(), main_frame));
+  FrameTreeNode* root = web_contents->GetFrameTree()->root();
+  FrameTreeNode* nested_iframe_node = root->child_at(0);
+  NavigateFrameToURL(nested_iframe_node, child_url);
+
+  ASSERT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport,
+            nested_iframe_node->current_frame_host()->visibility());
+}
+
+// Test that a frame is visible/hidden depending on its WebContents visibility
+// state.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, VisibilityChildInView) {
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+
+  GURL main_frame(embedded_test_server()->GetURL("/iframe_clipped.html"));
+  GURL child_url(embedded_test_server()->GetURL("/hello.html"));
+
+  // This will set up the page frame tree as A(A1()).
+  ASSERT_TRUE(NavigateToURL(shell(), main_frame));
+  FrameTreeNode* root = web_contents->GetFrameTree()->root();
+  FrameTreeNode* nested_iframe_node = root->child_at(0);
+  NavigateFrameToURL(nested_iframe_node, child_url);
+
+  ASSERT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
+            nested_iframe_node->current_frame_host()->visibility());
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc
index e70b7e4..6d7451f 100644
--- a/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -270,15 +270,20 @@
     return;
   }
 
-  // This message should only be received for subframes.  Note that we can't
-  // restrict it to just the current SiteInstances of the ancestors of this
-  // frame, because another frame in the tree may be able to detach this frame
-  // by navigating its parent.
-  if (frame_tree_node_->IsMainFrame()) {
-    bad_message::ReceivedBadMessage(GetProcess(), bad_message::RFPH_DETACH);
+  // For a main frame with no outer delegate, no further work is needed. In this
+  // case, detach can only be triggered by closing the entire RenderViewHost.
+  // Instead, this cleanup relies on the destructors of RenderFrameHost and
+  // RenderFrameProxyHost decrementing the refcounts of their associated
+  // RenderViewHost. When the refcount hits 0, the corresponding renderer object
+  // is cleaned up. Since WebContents destruction will also destroy
+  // RenderFrameHost/RenderFrameProxyHost objects in FrameTree, this eventually
+  // results in all the associated RenderViewHosts being closed.
+  if (frame_tree_node_->IsMainFrame())
     return;
-  }
 
+  // Otherwise, a remote child frame has been removed from the frame tree.
+  // Make sure that this action is mirrored to all the other renderers, so
+  // the frame tree remains consistent.
   frame_tree_node_->current_frame_host()->DetachFromProxy();
 }
 
diff --git a/content/browser/media/session/media_metadata_sanitizer.cc b/content/browser/media/session/media_metadata_sanitizer.cc
index e7e109e..c98ad630 100644
--- a/content/browser/media/session/media_metadata_sanitizer.cc
+++ b/content/browser/media/session/media_metadata_sanitizer.cc
@@ -54,63 +54,32 @@
   return true;
 }
 
-// Sanitize MediaImage. The method should not be called if |image.src| is bad.
-media_session::MediaMetadata::MediaImage SanitizeMediaImage(
-    const media_session::MediaMetadata::MediaImage& image) {
-  media_session::MediaMetadata::MediaImage sanitized_image;
-
-  sanitized_image.src = image.src;
-  sanitized_image.type = image.type.substr(0, kMaxMediaImageTypeLength);
-  for (const auto& size : image.sizes) {
-    sanitized_image.sizes.push_back(size);
-    if (sanitized_image.sizes.size() == kMaxNumberOfMediaImageSizes)
-      break;
-  }
-
-  return sanitized_image;
-}
-
 }  // anonymous namespace
 
-bool MediaMetadataSanitizer::CheckSanity(
-    const media_session::MediaMetadata& metadata) {
-  if (metadata.title.size() > kMaxIPCStringLength)
+bool MediaMetadataSanitizer::SanitizeAndConvert(
+    const blink::mojom::SpecMediaMetadataPtr& metadata,
+    media_session::MediaMetadata* metadata_out) {
+  if (metadata->title.size() > kMaxIPCStringLength)
     return false;
-  if (metadata.artist.size() > kMaxIPCStringLength)
+  if (metadata->artist.size() > kMaxIPCStringLength)
     return false;
-  if (metadata.album.size() > kMaxIPCStringLength)
+  if (metadata->album.size() > kMaxIPCStringLength)
     return false;
-  if (metadata.artwork.size() > kMaxNumberOfMediaImages)
+  if (metadata->artwork.size() > kMaxNumberOfMediaImages)
     return false;
 
-  for (const auto& image : metadata.artwork) {
+  metadata_out->title = metadata->title;
+  metadata_out->artist = metadata->artist;
+  metadata_out->album = metadata->album;
+
+  for (const auto& image : metadata->artwork) {
     if (!CheckMediaImageSanity(image))
       return false;
+
+    metadata_out->artwork.push_back(image);
   }
 
   return true;
 }
 
-media_session::MediaMetadata MediaMetadataSanitizer::Sanitize(
-    const media_session::MediaMetadata& metadata) {
-  media_session::MediaMetadata sanitized_metadata;
-
-  sanitized_metadata.title = metadata.title.substr(0, kMaxIPCStringLength);
-  sanitized_metadata.artist = metadata.artist.substr(0, kMaxIPCStringLength);
-  sanitized_metadata.album = metadata.album.substr(0, kMaxIPCStringLength);
-
-  for (const auto& image : metadata.artwork) {
-    if (!CheckMediaImageSrcSanity(image.src))
-      continue;
-
-    sanitized_metadata.artwork.push_back(
-        CheckMediaImageSanity(image) ? image : SanitizeMediaImage(image));
-
-    if (sanitized_metadata.artwork.size() == kMaxNumberOfMediaImages)
-      break;
-  }
-
-  return sanitized_metadata;
-}
-
 }  // namespace content
diff --git a/content/browser/media/session/media_metadata_sanitizer.h b/content/browser/media/session/media_metadata_sanitizer.h
index 10cd45a..c3719ee9 100644
--- a/content/browser/media/session/media_metadata_sanitizer.h
+++ b/content/browser/media/session/media_metadata_sanitizer.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_MEDIA_SESSION_MEDIA_METADATA_SANITIZER_H_
 #define CONTENT_BROWSER_MEDIA_SESSION_MEDIA_METADATA_SANITIZER_H_
 
+#include "third_party/blink/public/platform/modules/mediasession/media_session.mojom.h"
+
 namespace media_session {
 struct MediaMetadata;
 }  // namespace media_session
@@ -13,12 +15,11 @@
 
 class MediaMetadataSanitizer {
  public:
-  // Check the sanity of |metadata|.
-  static bool CheckSanity(const media_session::MediaMetadata& metadata);
-
-  // Sanitizes |metadata| and return the result.
-  static media_session::MediaMetadata Sanitize(
-      const media_session::MediaMetadata& metadata);
+  // Converts |metadata| to a media_session::MediaMetadata object and returns
+  // whether it is valid.
+  static bool SanitizeAndConvert(
+      const blink::mojom::SpecMediaMetadataPtr& metadata,
+      media_session::MediaMetadata* metadata_out);
 };
 
 }  // namespace content
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc
index c51de6a..8ee483b2 100644
--- a/content/browser/media/session/media_session_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -2197,11 +2197,18 @@
   // Set up the service and information.
   EnsureMediaSessionService();
 
-  media_session::MediaMetadata metadata;
-  metadata.title = base::ASCIIToUTF16("title");
-  metadata.artist = base::ASCIIToUTF16("artist");
-  metadata.album = base::ASCIIToUTF16("album");
-  mock_media_session_service_->SetMetadata(metadata);
+  media_session::MediaMetadata expected_metadata;
+  expected_metadata.title = base::ASCIIToUTF16("title");
+  expected_metadata.artist = base::ASCIIToUTF16("artist");
+  expected_metadata.album = base::ASCIIToUTF16("album");
+  expected_metadata.source_title = GetExpectedSourceTitle();
+
+  blink::mojom::SpecMediaMetadataPtr spec_metadata(
+      blink::mojom::SpecMediaMetadata::New());
+  spec_metadata->title = base::ASCIIToUTF16("title");
+  spec_metadata->artist = base::ASCIIToUTF16("artist");
+  spec_metadata->album = base::ASCIIToUTF16("album");
+  mock_media_session_service_->SetMetadata(std::move(spec_metadata));
 
   // Make sure the service is routed,
   auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(
@@ -2212,8 +2219,7 @@
     StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent);
     ResolveAudioFocusSuccess();
 
-    metadata.source_title = GetExpectedSourceTitle();
-    EXPECT_EQ(metadata, observer.WaitForNonEmptyMetadata());
+    EXPECT_EQ(expected_metadata, observer.WaitForNonEmptyMetadata());
   }
 }
 
diff --git a/content/browser/media/session/media_session_impl_service_routing_unittest.cc b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
index c25285d..683e4759 100644
--- a/content/browser/media/session/media_session_impl_service_routing_unittest.cc
+++ b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
@@ -277,7 +277,7 @@
 
   CreateServiceForFrame(main_frame_);
 
-  services_[main_frame_]->SetMetadata(media_session::MediaMetadata());
+  services_[main_frame_]->SetMetadata(nullptr);
   services_[main_frame_]->EnableAction(MediaSessionAction::kPlay);
 
   observer.WaitForActions();
@@ -312,7 +312,13 @@
     media_session::test::MockMediaSessionMojoObserver observer(
         *GetMediaSession());
 
-    services_[main_frame_]->SetMetadata(expected_metadata);
+    blink::mojom::SpecMediaMetadataPtr spec_metadata(
+        blink::mojom::SpecMediaMetadata::New());
+    spec_metadata->title = base::ASCIIToUTF16("title");
+    spec_metadata->artist = base::ASCIIToUTF16("artist");
+    spec_metadata->album = base::ASCIIToUTF16("album");
+
+    services_[main_frame_]->SetMetadata(std::move(spec_metadata));
     services_[main_frame_]->EnableAction(MediaSessionAction::kSeekForward);
 
     observer.WaitForActions();
@@ -334,7 +340,16 @@
 
   CreateServiceForFrame(main_frame_);
 
-  services_[main_frame_]->SetMetadata(expected_metadata);
+  {
+    blink::mojom::SpecMediaMetadataPtr spec_metadata(
+        blink::mojom::SpecMediaMetadata::New());
+    spec_metadata->title = base::ASCIIToUTF16("title");
+    spec_metadata->artist = base::ASCIIToUTF16("artist");
+    spec_metadata->album = base::ASCIIToUTF16("album");
+
+    services_[main_frame_]->SetMetadata(std::move(spec_metadata));
+  }
+
   services_[main_frame_]->EnableAction(MediaSessionAction::kSeekForward);
 
   {
@@ -372,7 +387,15 @@
 
   CreateServiceForFrame(main_frame_);
 
-  services_[main_frame_]->SetMetadata(expected_metadata);
+  {
+    blink::mojom::SpecMediaMetadataPtr spec_metadata(
+        blink::mojom::SpecMediaMetadata::New());
+    spec_metadata->title = base::ASCIIToUTF16("title");
+    spec_metadata->artist = base::ASCIIToUTF16("artist");
+    spec_metadata->album = base::ASCIIToUTF16("album");
+
+    services_[main_frame_]->SetMetadata(std::move(spec_metadata));
+  }
 
   StartPlayerForFrame(main_frame_);
 
@@ -577,7 +600,15 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(
         *GetMediaSession());
-    services_[main_frame_]->SetMetadata(expected_metadata);
+
+    blink::mojom::SpecMediaMetadataPtr spec_metadata(
+        blink::mojom::SpecMediaMetadata::New());
+    spec_metadata->title = base::ASCIIToUTF16("title");
+    spec_metadata->artist = base::ASCIIToUTF16("artist");
+    spec_metadata->album = base::ASCIIToUTF16("album");
+
+    services_[main_frame_]->SetMetadata(std::move(spec_metadata));
+
     EXPECT_EQ(expected_metadata, observer.WaitForNonEmptyMetadata());
   }
 }
@@ -593,7 +624,7 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(
         *GetMediaSession());
-    services_[main_frame_]->SetMetadata(base::nullopt);
+    services_[main_frame_]->SetMetadata(nullptr);
 
     // When the session becomes controllable we should receive default
     // metadata. The |is_controllable| boolean will also become true.
diff --git a/content/browser/media/session/media_session_service_impl.cc b/content/browser/media/session/media_session_service_impl.cc
index fbb3b101..2173235 100644
--- a/content/browser/media/session/media_session_service_impl.cc
+++ b/content/browser/media/session/media_session_service_impl.cc
@@ -47,7 +47,7 @@
   // At this point the BrowsingContext of the frame has changed, so the members
   // need to be reset, and notify MediaSessionImpl.
   SetPlaybackState(blink::mojom::MediaSessionPlaybackState::NONE);
-  SetMetadata(base::nullopt);
+  SetMetadata(nullptr);
   ClearActions();
 }
 
@@ -69,19 +69,25 @@
 }
 
 void MediaSessionServiceImpl::SetMetadata(
-    const base::Optional<media_session::MediaMetadata>& metadata) {
+    blink::mojom::SpecMediaMetadataPtr metadata) {
+  metadata_.reset();
+
   // When receiving a MediaMetadata, the browser process can't trust that it is
   // coming from a known and secure source. It must be processed accordingly.
-  if (metadata.has_value() &&
-      !MediaMetadataSanitizer::CheckSanity(metadata.value())) {
-    RenderFrameHost* rfh = GetRenderFrameHost();
-    if (rfh) {
-      rfh->GetProcess()->ShutdownForBadMessage(
-          RenderProcessHost::CrashReportMode::GENERATE_CRASH_DUMP);
+  if (!metadata.is_null()) {
+    media_session::MediaMetadata new_metadata;
+
+    if (!MediaMetadataSanitizer::SanitizeAndConvert(metadata, &new_metadata)) {
+      RenderFrameHost* rfh = GetRenderFrameHost();
+      if (rfh) {
+        rfh->GetProcess()->ShutdownForBadMessage(
+            RenderProcessHost::CrashReportMode::GENERATE_CRASH_DUMP);
+      }
+      return;
     }
-    return;
+
+    metadata_ = new_metadata;
   }
-  metadata_ = metadata;
 
   MediaSessionImpl* session = GetMediaSession();
   if (session)
diff --git a/content/browser/media/session/media_session_service_impl.h b/content/browser/media/session/media_session_service_impl.h
index 305b8a95..15baf5d 100644
--- a/content/browser/media/session/media_session_service_impl.h
+++ b/content/browser/media/session/media_session_service_impl.h
@@ -48,8 +48,7 @@
   void SetClient(blink::mojom::MediaSessionClientPtr client) override;
 
   void SetPlaybackState(blink::mojom::MediaSessionPlaybackState state) override;
-  void SetMetadata(
-      const base::Optional<media_session::MediaMetadata>& metadata) override;
+  void SetMetadata(blink::mojom::SpecMediaMetadataPtr metadata) override;
 
   void EnableAction(media_session::mojom::MediaSessionAction action) override;
   void DisableAction(media_session::mojom::MediaSessionAction action) override;
diff --git a/content/browser/portal/portal_browsertest.cc b/content/browser/portal/portal_browsertest.cc
index db38596..69f6b3c 100644
--- a/content/browser/portal/portal_browsertest.cc
+++ b/content/browser/portal/portal_browsertest.cc
@@ -318,4 +318,44 @@
   EXPECT_TRUE(proxy_host->is_render_frame_proxy_live());
 }
 
+// Tests that the portal's outer delegate frame tree node and any iframes
+// inside the portal are deleted when the portal element is removed from the
+// document.
+IN_PROC_BROWSER_TEST_F(PortalBrowserTest, DetachPortal) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  RenderFrameHostImpl* main_frame = web_contents->GetMainFrame();
+
+  Portal* portal = nullptr;
+  PortalCreatedObserver portal_created_observer(main_frame);
+  GURL a_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(a)"));
+  EXPECT_TRUE(ExecJs(main_frame,
+                     JsReplace("var portal = document.createElement('portal');"
+                               "portal.src = $1;"
+                               "document.body.appendChild(portal);",
+                               a_url)));
+
+  // Wait for portal to be created.
+  portal = portal_created_observer.WaitUntilPortalCreated();
+  WebContentsImpl* portal_contents = portal->GetPortalContents();
+  FrameTreeNode* portal_main_frame_node =
+      portal_contents->GetFrameTree()->root();
+
+  // The portal should not have navigated yet, wait for the first navigation.
+  TestNavigationObserver navigation_observer(portal_contents);
+  navigation_observer.Wait();
+
+  // Remove portal from document and wait for frames to be deleted.
+  FrameDeletedObserver fdo1(portal_main_frame_node->render_manager()
+                                ->GetOuterDelegateNode()
+                                ->current_frame_host());
+  FrameDeletedObserver fdo2(
+      portal_main_frame_node->child_at(0)->current_frame_host());
+  EXPECT_TRUE(ExecJs(main_frame, "document.body.removeChild(portal);"));
+  fdo1.Wait();
+  fdo2.Wait();
+}
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 43baa4c..60e37cf 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -854,15 +854,35 @@
 
   GetScreenInfo(&visual_properties->screen_info);
 
-  if (delegate_) {
+  if (!delegate_) {
+    visual_properties->display_mode = blink::kWebDisplayModeBrowser;
+  } else {
     visual_properties->is_fullscreen_granted =
         delegate_->IsFullscreenForCurrentTab();
     visual_properties->display_mode = delegate_->GetDisplayMode(this);
     visual_properties->zoom_level = delegate_->GetPendingPageZoomLevel();
-  } else {
-    visual_properties->is_fullscreen_granted = false;
-    visual_properties->display_mode = blink::kWebDisplayModeBrowser;
-    visual_properties->zoom_level = 0;
+
+    RenderViewHostDelegateView* rvh_delegate_view =
+        delegate_->GetDelegateView();
+    DCHECK(rvh_delegate_view);
+
+    visual_properties->browser_controls_shrink_blink_size =
+        rvh_delegate_view->DoBrowserControlsShrinkRendererSize();
+
+    float top_controls_height = rvh_delegate_view->GetTopControlsHeight();
+    float bottom_controls_height = rvh_delegate_view->GetBottomControlsHeight();
+    float browser_controls_dsf_multiplier = 1.f;
+    // The top and bottom control sizes are physical pixels but the IPC wants
+    // DIPs *when not using page zoom for DSF* because blink layout is working
+    // in DIPs then.
+    if (!IsUseZoomForDSFEnabled()) {
+      browser_controls_dsf_multiplier =
+          visual_properties->screen_info.device_scale_factor;
+    }
+    visual_properties->top_controls_height =
+        top_controls_height / browser_controls_dsf_multiplier;
+    visual_properties->bottom_controls_height =
+        bottom_controls_height / browser_controls_dsf_multiplier;
   }
 
   visual_properties->auto_resize_enabled = auto_resize_enabled_;
@@ -872,32 +892,6 @@
   visual_properties->page_scale_factor = page_scale_factor_;
 
   if (view_) {
-    // TODO(danakj): Move this browser controls code out of the if-view block?
-    if (delegate_) {
-      RenderViewHostDelegateView* rvh_delegate_view =
-          delegate_->GetDelegateView();
-      DCHECK(rvh_delegate_view);
-
-      visual_properties->browser_controls_shrink_blink_size =
-          rvh_delegate_view->DoBrowserControlsShrinkRendererSize();
-
-      float top_controls_height = rvh_delegate_view->GetTopControlsHeight();
-      float bottom_controls_height =
-          rvh_delegate_view->GetBottomControlsHeight();
-      float browser_controls_dsf_multiplier = 1.f;
-      // The top and bottom control sizes are physical pixels but the IPC wants
-      // DIPs *when not using page zoom for DSF* because blink layout is working
-      // in DIPs then.
-      if (!IsUseZoomForDSFEnabled()) {
-        browser_controls_dsf_multiplier =
-            visual_properties->screen_info.device_scale_factor;
-      }
-      visual_properties->top_controls_height =
-          top_controls_height / browser_controls_dsf_multiplier;
-      visual_properties->bottom_controls_height =
-          bottom_controls_height / browser_controls_dsf_multiplier;
-    }
-
     visual_properties->new_size = view_->GetRequestedRendererSize();
     visual_properties->capture_sequence_number =
         view_->GetCaptureSequenceNumber();
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index c9dc674..001baf7 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -2276,6 +2276,29 @@
 #endif
 }
 
+IN_PROC_BROWSER_TEST_F(SitePerProcessProgrammaticScrollTest,
+                       ScrolledOutOfView) {
+  GURL main_frame(
+      embedded_test_server()->GetURL("a.com", kIframeOutOfViewHTML));
+  GURL child_url_b(
+      embedded_test_server()->GetURL("b.com", kIframeOutOfViewHTML));
+
+  // This will set up the page frame tree as A(B()).
+  ASSERT_TRUE(NavigateToURL(shell(), main_frame));
+  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+  WaitForOnLoad(root);
+  NavigateFrameToURL(root->child_at(0), child_url_b);
+  WaitForOnLoad(root->child_at(0));
+
+  FrameTreeNode* nested_iframe_node = root->child_at(0);
+  RenderFrameProxyHost* proxy_to_parent =
+      nested_iframe_node->render_manager()->GetProxyToParent();
+  CrossProcessFrameConnector* connector =
+      proxy_to_parent->cross_process_frame_connector();
+  ASSERT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport,
+            connector->visibility());
+}
+
 // This test verifies that smooth scrolling works correctly inside nested OOPIFs
 // which are same origin with the parent. Note that since the frame tree has
 // a A(B(A1())) structure, if and A1 and A2 shared the same
@@ -2394,68 +2417,6 @@
   EXPECT_FALSE(RenderViewHost::FromID(subframe_process_id, subframe_rvh_id));
 }
 
-// Ensure that root frames cannot be detached.
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, RestrictFrameDetach) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // It is safe to obtain the root frame tree node here, as it doesn't change.
-  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
-
-  TestNavigationObserver observer(shell()->web_contents());
-
-  // Load cross-site pages into both iframes.
-  GURL foo_url = embedded_test_server()->GetURL("foo.com", "/title2.html");
-  NavigateFrameToURL(root->child_at(0), foo_url);
-  EXPECT_TRUE(observer.last_navigation_succeeded());
-  EXPECT_EQ(foo_url, observer.last_navigation_url());
-  GURL bar_url = embedded_test_server()->GetURL("bar.com", "/title2.html");
-  NavigateFrameToURL(root->child_at(1), bar_url);
-  EXPECT_TRUE(observer.last_navigation_succeeded());
-  EXPECT_EQ(bar_url, observer.last_navigation_url());
-
-  // Ensure that we have created new processes for the subframes.
-  ASSERT_EQ(2U, root->child_count());
-  FrameTreeNode* foo_child = root->child_at(0);
-  SiteInstance* foo_site_instance =
-      foo_child->current_frame_host()->GetSiteInstance();
-  EXPECT_NE(shell()->web_contents()->GetSiteInstance(), foo_site_instance);
-  FrameTreeNode* bar_child = root->child_at(1);
-  SiteInstance* bar_site_instance =
-      bar_child->current_frame_host()->GetSiteInstance();
-  EXPECT_NE(shell()->web_contents()->GetSiteInstance(), bar_site_instance);
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B C\n"
-      "   |--Site B ------- proxies for A C\n"
-      "   +--Site C ------- proxies for A B\n"
-      "Where A = http://a.com/\n"
-      "      B = http://foo.com/\n"
-      "      C = http://bar.com/",
-      DepictFrameTree(root));
-
-  // Simulate an attempt to detach the root frame from foo_site_instance.  This
-  // should kill foo_site_instance's process.
-  RenderFrameProxyHost* foo_mainframe_rfph =
-      root->render_manager()->GetRenderFrameProxyHost(foo_site_instance);
-  content::RenderProcessHostKillWaiter kill_waiter(
-      foo_mainframe_rfph->GetProcess());
-  FrameHostMsg_Detach evil_msg2(foo_mainframe_rfph->GetRoutingID());
-  IPC::IpcSecurityTestUtil::PwnMessageReceived(
-      foo_mainframe_rfph->GetProcess()->GetChannel(), evil_msg2);
-  EXPECT_EQ(bad_message::RFPH_DETACH, kill_waiter.Wait());
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B C\n"
-      "   |--Site B ------- proxies for A C\n"
-      "   +--Site C ------- proxies for A B\n"
-      "Where A = http://a.com/\n"
-      "      B = http://foo.com/ (no process)\n"
-      "      C = http://bar.com/",
-      DepictFrameTree(root));
-}
-
 IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigateRemoteFrame) {
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))"));
diff --git a/content/common/frame.mojom b/content/common/frame.mojom
index 7abc0ae2..57f2cfc 100644
--- a/content/common/frame.mojom
+++ b/content/common/frame.mojom
@@ -20,6 +20,7 @@
 import "services/viz/public/interfaces/compositing/surface_id.mojom";
 import "third_party/blink/public/mojom/blob/blob_url_store.mojom";
 import "third_party/blink/public/mojom/feature_policy/feature_policy.mojom";
+import "third_party/blink/public/mojom/frame/lifecycle.mojom";
 import "third_party/blink/public/mojom/frame/navigation_initiator.mojom";
 import "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom";
 import "third_party/blink/public/mojom/portal/portal.mojom";
@@ -383,6 +384,10 @@
   // longer fullscreen.
   FullscreenStateChanged(bool is_fullscreen);
 
+  // Notifies the browser that the current frame has changed its visibility
+  // status.
+  VisibilityChanged(blink.mojom.FrameVisibility visibility);
+
   // Updates information to determine whether a user gesture should carryover to
   // future navigations. This is needed so navigations within a certain
   // timeframe of a request initiated by a gesture will be treated as if they
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 5786c40..c8a145df 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -62,6 +62,7 @@
 #include "third_party/blink/public/common/messaging/transferable_message.h"
 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
+#include "third_party/blink/public/mojom/frame/lifecycle.mojom.h"
 #include "third_party/blink/public/platform/web_focus_type.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/public/platform/web_intrinsic_sizing_info.h"
@@ -155,6 +156,8 @@
                               content::NavigationDownloadPolicy::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::FeaturePolicyDisposition,
                           blink::mojom::FeaturePolicyDisposition::kMaxValue)
+IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::FrameVisibility,
+                          blink::mojom::FrameVisibility::kMaxValue)
 
 IPC_STRUCT_TRAITS_BEGIN(blink::WebFloatSize)
   IPC_STRUCT_TRAITS_MEMBER(width)
@@ -1462,7 +1465,8 @@
                     bool /* occluded or obscured */)
 
 // Informs the child that the frame has changed visibility.
-IPC_MESSAGE_ROUTED1(FrameHostMsg_VisibilityChanged, bool /* visible */)
+IPC_MESSAGE_ROUTED1(FrameHostMsg_VisibilityChanged,
+                    blink::mojom::FrameVisibility /* visibility */)
 
 // Sent by a RenderFrameProxy to the browser signaling that the renderer
 // has determined the DOM subtree it represents is inert and should no
diff --git a/content/public/test/unittest_test_suite.cc b/content/public/test/unittest_test_suite.cc
index 5d1c4b9..788e922 100644
--- a/content/public/test/unittest_test_suite.cc
+++ b/content/public/test/unittest_test_suite.cc
@@ -36,6 +36,18 @@
   std::string disabled =
       command_line->GetSwitchValueASCII(switches::kDisableFeatures);
 
+  // Unit tests don't currently work with the Network Service enabled.
+  // base::TestSuite will reset the FeatureList, so modify the underlying
+  // CommandLine object to disable the network service when it's parsed again.
+  disabled += ",NetworkService";
+  base::CommandLine new_command_line(command_line->GetProgram());
+  base::CommandLine::SwitchMap switches = command_line->GetSwitches();
+  switches.erase(switches::kDisableFeatures);
+  new_command_line.AppendSwitchASCII(switches::kDisableFeatures, disabled);
+  for (const auto& iter : switches)
+    new_command_line.AppendSwitchNative(iter.first, iter.second);
+  *base::CommandLine::ForCurrentProcess() = new_command_line;
+
   // The TaskScheduler created by the test launcher is never destroyed.
   // Similarly, the FeatureList created here is never destroyed so it
   // can safely be accessed by the TaskScheduler.
@@ -47,6 +59,7 @@
 #if defined(OS_FUCHSIA)
   // Use headless ozone platform on Fuchsia by default.
   // TODO(crbug.com/865172): Remove this flag.
+  command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line->HasSwitch(switches::kOzonePlatform))
     command_line->AppendSwitchASCII(switches::kOzonePlatform, "headless");
 #endif
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index db91a60..c5a69ff 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2561,6 +2561,11 @@
   frame_->OnPortalActivated();
 }
 
+void RenderFrameImpl::VisibilityChanged(
+    blink::mojom::FrameVisibility visibility) {
+  GetFrameHost()->VisibilityChanged(visibility);
+}
+
 #if defined(OS_ANDROID)
 void RenderFrameImpl::ExtractSmartClipData(
     const gfx::Rect& rect,
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 381d964..d8271dc 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -824,6 +824,7 @@
   void BubbleLogicalScrollInParentFrame(
       blink::WebScrollDirection direction,
       blink::WebScrollGranularity granularity) override;
+  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override;
 
   // WebFrameSerializerClient implementation:
   void DidSerializeDataForFrame(
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index d29afb3..b2eef59 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -729,7 +729,7 @@
   mus_embedded_frame_.reset();
 #endif
 
-  if (type == DetachType::kRemove && web_frame_->Parent()) {
+  if (type == DetachType::kRemove) {
     // Let the browser process know this subframe is removed, so that it is
     // destroyed in its current process.
     Send(new FrameHostMsg_Detach(routing_id_));
@@ -858,8 +858,9 @@
       last_compositor_visible_rect_, last_occluded_or_obscured_));
 }
 
-void RenderFrameProxy::VisibilityChanged(bool visible) {
-  Send(new FrameHostMsg_VisibilityChanged(routing_id_, visible));
+void RenderFrameProxy::VisibilityChanged(
+    blink::mojom::FrameVisibility visibility) {
+  Send(new FrameHostMsg_VisibilityChanged(routing_id_, visibility));
 }
 
 void RenderFrameProxy::SetIsInert(bool inert) {
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index 0f94066..05438f7 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -209,7 +209,7 @@
   void UpdateRemoteViewportIntersection(
       const blink::WebRect& viewport_intersection,
       bool occluded_or_obscured) override;
-  void VisibilityChanged(bool visible) override;
+  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override;
   void SetIsInert(bool) override;
   void SetInheritedEffectiveTouchAction(cc::TouchAction) override;
   void UpdateRenderThrottlingStatus(bool is_throttled,
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 318f5078..398e0972 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -908,8 +908,6 @@
   settings->SetSmoothScrollForFindEnabled(prefs.smooth_scroll_for_find_enabled);
 
   settings->SetHideDownloadUI(prefs.hide_download_ui);
-  WebRuntimeFeatures::EnableNewRemotePlaybackPipeline(
-      base::FeatureList::IsEnabled(media::kNewRemotePlaybackPipeline));
 
   settings->SetPresentationReceiver(prefs.presentation_receiver);
 
diff --git a/content/renderer/renderer_main_platform_delegate_fuchsia.cc b/content/renderer/renderer_main_platform_delegate_fuchsia.cc
index 7af6212..376b47f 100644
--- a/content/renderer/renderer_main_platform_delegate_fuchsia.cc
+++ b/content/renderer/renderer_main_platform_delegate_fuchsia.cc
@@ -16,9 +16,6 @@
 void RendererMainPlatformDelegate::PlatformUninitialize() {}
 
 bool RendererMainPlatformDelegate::EnableSandbox() {
-  // TODO(750938): Report NOTIMPLEMENTED() here until we re-enable sandboxing
-  // of sub-processes.
-  NOTIMPLEMENTED();
   return true;
 }
 
diff --git a/content/test/test_render_frame.cc b/content/test/test_render_frame.cc
index 0d148c8a..4cc3e61f 100644
--- a/content/test/test_render_frame.cc
+++ b/content/test/test_render_frame.cc
@@ -145,6 +145,8 @@
 
   void FullscreenStateChanged(bool is_fullscreen) override {}
 
+  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override {}
+
 #if defined(OS_ANDROID)
   void UpdateUserGestureCarryoverInfo() override {}
 #endif
diff --git a/fuchsia/common/webrunner_content_client.cc b/fuchsia/common/webrunner_content_client.cc
index 019d7e0..f27320e6 100644
--- a/fuchsia/common/webrunner_content_client.cc
+++ b/fuchsia/common/webrunner_content_client.cc
@@ -36,7 +36,7 @@
 }
 
 blink::OriginTrialPolicy* WebRunnerContentClient::GetOriginTrialPolicy() {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return nullptr;
 }
 
diff --git a/fuchsia/http/sandbox_policy b/fuchsia/http/sandbox_policy
index 15be6c0..63bb84253 100644
--- a/fuchsia/http/sandbox_policy
+++ b/fuchsia/http/sandbox_policy
@@ -2,6 +2,7 @@
   "features": [ "root-ssl-certificates" ],
   "services": [
       "fuchsia.net.LegacySocketProvider",
+      "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack"
   ]
 }
diff --git a/fuchsia/runners/cast/sandbox_policy b/fuchsia/runners/cast/sandbox_policy
index aa36caa..bb596db1 100644
--- a/fuchsia/runners/cast/sandbox_policy
+++ b/fuchsia/runners/cast/sandbox_policy
@@ -5,6 +5,7 @@
       "fuchsia.fonts.Provider",
       "fuchsia.media.Audio",
       "fuchsia.net.LegacySocketProvider",
+      "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
       "fuchsia.process.Launcher",
       "fuchsia.ui.input.ImeService",
diff --git a/fuchsia/runners/web/sandbox_policy b/fuchsia/runners/web/sandbox_policy
index 9f8cb63..7db7d14 100644
--- a/fuchsia/runners/web/sandbox_policy
+++ b/fuchsia/runners/web/sandbox_policy
@@ -6,6 +6,7 @@
       "fuchsia.media.Audio",
       "fuchsia.mediacodec.CodecFactory",
       "fuchsia.net.LegacySocketProvider",
+      "fuchsia.net.SocketProvider",
       "fuchsia.netstack.Netstack",
       "fuchsia.process.Launcher",
       "fuchsia.ui.input.ImeService",
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index cd1605f..24c1570 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -272,6 +272,7 @@
   include_dirs = [ "//third_party/mesa_headers" ]
 
   public_deps = [
+    "//cc/paint",
     "//gpu/command_buffer/common",
     "//gpu/command_buffer/common:gles2_sources",
     "//gpu/command_buffer/common:raster_sources",
@@ -283,7 +284,6 @@
     ":service",
     "//base",
     "//base/third_party/dynamic_annotations",
-    "//cc/paint",
     "//components/viz/common:resource_format_utils",
     "//gpu/command_buffer/client",
     "//gpu/command_buffer/common:gles2_utils",
diff --git a/gpu/ipc/service/DEPS b/gpu/ipc/service/DEPS
index 0af5af5..0f692245 100644
--- a/gpu/ipc/service/DEPS
+++ b/gpu/ipc/service/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+cc/paint",
   "+components/viz/common/features.h",
   "+components/viz/common/resources/resource_format.h",
   "+third_party/skia",
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc
index 500fcc4..899ad32 100644
--- a/gpu/ipc/service/command_buffer_stub.cc
+++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/shared_memory.h"
+#include "base/no_destructor.h"
 #include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
@@ -782,13 +783,23 @@
 }
 
 std::unique_ptr<MemoryTracker> CommandBufferStub::CreateMemoryTracker(
-    const GPUCreateCommandBufferConfig init_params) const {
+    const GPUCreateCommandBufferConfig& init_params) const {
+  MemoryTrackerFactory current_factory = GetMemoryTrackerFactory();
+  if (current_factory)
+    return current_factory.Run(init_params);
+
   return std::make_unique<GpuCommandBufferMemoryTracker>(
       channel_->client_id(), channel_->client_tracing_id(),
       command_buffer_id_.GetUnsafeValue(), init_params.attribs.context_type,
       channel_->task_runner());
 }
 
+// static
+void CommandBufferStub::SetMemoryTrackerFactoryForTesting(
+    MemoryTrackerFactory factory) {
+  SetOrGetMemoryTrackerFactory(factory);
+}
+
 MemoryTracker* CommandBufferStub::GetMemoryTracker() const {
   return context_group_->memory_tracker();
 }
@@ -820,4 +831,20 @@
   command_buffer_->SetParseError(error::kLostContext);
 }
 
+// static
+CommandBufferStub::MemoryTrackerFactory
+CommandBufferStub::GetMemoryTrackerFactory() {
+  return SetOrGetMemoryTrackerFactory(base::NullCallback());
+}
+
+// static
+CommandBufferStub::MemoryTrackerFactory
+CommandBufferStub::SetOrGetMemoryTrackerFactory(MemoryTrackerFactory factory) {
+  static base::NoDestructor<MemoryTrackerFactory> current_factory{
+      base::NullCallback()};
+  if (factory)
+    *current_factory = factory;
+  return *current_factory;
+}
+
 }  // namespace gpu
diff --git a/gpu/ipc/service/command_buffer_stub.h b/gpu/ipc/service/command_buffer_stub.h
index a1fb4bed..69fa89e 100644
--- a/gpu/ipc/service/command_buffer_stub.h
+++ b/gpu/ipc/service/command_buffer_stub.h
@@ -12,6 +12,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -44,6 +45,7 @@
 namespace gpu {
 class DecoderContext;
 struct Mailbox;
+class MemoryTracker;
 struct SyncToken;
 struct WaitForCommandState;
 class GpuChannel;
@@ -102,6 +104,14 @@
   void OnRescheduleAfterFinished() override;
   void ScheduleGrContextCleanup() override;
 
+  using MemoryTrackerFactory =
+      base::RepeatingCallback<std::unique_ptr<MemoryTracker>(
+          const GPUCreateCommandBufferConfig&)>;
+
+  // Overrides the way CreateMemoryTracker() uses to create a MemoryTracker.
+  // This is intended for mocking the MemoryTracker in tests.
+  static void SetMemoryTrackerFactoryForTesting(MemoryTrackerFactory factory);
+
   MemoryTracker* GetMemoryTracker() const;
 
   // Whether this command buffer can currently handle IPC messages.
@@ -138,7 +148,7 @@
                                GpuChannel* channel);
 
   std::unique_ptr<MemoryTracker> CreateMemoryTracker(
-      const GPUCreateCommandBufferConfig init_params) const;
+      const GPUCreateCommandBufferConfig& init_params) const;
 
   // Must be called during Initialize(). Takes ownership to co-ordinate
   // teardown in Destroy().
@@ -233,6 +243,16 @@
   static void SetContextGpuFeatureInfo(gl::GLContext* context,
                                        const GpuFeatureInfo& gpu_feature_info);
 
+  static MemoryTrackerFactory GetMemoryTrackerFactory();
+
+  // Overrides the way CreateMemoryTracker() uses to create a MemoryTracker. If
+  // |factory| is base::NullCallback(), it returns the current
+  // MemoryTrackerFactory (initially base::NullCallback() which
+  // CreateMemoryTracker() should interpret as a signal to use the default).
+  // This is intended for mocking the MemoryTracker in tests.
+  static MemoryTrackerFactory SetOrGetMemoryTrackerFactory(
+      MemoryTrackerFactory factory);
+
   std::unique_ptr<DecoderContext> decoder_context_;
 
   uint32_t last_flush_id_;
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index b542d16..9546122 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -267,6 +267,7 @@
       static_cast<int32_t>(GpuChannelReservedRoutes::kImageDecodeAccelerator)) {
     if (!image_decode_accelerator_stub_->OnMessageReceived(message))
       return MessageErrorHandler(message, "Invalid image decode request");
+    return true;
   }
 
   bool handle_out_of_order =
diff --git a/gpu/ipc/service/gpu_channel_manager_unittest.cc b/gpu/ipc/service/gpu_channel_manager_unittest.cc
index 25dbb562..5d170b3 100644
--- a/gpu/ipc/service/gpu_channel_manager_unittest.cc
+++ b/gpu/ipc/service/gpu_channel_manager_unittest.cc
@@ -15,7 +15,8 @@
 
 class GpuChannelManagerTest : public GpuChannelTestCommon {
  public:
-  GpuChannelManagerTest() : GpuChannelTestCommon() {}
+  GpuChannelManagerTest()
+      : GpuChannelTestCommon(true /* use_stub_bindings */) {}
   ~GpuChannelManagerTest() override = default;
 
 #if defined(OS_ANDROID)
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc
index 81a5e03..4a3ba8f4 100644
--- a/gpu/ipc/service/gpu_channel_test_common.cc
+++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -46,18 +46,22 @@
   DISALLOW_COPY_AND_ASSIGN(TestGpuChannelManagerDelegate);
 };
 
-GpuChannelTestCommon::GpuChannelTestCommon()
-    : GpuChannelTestCommon(std::vector<int32_t>()) {}
+GpuChannelTestCommon::GpuChannelTestCommon(bool use_stub_bindings)
+    : GpuChannelTestCommon(std::vector<int32_t>(), use_stub_bindings) {}
 
 GpuChannelTestCommon::GpuChannelTestCommon(
-    std::vector<int32_t> enabled_workarounds)
+    std::vector<int32_t> enabled_workarounds,
+    bool use_stub_bindings)
     : task_runner_(new base::TestSimpleTaskRunner),
       io_task_runner_(new base::TestSimpleTaskRunner),
       sync_point_manager_(new SyncPointManager()),
       scheduler_(new Scheduler(task_runner_, sync_point_manager_.get())),
       channel_manager_delegate_(new TestGpuChannelManagerDelegate()) {
   // We need GL bindings to actually initialize command buffers.
-  gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings();
+  if (use_stub_bindings)
+    gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings();
+  else
+    gl::GLSurfaceTestSupport::InitializeOneOff();
 
   GpuFeatureInfo feature_info;
   feature_info.enabled_gpu_driver_bug_workarounds =
diff --git a/gpu/ipc/service/gpu_channel_test_common.h b/gpu/ipc/service/gpu_channel_test_common.h
index cb2ccce..a8575a39 100644
--- a/gpu/ipc/service/gpu_channel_test_common.h
+++ b/gpu/ipc/service/gpu_channel_test_common.h
@@ -6,6 +6,7 @@
 #define GPU_IPC_SERVICE_GPU_CHANNEL_TEST_COMMON_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/unsafe_shared_memory_region.h"
@@ -29,9 +30,10 @@
 
 class GpuChannelTestCommon : public testing::Test {
  public:
-  GpuChannelTestCommon();
+  explicit GpuChannelTestCommon(bool use_stub_bindings);
   // Constructor which allows a custom set of GPU driver bug workarounds.
-  explicit GpuChannelTestCommon(std::vector<int32_t> enabled_workarounds);
+  GpuChannelTestCommon(std::vector<int32_t> enabled_workarounds,
+                       bool use_stub_bindings);
   ~GpuChannelTestCommon() override;
 
  protected:
diff --git a/gpu/ipc/service/gpu_channel_unittest.cc b/gpu/ipc/service/gpu_channel_unittest.cc
index b290851e..08d3aa4 100644
--- a/gpu/ipc/service/gpu_channel_unittest.cc
+++ b/gpu/ipc/service/gpu_channel_unittest.cc
@@ -12,7 +12,11 @@
 
 namespace gpu {
 
-class GpuChannelTest : public GpuChannelTestCommon {};
+class GpuChannelTest : public GpuChannelTestCommon {
+ public:
+  GpuChannelTest() : GpuChannelTestCommon(true /* use_stub_bindings */) {}
+  ~GpuChannelTest() override = default;
+};
 
 #if defined(OS_WIN)
 const SurfaceHandle kFakeSurfaceHandle = reinterpret_cast<SurfaceHandle>(1);
@@ -234,7 +238,8 @@
 class GpuChannelExitForContextLostTest : public GpuChannelTestCommon {
  public:
   GpuChannelExitForContextLostTest()
-      : GpuChannelTestCommon({EXIT_ON_CONTEXT_LOST}) {}
+      : GpuChannelTestCommon({EXIT_ON_CONTEXT_LOST} /* enabled_workarounds */,
+                             true /* use_stub_bindings */) {}
 };
 
 TEST_F(GpuChannelExitForContextLostTest, CreateFailsDuringLostContextShutdown) {
diff --git a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
index ac9f6a2..9a80565 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
+++ b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
@@ -6,32 +6,68 @@
 #include <stdint.h>
 
 #include <utility>
-#include <vector>
 
+#include "base/atomicops.h"
+#include "base/bind.h"
 #include "base/containers/queue.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/numerics/checked_math.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_simple_task_runner.h"
+#include "cc/paint/image_transfer_cache_entry.h"
+#include "cc/paint/transfer_cache_entry.h"
+#include "gpu/command_buffer/common/buffer.h"
+#include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/command_buffer/common/constants.h"
+#include "gpu/command_buffer/common/context_creation_attribs.h"
+#include "gpu/command_buffer/common/context_result.h"
+#include "gpu/command_buffer/common/scheduling_priority.h"
 #include "gpu/command_buffer/common/sync_token.h"
+#include "gpu/command_buffer/service/context_group.h"
+#include "gpu/command_buffer/service/decoder_context.h"
+#include "gpu/command_buffer/service/mocks.h"
+#include "gpu/command_buffer/service/service_transfer_cache.h"
+#include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
+#include "gpu/command_buffer/service/transfer_buffer_manager.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "gpu/ipc/common/command_buffer_id.h"
 #include "gpu/ipc/common/gpu_messages.h"
+#include "gpu/ipc/common/surface_handle.h"
+#include "gpu/ipc/service/command_buffer_stub.h"
+#include "gpu/ipc/service/gpu_channel.h"
 #include "gpu/ipc/service/gpu_channel_manager.h"
 #include "gpu/ipc/service/gpu_channel_test_common.h"
 #include "gpu/ipc/service/image_decode_accelerator_worker.h"
+#include "ipc/ipc_message.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkSize.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
+#include "url/gurl.h"
 
 using testing::InSequence;
 using testing::StrictMock;
 
 namespace gpu {
-class GpuChannel;
+class MemoryTracker;
+
+namespace {
+
+std::unique_ptr<MemoryTracker> CreateMockMemoryTracker(
+    const GPUCreateCommandBufferConfig& init_params) {
+  return std::make_unique<gles2::MockMemoryTracker>();
+}
+
+scoped_refptr<Buffer> MakeBufferForTesting() {
+  return MakeMemoryBuffer(sizeof(base::subtle::Atomic32));
+}
+
+}  // namespace
 
 // This mock allows individual tests to decide asynchronously when to finish a
 // decode by using the FinishOneDecode() method.
@@ -83,19 +119,45 @@
 
 const int kChannelId = 1;
 
+const int32_t kCommandBufferRouteId =
+    static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue) + 1;
+
 // Test fixture: the general strategy for testing is to have a GPU channel test
 // infrastructure (provided by GpuChannelTestCommon), ask the channel to handle
-// decode requests, and expect sync token releases and invokations to the
-// ImageDecodeAcceleratorWorker functionality.
+// decode requests, and expect sync token releases, invocations to the
+// ImageDecodeAcceleratorWorker functionality, and transfer cache entry
+// creation.
 class ImageDecodeAcceleratorStubTest : public GpuChannelTestCommon {
  public:
-  ImageDecodeAcceleratorStubTest() : GpuChannelTestCommon() {}
+  ImageDecodeAcceleratorStubTest()
+      : GpuChannelTestCommon(false /* use_stub_bindings */) {}
   ~ImageDecodeAcceleratorStubTest() override = default;
 
   SyncPointManager* sync_point_manager() const {
     return channel_manager()->sync_point_manager();
   }
 
+  ServiceTransferCache* GetServiceTransferCache() {
+    ContextResult context_result;
+    scoped_refptr<SharedContextState> shared_context_state =
+        channel_manager()->GetSharedContextState(&context_result);
+    if (context_result != ContextResult::kSuccess || !shared_context_state) {
+      return nullptr;
+    }
+    return shared_context_state->transfer_cache();
+  }
+
+  int GetRasterDecoderId() {
+    GpuChannel* channel = channel_manager()->LookupChannel(kChannelId);
+    if (!channel)
+      return -1;
+    CommandBufferStub* command_buffer =
+        channel->LookupCommandBuffer(kCommandBufferRouteId);
+    if (!command_buffer || !command_buffer->decoder_context())
+      return -1;
+    return command_buffer->decoder_context()->GetRasterDecoderId();
+  }
+
   void SetUp() override {
     GpuChannelTestCommon::SetUp();
     // TODO(andrescj): get rid of the |feature_list_| when the feature is
@@ -104,7 +166,49 @@
         features::kVaapiJpegImageDecodeAcceleration);
     channel_manager()->SetImageDecodeAcceleratorWorkerForTesting(
         &image_decode_accelerator_worker_);
-    ASSERT_TRUE(CreateChannel(kChannelId, false /* is_gpu_host */));
+
+    // Initialize the GrContext so that texture uploading works.
+    ContextResult context_result;
+    scoped_refptr<SharedContextState> shared_context_state =
+        channel_manager()->GetSharedContextState(&context_result);
+    ASSERT_EQ(ContextResult::kSuccess, context_result);
+    ASSERT_TRUE(shared_context_state);
+    shared_context_state->InitializeGrContext(GpuDriverBugWorkarounds(),
+                                              nullptr);
+
+    GpuChannel* channel = CreateChannel(kChannelId, false /* is_gpu_host */);
+    ASSERT_TRUE(channel);
+
+    // Create a raster command buffer so that the ImageDecodeAcceleratorStub can
+    // have access to a TransferBufferManager. Note that we mock the
+    // MemoryTracker because GpuCommandBufferMemoryTracker uses a timer that
+    // would make RunTasksUntilIdle() run forever.
+    CommandBufferStub::SetMemoryTrackerFactoryForTesting(
+        base::BindRepeating(&CreateMockMemoryTracker));
+    GPUCreateCommandBufferConfig init_params;
+    init_params.surface_handle = kNullSurfaceHandle;
+    init_params.share_group_id = MSG_ROUTING_NONE;
+    init_params.stream_id = 0;
+    init_params.stream_priority = SchedulingPriority::kNormal;
+    init_params.attribs = ContextCreationAttribs();
+    init_params.attribs.enable_gles2_interface = false;
+    init_params.attribs.enable_raster_interface = true;
+    init_params.attribs.bind_generates_resource = false;
+    init_params.active_url = GURL();
+    ContextResult result = ContextResult::kTransientFailure;
+    Capabilities capabilities;
+    HandleMessage(channel,
+                  new GpuChannelMsg_CreateCommandBuffer(
+                      init_params, kCommandBufferRouteId,
+                      GetSharedMemoryRegion(), &result, &capabilities));
+    ASSERT_EQ(ContextResult::kSuccess, result);
+    CommandBufferStub* command_buffer =
+        channel->LookupCommandBuffer(kCommandBufferRouteId);
+    ASSERT_TRUE(command_buffer);
+
+    // Make sure there are no pending tasks before starting the test.
+    ASSERT_EQ(0u, task_runner()->NumPendingTasks());
+    ASSERT_EQ(0u, io_task_runner()->NumPendingTasks());
   }
 
   void TearDown() override {
@@ -114,7 +218,8 @@
   }
 
   SyncToken SendDecodeRequest(const gfx::Size& output_size,
-                              uint64_t release_count) {
+                              uint64_t release_count,
+                              uint32_t transfer_cache_entry_id) {
     GpuChannel* channel = channel_manager()->LookupChannel(kChannelId);
     if (!channel) {
       // It's possible that the channel was destroyed as part of an earlier
@@ -124,18 +229,41 @@
       return SyncToken();
     }
 
+    // Create the decode sync token for the decode request so that we can test
+    // that it's actually released.
     SyncToken decode_sync_token(
         CommandBufferNamespace::GPU_IO,
         CommandBufferIdFromChannelAndRoute(
             kChannelId, static_cast<int32_t>(
                             GpuChannelReservedRoutes::kImageDecodeAccelerator)),
         release_count);
+
+    // We need a buffer to make sure that the ImageDecodeAcceleratorStub can
+    // create a ServiceDiscardableHandle.
+    scoped_refptr<Buffer> handle_buffer = MakeBufferForTesting();
+    CommandBufferStub* command_buffer =
+        channel->LookupCommandBuffer(kCommandBufferRouteId);
+    if (!command_buffer)
+      return SyncToken();
+    scoped_refptr<gles2::ContextGroup> context_group =
+        command_buffer->context_group();
+    if (!context_group)
+      return SyncToken();
+    TransferBufferManager* transfer_buffer_manager =
+        context_group->transfer_buffer_manager();
+    if (!transfer_buffer_manager)
+      return SyncToken();
+    int32_t buffer_shm_id = GetNextBufferId();
+    transfer_buffer_manager->RegisterTransferBuffer(buffer_shm_id,
+                                                    std::move(handle_buffer));
+
+    // Send the IPC decode request.
     GpuChannelMsg_ScheduleImageDecode_Params decode_params;
     decode_params.encoded_data = std::vector<uint8_t>();
     decode_params.output_size = output_size;
-    decode_params.raster_decoder_route_id = 1;
-    decode_params.transfer_cache_entry_id = 1u;
-    decode_params.discardable_handle_shm_id = 0;
+    decode_params.raster_decoder_route_id = kCommandBufferRouteId;
+    decode_params.transfer_cache_entry_id = transfer_cache_entry_id;
+    decode_params.discardable_handle_shm_id = buffer_shm_id;
     decode_params.discardable_handle_shm_offset = 0u;
     decode_params.target_color_space = gfx::ColorSpace();
     decode_params.needs_mips = false;
@@ -157,6 +285,35 @@
     }
   }
 
+  void CheckTransferCacheEntries(std::vector<SkISize> expected_sizes) {
+    ServiceTransferCache* transfer_cache = GetServiceTransferCache();
+    ASSERT_TRUE(transfer_cache);
+
+    // First, check the number of entries and early out if 0 entries are
+    // expected.
+    const size_t num_actual_cache_entries =
+        transfer_cache->entries_count_for_testing();
+    ASSERT_EQ(expected_sizes.size(), num_actual_cache_entries);
+    if (expected_sizes.empty())
+      return;
+
+    // Then, check the dimensions of the entries to make sure they are as
+    // expected.
+    int raster_decoder_id = GetRasterDecoderId();
+    ASSERT_GE(raster_decoder_id, 0);
+    for (size_t i = 0; i < num_actual_cache_entries; i++) {
+      auto* decode_entry = static_cast<cc::ServiceImageTransferCacheEntry*>(
+          transfer_cache->GetEntry(ServiceTransferCache::EntryKey(
+              raster_decoder_id, cc::TransferCacheEntryType::kImage, i + 1)));
+      ASSERT_TRUE(decode_entry);
+      ASSERT_TRUE(decode_entry->image());
+      EXPECT_EQ(expected_sizes[i].width(),
+                decode_entry->image()->dimensions().width());
+      EXPECT_EQ(expected_sizes[i].height(),
+                decode_entry->image()->dimensions().height());
+    }
+  }
+
  protected:
   StrictMock<MockImageDecodeAcceleratorWorker> image_decode_accelerator_worker_;
 
@@ -170,11 +327,8 @@
 // completed. This should cause one sync token to be released and the scheduler
 // sequence to be disabled. Then, the second decode is completed. This should
 // cause the other sync token to be released.
-//
-// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
-// infrastructure. See https://crbug.com/868400.
 TEST_F(ImageDecodeAcceleratorStubTest,
-       DISABLED_MultipleDecodesCompletedAfterSequenceIsDisabled) {
+       MultipleDecodesCompletedAfterSequenceIsDisabled) {
   {
     InSequence call_sequence;
     EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
@@ -183,9 +337,11 @@
         .Times(1);
   }
   const SyncToken decode1_sync_token = SendDecodeRequest(
-      gfx::Size(100, 100) /* output_size */, 1u /* release_count */);
+      gfx::Size(100, 100) /* output_size */, 1u /* release_count */,
+      1u /* transfer_cache_entry_id */);
   const SyncToken decode2_sync_token = SendDecodeRequest(
-      gfx::Size(200, 200) /* output_size */, 2u /* release_count */);
+      gfx::Size(200, 200) /* output_size */, 2u /* release_count */,
+      2u /* transfer_cache_entry_id */);
 
   // A decode sync token should not be released before a decode is finished.
   RunTasksUntilIdle();
@@ -208,17 +364,17 @@
 
   // The channel should still exist at the end.
   EXPECT_TRUE(channel_manager()->LookupChannel(kChannelId));
+
+  // Check that the decoded images are in the transfer cache.
+  CheckTransferCacheEntries({SkISize::Make(100, 100), SkISize::Make(200, 200)});
 }
 
 // Tests the following flow: three decode requests are sent. The first decode
 // completes which should cause the scheduler sequence to be enabled. Right
 // after that (while the sequence is still enabled), the other two decodes
 // complete. At the end, all the sync tokens should be released.
-//
-// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
-// infrastructure. See https://crbug.com/868400.
 TEST_F(ImageDecodeAcceleratorStubTest,
-       DISABLED_MultipleDecodesCompletedWhileSequenceIsEnabled) {
+       MultipleDecodesCompletedWhileSequenceIsEnabled) {
   {
     InSequence call_sequence;
     EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
@@ -229,11 +385,14 @@
         .Times(1);
   }
   const SyncToken decode1_sync_token = SendDecodeRequest(
-      gfx::Size(100, 100) /* output_size */, 1u /* release_count */);
+      gfx::Size(100, 100) /* output_size */, 1u /* release_count */,
+      1u /* transfer_cache_entry_id */);
   const SyncToken decode2_sync_token = SendDecodeRequest(
-      gfx::Size(200, 200) /* output_size */, 2u /* release_count */);
+      gfx::Size(200, 200) /* output_size */, 2u /* release_count */,
+      2u /* transfer_cache_entry_id */);
   const SyncToken decode3_sync_token = SendDecodeRequest(
-      gfx::Size(300, 300) /* output_size */, 3u /* release_count */);
+      gfx::Size(300, 300) /* output_size */, 3u /* release_count */,
+      3u /* transfer_cache_entry_id */);
 
   // A decode sync token should not be released before a decode is finished.
   RunTasksUntilIdle();
@@ -252,16 +411,17 @@
 
   // The channel should still exist at the end.
   EXPECT_TRUE(channel_manager()->LookupChannel(kChannelId));
+
+  // Check that the decoded images are in the transfer cache.
+  CheckTransferCacheEntries({SkISize::Make(100, 100), SkISize::Make(200, 200),
+                             SkISize::Make(300, 300)});
 }
 
 // Tests the following flow: three decode requests are sent. The first decode
 // fails which should trigger the destruction of the channel. The second
 // succeeds and the third one fails. Regardless, the channel should still be
 // destroyed and all sync tokens should be released.
-//
-// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
-// infrastructure. See https://crbug.com/868400.
-TEST_F(ImageDecodeAcceleratorStubTest, DISABLED_FailedDecodes) {
+TEST_F(ImageDecodeAcceleratorStubTest, FailedDecodes) {
   {
     InSequence call_sequence;
     EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
@@ -272,11 +432,14 @@
         .Times(1);
   }
   const SyncToken decode1_sync_token = SendDecodeRequest(
-      gfx::Size(100, 100) /* output_size */, 1u /* release_count */);
+      gfx::Size(100, 100) /* output_size */, 1u /* release_count */,
+      1u /* transfer_cache_entry_id */);
   const SyncToken decode2_sync_token = SendDecodeRequest(
-      gfx::Size(200, 200) /* output_size */, 2u /* release_count */);
+      gfx::Size(200, 200) /* output_size */, 2u /* release_count */,
+      2u /* transfer_cache_entry_id */);
   const SyncToken decode3_sync_token = SendDecodeRequest(
-      gfx::Size(300, 300) /* output_size */, 3u /* release_count */);
+      gfx::Size(300, 300) /* output_size */, 3u /* release_count */,
+      3u /* transfer_cache_entry_id */);
 
   // A decode sync token should not be released before a decode is finished.
   RunTasksUntilIdle();
@@ -294,17 +457,20 @@
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode1_sync_token));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode2_sync_token));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode3_sync_token));
+
+  // We expect no entries in the transfer cache.
+  CheckTransferCacheEntries({});
 }
 
-// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
-// infrastructure. See https://crbug.com/868400.
-TEST_F(ImageDecodeAcceleratorStubTest, DISABLED_OutOfOrderSyncTokens) {
+TEST_F(ImageDecodeAcceleratorStubTest, OutOfOrderSyncTokens) {
   EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
       .Times(1);
   const SyncToken decode1_sync_token = SendDecodeRequest(
-      gfx::Size(100, 100) /* output_size */, 2u /* release_count */);
+      gfx::Size(100, 100) /* output_size */, 2u /* release_count */,
+      1u /* transfer_cache_entry_id */);
   const SyncToken decode2_sync_token = SendDecodeRequest(
-      gfx::Size(200, 200) /* output_size */, 1u /* release_count */);
+      gfx::Size(200, 200) /* output_size */, 1u /* release_count */,
+      2u /* transfer_cache_entry_id */);
 
   // We expect the destruction of the ImageDecodeAcceleratorStub, which also
   // implies that all decode sync tokens should be released.
@@ -312,45 +478,54 @@
   EXPECT_FALSE(channel_manager()->LookupChannel(kChannelId));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode1_sync_token));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode2_sync_token));
+
+  // We expect no entries in the transfer cache.
+  CheckTransferCacheEntries({});
 }
 
-// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
-// infrastructure. See https://crbug.com/868400.
-TEST_F(ImageDecodeAcceleratorStubTest, DISABLED_ZeroReleaseCountSyncToken) {
+TEST_F(ImageDecodeAcceleratorStubTest, ZeroReleaseCountSyncToken) {
   const SyncToken decode_sync_token = SendDecodeRequest(
-      gfx::Size(100, 100) /* output_size */, 0u /* release_count */);
+      gfx::Size(100, 100) /* output_size */, 0u /* release_count */,
+      1u /* transfer_cache_entry_id */);
 
   // We expect the destruction of the ImageDecodeAcceleratorStub, which also
   // implies that all decode sync tokens should be released.
   RunTasksUntilIdle();
   EXPECT_FALSE(channel_manager()->LookupChannel(kChannelId));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
+
+  // We expect no entries in the transfer cache.
+  CheckTransferCacheEntries({});
 }
 
-// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
-// infrastructure. See https://crbug.com/868400.
-TEST_F(ImageDecodeAcceleratorStubTest, DISABLED_ZeroWidthOutputSize) {
+TEST_F(ImageDecodeAcceleratorStubTest, ZeroWidthOutputSize) {
   const SyncToken decode_sync_token = SendDecodeRequest(
-      gfx::Size(0, 100) /* output_size */, 1u /* release_count */);
+      gfx::Size(0, 100) /* output_size */, 1u /* release_count */,
+      1u /* transfer_cache_entry_id */);
 
   // We expect the destruction of the ImageDecodeAcceleratorStub, which also
   // implies that all decode sync tokens should be released.
   RunTasksUntilIdle();
   EXPECT_FALSE(channel_manager()->LookupChannel(kChannelId));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
+
+  // We expect no entries in the transfer cache.
+  CheckTransferCacheEntries({});
 }
 
-// Disabled until ImageDecodeAcceleratorStubTest supports transfer cache
-// infrastructure. See https://crbug.com/868400.
-TEST_F(ImageDecodeAcceleratorStubTest, DISABLED_ZeroHeightOutputSize) {
+TEST_F(ImageDecodeAcceleratorStubTest, ZeroHeightOutputSize) {
   const SyncToken decode_sync_token = SendDecodeRequest(
-      gfx::Size(100, 0) /* output_size */, 1u /* release_count */);
+      gfx::Size(100, 0) /* output_size */, 1u /* release_count */,
+      1u /* transfer_cache_entry_id */);
 
   // We expect the destruction of the ImageDecodeAcceleratorStub, which also
   // implies that all decode sync tokens should be released.
   RunTasksUntilIdle();
   EXPECT_FALSE(channel_manager()->LookupChannel(kChannelId));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
+
+  // We expect no entries in the transfer cache.
+  CheckTransferCacheEntries({});
 }
 
 }  // namespace gpu
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 18aed07..951c8a0 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -2327,57 +2327,83 @@
   manifest_name: "REVISION"
   builders {
     name: "buildbucket/luci.chromium.ci/Win Builder Goma Canary"
+    category: "win|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Win Builder (dbg) Goma Canary"
+    category: "win|dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Win Goma Canary LocalOutputCache"
+    category: "win|rel"
+    short_name: "loc"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Win cl.exe Goma Canary LocalOutputCache"
+    category: "cl.exe|rel"
+    short_name: "loc"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Win7 Builder Goma Canary"
+    category: "win7|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Win7 Builder (dbg) Goma Canary"
+    category: "win7|dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/WinMSVC64 Goma Canary"
+    category: "cl.exe|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Mac Builder Goma Canary"
+    category: "mac|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Mac Builder (dbg) Goma Canary"
+    category: "mac|dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Mac Goma Canary (clobber)"
+    category: "mac|rel"
+    short_name: "clb"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Mac Builder (dbg) Goma Canary (clobber)"
+    category: "mac|dbg"
+    short_name: "clb"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Mac Goma Canary LocalOutputCache"
+    category: "mac|rel"
+    short_name: "loc"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-rel-goma-canary"
+    category: "cros|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Linux Builder Goma Canary"
+    category: "linux|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Linux x64 Goma Canary (clobber)"
+    category: "linux|rel"
+    short_name: "clb"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Linux x64 Goma Canary LocalOutputCache"
+    category: "linux|rel"
+    short_name: "loc"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Android Builder (dbg) Goma Canary"
+    category: "android|dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/ios-device-goma-canary-clobber"
+    category: "ios|rel"
+    short_name: "clb"
   }
 }
 
@@ -2390,57 +2416,83 @@
   manifest_name: "REVISION"
   builders {
     name: "buildbucket/luci.chromium.ci/Win Builder Goma Latest Client"
+    category: "win|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Win Builder (dbg) Goma Latest Client"
+    category: "win|dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Win Goma Latest Client LocalOutputCache"
+    category: "win|rel"
+    short_name: "loc"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Win cl.exe Goma Latest Client LocalOutputCache"
+    category: "cl.exe|rel"
+    short_name: "loc"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Win7 Builder Goma Latest Client"
+    category: "win7|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Win7 Builder (dbg) Goma Latest Client"
+    category: "win7|dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/WinMSVC64 Goma Latest Client"
+    category: "cl.exe|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Mac Builder Goma Latest Client"
+    category: "mac|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Mac Builder (dbg) Goma Latest Client"
+    category: "mac|dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Mac Goma Latest Client (clobber)"
+    category: "mac|rel"
+    short_name: "clb"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Mac Builder (dbg) Goma Latest Client (clobber)"
+    category: "mac|dbg"
+    short_name: "clb"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Mac Goma Latest Client LocalOutputCache"
+    category: "mac|rel"
+    short_name: "loc"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/chromeos-amd64-generic-rel-goma-latest-client"
+    category: "cros|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Linux Builder Goma Latest Client"
+    category: "linux|rel"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Linux x64 Goma Latest Client (clobber)"
+    category: "linux|rel"
+    short_name: "clb"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Linux x64 Goma Latest Client LocalOutputCache"
+    category: "linux|rel"
+    short_name: "loc"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Android Builder (dbg) Goma Latest Client"
+    category: "android|dbg"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/ios-device-goma-latest-client-clobber"
+    category: "ios"
+    short_name: "clb"
   }
 }
 
diff --git a/ios/build/bots/scripts/xcodebuild_runner.py b/ios/build/bots/scripts/xcodebuild_runner.py
index fc96327..c4ca40c7 100644
--- a/ios/build/bots/scripts/xcodebuild_runner.py
+++ b/ios/build/bots/scripts/xcodebuild_runner.py
@@ -75,13 +75,11 @@
   return status_summary
 
 
-def collect_test_results(cmd_list, plist_path, return_code, output):
+def collect_test_results(plist_path, output):
   """Gets test result data from Info.plist and from test output.
 
   Args:
-    cmd_list: (list(str)) A running command.
     plist_path: (str) A path to plist-file.
-    return_code: (int) A return code of cmd_list command.
     output: (str) An output of command.
   Returns:
     Test result as a map:
@@ -97,16 +95,14 @@
   """
   test_results = {
       'passed': {},
-      'failed': {},
-      'errors': []
+      'failed': {}
   }
   root = plistlib.readPlist(plist_path)
 
   for action in root['Actions']:
     action_result = action['ActionResult']
-    if ((action_result['TestsCount'] == 0 and
-         action_result['TestsFailedCount'] == 0 and
-         action_result['ErrorCount'] != 0)
+    if ((root['TestsCount'] == 0 and
+         root['TestsFailedCount'] == 0)
         or 'TestSummaryPath' not in action_result):
       test_results['failed']['TESTS_DID_NOT_START'] = []
     else:
@@ -115,15 +111,6 @@
       summary = test_status_summary(summary_plist)
       test_results['failed'] = summary['failed']
       test_results['passed'] = summary['passed']
-
-    for error_summary in action_result['ErrorSummaries']:
-      test_results['errors'].append(error_summary['Message'])
-  # If xcodebuild finished with non-zero status and no failure/error
-  # in the Info.plist log.
-  if return_code and (
-      not test_results['failed'] and not test_results['errors']):
-    test_results['errors'].append(
-        'Return code of "%s" is not 0!' % cmd_list)
   test_results['output'] = output
   return test_results
 
@@ -328,11 +315,11 @@
     """Calculates test summary - how many passed, failed and error tests.
 
     Returns:
-      Dictionary with number of errors, passed and failed tests.
+      Dictionary with number of passed and failed tests.
       Failed tests will be calculated from the last test attempt.
-      Passed tests and errors calculated for each test attempt.
+      Passed tests calculated for each test attempt.
     """
-    test_statuses = ['errors', 'passed', 'failed']
+    test_statuses = ['passed', 'failed']
     for status in test_statuses:
       self.logs[status] = 0
 
@@ -341,9 +328,6 @@
         if test_status not in test_attempt_results:
           continue
         results = test_attempt_results[test_status]
-        if test_status == 'errors':
-          self.logs['errors'] += len(results)
-          continue
         for _, destinations_egtests in results.iteritems():
           if test_status == 'passed' or (
               # Number of failed tests is taken only from last run.
@@ -351,11 +335,12 @@
                   self.test_results['attempts']) - 1):
             self.logs[test_status] += len(destinations_egtests)
 
-  def launch_attempt(self, cmd):
+  def launch_attempt(self, cmd, out_dir):
     """Launch a process and do logging simultaneously.
 
     Args:
       cmd: (list[str]) A command to run.
+      out_dir: (str) Output directory given to the command. Used in tests only.
 
     Returns:
       A tuple (cmd, returncode, output) where:
@@ -392,33 +377,32 @@
     # total number of attempts is self.retries+1
     for attempt in range(self.retries + 1):
       outdir_attempt = os.path.join(self.out_dir, 'attempt_%d' % attempt)
-      if attempt == 0:
+      # Create a command for the 1st run or if tests did not start,
+      # re-run the same command but with different output folder.
+      # (http://crbug.com/916620) If tests did not start, repeat the command.
+      if (not self.test_results['attempts'] or 'TESTS_DID_NOT_START'
+          in self.test_results['attempts'][-1]['failed']):
         cmd_list = self.command(self.egtests_app,
                                 outdir_attempt,
                                 self.destination,
                                 self.shards)
-        initial_command = list(cmd_list)
-      # (http://crbug.com/916620) If tests has not started, repeat the command
-      # otherwise re-init based on list of failed tests.
-      elif 'TESTS_DID_NOT_START' not in self.test_results[
-          'attempts'][-1]['failed']:
+        if attempt == 0:
+          initial_command = list(cmd_list)
+      # Re-init the command based on list of failed tests.
+      else:
         cmd_list = self._make_cmd_list_for_failed_tests(
             self.test_results['attempts'][-1]['failed'],
             outdir_attempt,
             test_args=self.egtests_app.test_args,
             env_vars=self.egtests_app.env_vars)
-      else:
-        # If tests did not start, re-run the same command
-        # but with different output folder.
-        cmd_list = cmd_list[:-2] + ['-resultBundlePath', outdir_attempt]
+
       # TODO(crbug.com/914878): add heartbeat logging to xcodebuild_runner.
       print 'Start test attempt #%d for command [%s]' % (
           attempt, ' '.join(cmd_list))
-      return_code, output = self.launch_attempt(cmd_list)
+      _, output = self.launch_attempt(cmd_list, outdir_attempt)
       self.test_results['attempts'].append(
-          collect_test_results(cmd_list, os.path.join(outdir_attempt,
-                                                      'Info.plist'),
-                               return_code, output))
+          collect_test_results(os.path.join(outdir_attempt, 'Info.plist'),
+                               output))
       if self.retries == attempt or not self.test_results[
           'attempts'][-1]['failed']:
         break
@@ -616,10 +600,8 @@
           {'cmd': ' '.join(result['cmd']), 'logs': result['logs']})
 
     self.test_results['end_run'] = int(time.time())
-    # Test is failed if any error occurs during test run
-    # or there are failures for last run.
-    return not self.test_results['commands'][-1]['logs']['failed'] and all(
-        [not r['logs']['errors'] for r in self.test_results['commands']])
+    # Test is failed if there are failures for the last run.
+    return not self.test_results['commands'][-1]['logs']['failed']
 
   def erase_all_simulators(self):
     """Erases all simulator devices.
diff --git a/ios/build/bots/scripts/xcodebuild_runner_test.py b/ios/build/bots/scripts/xcodebuild_runner_test.py
index 614c218..4fe768d2 100644
--- a/ios/build/bots/scripts/xcodebuild_runner_test.py
+++ b/ios/build/bots/scripts/xcodebuild_runner_test.py
@@ -7,6 +7,9 @@
 import mock
 import os
 
+import plistlib
+import shutil
+import tempfile
 import test_runner
 import test_runner_test
 import xcodebuild_runner
@@ -14,8 +17,7 @@
 
 _ROOT_FOLDER_PATH = 'root/folder'
 _XCODE_BUILD_VERSION = '10B61'
-_DESTINATION = ('platform=iOS Simulator,OS=12.0,name=iPhone 7 Plus;'
-                'platform=iOS Simulator,OS=12.0,name=iPhone X')
+_DESTINATION = 'platform=iOS Simulator,OS=12.0,name=iPhone X'
 _OUT_DIR = 'out/dir'
 _XTEST_RUN = '/tmp/temp_file.xctestrun'
 _EGTESTS_APP_PATH = '%s/any_egtests.app' % _ROOT_FOLDER_PATH
@@ -24,41 +26,108 @@
 class XCodebuildRunnerTest(test_runner_test.TestCase):
   """Test case to test xcodebuild_runner."""
 
+  def setUp(self):
+    super(XCodebuildRunnerTest, self).setUp()
+    self.mock(os.path, 'exists', lambda _: True)
+    self.mock(xcodebuild_runner.LaunchCommand, '_copy_screenshots',
+              lambda _1, _2, _3: None)
+    self.mock(xcodebuild_runner.LaunchCommand, 'summary_log', lambda _: None)
+    self.tmpdir = tempfile.mkdtemp()
+
+  def tearDown(self):
+    shutil.rmtree(self.tmpdir, ignore_errors=True)
+    super(XCodebuildRunnerTest, self).tearDown()
+
+  def fake_launch_attempt(self, obj, statuses):
+    attempt = [0]
+    info_plist_statuses = {
+        'not_started': {
+            'Actions': [{'ActionResult': {}}],
+            'TestsCount': 0, 'TestsFailedCount': 0
+        },
+        'fail': {
+            'Actions': [
+                {'ActionResult': {
+                    'TestSummaryPath': '1_Test/TestSummaries.plist'}
+                }
+            ],
+            'TestsCount': 99,
+            'TestsFailedCount': 1},
+        'pass': {
+            'Actions': [
+                {'ActionResult': {
+                    'TestSummaryPath': '1_Test/TestSummaries.plist'}
+                }
+            ],
+            'TestsCount': 100,
+            'TestsFailedCount': 0}
+    }
+
+    test_summary = {
+        'TestableSummaries': [
+            {'TargetName': 'egtests',
+             'Tests': [
+                 {'Subtests': [
+                     {'Subtests': [
+                         {'Subtests': [
+                             {'TestIdentifier': 'passed_test',
+                              'TestStatus': 'Success'
+                             }
+                         ]
+                         }
+                     ]
+                     }
+                 ]
+                 }
+             ]
+            }
+        ]
+        }
+
+    def the_fake(cmd, attempt_outdir):
+      index = attempt[0]
+      attempt[0] += 1
+      self.assertEqual(os.path.join(self.tmpdir, 'attempt_%d' % index),
+                       attempt_outdir)
+      self.assertEqual(1, cmd.count(attempt_outdir))
+      os.mkdir(attempt_outdir)
+      with open(os.path.join(attempt_outdir, 'Info.plist'), 'w') as f:
+        plistlib.writePlist(info_plist_statuses[statuses[index]], f)
+      summary_folder = os.path.join(attempt_outdir, '1_Test')
+      os.mkdir(summary_folder)
+      with open(os.path.join(summary_folder, 'TestSummaries.plist'), 'w') as f:
+        plistlib.writePlist(test_summary, f)
+      return (-6, 'Output for attempt_%d' % index)
+
+    obj.launch_attempt = the_fake
+
   def testEgtests_not_found_egtests_app(self):
+    self.mock(os.path, 'exists', lambda _: False)
     with self.assertRaises(test_runner.AppNotFoundError):
       xcodebuild_runner.EgtestsApp(_EGTESTS_APP_PATH)
 
-  @mock.patch('os.path.exists', autospec=True)
-  def testEgtests_not_found_plugins(self, mock_path_exists):
-    mock_path_exists.return_value = True
+  def testEgtests_not_found_plugins(self):
     egtests = xcodebuild_runner.EgtestsApp(_EGTESTS_APP_PATH)
-    mock_path_exists.return_value = False
+    self.mock(os.path, 'exists', lambda _: False)
     with self.assertRaises(test_runner.PlugInsNotFoundError):
       egtests._xctest_path()
 
   @mock.patch('os.listdir', autospec=True)
-  @mock.patch('os.path.exists', autospec=True)
-  def testEgtests_found_xctest(self, mock_path_exists, mock_listdir):
-    mock_path_exists.return_value = True
+  def testEgtests_found_xctest(self, mock_listdir):
     mock_listdir.return_value = ['any_egtests.xctest']
     self.assertEqual('/PlugIns/any_egtests.xctest',
                      xcodebuild_runner.EgtestsApp(
                          _EGTESTS_APP_PATH)._xctest_path())
 
   @mock.patch('os.listdir', autospec=True)
-  @mock.patch('os.path.exists', autospec=True)
-  def testEgtests_not_found_xctest(self, mock_path_exists, mock_listdir):
-    mock_path_exists.return_value = True
+  def testEgtests_not_found_xctest(self, mock_listdir):
     mock_listdir.return_value = ['some_egtests.xctest']
     egtest = xcodebuild_runner.EgtestsApp(_EGTESTS_APP_PATH)
     with self.assertRaises(test_runner.XCTestPlugInNotFoundError):
       egtest._xctest_path()
 
   @mock.patch('os.listdir', autospec=True)
-  @mock.patch('os.path.exists', autospec=True)
-  def testEgtests_xctestRunNode_without_filter(self, mock_path_exists,
-                                               mock_listdir):
-    mock_path_exists.return_value = True
+  def testEgtests_xctestRunNode_without_filter(self, mock_listdir):
     mock_listdir.return_value = ['any_egtests.xctest']
     egtest_node = xcodebuild_runner.EgtestsApp(
         _EGTESTS_APP_PATH).xctestrun_node()['any_egtests_module']
@@ -66,10 +135,8 @@
     self.assertNotIn('SkipTestIdentifiers', egtest_node)
 
   @mock.patch('os.listdir', autospec=True)
-  @mock.patch('os.path.exists', autospec=True)
-  def testEgtests_xctestRunNode_with_filter_only_identifiers(
-      self, mock_path_exists, mock_listdir):
-    mock_path_exists.return_value = True
+  def testEgtests_xctestRunNode_with_filter_only_identifiers(self,
+                                                             mock_listdir):
     mock_listdir.return_value = ['any_egtests.xctest']
     filtered_tests = ['TestCase1/testMethod1', 'TestCase1/testMethod2',
                       'TestCase2/testMethod1', 'TestCase1/testMethod2']
@@ -80,10 +147,8 @@
     self.assertNotIn('SkipTestIdentifiers', egtest_node)
 
   @mock.patch('os.listdir', autospec=True)
-  @mock.patch('os.path.exists', autospec=True)
-  def testEgtests_xctestRunNode_with_filter_skip_identifiers(
-      self, mock_path_exists, mock_listdir):
-    mock_path_exists.return_value = True
+  def testEgtests_xctestRunNode_with_filter_skip_identifiers(self,
+                                                             mock_listdir):
     mock_listdir.return_value = ['any_egtests.xctest']
     skipped_tests = ['TestCase1/testMethod1', 'TestCase1/testMethod2',
                      'TestCase2/testMethod1', 'TestCase1/testMethod2']
@@ -94,16 +159,13 @@
     self.assertNotIn('OnlyTestIdentifiers', egtest_node)
 
   @mock.patch('xcodebuild_runner.LaunchCommand.fill_xctest_run', autospec=True)
-  @mock.patch('os.path.exists', autospec=True)
-  def testLaunchCommand_command(self, mock_path_exists, mock_fill_xctestrun):
-    mock_path_exists.return_value = True
-    destination = 'platform=iOS Simulator,OS=12.0,name=iPhone X'
+  def testLaunchCommand_command(self, mock_fill_xctestrun):
     mock_fill_xctestrun.return_value = _XTEST_RUN
     mock_egtest = mock.MagicMock(spec=xcodebuild_runner.EgtestsApp)
     type(mock_egtest).egtests_path = mock.PropertyMock(
         return_value=_EGTESTS_APP_PATH)
     cmd = xcodebuild_runner.LaunchCommand(
-        mock_egtest, destination, shards=3, retries=1, out_dir=_OUT_DIR)
+        mock_egtest, _DESTINATION, shards=3, retries=1, out_dir=_OUT_DIR)
     self.assertEqual(['xcodebuild', 'test-without-building',
                       '-xctestrun', '/tmp/temp_file.xctestrun',
                       '-destination',
@@ -113,19 +175,17 @@
                       '-parallel-testing-worker-count', '3'],
                      cmd.command(egtests_app=mock_egtest,
                                  out_dir=_OUT_DIR,
-                                 destination=destination,
+                                 destination=_DESTINATION,
                                  shards=3))
 
   @mock.patch('plistlib.writePlist', autospec=True)
-  @mock.patch('os.path.exists', autospec=True)
   @mock.patch('os.path.join', autospec=True)
-  def testFill_xctest_run(self, mock_path_join, mock_path_exists, _):
+  def testFill_xctest_run(self, mock_path_join, _):
+    self._mocks[xcodebuild_runner.LaunchCommand].pop('fill_xctest_run', None)
     mock_path_join.return_value = _XTEST_RUN
-    mock_path_exists.return_value = True
-    destination = 'platform=iOS Simulator,OS=12.0,name=iPhone X'
     mock_egtest = mock.MagicMock(spec=xcodebuild_runner.EgtestsApp)
     launch_command = xcodebuild_runner.LaunchCommand(
-        mock_egtest, destination, shards=1, retries=1, out_dir=_OUT_DIR)
+        mock_egtest, _DESTINATION, shards=1, retries=1, out_dir=_OUT_DIR)
     self.assertEqual(_XTEST_RUN, launch_command.fill_xctest_run(mock_egtest))
     self.assertEqual([mock.call.xctestrun_node()], mock_egtest.method_calls)
 
@@ -135,15 +195,12 @@
                                       out_dir=_OUT_DIR).fill_xctest_run([])
 
   @mock.patch('xcodebuild_runner.LaunchCommand.fill_xctest_run', autospec=True)
-  @mock.patch('os.path.exists', autospec=True)
-  def testLaunchCommand_make_cmd_list_for_failed_tests(self, mock_path_exists,
+  def testLaunchCommand_make_cmd_list_for_failed_tests(self,
                                                        fill_xctest_run_mock):
-    mock_path_exists.return_value = True
     fill_xctest_run_mock.side_effect = [
         '/var/folders/tmpfile1'
     ]
     egtest_app = 'module_1_egtests.app'
-    destination = 'platform=iOS Simulator,OS=12.0,name=iPhone X'
     egtest_app_path = '%s/%s' % (_ROOT_FOLDER_PATH, egtest_app)
     failed_tests = {
         egtest_app: [
@@ -159,7 +216,7 @@
         return_value=egtest_app_path)
     cmd = xcodebuild_runner.LaunchCommand(
         egtests_app=mock_egtest,
-        destination=destination,
+        destination=_DESTINATION,
         out_dir='out/dir/attempt_2/iPhone X 12.0',
         shards=1,
         retries=1
@@ -169,3 +226,16 @@
     self.assertEqual(1, len(fill_xctest_run_mock.mock_calls))
     self.assertItemsEqual(expected_egtests.__dict__,
                           fill_xctest_run_mock.mock_calls[0][1][1].__dict__)
+
+  @mock.patch('os.listdir', autospec=True)
+  def testLaunchCommand_restartFailed1stAttempt(self, mock_listdir):
+    mock_listdir.return_value = ['any_egtests.xctest']
+    egtests = xcodebuild_runner.EgtestsApp(_EGTESTS_APP_PATH)
+    launch_command = xcodebuild_runner.LaunchCommand(egtests,
+                                                     _DESTINATION,
+                                                     shards=1,
+                                                     retries=3,
+                                                     out_dir=self.tmpdir)
+    self.fake_launch_attempt(launch_command, ['not_started', 'pass'])
+    launch_command.launch()
+    self.assertEqual(2, len(launch_command.test_results))
diff --git a/ios/chrome/browser/translate/BUILD.gn b/ios/chrome/browser/translate/BUILD.gn
index 26760ce2..5521bf0 100644
--- a/ios/chrome/browser/translate/BUILD.gn
+++ b/ios/chrome/browser/translate/BUILD.gn
@@ -79,23 +79,18 @@
     ":translate",
     "//base",
     "//base/test:test_support",
-    "//components/language/core/browser",
-    "//components/sync_preferences:test_support",
-    "//components/translate/core/browser",
-    "//components/translate/core/browser:unit_tests",
+    "//components/translate/core/browser:test_support",
     "//components/translate/ios/browser",
     "//ios/chrome/browser",
-    "//ios/chrome/browser/browser_state:test_support",
-    "//ios/chrome/browser/translate",
     "//ios/chrome/browser/web:test_support",
     "//ios/chrome/common",
     "//ios/public/provider/chrome/browser:test_support",
-    "//ios/web",
-    "//ios/web/public/test",
+    "//ios/web/public",
+    "//ios/web/public/test:util",
     "//skia",
-    "//testing/gmock:gmock",
+    "//testing/gmock",
     "//testing/gtest",
-    "//url",
+    "//url:url",
   ]
 }
 
diff --git a/ios/chrome/browser/translate/chrome_ios_translate_client.h b/ios/chrome/browser/translate/chrome_ios_translate_client.h
index 68d2c86..4b46653 100644
--- a/ios/chrome/browser/translate/chrome_ios_translate_client.h
+++ b/ios/chrome/browser/translate/chrome_ios_translate_client.h
@@ -78,15 +78,27 @@
   bool IsTranslatableURL(const GURL& url) override;
   void ShowReportLanguageDetectionErrorUI(const GURL& report_url) override;
 
+  id<LanguageSelectionHandler> language_selection_handler() {
+    return language_selection_handler_;
+  }
+
   void set_language_selection_handler(id<LanguageSelectionHandler> handler) {
     language_selection_handler_ = handler;
   }
 
+  id<TranslateOptionSelectionHandler> translate_option_selection_handler() {
+    return translate_option_selection_handler_;
+  }
+
   void set_translate_option_selection_handler(
       id<TranslateOptionSelectionHandler> handler) {
     translate_option_selection_handler_ = handler;
   }
 
+  id<TranslateNotificationHandler> translate_notification_handler() {
+    return translate_notification_handler_;
+  }
+
   void set_translate_notification_handler(
       id<TranslateNotificationHandler> handler) {
     translate_notification_handler_ = handler;
diff --git a/ios/chrome/browser/translate/translate_infobar_delegate_observer_bridge_unittest.mm b/ios/chrome/browser/translate/translate_infobar_delegate_observer_bridge_unittest.mm
index d6943d0..fae6ee7 100644
--- a/ios/chrome/browser/translate/translate_infobar_delegate_observer_bridge_unittest.mm
+++ b/ios/chrome/browser/translate/translate_infobar_delegate_observer_bridge_unittest.mm
@@ -6,16 +6,7 @@
 
 #include <memory>
 
-#include "components/language/core/browser/language_model.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "components/translate/core/browser/mock_translate_client.h"
-#include "components/translate/core/browser/mock_translate_driver.h"
-#include "components/translate/core/browser/mock_translate_ranker.h"
-#include "components/translate/core/browser/translate_infobar_delegate.h"
-#include "components/translate/core/browser/translate_manager.h"
-#include "components/translate/core/browser/translate_pref_names.h"
-#include "components/translate/core/browser/translate_prefs.h"
-#include "testing/gmock/include/gmock/gmock.h"
+#include "components/translate/core/browser/mock_translate_infobar_delegate.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 
@@ -23,75 +14,8 @@
 #error "This file requires ARC support."
 #endif
 
-using testing::_;
-using translate::testing::MockTranslateClient;
-using translate::testing::MockTranslateDriver;
-using translate::testing::MockTranslateRanker;
-
-class MockLanguageModel : public language::LanguageModel {
-  std::vector<LanguageDetails> GetLanguages() override {
-    return {LanguageDetails("en", 1.0)};
-  }
-};
-
-class MockTranslateInfoBarDelegate
-    : public translate::TranslateInfoBarDelegate {
- public:
-  MockTranslateInfoBarDelegate(
-      const base::WeakPtr<translate::TranslateManager>& translate_manager,
-      bool is_off_the_record,
-      translate::TranslateStep step,
-      const std::string& original_language,
-      const std::string& target_language,
-      translate::TranslateErrors::Type error_type,
-      bool triggered_from_menu)
-      : translate::TranslateInfoBarDelegate(translate_manager,
-                                            is_off_the_record,
-                                            step,
-                                            original_language,
-                                            target_language,
-                                            error_type,
-                                            triggered_from_menu) {}
-  ~MockTranslateInfoBarDelegate() override {}
-
-  MOCK_METHOD1(SetObserver, void(Observer* observer));
-};
-
-class MockTranslateInfoBarDelegateFactory {
- public:
-  MockTranslateInfoBarDelegateFactory() {
-    pref_service_ =
-        std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
-    translate::TranslatePrefs::RegisterProfilePrefs(pref_service_->registry());
-    pref_service_->registry()->RegisterBooleanPref(
-        prefs::kOfferTranslateEnabled, true);
-    client_ =
-        std::make_unique<MockTranslateClient>(&driver_, pref_service_.get());
-    ranker_ = std::make_unique<MockTranslateRanker>();
-    language_model_ = std::make_unique<MockLanguageModel>();
-    manager_ = std::make_unique<translate::TranslateManager>(
-        client_.get(), ranker_.get(), language_model_.get());
-    delegate_ = std::make_unique<MockTranslateInfoBarDelegate>(
-        manager_->GetWeakPtr(), false,
-        translate::TranslateStep::TRANSLATE_STEP_BEFORE_TRANSLATE, "fr", "en",
-        translate::TranslateErrors::Type::NONE, false);
-  }
-
-  ~MockTranslateInfoBarDelegateFactory() = default;
-
-  MockTranslateInfoBarDelegate* GetMockTranslateInfoBarDelegate() {
-    return delegate_.get();
-  }
-
- private:
-  MockTranslateDriver driver_;
-  std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_;
-  std::unique_ptr<MockTranslateClient> client_;
-  std::unique_ptr<MockTranslateRanker> ranker_;
-  std::unique_ptr<MockLanguageModel> language_model_;
-  std::unique_ptr<translate::TranslateManager> manager_;
-  std::unique_ptr<MockTranslateInfoBarDelegate> delegate_;
-};
+using translate::testing::MockTranslateInfoBarDelegate;
+using translate::testing::MockTranslateInfoBarDelegateFactory;
 
 @interface TestTranslateInfoBarDelegateObserver
     : NSObject <TranslateInfobarDelegateObserving>
@@ -129,7 +53,8 @@
 class TranslateInfobarDelegateObserverBridgeTest : public PlatformTest {
  protected:
   TranslateInfobarDelegateObserverBridgeTest()
-      : observer_([[TestTranslateInfoBarDelegateObserver alloc] init]) {
+      : delegate_factory_("fr", "en"),
+        observer_([[TestTranslateInfoBarDelegateObserver alloc] init]) {
     // Make sure the observer bridge sets up itself as an observer.
     EXPECT_CALL(*GetDelegate(), SetObserver(_)).Times(1);
 
diff --git a/ios/chrome/browser/ui/translate/BUILD.gn b/ios/chrome/browser/ui/translate/BUILD.gn
index d0ab04d0..ad3499c 100644
--- a/ios/chrome/browser/ui/translate/BUILD.gn
+++ b/ios/chrome/browser/ui/translate/BUILD.gn
@@ -71,3 +71,28 @@
     "//ui/base",
   ]
 }
+
+source_set("unit_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "translate_infobar_mediator_unittest.mm",
+  ]
+  deps = [
+    ":translate",
+    "//components/translate/core/browser:test_support",
+    "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/translate",
+    "//ios/chrome/browser/ui/popup_menu/public:popup_menu_ui",
+    "//ios/chrome/browser/ui/translate:translate_ui",
+    "//ios/chrome/browser/ui/translate/cells",
+    "//ios/chrome/browser/web_state_list",
+    "//ios/chrome/browser/web_state_list:test_support",
+    "//ios/web/public/test",
+    "//ios/web/public/test/fakes",
+    "//skia",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/ocmock:ocmock",
+  ]
+}
diff --git a/ios/chrome/browser/ui/translate/translate_infobar_language_tab_view.mm b/ios/chrome/browser/ui/translate/translate_infobar_language_tab_view.mm
index c67aff5..955bf22 100644
--- a/ios/chrome/browser/ui/translate/translate_infobar_language_tab_view.mm
+++ b/ios/chrome/browser/ui/translate/translate_infobar_language_tab_view.mm
@@ -75,12 +75,12 @@
     // the ripple effect on the button won't be seen.
     [UIView animateWithDuration:kActivityIndicatorVisbilityAnimationDuration
                      animations:^{
-                       self.activityIndicator.hidden = NO;
-                       self.button.hidden = YES;
+                       self.activityIndicator.alpha = 1.0;
+                       self.button.alpha = 0.0;
                      }];
   } else {
-    self.button.hidden = NO;
-    self.activityIndicator.hidden = YES;
+    self.button.alpha = 1.0;
+    self.activityIndicator.alpha = 0.0;
     [self.activityIndicator stopAnimating];
 
     [self.button setTitleColor:[self titleColor] forState:UIControlStateNormal];
@@ -96,7 +96,7 @@
   self.activityIndicator.cycleColors =
       @[ [[MDCPalette cr_bluePalette] tint500] ];
   [self.activityIndicator setRadius:kActivityIndicatorRadius];
-  self.activityIndicator.hidden = YES;  // Initially hidden.
+  self.activityIndicator.alpha = 0.0;  // Initially hidden.
   [self addSubview:self.activityIndicator];
 
   [NSLayoutConstraint activateConstraints:@[
diff --git a/ios/chrome/browser/ui/translate/translate_infobar_mediator_unittest.mm b/ios/chrome/browser/ui/translate/translate_infobar_mediator_unittest.mm
new file mode 100644
index 0000000..8a9c84b
--- /dev/null
+++ b/ios/chrome/browser/ui/translate/translate_infobar_mediator_unittest.mm
@@ -0,0 +1,260 @@
+// 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/chrome/browser/ui/translate/translate_infobar_mediator.h"
+
+#include <memory>
+
+#import "base/mac/foundation_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/translate/core/browser/mock_translate_infobar_delegate.h"
+#import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
+#import "ios/chrome/browser/translate/language_selection_handler.h"
+#import "ios/chrome/browser/translate/translate_option_selection_handler.h"
+#import "ios/chrome/browser/ui/popup_menu/public/popup_menu_consumer.h"
+#import "ios/chrome/browser/ui/translate/cells/select_language_popup_menu_item.h"
+#import "ios/chrome/browser/ui/translate/cells/translate_popup_menu_item.h"
+#import "ios/chrome/browser/ui/translate/translate_notification_handler.h"
+#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/test/fakes/crw_test_js_injection_receiver.h"
+#import "ios/web/public/test/fakes/test_navigation_manager.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#include "ios/web/public/test/test_web_thread_bundle.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using translate::testing::MockTranslateInfoBarDelegate;
+using translate::testing::MockTranslateInfoBarDelegateFactory;
+
+// A protocol used to mock a
+// id<LanguageSelectionHandler,TranslateOptionSelectionHandler>.
+@protocol TestSelectionHandlerProtocol <LanguageSelectionHandler,
+                                        TranslateOptionSelectionHandler>
+@end
+
+// Test class that conforms to PopupMenuConsumer and exposes the menu items.
+@interface TestPopupMenuConsumer : NSObject <PopupMenuConsumer>
+
+@property(nonatomic, strong)
+    NSMutableArray<TableViewItem<PopupMenuItem>*>* items;
+
+@end
+
+@implementation TestPopupMenuConsumer
+
+@synthesize itemToHighlight;
+
+- (void)setPopupMenuItems:
+    (NSArray<NSArray<TableViewItem<PopupMenuItem>*>*>*)items {
+  _items = [[NSMutableArray alloc] init];
+
+  for (NSArray* innerArray in items) {
+    [_items addObjectsFromArray:innerArray];
+  }
+}
+
+- (void)itemsHaveChanged:(NSArray<TableViewItem<PopupMenuItem>*>*)items {
+  EXPECT_TRUE(false) << "This method should not be called.";
+}
+
+@end
+
+class TranslateInfobarMediatorTest : public PlatformTest {
+ protected:
+  TranslateInfobarMediatorTest()
+      : browser_state_(TestChromeBrowserState::Builder().Build()),
+        web_state_list_(
+            std::make_unique<WebStateList>(&web_state_list_delegate_)),
+        delegate_factory_("fr", "en"),
+        selection_handler_([OCMockObject
+            niceMockForProtocol:@protocol(TestSelectionHandlerProtocol)]),
+        notification_handler_([OCMockObject
+            niceMockForProtocol:@protocol(TranslateNotificationHandler)]),
+        mediator_([[TranslateInfobarMediator alloc]
+            initWithSelectionHandler:selection_handler_
+                 notificationHandler:notification_handler_]) {
+    CreateTranslateClient();
+  }
+
+  WebStateList* web_state_list() { return web_state_list_.get(); }
+
+  id selection_handler() { return selection_handler_; }
+
+  id notification_handler() { return notification_handler_; }
+
+  TranslateInfobarMediator* mediator() { return mediator_; }
+
+  void CreateTranslateClient() {
+    auto web_state = std::make_unique<web::TestWebState>();
+
+    // Set up browser state.
+    web_state->SetBrowserState(browser_state_.get());
+
+    // Set up navigation manager.
+    std::unique_ptr<web::TestNavigationManager> navigation_manager =
+        std::make_unique<web::TestNavigationManager>();
+    navigation_manager->SetBrowserState(browser_state_.get());
+    web_state->SetNavigationManager(std::move(navigation_manager));
+
+    // Set up JS injection receiver.
+    CRWTestJSInjectionReceiver* injectionReceiver =
+        [[CRWTestJSInjectionReceiver alloc] init];
+    web_state->SetJSInjectionReceiver(injectionReceiver);
+
+    // Create ChromeIOSTranslateClient.
+    ChromeIOSTranslateClient::CreateForWebState(web_state.get());
+
+    int effective_index = web_state_list_->InsertWebState(
+        0, std::move(web_state), WebStateList::INSERT_NO_FLAGS,
+        WebStateOpener());
+    web_state_list_->ActivateWebStateAt(effective_index);
+  }
+
+  ChromeIOSTranslateClient* GetTranslateClient() {
+    return ChromeIOSTranslateClient::FromWebState(
+        web_state_list_->GetActiveWebState());
+  }
+
+  MockTranslateInfoBarDelegate* GetDelegate() {
+    return delegate_factory_.GetMockTranslateInfoBarDelegate();
+  }
+
+ private:
+  web::TestWebThreadBundle web_thread_bundle_;
+  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  FakeWebStateListDelegate web_state_list_delegate_;
+  std::unique_ptr<WebStateList> web_state_list_;
+  MockTranslateInfoBarDelegateFactory delegate_factory_;
+  id selection_handler_;
+  id notification_handler_;
+  TranslateInfobarMediator* mediator_;
+
+  DISALLOW_COPY_AND_ASSIGN(TranslateInfobarMediatorTest);
+};
+
+// Tests that the mediator installs UI handlers on existing
+// ChromeIOSTranslateClient instances as well as new ones that become available.
+TEST_F(TranslateInfobarMediatorTest, InstallHandlers) {
+  ChromeIOSTranslateClient* translate_client = GetTranslateClient();
+
+  // Make sure the handlers are not set.
+  EXPECT_EQ(nil, translate_client->language_selection_handler());
+  EXPECT_EQ(nil, translate_client->translate_option_selection_handler());
+  EXPECT_EQ(nil, translate_client->translate_notification_handler());
+
+  TranslateInfobarMediator* translate_infobar_mediator = mediator();
+  translate_infobar_mediator.webStateList = web_state_list();
+
+  EXPECT_EQ(selection_handler(),
+            translate_client->language_selection_handler());
+  EXPECT_EQ(selection_handler(),
+            translate_client->translate_option_selection_handler());
+  EXPECT_EQ(notification_handler(),
+            translate_client->translate_notification_handler());
+
+  CreateTranslateClient();
+  ChromeIOSTranslateClient* new_translate_client = GetTranslateClient();
+  EXPECT_NE(new_translate_client, translate_client);
+
+  EXPECT_EQ(selection_handler(),
+            new_translate_client->language_selection_handler());
+  EXPECT_EQ(selection_handler(),
+            new_translate_client->translate_option_selection_handler());
+  EXPECT_EQ(notification_handler(),
+            new_translate_client->translate_notification_handler());
+}
+
+// Tests that the mediator sets the expected menu items for the translate
+// options popup menu on its consumer.
+TEST_F(TranslateInfobarMediatorTest, TranslateOptionMenuItems) {
+  // Set up what TranslateInfoBarDelegate should return.
+  EXPECT_CALL(*GetDelegate(), original_language_name())
+      .WillRepeatedly(testing::Return(base::UTF8ToUTF16("French")));
+  EXPECT_CALL(*GetDelegate(), ShouldAlwaysTranslate())
+      .WillOnce(testing::Return(true));
+
+  TranslateInfobarMediator* translate_infobar_mediator = mediator();
+  translate_infobar_mediator.type =
+      TranslatePopupMenuTypeTranslateOptionSelection;
+  translate_infobar_mediator.infobarDelegate = GetDelegate();
+  TestPopupMenuConsumer* consumer = [[TestPopupMenuConsumer alloc] init];
+  translate_infobar_mediator.consumer = consumer;
+
+  ASSERT_EQ(5U, consumer.items.count);
+
+  TranslatePopupMenuItem* firstItem =
+      base::mac::ObjCCastStrict<TranslatePopupMenuItem>(consumer.items[0]);
+  EXPECT_EQ(PopupMenuActionChangeTargetLanguage, firstItem.actionIdentifier);
+  EXPECT_FALSE(firstItem.selected);
+
+  TranslatePopupMenuItem* secondItem =
+      base::mac::ObjCCastStrict<TranslatePopupMenuItem>(consumer.items[1]);
+  EXPECT_EQ(PopupMenuActionAlwaysTranslateSourceLanguage,
+            secondItem.actionIdentifier);
+  EXPECT_TRUE(secondItem.selected);
+
+  TranslatePopupMenuItem* thirdItem =
+      base::mac::ObjCCastStrict<TranslatePopupMenuItem>(consumer.items[2]);
+  EXPECT_EQ(PopupMenuActionNeverTranslateSourceLanguage,
+            thirdItem.actionIdentifier);
+  EXPECT_FALSE(thirdItem.selected);
+
+  TranslatePopupMenuItem* fourthItem =
+      base::mac::ObjCCastStrict<TranslatePopupMenuItem>(consumer.items[3]);
+  EXPECT_EQ(PopupMenuActionNeverTranslateSite, fourthItem.actionIdentifier);
+  EXPECT_FALSE(fourthItem.selected);
+
+  TranslatePopupMenuItem* fifthItem =
+      base::mac::ObjCCastStrict<TranslatePopupMenuItem>(consumer.items[4]);
+  EXPECT_EQ(PopupMenuActionChangeSourceLanguage, fifthItem.actionIdentifier);
+  EXPECT_FALSE(fifthItem.selected);
+}
+
+// Tests that the mediator sets the expected menu items for the language
+// selection popup menu on its consumer.
+TEST_F(TranslateInfobarMediatorTest, LanguageSelectionMenuItems) {
+  // Set up what TranslateInfoBarDelegate should return.
+  EXPECT_CALL(*GetDelegate(), num_languages())
+      .WillRepeatedly(testing::Return(3ul));
+  EXPECT_CALL(*GetDelegate(), language_code_at(0))
+      .WillOnce(testing::Return("en"));
+  EXPECT_CALL(*GetDelegate(), language_name_at(0))
+      .WillOnce(testing::Return(base::UTF8ToUTF16("English")));
+  EXPECT_CALL(*GetDelegate(), language_code_at(2))
+      .WillOnce(testing::Return("fr"));
+  EXPECT_CALL(*GetDelegate(), language_name_at(2))
+      .WillOnce(testing::Return(base::UTF8ToUTF16("French")));
+
+  TranslateInfobarMediator* translate_infobar_mediator = mediator();
+  translate_infobar_mediator.type = TranslatePopupMenuTypeLanguageSelection;
+  translate_infobar_mediator.infobarDelegate = GetDelegate();
+  translate_infobar_mediator.unavailableLanguageIndex = 1;
+  TestPopupMenuConsumer* consumer = [[TestPopupMenuConsumer alloc] init];
+  translate_infobar_mediator.consumer = consumer;
+
+  ASSERT_EQ(2U, consumer.items.count);
+
+  SelectLanguagePopupMenuItem* firstItem =
+      base::mac::ObjCCastStrict<SelectLanguagePopupMenuItem>(consumer.items[0]);
+  EXPECT_EQ(PopupMenuActionSelectLanguage, firstItem.actionIdentifier);
+  EXPECT_FALSE(firstItem.selected);
+  EXPECT_TRUE([firstItem.languageCode isEqualToString:@"en"]);
+  EXPECT_TRUE([firstItem.title isEqualToString:@"English"]);
+
+  SelectLanguagePopupMenuItem* secondItem =
+      base::mac::ObjCCastStrict<SelectLanguagePopupMenuItem>(consumer.items[1]);
+  EXPECT_EQ(PopupMenuActionSelectLanguage, secondItem.actionIdentifier);
+  EXPECT_FALSE(secondItem.selected);
+  EXPECT_TRUE([secondItem.languageCode isEqualToString:@"fr"]);
+  EXPECT_TRUE([secondItem.title isEqualToString:@"French"]);
+}
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 7a3808b5..bb4f959 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -242,6 +242,7 @@
     "//ios/chrome/browser/ui/toolbar:unit_tests",
     "//ios/chrome/browser/ui/toolbar/fullscreen:unit_tests",
     "//ios/chrome/browser/ui/toolbar_container:unit_tests",
+    "//ios/chrome/browser/ui/translate:unit_tests",
     "//ios/chrome/browser/ui/util:unit_tests",
     "//ios/chrome/browser/ui/voice:unit_tests",
     "//ios/chrome/browser/update_client:unit_tests",
diff --git a/ios/web_view/shell/shell_view_controller.m b/ios/web_view/shell/shell_view_controller.m
index 9227aba..1ed8f80 100644
--- a/ios/web_view/shell/shell_view_controller.m
+++ b/ios/web_view/shell/shell_view_controller.m
@@ -141,6 +141,7 @@
                   action:@selector(showMenu)
         forControlEvents:UIControlEventTouchUpInside];
 
+  _field.placeholder = @"Search or type URL";
   _field.backgroundColor = [UIColor whiteColor];
   _field.tintColor = _headerBackgroundView.backgroundColor;
   [_field setContentHuggingPriority:UILayoutPriorityDefaultLow - 1
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 7cfab24..972ca80d 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -281,10 +281,6 @@
 const base::Feature kNewEncodeCpuLoadEstimator{
     "NewEncodeCpuLoadEstimator", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Use the new Remote Playback / media flinging pipeline.
-const base::Feature kNewRemotePlaybackPipeline{
-    "NewRemotePlaybackPipeline", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Use the new RTC hardware decode path via RTCVideoDecoderAdapter.
 const base::Feature kRTCVideoDecoderAdapter{"RTCVideoDecoderAdapter",
                                             base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index bd8b8cd..0400e04 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -118,7 +118,6 @@
 MEDIA_EXPORT extern const base::Feature kMojoVideoDecoder;
 MEDIA_EXPORT extern const base::Feature kMseBufferByPts;
 MEDIA_EXPORT extern const base::Feature kNewEncodeCpuLoadEstimator;
-MEDIA_EXPORT extern const base::Feature kNewRemotePlaybackPipeline;
 MEDIA_EXPORT extern const base::Feature kOverflowIconsForMediaControls;
 MEDIA_EXPORT extern const base::Feature kOverlayFullscreenVideo;
 MEDIA_EXPORT extern const base::Feature kPictureInPicture;
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index b7ec12c..fab6bfba 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -128,10 +128,6 @@
   return base::FeatureList::IsEnabled(kBackgroundVideoPauseOptimization);
 }
 
-bool IsNewRemotePlaybackPipelineEnabled() {
-  return base::FeatureList::IsEnabled(kNewRemotePlaybackPipeline);
-}
-
 bool IsNetworkStateError(blink::WebMediaPlayer::NetworkState state) {
   bool result = state == blink::WebMediaPlayer::kNetworkStateFormatError ||
                 state == blink::WebMediaPlayer::kNetworkStateNetworkError ||
@@ -2399,7 +2395,7 @@
   DVLOG(1) << __func__;
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
-  if (observer_ && IsNewRemotePlaybackPipelineEnabled() && mb_data_source_)
+  if (observer_ && mb_data_source_)
     observer_->OnDataSourceInitialized(mb_data_source_->GetUrlAfterRedirects());
 
   if (!success) {
diff --git a/media/capabilities/learning_helper.cc b/media/capabilities/learning_helper.cc
index 2102d70..a70c0ced 100644
--- a/media/capabilities/learning_helper.cc
+++ b/media/capabilities/learning_helper.cc
@@ -13,6 +13,7 @@
 using learning::LabelledExample;
 using learning::LearningSessionImpl;
 using learning::LearningTask;
+using learning::SequenceBoundFeatureProvider;
 using learning::TargetValue;
 
 const char* const kDroppedFrameRatioTreeTaskName = "DroppedFrameRatioTreeTask";
@@ -49,7 +50,7 @@
   dropped_frame_task.uma_hacky_confusion_matrix =
       "Media.Learning.MediaCapabilities.DroppedFrameRatioTask.BaseTree";
   learning_session_.Post(FROM_HERE, &LearningSessionImpl::RegisterTask,
-                         dropped_frame_task);
+                         dropped_frame_task, SequenceBoundFeatureProvider());
 
   // Modify the task to use a table-based learner.
   dropped_frame_task.name = kDroppedFrameRatioTableTaskName;
@@ -57,7 +58,7 @@
   dropped_frame_task.uma_hacky_confusion_matrix =
       "Media.Learning.MediaCapabilities.DroppedFrameRatioTask.BaseTable";
   learning_session_.Post(FROM_HERE, &LearningSessionImpl::RegisterTask,
-                         dropped_frame_task);
+                         dropped_frame_task, SequenceBoundFeatureProvider());
 }
 
 LearningHelper::~LearningHelper() = default;
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index b42ad52..9c7c5eba5 100644
--- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -230,7 +230,7 @@
     return true;
 
   Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
-  hr = dxgi_device->GetAdapter(adapter.GetAddressOf());
+  hr = dxgi_device->GetAdapter(&adapter);
   if (FAILED(hr))
     return true;
 
@@ -393,7 +393,7 @@
                     Microsoft::WRL::ComPtr<IMFSample>());
 
   Microsoft::WRL::ComPtr<IMFMediaBuffer> buffer;
-  HRESULT hr = sample->GetBufferByIndex(0, buffer.GetAddressOf());
+  HRESULT hr = sample->GetBufferByIndex(0, &buffer);
   RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from sample",
                        Microsoft::WRL::ComPtr<IMFSample>());
 
@@ -874,7 +874,7 @@
 
   HRESULT hr = E_FAIL;
 
-  hr = Direct3DCreate9Ex(D3D_SDK_VERSION, d3d9_.GetAddressOf());
+  hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d9_);
   RETURN_ON_HR_FAILURE(hr, "Direct3DCreate9Ex failed", false);
 
   hr = d3d9_->CheckDeviceFormatConversion(
@@ -883,13 +883,9 @@
   RETURN_ON_HR_FAILURE(hr, "D3D9 driver does not support H/W format conversion",
                        false);
 
-  Microsoft::WRL::ComPtr<IDirect3DDevice9> angle_device =
-      gl::QueryD3D9DeviceObjectFromANGLE();
-  if (angle_device.Get())
+  if (auto angle_device = gl::QueryD3D9DeviceObjectFromANGLE()) {
     using_angle_device_ = true;
-
-  if (using_angle_device_) {
-    hr = angle_device.CopyTo(d3d9_device_ex_.GetAddressOf());
+    hr = angle_device.As(&d3d9_device_ex_);
     RETURN_ON_HR_FAILURE(
         hr, "QueryInterface for IDirect3DDevice9Ex from angle device failed",
         false);
@@ -906,23 +902,23 @@
     present_params.FullScreen_RefreshRateInHz = 0;
     present_params.PresentationInterval = 0;
 
-    hr = d3d9_->CreateDeviceEx(
-        D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL,
-        D3DCREATE_FPU_PRESERVE | D3DCREATE_MIXED_VERTEXPROCESSING |
-            D3DCREATE_MULTITHREADED,
-        &present_params, NULL, d3d9_device_ex_.GetAddressOf());
+    hr = d3d9_->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL,
+                               D3DCREATE_FPU_PRESERVE |
+                                   D3DCREATE_MIXED_VERTEXPROCESSING |
+                                   D3DCREATE_MULTITHREADED,
+                               &present_params, NULL, &d3d9_device_ex_);
     RETURN_ON_HR_FAILURE(hr, "Failed to create D3D device", false);
   }
 
   hr = DXVA2CreateDirect3DDeviceManager9(&dev_manager_reset_token_,
-                                         device_manager_.GetAddressOf());
+                                         &device_manager_);
   RETURN_ON_HR_FAILURE(hr, "DXVA2CreateDirect3DDeviceManager9 failed", false);
 
   hr = device_manager_->ResetDevice(d3d9_device_ex_.Get(),
                                     dev_manager_reset_token_);
   RETURN_ON_HR_FAILURE(hr, "Failed to reset device", false);
 
-  hr = d3d9_device_ex_->CreateQuery(D3DQUERYTYPE_EVENT, query_.GetAddressOf());
+  hr = d3d9_device_ex_->CreateQuery(D3DQUERYTYPE_EVENT, &query_);
   RETURN_ON_HR_FAILURE(hr, "Failed to create D3D device query", false);
   // Ensure query_ API works (to avoid an infinite loop later in
   // CopyOutputSampleDataToPictureBuffer).
@@ -996,7 +992,7 @@
 
     // Create video processor
     hr = video_processor_service_->CreateVideoProcessor(
-        guids[g], &inputDesc, D3DFMT_X8R8G8B8, 0, processor_.GetAddressOf());
+        guids[g], &inputDesc, D3DFMT_X8R8G8B8, 0, &processor_);
     if (hr)
       continue;
 
@@ -1020,8 +1016,8 @@
   if (D3D11Device())
     return true;
 
-  HRESULT hr = create_dxgi_device_manager_(
-      &dx11_dev_manager_reset_token_, d3d11_device_manager_.GetAddressOf());
+  HRESULT hr = create_dxgi_device_manager_(&dx11_dev_manager_reset_token_,
+                                           &d3d11_device_manager_);
   RETURN_ON_HR_FAILURE(hr, "MFCreateDXGIDeviceManager failed", false);
 
   angle_device_ = gl::QueryD3D11DeviceObjectFromANGLE();
@@ -1033,9 +1029,9 @@
 
     using_angle_device_ = true;
     DCHECK(!use_fp16_);
-    angle_device_->GetImmediateContext(d3d11_device_context_.GetAddressOf());
+    angle_device_->GetImmediateContext(&d3d11_device_context_);
 
-    hr = angle_device_.CopyTo(video_device_.GetAddressOf());
+    hr = angle_device_.As(&video_device_);
     RETURN_ON_HR_FAILURE(hr, "Failed to get video device", false);
   } else {
     // This array defines the set of DirectX hardware feature levels we support.
@@ -1054,9 +1050,8 @@
 
     hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags,
                            feature_levels, base::size(feature_levels),
-                           D3D11_SDK_VERSION, d3d11_device_.GetAddressOf(),
-                           &feature_level_out,
-                           d3d11_device_context_.GetAddressOf());
+                           D3D11_SDK_VERSION, &d3d11_device_,
+                           &feature_level_out, &d3d11_device_context_);
     if (hr == DXGI_ERROR_SDK_COMPONENT_MISSING) {
       LOG(ERROR)
           << "Debug DXGI device creation failed, falling back to release.";
@@ -1068,17 +1063,16 @@
     if (!d3d11_device_context_) {
       hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags,
                              feature_levels, base::size(feature_levels),
-                             D3D11_SDK_VERSION, d3d11_device_.GetAddressOf(),
-                             &feature_level_out,
-                             d3d11_device_context_.GetAddressOf());
+                             D3D11_SDK_VERSION, &d3d11_device_,
+                             &feature_level_out, &d3d11_device_context_);
       RETURN_ON_HR_FAILURE(hr, "Failed to create DX11 device", false);
     }
 
-    hr = d3d11_device_.CopyTo(video_device_.GetAddressOf());
+    hr = d3d11_device_.As(&video_device_);
     RETURN_ON_HR_FAILURE(hr, "Failed to get video device", false);
   }
 
-  hr = d3d11_device_context_.CopyTo(video_context_.GetAddressOf());
+  hr = d3d11_device_context_.As(&video_context_);
   RETURN_ON_HR_FAILURE(hr, "Failed to get video context", false);
 
   D3D11_FEATURE_DATA_D3D11_OPTIONS options;
@@ -1121,7 +1115,7 @@
   D3D11_QUERY_DESC query_desc;
   query_desc.Query = D3D11_QUERY_EVENT;
   query_desc.MiscFlags = 0;
-  hr = D3D11Device()->CreateQuery(&query_desc, d3d11_query_.GetAddressOf());
+  hr = D3D11Device()->CreateQuery(&query_desc, &d3d11_query_);
   RETURN_ON_HR_FAILURE(hr, "Failed to create DX11 device query", false);
 
   return true;
@@ -1462,7 +1456,7 @@
     // creating surfaces larger than 1920 x 1088.
     if (device && !IsLegacyGPU(device.Get())) {
       Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device;
-      if (SUCCEEDED(device.CopyTo(IID_PPV_ARGS(&video_device)))) {
+      if (SUCCEEDED(device.As(&video_device))) {
         max_h264_resolutions = GetMaxResolutionsForGUIDs(
             max_h264_resolutions.first, video_device.Get(),
             {DXVA2_ModeH264_E, DXVA2_Intel_ModeH264_E},
@@ -1703,7 +1697,7 @@
 
 bool DXVAVideoDecodeAccelerator::CheckDecoderDxvaSupport() {
   Microsoft::WRL::ComPtr<IMFAttributes> attributes;
-  HRESULT hr = decoder_->GetAttributes(attributes.GetAddressOf());
+  HRESULT hr = decoder_->GetAttributes(&attributes);
   RETURN_ON_HR_FAILURE(hr, "Failed to get decoder attributes", false);
 
   UINT32 dxva = 0;
@@ -1773,7 +1767,7 @@
 
 bool DXVAVideoDecodeAccelerator::SetDecoderInputMediaType() {
   Microsoft::WRL::ComPtr<IMFMediaType> media_type;
-  HRESULT hr = MFCreateMediaType(media_type.GetAddressOf());
+  HRESULT hr = MFCreateMediaType(&media_type);
   RETURN_ON_HR_FAILURE(hr, "MFCreateMediaType failed", false);
 
   hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
@@ -1812,8 +1806,7 @@
   // ensure that we get the proper surfaces created under the hood.
   if (GetPictureBufferMechanism() == PictureBufferMechanism::BIND) {
     Microsoft::WRL::ComPtr<IMFAttributes> out_attributes;
-    HRESULT hr =
-        decoder_->GetOutputStreamAttributes(0, out_attributes.GetAddressOf());
+    HRESULT hr = decoder_->GetOutputStreamAttributes(0, &out_attributes);
     RETURN_ON_HR_FAILURE(hr, "Failed to get stream attributes", false);
     out_attributes->SetUINT32(MF_SA_D3D11_BINDFLAGS,
                               D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DECODER);
@@ -2064,8 +2057,8 @@
       }
 
       Microsoft::WRL::ComPtr<IMFMediaBuffer> output_buffer;
-      HRESULT hr = pending_sample->output_sample->GetBufferByIndex(
-          0, output_buffer.GetAddressOf());
+      HRESULT hr =
+          pending_sample->output_sample->GetBufferByIndex(0, &output_buffer);
       RETURN_AND_NOTIFY_ON_HR_FAILURE(
           hr, "Failed to get buffer from output sample", PLATFORM_FAILURE, );
 
@@ -2074,16 +2067,15 @@
 
       if (use_dx11_) {
         Microsoft::WRL::ComPtr<IMFDXGIBuffer> dxgi_buffer;
-        hr = output_buffer.CopyTo(dxgi_buffer.GetAddressOf());
+        hr = output_buffer.As(&dxgi_buffer);
         RETURN_AND_NOTIFY_ON_HR_FAILURE(
             hr, "Failed to get DXGIBuffer from output sample",
             PLATFORM_FAILURE, );
-        hr = dxgi_buffer->GetResource(
-            __uuidof(ID3D11Texture2D),
-            reinterpret_cast<void**>(d3d11_texture.GetAddressOf()));
+        hr =
+            dxgi_buffer->GetResource(__uuidof(ID3D11Texture2D), &d3d11_texture);
       } else {
         hr = MFGetService(output_buffer.Get(), MR_BUFFER_SERVICE,
-                          IID_PPV_ARGS(surface.GetAddressOf()));
+                          IID_PPV_ARGS(&surface));
       }
       RETURN_AND_NOTIFY_ON_HR_FAILURE(
           hr, "Failed to get surface from output sample", PLATFORM_FAILURE, );
@@ -2812,12 +2804,12 @@
   }
 
   Microsoft::WRL::ComPtr<IMFMediaBuffer> output_buffer;
-  hr = input_sample->GetBufferByIndex(0, output_buffer.GetAddressOf());
+  hr = input_sample->GetBufferByIndex(0, &output_buffer);
   RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to get buffer from output sample",
                                   PLATFORM_FAILURE, );
 
   Microsoft::WRL::ComPtr<IMFDXGIBuffer> dxgi_buffer;
-  hr = output_buffer.CopyTo(dxgi_buffer.GetAddressOf());
+  hr = output_buffer.As(&dxgi_buffer);
   RETURN_AND_NOTIFY_ON_HR_FAILURE(
       hr, "Failed to get DXGIBuffer from output sample", PLATFORM_FAILURE, );
   UINT index = 0;
@@ -2826,8 +2818,7 @@
                                   PLATFORM_FAILURE, );
 
   Microsoft::WRL::ComPtr<ID3D11Texture2D> dx11_decoding_texture;
-  hr = dxgi_buffer->GetResource(
-      IID_PPV_ARGS(dx11_decoding_texture.GetAddressOf()));
+  hr = dxgi_buffer->GetResource(IID_PPV_ARGS(&dx11_decoding_texture));
   RETURN_AND_NOTIFY_ON_HR_FAILURE(
       hr, "Failed to get resource from output sample", PLATFORM_FAILURE, );
 
@@ -2836,8 +2827,7 @@
   output_view_desc.Texture2D.MipSlice = 0;
   Microsoft::WRL::ComPtr<ID3D11VideoProcessorOutputView> output_view;
   hr = video_device_->CreateVideoProcessorOutputView(
-      dest_texture, enumerator_.Get(), &output_view_desc,
-      output_view.GetAddressOf());
+      dest_texture, enumerator_.Get(), &output_view_desc, &output_view);
   RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to get output view",
                                   PLATFORM_FAILURE, );
 
@@ -2848,7 +2838,7 @@
   Microsoft::WRL::ComPtr<ID3D11VideoProcessorInputView> input_view;
   hr = video_device_->CreateVideoProcessorInputView(
       dx11_decoding_texture.Get(), enumerator_.Get(), &input_view_desc,
-      input_view.GetAddressOf());
+      &input_view);
   RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to get input view",
                                   PLATFORM_FAILURE, );
 
@@ -2941,6 +2931,9 @@
     int width,
     int height,
     const gfx::ColorSpace& color_space) {
+  // This code path is never used by PictureBufferMechanism::BIND paths.
+  DCHECK_NE(GetPictureBufferMechanism(), PictureBufferMechanism::BIND);
+
   if (width < processor_width_ || height != processor_height_) {
     d3d11_processor_.Reset();
     enumerator_.Reset();
@@ -2959,13 +2952,13 @@
     desc.OutputHeight = height;
     desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
 
-    HRESULT hr = video_device_->CreateVideoProcessorEnumerator(
-        &desc, enumerator_.GetAddressOf());
+    HRESULT hr =
+        video_device_->CreateVideoProcessorEnumerator(&desc, &enumerator_);
     RETURN_ON_HR_FAILURE(hr, "Failed to enumerate video processors", false);
 
     // TODO(Hubbe): Find correct index
     hr = video_device_->CreateVideoProcessor(enumerator_.Get(), 0,
-                                             d3d11_processor_.GetAddressOf());
+                                             &d3d11_processor_);
     RETURN_ON_HR_FAILURE(hr, "Failed to create video processor.", false);
     processor_width_ = width;
     processor_height_ = height;
@@ -2974,76 +2967,81 @@
         d3d11_processor_.Get(), 0, false);
   }
 
-  if (GetPictureBufferMechanism() == PictureBufferMechanism::COPY_TO_NV12 ||
+  // If we're copying textures or just not using color space information, set
+  // the same color space on input and output.
+  if ((!use_color_info_ && !use_fp16_) ||
+      GetPictureBufferMechanism() == PictureBufferMechanism::COPY_TO_NV12 ||
       GetPictureBufferMechanism() ==
           PictureBufferMechanism::DELAYED_COPY_TO_NV12) {
-    // If we're copying NV12 textures, make sure we set the same
-    // color space on input and output.
     const auto d3d11_color_space =
         gfx::ColorSpaceWin::GetD3D11ColorSpace(color_space);
     video_context_->VideoProcessorSetOutputColorSpace(d3d11_processor_.Get(),
                                                       &d3d11_color_space);
-
     video_context_->VideoProcessorSetStreamColorSpace(d3d11_processor_.Get(), 0,
                                                       &d3d11_color_space);
     dx11_converter_output_color_space_ = color_space;
+    return true;
+  }
+
+  // This path is only used for copying to RGB textures.
+  DCHECK_EQ(GetPictureBufferMechanism(), PictureBufferMechanism::COPY_TO_RGB);
+
+  // On platforms prior to Windows 10 we won't have a ID3D11VideoContext1.
+  Microsoft::WRL::ComPtr<ID3D11VideoContext1> video_context1;
+  if (FAILED(video_context_.As(&video_context1))) {
+    auto d3d11_color_space =
+        gfx::ColorSpaceWin::GetD3D11ColorSpace(color_space);
+    video_context_->VideoProcessorSetStreamColorSpace(d3d11_processor_.Get(), 0,
+                                                      &d3d11_color_space);
+
+    // Since older platforms won't have HDR, just use SRGB.
+    dx11_converter_output_color_space_ = gfx::ColorSpace::CreateSRGB();
+    d3d11_color_space = gfx::ColorSpaceWin::GetD3D11ColorSpace(
+        dx11_converter_output_color_space_);
+    video_context_->VideoProcessorSetOutputColorSpace(d3d11_processor_.Get(),
+                                                      &d3d11_color_space);
+    return true;
+  }
+
+  // Since the video processor doesn't support HLG, lets just do the YUV->RGB
+  // conversion and let the output color space be HLG. This won't work well
+  // unless color management is on, but if color management is off we don't
+  // support HLG anyways.
+  if (color_space == gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020,
+                                     gfx::ColorSpace::TransferID::ARIB_STD_B67,
+                                     gfx::ColorSpace::MatrixID::BT709,
+                                     gfx::ColorSpace::RangeID::LIMITED)) {
+    video_context1->VideoProcessorSetStreamColorSpace1(
+        d3d11_processor_.Get(), 0,
+        DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020);
+    video_context1->VideoProcessorSetOutputColorSpace1(
+        d3d11_processor_.Get(), DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
+    dx11_converter_output_color_space_ = color_space.GetAsFullRangeRGB();
+    return true;
+  }
+
+  if (use_fp16_ && config_.target_color_space.IsHDR() && color_space.IsHDR()) {
+    // Note, we only use the SCRGBLinear output color space when the input is
+    // PQ, because nvidia drivers will not convert G22 to G10 for some reason.
+    dx11_converter_output_color_space_ = gfx::ColorSpace::CreateSCRGBLinear();
   } else {
     dx11_converter_output_color_space_ = gfx::ColorSpace::CreateSRGB();
-    if (use_color_info_ || use_fp16_) {
-      Microsoft::WRL::ComPtr<ID3D11VideoContext1> video_context1;
-      HRESULT hr = video_context_.CopyTo(video_context1.GetAddressOf());
-      if (SUCCEEDED(hr)) {
-        if (use_fp16_ && config_.target_color_space.IsHDR() &&
-            color_space.IsHDR()) {
-          // Note, we only use the SCRGBLinear output color space when
-          // the input is PQ, because nvidia drivers will not convert
-          // G22 to G10 for some reason.
-          dx11_converter_output_color_space_ =
-              gfx::ColorSpace::CreateSCRGBLinear();
-        }
-        // Since the video processor doesn't support HLG, let's just do the
-        // YUV->RGB conversion and let the output color space be HLG.
-        // This won't work well unless color management is on, but if color
-        // management is off we don't support HLG anyways.
-        if (color_space ==
-            gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020,
-                            gfx::ColorSpace::TransferID::ARIB_STD_B67,
-                            gfx::ColorSpace::MatrixID::BT709,
-                            gfx::ColorSpace::RangeID::LIMITED)) {
-          video_context1->VideoProcessorSetStreamColorSpace1(
-              d3d11_processor_.Get(), 0,
-              DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020);
-          video_context1->VideoProcessorSetOutputColorSpace1(
-              d3d11_processor_.Get(),
-              DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
-          dx11_converter_output_color_space_ = color_space.GetAsFullRangeRGB();
-        } else {
-          DVLOG(2) << "input color space: " << color_space
-                   << " DXGIColorSpace: "
-                   << gfx::ColorSpaceWin::GetDXGIColorSpace(color_space);
-          DVLOG(2) << "output color space:"
-                   << dx11_converter_output_color_space_ << " DXGIColorSpace: "
-                   << gfx::ColorSpaceWin::GetDXGIColorSpace(
-                          dx11_converter_output_color_space_);
-          video_context1->VideoProcessorSetStreamColorSpace1(
-              d3d11_processor_.Get(), 0,
-              gfx::ColorSpaceWin::GetDXGIColorSpace(color_space));
-          video_context1->VideoProcessorSetOutputColorSpace1(
-              d3d11_processor_.Get(), gfx::ColorSpaceWin::GetDXGIColorSpace(
-                                          dx11_converter_output_color_space_));
-        }
-      } else {
-        D3D11_VIDEO_PROCESSOR_COLOR_SPACE d3d11_color_space =
-            gfx::ColorSpaceWin::GetD3D11ColorSpace(color_space);
-        video_context_->VideoProcessorSetStreamColorSpace(
-            d3d11_processor_.Get(), 0, &d3d11_color_space);
-        d3d11_color_space = gfx::ColorSpaceWin::GetD3D11ColorSpace(
-            dx11_converter_output_color_space_);
-        video_context_->VideoProcessorSetOutputColorSpace(
-            d3d11_processor_.Get(), &d3d11_color_space);
-      }
-    }
   }
+
+  DVLOG(2) << "input color space: " << color_space << " DXGIColorSpace: "
+           << gfx::ColorSpaceWin::GetDXGIColorSpace(color_space);
+  DVLOG(2) << "output color space:" << dx11_converter_output_color_space_
+           << " DXGIColorSpace: "
+           << gfx::ColorSpaceWin::GetDXGIColorSpace(
+                  dx11_converter_output_color_space_);
+
+  video_context1->VideoProcessorSetStreamColorSpace1(
+      d3d11_processor_.Get(), 0,
+      gfx::ColorSpaceWin::GetDXGIColorSpace(color_space));
+  video_context1->VideoProcessorSetOutputColorSpace1(
+      d3d11_processor_.Get(), gfx::ColorSpaceWin::GetDXGIColorSpace(
+                                  dx11_converter_output_color_space_));
+
   return true;
 }
 
@@ -3051,18 +3049,16 @@
                                                          int* width,
                                                          int* height) {
   Microsoft::WRL::ComPtr<IMFMediaBuffer> output_buffer;
-  HRESULT hr = sample->GetBufferByIndex(0, output_buffer.GetAddressOf());
+  HRESULT hr = sample->GetBufferByIndex(0, &output_buffer);
   RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from output sample", false);
 
   if (use_dx11_) {
     Microsoft::WRL::ComPtr<IMFDXGIBuffer> dxgi_buffer;
     Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
-    hr = output_buffer.CopyTo(dxgi_buffer.GetAddressOf());
+    hr = output_buffer.As(&dxgi_buffer);
     RETURN_ON_HR_FAILURE(hr, "Failed to get DXGIBuffer from output sample",
                          false);
-    hr = dxgi_buffer->GetResource(
-        __uuidof(ID3D11Texture2D),
-        reinterpret_cast<void**>(d3d11_texture.GetAddressOf()));
+    hr = dxgi_buffer->GetResource(__uuidof(ID3D11Texture2D), &d3d11_texture);
     RETURN_ON_HR_FAILURE(hr, "Failed to get D3D11Texture from output buffer",
                          false);
     D3D11_TEXTURE2D_DESC d3d11_texture_desc;
@@ -3073,7 +3069,7 @@
   } else {
     Microsoft::WRL::ComPtr<IDirect3DSurface9> surface;
     hr = MFGetService(output_buffer.Get(), MR_BUFFER_SERVICE,
-                      IID_PPV_ARGS(surface.GetAddressOf()));
+                      IID_PPV_ARGS(&surface));
     RETURN_ON_HR_FAILURE(hr, "Failed to get D3D surface from output sample",
                          false);
     D3DSURFACE_DESC surface_desc;
@@ -3092,9 +3088,8 @@
   HRESULT hr = E_FAIL;
   Microsoft::WRL::ComPtr<IMFMediaType> media_type;
 
-  for (uint32_t i = 0; SUCCEEDED(
-           transform->GetOutputAvailableType(0, i, media_type.GetAddressOf()));
-       ++i) {
+  for (uint32_t i = 0;
+       SUCCEEDED(transform->GetOutputAvailableType(0, i, &media_type)); ++i) {
     GUID out_subtype = {0};
     hr = media_type->GetGUID(MF_MT_SUBTYPE, &out_subtype);
     RETURN_ON_HR_FAILURE(hr, "Failed to get output major type", false);
@@ -3122,7 +3117,7 @@
   }
 
   Microsoft::WRL::ComPtr<IMFMediaBuffer> buffer;
-  HRESULT hr = sample->GetBufferByIndex(0, buffer.GetAddressOf());
+  HRESULT hr = sample->GetBufferByIndex(0, &buffer);
   RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from input sample", hr);
 
   mf::MediaBufferScopedPointer scoped_media_buffer(buffer.Get());
diff --git a/media/learning/common/labelled_example.cc b/media/learning/common/labelled_example.cc
index 6c8cb7d..76d0850 100644
--- a/media/learning/common/labelled_example.cc
+++ b/media/learning/common/labelled_example.cc
@@ -69,6 +69,8 @@
 
 TrainingData::~TrainingData() = default;
 
+TrainingData& TrainingData::operator=(const TrainingData& rhs) = default;
+
 TrainingData& TrainingData::operator=(TrainingData&& rhs) = default;
 
 TrainingData TrainingData::DeDuplicate() const {
diff --git a/media/learning/common/labelled_example.h b/media/learning/common/labelled_example.h
index ee89586f..4f43c54 100644
--- a/media/learning/common/labelled_example.h
+++ b/media/learning/common/labelled_example.h
@@ -65,6 +65,7 @@
   TrainingData(const TrainingData& rhs);
   TrainingData(TrainingData&& rhs);
 
+  TrainingData& operator=(const TrainingData& rhs);
   TrainingData& operator=(TrainingData&& rhs);
 
   ~TrainingData();
diff --git a/media/learning/impl/BUILD.gn b/media/learning/impl/BUILD.gn
index aae0ddd..4275583e 100644
--- a/media/learning/impl/BUILD.gn
+++ b/media/learning/impl/BUILD.gn
@@ -16,6 +16,8 @@
     "distribution_reporter.h",
     "extra_trees_trainer.cc",
     "extra_trees_trainer.h",
+    "feature_provider.cc",
+    "feature_provider.h",
     "learning_session_impl.cc",
     "learning_session_impl.h",
     "learning_task_controller.h",
diff --git a/media/learning/impl/feature_provider.cc b/media/learning/impl/feature_provider.cc
new file mode 100644
index 0000000..2408b4d
--- /dev/null
+++ b/media/learning/impl/feature_provider.cc
@@ -0,0 +1,15 @@
+// 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 "media/learning/impl/feature_provider.h"
+
+namespace media {
+namespace learning {
+
+FeatureProvider::FeatureProvider() = default;
+
+FeatureProvider::~FeatureProvider() = default;
+
+}  // namespace learning
+}  // namespace media
diff --git a/media/learning/impl/feature_provider.h b/media/learning/impl/feature_provider.h
new file mode 100644
index 0000000..a8f0a57
--- /dev/null
+++ b/media/learning/impl/feature_provider.h
@@ -0,0 +1,54 @@
+// 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 MEDIA_LEARNING_IMPL_FEATURE_PROVIDER_H_
+#define MEDIA_LEARNING_IMPL_FEATURE_PROVIDER_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "base/threading/sequence_bound.h"
+#include "media/learning/common/labelled_example.h"
+#include "media/learning/common/learning_task.h"
+
+namespace media {
+namespace learning {
+
+// Add features to a training example.  If the LearningTask's feature
+// description includes feature names that a FeatureProvider knows about, then
+// it will replace their value in the examples with whatever value that feature
+// should have.  For example, "NetworkType" might be replaced by a value that
+// indicates the type of network connection.
+class COMPONENT_EXPORT(LEARNING_IMPL) FeatureProvider {
+ public:
+  using LabelledExampleCB = base::OnceCallback<void(LabelledExample)>;
+
+  FeatureProvider();
+  virtual ~FeatureProvider();
+
+  // Update |example| to include whatever features are specified by |task_|,
+  // and call |cb| once they're filled in.
+  virtual void AddFeatures(const LabelledExample& example,
+                           LabelledExampleCB cb) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FeatureProvider);
+};
+
+// Since FeatureProviders are often going to thread-hop, provide this typedef.
+using SequenceBoundFeatureProvider = base::SequenceBound<FeatureProvider>;
+
+// Factory callback, since things that create implementations will likely be
+// elsewhere (e.g., content/) from the things which use them (e.g., here).  May
+// return an empty provider if the task doesn't require one.
+using FeatureProviderFactoryCB =
+    base::RepeatingCallback<SequenceBoundFeatureProvider(const LearningTask&)>;
+
+}  // namespace learning
+}  // namespace media
+
+#endif  // MEDIA_LEARNING_IMPL_FEATURE_PROVIDER_H_
diff --git a/media/learning/impl/learning_session_impl.cc b/media/learning/impl/learning_session_impl.cc
index aee1e93..a7d6e63 100644
--- a/media/learning/impl/learning_session_impl.cc
+++ b/media/learning/impl/learning_session_impl.cc
@@ -4,6 +4,8 @@
 
 #include "media/learning/impl/learning_session_impl.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/logging.h"
 #include "media/learning/impl/distribution_reporter.h"
@@ -14,10 +16,12 @@
 
 LearningSessionImpl::LearningSessionImpl()
     : controller_factory_(
-          base::BindRepeating([](const LearningTask& task)
+          base::BindRepeating([](const LearningTask& task,
+                                 SequenceBoundFeatureProvider feature_provider)
                                   -> std::unique_ptr<LearningTaskController> {
             return std::make_unique<LearningTaskControllerImpl>(
-                task, DistributionReporter::Create(task));
+                task, DistributionReporter::Create(task),
+                std::move(feature_provider));
           })) {}
 
 LearningSessionImpl::~LearningSessionImpl() = default;
@@ -34,9 +38,12 @@
     iter->second->AddExample(example);
 }
 
-void LearningSessionImpl::RegisterTask(const LearningTask& task) {
+void LearningSessionImpl::RegisterTask(
+    const LearningTask& task,
+    SequenceBoundFeatureProvider feature_provider) {
   DCHECK(task_map_.count(task.name) == 0);
-  task_map_.emplace(task.name, controller_factory_.Run(task));
+  task_map_.emplace(task.name,
+                    controller_factory_.Run(task, std::move(feature_provider)));
 }
 
 }  // namespace learning
diff --git a/media/learning/impl/learning_session_impl.h b/media/learning/impl/learning_session_impl.h
index 80c50fa..1938f0a 100644
--- a/media/learning/impl/learning_session_impl.h
+++ b/media/learning/impl/learning_session_impl.h
@@ -8,7 +8,9 @@
 #include <map>
 
 #include "base/component_export.h"
+#include "base/threading/sequence_bound.h"
 #include "media/learning/common/learning_session.h"
+#include "media/learning/impl/feature_provider.h"
 #include "media/learning/impl/learning_task_controller.h"
 
 namespace media {
@@ -19,12 +21,13 @@
 class COMPONENT_EXPORT(LEARNING_IMPL) LearningSessionImpl
     : public LearningSession {
  public:
-  explicit LearningSessionImpl();
+  LearningSessionImpl();
   ~LearningSessionImpl() override;
 
   using CreateTaskControllerCB =
       base::RepeatingCallback<std::unique_ptr<LearningTaskController>(
-          const LearningTask&)>;
+          const LearningTask&,
+          SequenceBoundFeatureProvider)>;
 
   void SetTaskControllerFactoryCBForTesting(CreateTaskControllerCB cb);
 
@@ -34,7 +37,9 @@
 
   // Registers |task|, so that calls to AddExample with |task.name| will work.
   // This will create a new controller for the task.
-  void RegisterTask(const LearningTask& task);
+  void RegisterTask(const LearningTask& task,
+                    SequenceBoundFeatureProvider feature_provider =
+                        SequenceBoundFeatureProvider());
 
  private:
   // [task_name] = task controller.
diff --git a/media/learning/impl/learning_session_impl_unittest.cc b/media/learning/impl/learning_session_impl_unittest.cc
index 96dae4f..36f343e 100644
--- a/media/learning/impl/learning_session_impl_unittest.cc
+++ b/media/learning/impl/learning_session_impl_unittest.cc
@@ -3,9 +3,12 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "media/learning/impl/learning_session_impl.h"
 #include "media/learning/impl/learning_task_controller.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -17,23 +20,49 @@
  public:
   class FakeLearningTaskController : public LearningTaskController {
    public:
-    FakeLearningTaskController(const LearningTask& task) {}
+    FakeLearningTaskController(const LearningTask& task,
+                               SequenceBoundFeatureProvider feature_provider)
+        : feature_provider_(std::move(feature_provider)) {
+      // As a complete hack, call the only public method on fp so that
+      // we can verify that it was given to us by the session.
+      if (!feature_provider_.is_null()) {
+        feature_provider_.Post(FROM_HERE, &FeatureProvider::AddFeatures,
+                               LabelledExample(),
+                               FeatureProvider::LabelledExampleCB());
+      }
+    }
 
     void AddExample(const LabelledExample& example) override {
       example_ = example;
     }
 
+    SequenceBoundFeatureProvider feature_provider_;
     LabelledExample example_;
   };
 
+  class FakeFeatureProvider : public FeatureProvider {
+   public:
+    FakeFeatureProvider(bool* flag_ptr) : flag_ptr_(flag_ptr) {}
+
+    // Do nothing, except note that we were called.
+    void AddFeatures(const LabelledExample& example,
+                     FeatureProvider::LabelledExampleCB cb) override {
+      *flag_ptr_ = true;
+    };
+
+    bool* flag_ptr_ = nullptr;
+  };
+
   using ControllerVector = std::vector<FakeLearningTaskController*>;
 
   LearningSessionImplTest() {
     session_ = std::make_unique<LearningSessionImpl>();
     session_->SetTaskControllerFactoryCBForTesting(base::BindRepeating(
-        [](ControllerVector* controllers, const LearningTask& task)
+        [](ControllerVector* controllers, const LearningTask& task,
+           SequenceBoundFeatureProvider feature_provider)
             -> std::unique_ptr<LearningTaskController> {
-          auto controller = std::make_unique<FakeLearningTaskController>(task);
+          auto controller = std::make_unique<FakeLearningTaskController>(
+              task, std::move(feature_provider));
           controllers->push_back(controller.get());
           return controller;
         },
@@ -43,6 +72,8 @@
     task_1_.name = "task_1";
   }
 
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
   std::unique_ptr<LearningSessionImpl> session_;
 
   LearningTask task_0_;
@@ -74,5 +105,17 @@
   EXPECT_EQ(task_controllers_[1]->example_, example_1);
 }
 
+TEST_F(LearningSessionImplTest, FeatureProviderIsForwarded) {
+  // Verify that a FeatureProvider actually gets forwarded to the LTC.
+  bool flag = false;
+  session_->RegisterTask(task_0_,
+                         base::SequenceBound<FakeFeatureProvider>(
+                             base::SequencedTaskRunnerHandle::Get(), &flag));
+  scoped_task_environment_.RunUntilIdle();
+  // Registering the task should create a FakeLearningTaskController, which will
+  // call AddFeatures on the fake FeatureProvider.
+  EXPECT_TRUE(flag);
+}
+
 }  // namespace learning
 }  // namespace media
diff --git a/media/learning/impl/learning_task_controller_impl.cc b/media/learning/impl/learning_task_controller_impl.cc
index 31e182f..d5cf92b 100644
--- a/media/learning/impl/learning_task_controller_impl.cc
+++ b/media/learning/impl/learning_task_controller_impl.cc
@@ -15,9 +15,11 @@
 
 LearningTaskControllerImpl::LearningTaskControllerImpl(
     const LearningTask& task,
-    std::unique_ptr<DistributionReporter> reporter)
+    std::unique_ptr<DistributionReporter> reporter,
+    SequenceBoundFeatureProvider feature_provider)
     : task_(task),
       training_data_(std::make_unique<TrainingData>()),
+      feature_provider_(std::move(feature_provider)),
       reporter_(std::move(reporter)) {
   switch (task_.model) {
     case LearningTask::Model::kExtraTrees:
@@ -32,6 +34,18 @@
 LearningTaskControllerImpl::~LearningTaskControllerImpl() = default;
 
 void LearningTaskControllerImpl::AddExample(const LabelledExample& example) {
+  if (!feature_provider_.is_null()) {
+    // TODO(liberato): BindToCurrentSequence
+    feature_provider_.Post(
+        FROM_HERE, &FeatureProvider::AddFeatures, example,
+        base::BindOnce(&LearningTaskControllerImpl::OnExampleReady,
+                       AsWeakPtr()));
+  } else {
+    OnExampleReady(example);
+  }
+}
+
+void LearningTaskControllerImpl::OnExampleReady(LabelledExample example) {
   if (training_data_->size() >= task_.max_data_set_size) {
     // Replace a random example.  We don't necessarily want to replace the
     // oldest, since we don't necessarily want to enforce an ad-hoc recency
diff --git a/media/learning/impl/learning_task_controller_impl.h b/media/learning/impl/learning_task_controller_impl.h
index b2d4a6a..6f40924 100644
--- a/media/learning/impl/learning_task_controller_impl.h
+++ b/media/learning/impl/learning_task_controller_impl.h
@@ -11,6 +11,7 @@
 #include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
 #include "media/learning/impl/distribution_reporter.h"
+#include "media/learning/impl/feature_provider.h"
 #include "media/learning/impl/learning_task_controller.h"
 #include "media/learning/impl/random_number_generator.h"
 #include "media/learning/impl/training_algorithm.h"
@@ -27,13 +28,19 @@
  public:
   LearningTaskControllerImpl(
       const LearningTask& task,
-      std::unique_ptr<DistributionReporter> reporter = nullptr);
+      std::unique_ptr<DistributionReporter> reporter = nullptr,
+      SequenceBoundFeatureProvider feature_provider =
+          SequenceBoundFeatureProvider());
   ~LearningTaskControllerImpl() override;
 
   // LearningTaskController
   void AddExample(const LabelledExample& example) override;
 
  private:
+  // Called when a new example has been finished by |feature_provider_|, if
+  // needed, to actually add the example.
+  void OnExampleReady(LabelledExample example);
+
   // Called by |training_cb_| when the model is trained.
   void OnModelTrained(std::unique_ptr<Model> model);
 
@@ -57,6 +64,9 @@
   // Training algorithm that we'll use.
   std::unique_ptr<TrainingAlgorithm> trainer_;
 
+  // Optional feature provider.
+  SequenceBoundFeatureProvider feature_provider_;
+
   // Optional reporter for training accuracy.
   std::unique_ptr<DistributionReporter> reporter_;
 
diff --git a/media/learning/impl/learning_task_controller_impl_unittest.cc b/media/learning/impl/learning_task_controller_impl_unittest.cc
index 32cfa21..3d854676 100644
--- a/media/learning/impl/learning_task_controller_impl_unittest.cc
+++ b/media/learning/impl/learning_task_controller_impl_unittest.cc
@@ -4,7 +4,11 @@
 
 #include "media/learning/impl/learning_task_controller_impl.h"
 
+#include <utility>
+
 #include "base/bind.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "media/learning/impl/distribution_reporter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -61,44 +65,75 @@
                const TrainingData& training_data,
                TrainedModelCB model_cb) override {
       (*num_models_)++;
+      training_data_ = training_data;
       std::move(model_cb).Run(std::make_unique<FakeModel>(target_value_));
     }
 
+    const TrainingData& training_data() const { return training_data_; }
+
    private:
     int* num_models_ = nullptr;
     TargetValue target_value_;
+
+    // Most recently provided training data.
+    TrainingData training_data_;
+  };
+
+  // Increments feature 0.
+  class FakeFeatureProvider : public FeatureProvider {
+   public:
+    void AddFeatures(const LabelledExample& example,
+                     LabelledExampleCB cb) override {
+      LabelledExample new_example = example;
+      new_example.features[0] = FeatureValue(example.features[0].value() + 1);
+      std::move(cb).Run(new_example);
+    }
   };
 
   LearningTaskControllerImplTest()
       : predicted_target_(123), not_predicted_target_(456) {
+    // Set the name so that we can check it later.
+    task_.name = "TestTask";
     // Don't require too many training examples per report.
     task_.max_data_set_size = 20;
     task_.min_new_data_fraction = 0.1;
+  }
 
+  void CreateController(SequenceBoundFeatureProvider feature_provider =
+                            SequenceBoundFeatureProvider()) {
     std::unique_ptr<FakeDistributionReporter> reporter =
         std::make_unique<FakeDistributionReporter>(task_);
     reporter_raw_ = reporter.get();
 
     controller_ = std::make_unique<LearningTaskControllerImpl>(
-        task_, std::move(reporter));
-    controller_->SetTrainerForTesting(
-        std::make_unique<FakeTrainer>(&num_models_, predicted_target_));
+        task_, std::move(reporter), std::move(feature_provider));
+
+    auto fake_trainer =
+        std::make_unique<FakeTrainer>(&num_models_, predicted_target_);
+    trainer_raw_ = fake_trainer.get();
+    controller_->SetTrainerForTesting(std::move(fake_trainer));
   }
 
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
   // Number of models that we trained.
   int num_models_ = 0;
+  FakeModel* last_model_ = nullptr;
 
   // Two distinct targets.
   const TargetValue predicted_target_;
   const TargetValue not_predicted_target_;
 
   FakeDistributionReporter* reporter_raw_ = nullptr;
+  FakeTrainer* trainer_raw_ = nullptr;
 
   LearningTask task_;
   std::unique_ptr<LearningTaskControllerImpl> controller_;
 };
 
 TEST_F(LearningTaskControllerImplTest, AddingExamplesTrainsModelAndReports) {
+  CreateController();
+
   LabelledExample example;
 
   // Up to the first 1/training_fraction examples should train on each example.
@@ -139,5 +174,19 @@
   EXPECT_EQ(reporter_raw_->num_correct_, count * 3 - 1);  // Unchanged.
 }
 
+TEST_F(LearningTaskControllerImplTest, FeatureProviderIsUsed) {
+  // If a FeatureProvider factory is provided, make sure that it's used to
+  // adjust new examples.
+  SequenceBoundFeatureProvider feature_provider =
+      base::SequenceBound<FakeFeatureProvider>(
+          base::SequencedTaskRunnerHandle::Get());
+  CreateController(std::move(feature_provider));
+  LabelledExample example;
+  example.features.push_back(FeatureValue(123));
+  controller_->AddExample(example);
+  scoped_task_environment_.RunUntilIdle();
+  EXPECT_EQ(trainer_raw_->training_data()[0].features[0], FeatureValue(124));
+}
+
 }  // namespace learning
 }  // namespace media
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc
index 6544e86..eeb06fc 100644
--- a/media/test/pipeline_integration_test_base.cc
+++ b/media/test/pipeline_integration_test_base.cc
@@ -597,10 +597,12 @@
     FakeEncryptedMedia* encrypted_media) {
   ParseTestTypeFlags(test_type);
 
-  if (fuzzing_)
+  if (fuzzing_) {
     EXPECT_CALL(*source, InitSegmentReceivedMock(_)).Times(AnyNumber());
-  else if (!(test_type & kExpectDemuxerFailure))
+    EXPECT_CALL(*source, OnParseWarningMock(_)).Times(AnyNumber());
+  } else if (!(test_type & kExpectDemuxerFailure)) {
     EXPECT_CALL(*source, InitSegmentReceivedMock(_)).Times(AtLeast(1));
+  }
 
   EXPECT_CALL(*this, OnMetadata(_))
       .Times(AtMost(1))
diff --git a/mojo/public/cpp/system/file_data_pipe_producer.cc b/mojo/public/cpp/system/file_data_pipe_producer.cc
index 0fc1f30..f207e0d 100644
--- a/mojo/public/cpp/system/file_data_pipe_producer.cc
+++ b/mojo/public/cpp/system/file_data_pipe_producer.cc
@@ -266,8 +266,11 @@
 
 void FileDataPipeProducer::InitializeNewRequest(CompletionCallback callback) {
   DCHECK(!file_sequence_state_);
+  // TODO(crbug.com/924416): Re-evaluate how TaskPriority is set here and in
+  // other file URL-loading-related code. Some callers require USER_VISIBLE
+  // (i.e., BEST_EFFORT is not enough).
   auto file_task_runner = base::CreateSequencedTaskRunnerWithTraits(
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
+      {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
   file_sequence_state_ = new FileSequenceState(
       std::move(producer_), file_task_runner,
       base::BindOnce(&FileDataPipeProducer::OnWriteComplete,
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 311e601..ace5ca4 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -6425,6 +6425,8 @@
   ]
   deps = [
     ":net_fuzzer_test_support",
+    ":quic_test_tools",
+    ":test_support",
     "//base",
     "//net",
   ]
diff --git a/net/http/http_auth_cache_unittest.cc b/net/http/http_auth_cache_unittest.cc
index eadfbe45..41016b0 100644
--- a/net/http/http_auth_cache_unittest.cc
+++ b/net/http/http_auth_cache_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_auth_cache.h"
-#include "net/http/http_auth_handler.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::ASCIIToUTF16;
@@ -21,43 +20,6 @@
 
 namespace {
 
-class MockAuthHandler : public HttpAuthHandler {
- public:
-  MockAuthHandler(HttpAuth::Scheme scheme,
-                  const std::string& realm,
-                  HttpAuth::Target target) {
-    // Can't use initializer list since these are members of the base class.
-    auth_scheme_ = scheme;
-    realm_ = realm;
-    score_ = 1;
-    target_ = target;
-    properties_ = 0;
-  }
-
-  HttpAuth::AuthorizationResult HandleAnotherChallenge(
-      HttpAuthChallengeTokenizer* challenge) override {
-    return HttpAuth::AUTHORIZATION_RESULT_REJECT;
-  }
-
- protected:
-  bool Init(HttpAuthChallengeTokenizer* challenge,
-            const SSLInfo& ssl_info) override {
-    return false;  // Unused.
-  }
-
-  int GenerateAuthTokenImpl(const AuthCredentials*,
-                            const HttpRequestInfo*,
-                            CompletionOnceCallback callback,
-                            std::string* auth_token) override {
-    *auth_token = "mock-credentials";
-    return OK;
-  }
-
-
- private:
-  ~MockAuthHandler() override = default;
-};
-
 const char kRealm1[] = "Realm1";
 const char kRealm2[] = "Realm2";
 const char kRealm3[] = "Realm3";
@@ -90,75 +52,54 @@
   // Add cache entries for 4 realms: "Realm1", "Realm2", "Realm3" and
   // "Realm4"
 
-  std::unique_ptr<HttpAuthHandler> realm1_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm1, HttpAuth::AUTH_SERVER));
-  cache.Add(origin, realm1_handler->realm(), realm1_handler->auth_scheme(),
-            "Basic realm=Realm1",
+  cache.Add(origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=Realm1",
             CreateASCIICredentials("realm1-user", "realm1-password"),
             "/foo/bar/index.html");
 
-  std::unique_ptr<HttpAuthHandler> realm2_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm2, HttpAuth::AUTH_SERVER));
-  cache.Add(origin, realm2_handler->realm(), realm2_handler->auth_scheme(),
-            "Basic realm=Realm2",
+  cache.Add(origin, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=Realm2",
             CreateASCIICredentials("realm2-user", "realm2-password"),
             "/foo2/index.html");
 
-  std::unique_ptr<HttpAuthHandler> realm3_basic_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm3, HttpAuth::AUTH_PROXY));
   cache.Add(
-      origin,
-      realm3_basic_handler->realm(),
-      realm3_basic_handler->auth_scheme(),
-      "Basic realm=Realm3",
+      origin, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=Realm3",
       CreateASCIICredentials("realm3-basic-user", "realm3-basic-password"),
       std::string());
 
-  std::unique_ptr<HttpAuthHandler> realm3_digest_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_DIGEST, kRealm3, HttpAuth::AUTH_PROXY));
-  cache.Add(origin, realm3_digest_handler->realm(),
-            realm3_digest_handler->auth_scheme(), "Digest realm=Realm3",
-            CreateASCIICredentials("realm3-digest-user",
-                                   "realm3-digest-password"),
-            "/baz/index.html");
+  cache.Add(
+      origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, "Digest realm=Realm3",
+      CreateASCIICredentials("realm3-digest-user", "realm3-digest-password"),
+      "/baz/index.html");
 
-  std::unique_ptr<HttpAuthHandler> realm4_basic_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm4, HttpAuth::AUTH_SERVER));
-  cache.Add(origin, realm4_basic_handler->realm(),
-            realm4_basic_handler->auth_scheme(), "Basic realm=Realm4",
-            CreateASCIICredentials("realm4-basic-user",
-                                   "realm4-basic-password"),
-            "/");
+  cache.Add(
+      origin, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=Realm4",
+      CreateASCIICredentials("realm4-basic-user", "realm4-basic-password"),
+      "/");
 
-  std::unique_ptr<HttpAuthHandler> origin2_realm5_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm5, HttpAuth::AUTH_SERVER));
-  cache.Add(origin2, origin2_realm5_handler->realm(),
-            origin2_realm5_handler->auth_scheme(), "Basic realm=Realm5",
+  cache.Add(origin2, kRealm5, HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=Realm5",
             CreateASCIICredentials("realm5-user", "realm5-password"), "/");
   cache.Add(
-      origin2, realm3_basic_handler->realm(),
-      realm3_basic_handler->auth_scheme(), "Basic realm=Realm3",
+      origin2, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=Realm3",
       CreateASCIICredentials("realm3-basic-user", "realm3-basic-password"),
       std::string());
 
   // There is no Realm5 in origin
   entry = cache.Lookup(origin, kRealm5, HttpAuth::AUTH_SCHEME_BASIC);
-  EXPECT_TRUE(NULL == entry);
+  EXPECT_FALSE(entry);
 
   // While Realm3 does exist, the origin scheme is wrong.
   entry = cache.Lookup(GURL("https://www.google.com"), kRealm3,
                        HttpAuth::AUTH_SCHEME_BASIC);
-  EXPECT_TRUE(NULL == entry);
+  EXPECT_FALSE(entry);
 
   // Realm, origin scheme ok, authentication scheme wrong
   entry = cache.Lookup
       (GURL("http://www.google.com"), kRealm1, HttpAuth::AUTH_SCHEME_DIGEST);
-  EXPECT_TRUE(NULL == entry);
+  EXPECT_FALSE(entry);
 
   // Valid lookup by origin, realm, scheme.
   entry = cache.Lookup(
       GURL("http://www.google.com:80"), kRealm3, HttpAuth::AUTH_SCHEME_BASIC);
-  ASSERT_FALSE(NULL == entry);
+  ASSERT_TRUE(entry);
   EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme());
   EXPECT_EQ(kRealm3, entry->realm());
   EXPECT_EQ("Basic realm=Realm3", entry->auth_challenge());
@@ -169,14 +110,14 @@
   // Same realm, scheme with different origins
   HttpAuthCache::Entry* entry2 = cache.Lookup(
       GURL("http://www.foobar.com:80"), kRealm3, HttpAuth::AUTH_SCHEME_BASIC);
-  ASSERT_FALSE(NULL == entry2);
+  ASSERT_TRUE(entry2);
   EXPECT_NE(entry, entry2);
 
   // Valid lookup by origin, realm, scheme when there's a duplicate
   // origin, realm in the cache
   entry = cache.Lookup(
       GURL("http://www.google.com:80"), kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
-  ASSERT_FALSE(NULL == entry);
+  ASSERT_TRUE(entry);
   EXPECT_EQ(HttpAuth::AUTH_SCHEME_DIGEST, entry->scheme());
   EXPECT_EQ(kRealm3, entry->realm());
   EXPECT_EQ("Digest realm=Realm3", entry->auth_challenge());
@@ -187,7 +128,7 @@
 
   // Valid lookup by realm.
   entry = cache.Lookup(origin, kRealm2, HttpAuth::AUTH_SCHEME_BASIC);
-  ASSERT_FALSE(NULL == entry);
+  ASSERT_TRUE(entry);
   EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme());
   EXPECT_EQ(kRealm2, entry->realm());
   EXPECT_EQ("Basic realm=Realm2", entry->auth_challenge());
@@ -199,8 +140,8 @@
       cache.Lookup(origin, kRealm2, HttpAuth::AUTH_SCHEME_BASIC);
   HttpAuthCache::Entry* p_realm4_entry =
       cache.Lookup(origin, kRealm4, HttpAuth::AUTH_SCHEME_BASIC);
-  EXPECT_FALSE(NULL == p_realm2_entry);
-  EXPECT_FALSE(NULL == p_realm4_entry);
+  EXPECT_TRUE(p_realm2_entry);
+  EXPECT_TRUE(p_realm4_entry);
   HttpAuthCache::Entry realm2_entry = *p_realm2_entry;
   HttpAuthCache::Entry realm4_entry = *p_realm4_entry;
   // Realm4 applies to '/' and Realm2 applies to '/foo2/'.
@@ -228,7 +169,7 @@
   // Confirm we find the same realm, different auth scheme by path lookup
   HttpAuthCache::Entry* p_realm3_digest_entry =
       cache.Lookup(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
-  EXPECT_FALSE(NULL == p_realm3_digest_entry);
+  EXPECT_TRUE(p_realm3_digest_entry);
   HttpAuthCache::Entry realm3_digest_entry = *p_realm3_digest_entry;
   entry = cache.LookupByPath(origin, "/baz/index.html");
   EXPECT_TRUE(realm3_digest_entry.IsEqualForTesting(*entry));
@@ -240,7 +181,7 @@
   // Confirm we find the same realm, different auth scheme by path lookup
   HttpAuthCache::Entry* p_realm3DigestEntry =
       cache.Lookup(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
-  EXPECT_FALSE(NULL == p_realm3DigestEntry);
+  EXPECT_TRUE(p_realm3DigestEntry);
   HttpAuthCache::Entry realm3DigestEntry = *p_realm3DigestEntry;
   entry = cache.LookupByPath(origin, "/baz/index.html");
   EXPECT_TRUE(realm3DigestEntry.IsEqualForTesting(*entry));
@@ -251,7 +192,7 @@
 
   // Lookup using empty path (may be used for proxy).
   entry = cache.LookupByPath(origin, std::string());
-  EXPECT_FALSE(NULL == entry);
+  EXPECT_TRUE(entry);
   EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme());
   EXPECT_EQ(kRealm3, entry->realm());
 }
@@ -293,20 +234,19 @@
 TEST(HttpAuthCacheTest, AddToExistingEntry) {
   HttpAuthCache cache;
   GURL origin("http://www.foobar.com:70");
-  const std::string auth_challenge = "Basic realm=MyRealm";
+  const std::string kAuthChallenge = "Basic realm=MyRealm";
+  const std::string kRealm = "MyRealm";
 
-  std::unique_ptr<HttpAuthHandler> handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, "MyRealm", HttpAuth::AUTH_SERVER));
-  HttpAuthCache::Entry* orig_entry = cache.Add(
-      origin, handler->realm(), handler->auth_scheme(), auth_challenge,
-      CreateASCIICredentials("user1", "password1"), "/x/y/z/");
-  cache.Add(origin, handler->realm(), handler->auth_scheme(), auth_challenge,
+  HttpAuthCache::Entry* orig_entry =
+      cache.Add(origin, kRealm, HttpAuth::AUTH_SCHEME_BASIC, kAuthChallenge,
+                CreateASCIICredentials("user1", "password1"), "/x/y/z/");
+  cache.Add(origin, kRealm, HttpAuth::AUTH_SCHEME_BASIC, kAuthChallenge,
             CreateASCIICredentials("user2", "password2"), "/z/y/x/");
-  cache.Add(origin, handler->realm(), handler->auth_scheme(), auth_challenge,
+  cache.Add(origin, kRealm, HttpAuth::AUTH_SCHEME_BASIC, kAuthChallenge,
             CreateASCIICredentials("user3", "password3"), "/z/y");
 
-  HttpAuthCache::Entry* entry = cache.Lookup(
-      origin, "MyRealm", HttpAuth::AUTH_SCHEME_BASIC);
+  HttpAuthCache::Entry* entry =
+      cache.Lookup(origin, kRealm, HttpAuth::AUTH_SCHEME_BASIC);
 
   EXPECT_TRUE(entry == orig_entry);
   EXPECT_EQ(ASCIIToUTF16("user3"), entry->credentials().username());
@@ -320,30 +260,15 @@
 TEST(HttpAuthCacheTest, Remove) {
   GURL origin("http://foobar2.com");
 
-  std::unique_ptr<HttpAuthHandler> realm1_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm1, HttpAuth::AUTH_SERVER));
-
-  std::unique_ptr<HttpAuthHandler> realm2_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm2, HttpAuth::AUTH_SERVER));
-
-  std::unique_ptr<HttpAuthHandler> realm3_basic_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm3, HttpAuth::AUTH_SERVER));
-
-  std::unique_ptr<HttpAuthHandler> realm3_digest_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_DIGEST, kRealm3, HttpAuth::AUTH_SERVER));
-
   HttpAuthCache cache;
-  cache.Add(origin, realm1_handler->realm(), realm1_handler->auth_scheme(),
-            "basic realm=Realm1", AuthCredentials(kAlice, k123), "/");
-  cache.Add(origin, realm2_handler->realm(), realm2_handler->auth_scheme(),
-            "basic realm=Realm2", CreateASCIICredentials("bob", "princess"),
-            "/");
-  cache.Add(origin, realm3_basic_handler->realm(),
-            realm3_basic_handler->auth_scheme(), "basic realm=Realm3",
+  cache.Add(origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm1",
+            AuthCredentials(kAlice, k123), "/");
+  cache.Add(origin, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm2",
+            CreateASCIICredentials("bob", "princess"), "/");
+  cache.Add(origin, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, "basic realm=Realm3",
             AuthCredentials(kAdmin, kPassword), "/");
-  cache.Add(origin, realm3_digest_handler->realm(),
-            realm3_digest_handler->auth_scheme(), "digest realm=Realm3",
-            AuthCredentials(kRoot, kWileCoyote), "/");
+  cache.Add(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST,
+            "digest realm=Realm3", AuthCredentials(kRoot, kWileCoyote), "/");
 
   // Fails, because there is no realm "Realm5".
   EXPECT_FALSE(cache.Remove(
@@ -387,9 +312,8 @@
       AuthCredentials(kRoot, kWileCoyote)));
 
   // Succeed as above, but when entries were added in opposite order
-  cache.Add(origin, realm3_digest_handler->realm(),
-            realm3_digest_handler->auth_scheme(), "digest realm=Realm3",
-            AuthCredentials(kRoot, kWileCoyote), "/");
+  cache.Add(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST,
+            "digest realm=Realm3", AuthCredentials(kRoot, kWileCoyote), "/");
   EXPECT_TRUE(cache.Remove(
       origin, kRealm3, HttpAuth::AUTH_SCHEME_BASIC,
       AuthCredentials(kAdmin, kPassword)));
@@ -531,12 +455,8 @@
 TEST(HttpAuthCacheTest, UpdateStaleChallenge) {
   HttpAuthCache cache;
   GURL origin("http://foobar2.com");
-  std::unique_ptr<HttpAuthHandler> digest_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_DIGEST, kRealm1, HttpAuth::AUTH_PROXY));
   HttpAuthCache::Entry* entry_pre = cache.Add(
-      origin,
-      digest_handler->realm(),
-      digest_handler->auth_scheme(),
+      origin, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST,
       "Digest realm=Realm1,"
       "nonce=\"s3MzvFhaBAA=4c520af5acd9d8d7ae26947529d18c8eae1e98f4\"",
       CreateASCIICredentials("realm-digest-user", "realm-digest-password"),
@@ -548,9 +468,7 @@
   EXPECT_EQ(4, entry_pre->IncrementNonceCount());
 
   bool update_success = cache.UpdateStaleChallenge(
-      origin,
-      digest_handler->realm(),
-      digest_handler->auth_scheme(),
+      origin, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST,
       "Digest realm=Realm1,"
       "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\","
       "stale=\"true\"");
@@ -558,18 +476,14 @@
 
   // After the stale update, the entry should still exist in the cache and
   // the nonce count should be reset to 0.
-  HttpAuthCache::Entry* entry_post = cache.Lookup(
-      origin,
-      digest_handler->realm(),
-      digest_handler->auth_scheme());
+  HttpAuthCache::Entry* entry_post =
+      cache.Lookup(origin, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST);
   ASSERT_TRUE(entry_post != NULL);
   EXPECT_EQ(2, entry_post->IncrementNonceCount());
 
   // UpdateStaleChallenge will fail if an entry doesn't exist in the cache.
   bool update_failure = cache.UpdateStaleChallenge(
-      origin,
-      kRealm2,
-      digest_handler->auth_scheme(),
+      origin, kRealm2, HttpAuth::AUTH_SCHEME_DIGEST,
       "Digest realm=Realm2,"
       "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\","
       "stale=\"true\"");
@@ -581,46 +495,30 @@
   std::string path("/some/path");
   std::string another_path("/another/path");
 
-  std::unique_ptr<HttpAuthHandler> realm1_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm1, HttpAuth::AUTH_SERVER));
-
-  std::unique_ptr<HttpAuthHandler> realm2_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm2, HttpAuth::AUTH_PROXY));
-
-  std::unique_ptr<HttpAuthHandler> realm3_digest_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_DIGEST, kRealm3, HttpAuth::AUTH_SERVER));
-
-  std::unique_ptr<HttpAuthHandler> realm4_handler(new MockAuthHandler(
-      HttpAuth::AUTH_SCHEME_BASIC, kRealm4, HttpAuth::AUTH_SERVER));
-
   HttpAuthCache first_cache;
   HttpAuthCache::Entry* entry;
 
-  first_cache.Add(origin, realm1_handler->realm(),
-                  realm1_handler->auth_scheme(), "basic realm=Realm1",
-                  AuthCredentials(kAlice, k123), path);
-  first_cache.Add(origin, realm2_handler->realm(),
-                  realm2_handler->auth_scheme(), "basic realm=Realm2",
-                  AuthCredentials(kAlice2, k1234), path);
-  first_cache.Add(origin, realm3_digest_handler->realm(),
-                  realm3_digest_handler->auth_scheme(), "digest realm=Realm3",
-                  AuthCredentials(kRoot, kWileCoyote), path);
-  entry = first_cache.Add(
-      origin, realm3_digest_handler->realm(),
-      realm3_digest_handler->auth_scheme(), "digest realm=Realm3",
-      AuthCredentials(kRoot, kWileCoyote), another_path);
+  first_cache.Add(origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
+                  "basic realm=Realm1", AuthCredentials(kAlice, k123), path);
+  first_cache.Add(origin, kRealm2, HttpAuth::AUTH_SCHEME_BASIC,
+                  "basic realm=Realm2", AuthCredentials(kAlice2, k1234), path);
+  first_cache.Add(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST,
+                  "digest realm=Realm3", AuthCredentials(kRoot, kWileCoyote),
+                  path);
+  entry = first_cache.Add(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST,
+                          "digest realm=Realm3",
+                          AuthCredentials(kRoot, kWileCoyote), another_path);
 
   EXPECT_EQ(2, entry->IncrementNonceCount());
 
   HttpAuthCache second_cache;
   // Will be overwritten by kRoot:kWileCoyote.
-  second_cache.Add(origin, realm3_digest_handler->realm(),
-                   realm3_digest_handler->auth_scheme(), "digest realm=Realm3",
-                   AuthCredentials(kAlice2, k1234), path);
+  second_cache.Add(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST,
+                   "digest realm=Realm3", AuthCredentials(kAlice2, k1234),
+                   path);
   // Should be left intact.
-  second_cache.Add(origin, realm4_handler->realm(),
-                   realm4_handler->auth_scheme(), "basic realm=Realm4",
-                   AuthCredentials(kAdmin, kRoot), path);
+  second_cache.Add(origin, kRealm4, HttpAuth::AUTH_SCHEME_BASIC,
+                   "basic realm=Realm4", AuthCredentials(kAdmin, kRoot), path);
 
   second_cache.UpdateAllFrom(first_cache);
 
diff --git a/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc b/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc
index d88170d..2f2af2f 100644
--- a/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc
+++ b/net/third_party/quic/core/qpack/fuzzer/qpack_encoder_stream_sender_fuzzer.cc
@@ -8,6 +8,7 @@
 #include <cstdint>
 #include <limits>
 
+#include "net/third_party/quic/core/qpack/qpack_encoder_test_utils.h"
 #include "net/third_party/quic/platform/api/quic_fuzzed_data_provider.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
@@ -15,22 +16,12 @@
 namespace quic {
 namespace test {
 
-// A QpackEncoderStreamSender::Delegate implementation that ignores encoded
-// data.
-class NoOpDelegate : public QpackEncoderStreamSender::Delegate {
- public:
-  NoOpDelegate() = default;
-  ~NoOpDelegate() override = default;
-
-  void WriteEncoderStreamData(QuicStringPiece data) override {}
-};
-
 // This fuzzer exercises QpackEncoderStreamSender.
 // TODO(bnc): Encoded data could be fed into QpackEncoderStreamReceiver and
 // decoded instructions directly compared to input.  Figure out how to get gMock
 // enabled for cc_fuzz_target target types.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  NoOpDelegate delegate;
+  NoopEncoderStreamSenderDelegate delegate;
   QpackEncoderStreamSender sender(&delegate);
 
   QuicFuzzedDataProvider provider(data, size);
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc
index 76215eea..4e4255e 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc
@@ -4,6 +4,7 @@
 
 #include "net/third_party/quic/core/qpack/qpack_encoder_stream_sender.h"
 
+#include "net/third_party/quic/core/qpack/qpack_encoder_test_utils.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
 #include "net/third_party/quic/platform/api/quic_text_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -16,19 +17,12 @@
 namespace test {
 namespace {
 
-class MockSenderDelegate : public QpackEncoderStreamSender::Delegate {
- public:
-  ~MockSenderDelegate() override = default;
-
-  MOCK_METHOD1(WriteEncoderStreamData, void(QuicStringPiece data));
-};
-
 class QpackEncoderStreamSenderTest : public QuicTest {
  protected:
   QpackEncoderStreamSenderTest() : stream_(&delegate_) {}
   ~QpackEncoderStreamSenderTest() override = default;
 
-  StrictMock<MockSenderDelegate> delegate_;
+  StrictMock<MockEncoderStreamSenderDelegate> delegate_;
   QpackEncoderStreamSender stream_;
 };
 
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_test_utils.h b/net/third_party/quic/core/qpack/qpack_encoder_test_utils.h
index 8c8d8ca6..cafd6fee 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_test_utils.h
+++ b/net/third_party/quic/core/qpack/qpack_encoder_test_utils.h
@@ -42,6 +42,15 @@
   void WriteEncoderStreamData(QuicStringPiece data) override;
 };
 
+// Mock QpackEncoderStreamSender::Delegate implementation.
+class MockEncoderStreamSenderDelegate
+    : public QpackEncoderStreamSender::Delegate {
+ public:
+  ~MockEncoderStreamSenderDelegate() override = default;
+
+  MOCK_METHOD1(WriteEncoderStreamData, void(QuicStringPiece data));
+};
+
 QuicString QpackEncode(
     QpackEncoder::DecoderStreamErrorDelegate* decoder_stream_error_delegate,
     QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate,
diff --git a/net/third_party/quic/core/qpack/qpack_header_table_test.cc b/net/third_party/quic/core/qpack/qpack_header_table_test.cc
index b058844..fa9096ff 100644
--- a/net/third_party/quic/core/qpack/qpack_header_table_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_header_table_test.cc
@@ -5,7 +5,6 @@
 #include "net/third_party/quic/core/qpack/qpack_header_table.h"
 
 #include "net/third_party/quic/core/qpack/qpack_static_table.h"
-#include "net/third_party/quic/core/qpack/qpack_test_utils.h"
 #include "net/third_party/quic/platform/api/quic_arraysize.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h"
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
index 7e26e4cd..35a5b1e 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -8,12 +8,17 @@
 #include <map>
 #include <utility>
 
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/json/json_writer.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
 #include "base/no_destructor.h"
+#include "base/pickle.h"
 #include "base/process/process_handle.h"
+#include "base/trace_event/common/trace_event_common.h"
 #include "base/trace_event/trace_buffer.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -515,12 +520,14 @@
     RegisterWithTraceLog();
   }
 
-  TraceLog::GetInstance()->SetEnabled(
-      TraceConfig(data_source_config.trace_config), TraceLog::RECORDING_MODE);
+  auto trace_config = TraceConfig(data_source_config.trace_config);
+  TraceLog::GetInstance()->SetEnabled(trace_config, TraceLog::RECORDING_MODE);
+  ResetHistograms(trace_config);
 }
 
 void TraceEventDataSource::StopTracing(
     base::OnceClosure stop_complete_callback) {
+  LogHistograms();
   stop_complete_callback_ = std::move(stop_complete_callback);
 
   auto on_tracing_stopped_callback =
@@ -571,6 +578,36 @@
   }
 }
 
+void TraceEventDataSource::LogHistogram(base::HistogramBase* histogram) {
+  if (!histogram) {
+    return;
+  }
+  auto samples = histogram->SnapshotSamples();
+  base::Pickle pickle;
+  samples->Serialize(&pickle);
+  std::string buckets;
+  base::Base64Encode(
+      std::string(static_cast<const char*>(pickle.data()), pickle.size()),
+      &buckets);
+  TRACE_EVENT_INSTANT2("benchmark", "UMAHistogramDelta",
+                       TRACE_EVENT_SCOPE_PROCESS, "name",
+                       histogram->histogram_name(), "buckets", buckets);
+}
+
+void TraceEventDataSource::ResetHistograms(const TraceConfig& trace_config) {
+  histograms_.clear();
+  for (const std::string& histogram_name : trace_config.histogram_names()) {
+    histograms_.push_back(histogram_name);
+    LogHistogram(base::StatisticsRecorder::FindHistogram(histogram_name));
+  }
+}
+
+void TraceEventDataSource::LogHistograms() {
+  for (const std::string& histogram_name : histograms_) {
+    LogHistogram(base::StatisticsRecorder::FindHistogram(histogram_name));
+  }
+}
+
 void TraceEventDataSource::Flush(
     base::RepeatingClosure flush_complete_callback) {
   DCHECK(TraceLog::GetInstance()->IsEnabled());
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.h b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
index b803d12..2ff06aae 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.h
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
@@ -11,7 +11,9 @@
 
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "base/metrics/histogram_base.h"
 #include "base/threading/thread_local.h"
+#include "base/trace_event/trace_config.h"
 #include "services/tracing/public/cpp/perfetto/producer_client.h"
 
 namespace perfetto {
@@ -108,6 +110,14 @@
   void ReturnTraceWriter(
       std::unique_ptr<perfetto::StartupTraceWriter> trace_writer);
 
+  // Extracts UMA histogram names that should be logged in traces and logs their
+  // starting values.
+  void ResetHistograms(const base::trace_event::TraceConfig& trace_config);
+  // Logs selected UMA histogram.
+  void LogHistograms();
+  // Logs a given histogram in traces.
+  void LogHistogram(base::HistogramBase* histogram);
+
   base::OnceClosure stop_complete_callback_;
 
   base::Lock lock_;  // Protects subsequent members.
@@ -118,6 +128,7 @@
   // SetupStartupTracing() is called.
   std::unique_ptr<perfetto::StartupTraceWriterRegistry>
       startup_writer_registry_;
+  std::vector<std::string> histograms_;
 
   DISALLOW_COPY_AND_ASSIGN(TraceEventDataSource);
 };
diff --git a/styleguide/c++/c++11.html b/styleguide/c++/c++11.html
index 82d92ee..3227676 100644
--- a/styleguide/c++/c++11.html
+++ b/styleguide/c++/c++11.html
@@ -319,14 +319,6 @@
 </tr>
 
 <tr>
-<td>thread_local storage class</td>
-<td><code>thread_local int foo = 1;</code></td>
-<td>Puts variables into thread local storage.</td>
-<td><a href="http://en.cppreference.com/w/cpp/language/storage_duration">Storage duration</a></td>
-<td>Allowed only for POD data. (<a href="https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/2msN8k3Xzgs">discussion</a>, <a href="https://groups.google.com/a/chromium.org/forum/#!topic/cxx/h7O5BdtWCZw">fork</a>). Use <a href="https://cs.chromium.org/chromium/src/base/threading/sequence_local_storage_slot.h">SequenceLocalStorageSlot</a> for sequence support, and <a href="https://cs.chromium.org/chromium/src/base/threading/thread_local.h">ThreadLocal</a>/<a href="https://cs.chromium.org/chromium/src/base/threading/thread_local_storage.h">ThreadLocalStorage</a> for other cases.</td>
-</tr>
-
-<tr>
 <td>Type Aliases ("using" instead of "typedef")</td>
 <td><code>using <i>new_alias</i> = <i>typename</i></code></td>
 <td>Allows parameterized typedefs</td>
@@ -784,6 +776,14 @@
 <td>Banned in the <a href="https://google.github.io/styleguide/cppguide.html#Operator_Overloading">Google Style Guide</a>.</td>
 </tr>
 
+<tr>
+<td>thread_local storage class</td>
+<td><code>thread_local int foo = 1;</code></td>
+<td>Puts variables into thread local storage.</td>
+<td><a href="http://en.cppreference.com/w/cpp/language/storage_duration">Storage duration</a></td>
+<td>Some surprising effects on Mac (<a href="https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/2msN8k3Xzgs">discussion</a>, <a href="https://groups.google.com/a/chromium.org/forum/#!topic/cxx/h7O5BdtWCZw">fork</a>). Use <a href="https://cs.chromium.org/chromium/src/base/threading/sequence_local_storage_slot.h">SequenceLocalStorageSlot</a> for sequence support, and <a href="https://cs.chromium.org/chromium/src/base/threading/thread_local.h">ThreadLocal</a>/<a href="https://cs.chromium.org/chromium/src/base/threading/thread_local_storage.h">ThreadLocalStorage</a> otherwise.</td>
+</tr>
+
 </tbody>
 </table>
 
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index fe4da53..20d8fdc 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -5627,24 +5627,6 @@
       {
         "args": [
           "--use-gpu-in-tests",
-          "--test-launcher-retry-limit=0"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-18.0.5",
-              "os": "Ubuntu",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "shards": 4
-        },
-        "test": "dawn_end2end_tests"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests",
           "--use-cmd-decoder=validating"
         ],
         "swarming": {
diff --git a/testing/buildbot/filters/webui_polymer2_browser_tests.filter b/testing/buildbot/filters/webui_polymer2_browser_tests.filter
index bead418..6cf3c55 100644
--- a/testing/buildbot/filters/webui_polymer2_browser_tests.filter
+++ b/testing/buildbot/filters/webui_polymer2_browser_tests.filter
@@ -254,6 +254,15 @@
 OnboardingWelcomeEmailChooserTest.*
 OnboardingWelcomeWelcomeAppTest.*
 OpenAudioFiles/*
+PDFAnnotationsTest.*
+PDFExtensionClipboardTest.*
+PDFExtensionHitTestTest.*
+PDFExtensionInternalLinkClickTest.*
+PDFExtensionLinkClickTest.*
+PDFExtensionLoadTest.*
+PDFExtensionTest.*
+PDFIsolatedExtensionTest.*
+PDFPluginDisabledTest.*
 PrintPreviewAdvancedDialogTest.*
 PrintPreviewAdvancedItemTest.*
 PrintPreviewBaseSettingsSectionTest.*
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 764dccf..3ef5a8b 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -435,6 +435,7 @@
   'dawn_end2end_tests': {
     'remove_from': [
       # chromium.gpu.fyi
+      'Linux FYI Experimental Release (Intel HD 630)', # https://crbug.com/927459
       'Linux FYI Release (AMD R7 240)', # https://crbug.com/915430
     ],
   },
diff --git a/testing/scripts/common.py b/testing/scripts/common.py
index 566d2e3b..1e60d71 100644
--- a/testing/scripts/common.py
+++ b/testing/scripts/common.py
@@ -300,12 +300,12 @@
           self.options.isolated_script_test_filter)
 
     # Augment test repeat if needed
-    if self.options.isolated_script_test_repeat:
+    if self.options.isolated_script_test_repeat is not None:
       isolated_script_cmd += self.generate_test_repeat_args(
           self.options.isolated_script_test_repeat)
 
     # Augment test launcher retry limit args if needed
-    if self.options.isolated_script_test_launcher_retry_limit:
+    if self.options.isolated_script_test_launcher_retry_limit is not None:
       isolated_script_cmd += self.generate_test_launcher_retry_limit_args(
           self.options.isolated_script_test_launcher_retry_limit)
 
diff --git a/testing/scripts/run_isolated_script_test.py b/testing/scripts/run_isolated_script_test.py
index ea5e54f..733de14b 100755
--- a/testing/scripts/run_isolated_script_test.py
+++ b/testing/scripts/run_isolated_script_test.py
@@ -76,7 +76,7 @@
     return ['--isolated-script-test-output=%s' % output]
 
   def generate_test_launcher_retry_limit_args(self, retry_limit):
-    return ['--isolated-script-test-retry-limit=%d' % retry_limit]
+    return ['--isolated-script-test-launcher-retry-limit=%d' % retry_limit]
 
   def generate_test_repeat_args(self, repeat_count):
     return ['--isolated-script-test-repeat=%d' % repeat_count]
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 516ebbdc..d0d93fd 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -42,6 +42,7 @@
     "frame/document_interface_broker.mojom",
     "frame/find_in_page.mojom",
     "frame/frame_host_test_interface.mojom",
+    "frame/lifecycle.mojom",
     "frame/navigation_initiator.mojom",
     "leak_detector/leak_detector.mojom",
     "loader/code_cache.mojom",
diff --git a/third_party/blink/public/mojom/frame/lifecycle.mojom b/third_party/blink/public/mojom/frame/lifecycle.mojom
new file mode 100644
index 0000000..4a97cc5
--- /dev/null
+++ b/third_party/blink/public/mojom/frame/lifecycle.mojom
@@ -0,0 +1,15 @@
+// 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.
+
+module blink.mojom;
+
+// The frame visibility status.
+enum FrameVisibility {
+  // Rendered but not in the current viewport.
+  kRenderedOutOfViewport,
+  // Rendered in current viewport.
+  kRenderedInViewport,
+  // Not visible, no layout object created. ie. display: none
+  kNotRendered,
+};
diff --git a/third_party/blink/public/platform/modules/mediasession/media_session.mojom b/third_party/blink/public/platform/modules/mediasession/media_session.mojom
index a1c7453..cf4324444 100644
--- a/third_party/blink/public/platform/modules/mediasession/media_session.mojom
+++ b/third_party/blink/public/platform/modules/mediasession/media_session.mojom
@@ -21,6 +21,15 @@
   DidReceiveAction(media_session.mojom.MediaSessionAction action);
 };
 
+// MediaMetadata
+// Spec: https://wicg.github.io/mediasession/
+struct SpecMediaMetadata {
+  mojo_base.mojom.String16 title;
+  mojo_base.mojom.String16 artist;
+  mojo_base.mojom.String16 album;
+  array<media_session.mojom.MediaImage> artwork;
+};
+
 interface MediaSessionService {
   // MediaSessionClient interface is used to notify Blink MediaSession of
   // media control actions.
@@ -31,7 +40,7 @@
 
   // Notifies the browser that the metadata is set, |metadata| will be displayed
   // on the UI.
-  SetMetadata(media_session.mojom.MediaMetadata? metadata);
+  SetMetadata(SpecMediaMetadata? metadata);
 
   // Notifies the browser that the event handler for |action| has been set,
   // browser needs to show a media button in the UI or register listeners to the
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 177112d..3e5e1d7 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -39,6 +39,7 @@
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
 #include "third_party/blink/public/common/frame/sandbox_flags.h"
 #include "third_party/blink/public/common/frame/user_activation_update_type.h"
+#include "third_party/blink/public/mojom/frame/lifecycle.mojom-shared.h"
 #include "third_party/blink/public/platform/blame_context.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider.h"
 #include "third_party/blink/public/platform/web_application_cache_host.h"
@@ -629,6 +630,8 @@
   // A performance timing event (e.g. first paint) occurred
   virtual void DidChangePerformanceTiming() {}
 
+  virtual void VisibilityChanged(blink::mojom::FrameVisibility visibility) {}
+
   // UseCounter ----------------------------------------------------------
   // Blink exhibited a certain loading behavior that the browser process will
   // use for segregated histograms.
diff --git a/third_party/blink/public/web/web_remote_frame_client.h b/third_party/blink/public/web/web_remote_frame_client.h
index f9a76268..21558161 100644
--- a/third_party/blink/public/web/web_remote_frame_client.h
+++ b/third_party/blink/public/web/web_remote_frame_client.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_REMOTE_FRAME_CLIENT_H_
 
 #include "cc/paint/paint_canvas.h"
+#include "third_party/blink/public/mojom/frame/lifecycle.mojom-shared.h"
 #include "third_party/blink/public/platform/web_focus_type.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/public/platform/web_touch_action.h"
@@ -50,7 +51,7 @@
       const WebRect& viewport_intersection,
       bool occluded_or_obscured) {}
 
-  virtual void VisibilityChanged(bool visible) {}
+  virtual void VisibilityChanged(blink::mojom::FrameVisibility visibility) {}
 
   // Set or clear the inert property on the remote frame.
   virtual void SetIsInert(bool) {}
diff --git a/third_party/blink/renderer/core/dom/document.idl b/third_party/blink/renderer/core/dom/document.idl
index 67f99ba..df10119 100644
--- a/third_party/blink/renderer/core/dom/document.idl
+++ b/third_party/blink/renderer/core/dom/document.idl
@@ -188,8 +188,8 @@
     [MeasureAs=DocumentCaretRangeFromPoint] Range caretRangeFromPoint([DefaultValue=Undefined] optional long x, [DefaultValue=Undefined] optional long y);
 
 
-    // https://wicg.github.io/feature-policy/#the-policy-object
-    [OriginTrialEnabled=FeaturePolicyJavaScriptInterface] readonly attribute FeaturePolicy featurePolicy;
+    // https://w3c.github.io/webappsec-feature-policy/#the-policy-object
+    [RuntimeEnabled=FeaturePolicyJavaScriptInterface] readonly attribute FeaturePolicy featurePolicy;
 
     // Deprecated prefixed page visibility API.
     // TODO(davidben): This is a property so attaching a deprecation warning results in false positives when outputting
diff --git a/third_party/blink/renderer/core/editing/reveal_selection_scope.cc b/third_party/blink/renderer/core/editing/reveal_selection_scope.cc
index afe5f8b..ee210ff 100644
--- a/third_party/blink/renderer/core/editing/reveal_selection_scope.cc
+++ b/third_party/blink/renderer/core/editing/reveal_selection_scope.cc
@@ -43,10 +43,12 @@
 RevealSelectionScope::~RevealSelectionScope() {
   DCHECK(GetEditor().PreventRevealSelection());
   GetEditor().DecreasePreventRevealSelection();
-  if (!GetEditor().PreventRevealSelection()) {
-    frame_->Selection().RevealSelection(ScrollAlignment::kAlignToEdgeIfNeeded,
-                                        kRevealExtent);
-  }
+  if (GetEditor().PreventRevealSelection())
+    return;
+  if (!frame_->Selection().IsAvailable())
+    return;
+  frame_->Selection().RevealSelection(ScrollAlignment::kAlignToEdgeIfNeeded,
+                                      kRevealExtent);
 }
 
 Editor& RevealSelectionScope::GetEditor() {
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index 4db791f0..950c306 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -762,6 +762,12 @@
   }
 }
 
+void LocalFrameClientImpl::VisibilityChanged(
+    blink::mojom::FrameVisibility visibility) {
+  if (WebLocalFrameClient* client = web_frame_->Client())
+    client->VisibilityChanged(visibility);
+}
+
 DocumentLoader* LocalFrameClientImpl::CreateDocumentLoader(
     LocalFrame* frame,
     WebNavigationType navigation_type,
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.h b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
index e075470e..339bbe9 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
@@ -156,6 +156,7 @@
   bool ShouldTrackUseCounter(const KURL&) override;
   void SelectorMatchChanged(const Vector<String>& added_selectors,
                             const Vector<String>& removed_selectors) override;
+  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override;
 
   // Creates a WebDocumentLoaderImpl that is a DocumentLoader but also has:
   // - storage to store an extra data that can be used by the content layer
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index 3fbb691..abc88263 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -10654,30 +10654,34 @@
 class TestWebRemoteFrameClientForVisibility
     : public frame_test_helpers::TestWebRemoteFrameClient {
  public:
-  TestWebRemoteFrameClientForVisibility() : visible_(true) {}
+  TestWebRemoteFrameClientForVisibility() = default;
   ~TestWebRemoteFrameClientForVisibility() override = default;
 
   // frame_test_helpers::TestWebRemoteFrameClient:
-  void VisibilityChanged(bool visible) override { visible_ = visible; }
+  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override {
+    visibility_ = visibility;
+  }
 
-  bool IsVisible() const { return visible_; }
+  blink::mojom::FrameVisibility visibility() const { return visibility_; }
 
  private:
-  bool visible_;
+  blink::mojom::FrameVisibility visibility_ =
+      blink::mojom::FrameVisibility::kRenderedInViewport;
 };
 
-class WebFrameVisibilityChangeTest : public WebFrameTest {
+class WebRemoteFrameVisibilityChangeTest : public WebFrameTest {
  public:
-  WebFrameVisibilityChangeTest() {
+  WebRemoteFrameVisibilityChangeTest() {
     RegisterMockedHttpURLLoad("visible_iframe.html");
     RegisterMockedHttpURLLoad("single_iframe.html");
     frame_ =
         web_view_helper_.InitializeAndLoad(base_url_ + "single_iframe.html")
             ->MainFrameImpl();
+    web_view_helper_.Resize(WebSize(640, 480));
     web_remote_frame_ = frame_test_helpers::CreateRemote(&remote_frame_client_);
   }
 
-  ~WebFrameVisibilityChangeTest() override = default;
+  ~WebRemoteFrameVisibilityChangeTest() override = default;
 
   void ExecuteScriptOnMainFrame(const WebScriptSource& script) {
     MainFrame()->ExecuteScript(script);
@@ -10703,23 +10707,132 @@
   Persistent<WebRemoteFrameImpl> web_remote_frame_;
 };
 
-TEST_F(WebFrameVisibilityChangeTest, RemoteFrameVisibilityChange) {
+TEST_F(WebRemoteFrameVisibilityChangeTest, FrameVisibilityChange) {
   SwapLocalFrameToRemoteFrame();
   ExecuteScriptOnMainFrame(WebScriptSource(
       "document.querySelector('iframe').style.display = 'none';"));
-  EXPECT_FALSE(RemoteFrameClient()->IsVisible());
+  EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
+            RemoteFrameClient()->visibility());
 
   ExecuteScriptOnMainFrame(WebScriptSource(
       "document.querySelector('iframe').style.display = 'block';"));
-  EXPECT_TRUE(RemoteFrameClient()->IsVisible());
+  EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
+            RemoteFrameClient()->visibility());
+
+  ExecuteScriptOnMainFrame(WebScriptSource(
+      "var padding = document.createElement('div');"
+      "padding.style = 'width: 400px; height: 800px;';"
+      "document.body.insertBefore(padding, document.body.firstChild);"));
+  EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport,
+            RemoteFrameClient()->visibility());
+
+  ExecuteScriptOnMainFrame(
+      WebScriptSource("document.scrollingElement.scrollTop = 800;"));
+  EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
+            RemoteFrameClient()->visibility());
 }
 
-TEST_F(WebFrameVisibilityChangeTest, RemoteFrameParentVisibilityChange) {
+TEST_F(WebRemoteFrameVisibilityChangeTest, ParentVisibilityChange) {
   SwapLocalFrameToRemoteFrame();
   ExecuteScriptOnMainFrame(
       WebScriptSource("document.querySelector('iframe').parentElement.style."
                       "display = 'none';"));
-  EXPECT_FALSE(RemoteFrameClient()->IsVisible());
+  EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
+            RemoteFrameClient()->visibility());
+}
+
+class TestWebLocalFrameClientForVisibility
+    : public frame_test_helpers::TestWebFrameClient {
+ public:
+  TestWebLocalFrameClientForVisibility() = default;
+  ~TestWebLocalFrameClientForVisibility() override = default;
+
+  // frame_test_helpers::TestWebFrameClient:
+  void VisibilityChanged(blink::mojom::FrameVisibility visibility) override {
+    visibility_ = visibility;
+  }
+
+  blink::mojom::FrameVisibility visibility() const { return visibility_; }
+
+ private:
+  blink::mojom::FrameVisibility visibility_ =
+      blink::mojom::FrameVisibility::kRenderedInViewport;
+};
+
+class WebLocalFrameVisibilityChangeTest
+    : public WebFrameTest,
+      public frame_test_helpers::TestWebFrameClient {
+ public:
+  WebLocalFrameVisibilityChangeTest() {
+    RegisterMockedHttpURLLoad("visible_iframe.html");
+    RegisterMockedHttpURLLoad("single_iframe.html");
+    frame_ = web_view_helper_
+                 .InitializeAndLoad(base_url_ + "single_iframe.html", this)
+                 ->MainFrameImpl();
+    web_view_helper_.Resize(WebSize(640, 480));
+  }
+
+  ~WebLocalFrameVisibilityChangeTest() override = default;
+
+  void ExecuteScriptOnMainFrame(const WebScriptSource& script) {
+    MainFrame()->ExecuteScript(script);
+    MainFrame()->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
+        WebWidget::LifecycleUpdateReason::kTest);
+    RunPendingTasks();
+  }
+
+  WebLocalFrame* MainFrame() { return frame_; }
+
+  // frame_test_helpers::TestWebFrameClient:
+  WebLocalFrame* CreateChildFrame(WebLocalFrame* parent,
+                                  WebTreeScopeType scope,
+                                  const WebString& name,
+                                  const WebString& fallback_name,
+                                  WebSandboxFlags,
+                                  const ParsedFeaturePolicy&,
+                                  const WebFrameOwnerProperties&,
+                                  FrameOwnerElementType) override {
+    return CreateLocalChild(*parent, scope, &child_client_);
+  }
+
+  TestWebLocalFrameClientForVisibility& ChildClient() { return child_client_; }
+
+ private:
+  TestWebLocalFrameClientForVisibility child_client_;
+  frame_test_helpers::WebViewHelper web_view_helper_;
+  WebLocalFrame* frame_;
+};
+
+TEST_F(WebLocalFrameVisibilityChangeTest, FrameVisibilityChange) {
+  ExecuteScriptOnMainFrame(WebScriptSource(
+      "document.querySelector('iframe').style.display = 'none';"));
+  EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
+            ChildClient().visibility());
+
+  ExecuteScriptOnMainFrame(WebScriptSource(
+      "document.querySelector('iframe').style.display = 'block';"));
+  EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
+            ChildClient().visibility());
+
+  ExecuteScriptOnMainFrame(WebScriptSource(
+      "var padding = document.createElement('div');"
+      "padding.style = 'width: 400px; height: 800px;';"
+      "document.body.insertBefore(padding, document.body.firstChild);"));
+  EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport,
+            ChildClient().visibility());
+
+  ExecuteScriptOnMainFrame(
+      WebScriptSource("document.scrollingElement.scrollTop = 800;"));
+  EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
+            ChildClient().visibility());
+}
+
+TEST_F(WebLocalFrameVisibilityChangeTest, ParentVisibilityChange) {
+  ExecuteScriptOnMainFrame(
+      WebScriptSource("document.querySelector('iframe').parentElement.style."
+                      "display = 'none';"));
+  EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
+            ChildClient().visibility());
 }
 
 static void EnableGlobalReuseForUnownedMainFrames(WebSettings* settings) {
diff --git a/third_party/blink/renderer/core/exported/web_input_method_controller_impl.cc b/third_party/blink/renderer/core/exported/web_input_method_controller_impl.cc
index 5cc3f254..5402a61 100644
--- a/third_party/blink/renderer/core/exported/web_input_method_controller_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_input_method_controller_impl.cc
@@ -82,7 +82,8 @@
       String(text), ImeTextSpanVectorBuilder::Build(ime_text_spans),
       selection_start, selection_end);
 
-  return text.IsEmpty() || GetInputMethodController().HasComposition();
+  return text.IsEmpty() ||
+         (GetFrame() && GetInputMethodController().HasComposition());
 }
 
 bool WebInputMethodControllerImpl::FinishComposingText(
@@ -202,6 +203,7 @@
 
 InputMethodController& WebInputMethodControllerImpl::GetInputMethodController()
     const {
+  DCHECK(GetFrame());
   return GetFrame()->GetInputMethodController();
 }
 
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy.idl b/third_party/blink/renderer/core/feature_policy/feature_policy.idl
index beadb01..e2f45f8 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy.idl
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy.idl
@@ -5,7 +5,7 @@
 // https://wicg.github.io/feature-policy/#the-policy-object
 [
   NoInterfaceObject,
-  OriginTrialEnabled=FeaturePolicyJavaScriptInterface,
+  RuntimeEnabled=FeaturePolicyJavaScriptInterface,
   ImplementedAs=DOMFeaturePolicy
 ] interface FeaturePolicy {
   [MeasureAs=FeaturePolicyJSAPI] boolean allowsFeature(DOMString feature, optional DOMString url);
diff --git a/third_party/blink/renderer/core/frame/frame_client.h b/third_party/blink/renderer/core/frame/frame_client.h
index 0bd60f8..5fb93df 100644
--- a/third_party/blink/renderer/core/frame/frame_client.h
+++ b/third_party/blink/renderer/core/frame/frame_client.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_CLIENT_H_
 
 #include "base/unguessable_token.h"
+#include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink.h"
 #include "third_party/blink/public/platform/blame_context.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -33,6 +34,8 @@
 
   virtual void FrameFocused() const = 0;
 
+  virtual void VisibilityChanged(blink::mojom::FrameVisibility visibility) = 0;
+
   virtual base::UnguessableToken GetDevToolsFrameToken() const = 0;
 
   virtual ~FrameClient() = default;
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index d1b9cc5..fecf84b9 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -367,6 +367,7 @@
                           [](LocalFrameView* frame_view, bool is_visible) {
                             if (!frame_view)
                               return;
+                            frame_view->UpdateVisibility(is_visible);
                             frame_view->UpdateRenderThrottlingStatus(
                                 !is_visible, frame_view->subtree_throttled_);
                           },
@@ -4263,4 +4264,21 @@
   lifecycle_observers_.erase(observer);
 }
 
+void LocalFrameView::UpdateVisibility(bool is_visible) {
+  blink::mojom::FrameVisibility visibility;
+  if (!is_attached_) {
+    visibility = blink::mojom::FrameVisibility::kNotRendered;
+  } else if (!is_visible) {
+    visibility = blink::mojom::FrameVisibility::kRenderedOutOfViewport;
+  } else {
+    visibility = blink::mojom::FrameVisibility::kRenderedInViewport;
+  }
+  if (visibility_ == visibility)
+    return;
+  visibility_ = visibility;
+  if (auto* client = GetFrame().Client()) {
+    client->VisibilityChanged(visibility);
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 9fd61ff7..eb3a569a 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -30,6 +30,7 @@
 #include <utility>
 
 #include "third_party/blink/public/common/manifest/web_display_mode.h"
+#include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink.h"
 #include "third_party/blink/public/platform/shape_properties.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/frame/frame_view.h"
@@ -862,6 +863,8 @@
 
   void LayoutFromRootObject(LayoutObject& root);
 
+  void UpdateVisibility(bool is_visible);
+
   LayoutSize size_;
 
   typedef HashSet<scoped_refptr<LayoutEmbeddedObject>> EmbeddedObjectSet;
@@ -874,6 +877,8 @@
   bool is_attached_;
   bool self_visible_;
   bool parent_visible_;
+  blink::mojom::FrameVisibility visibility_ =
+      blink::mojom::FrameVisibility::kRenderedInViewport;
 
   WebDisplayMode display_mode_;
 
diff --git a/third_party/blink/renderer/core/frame/remote_frame_client.h b/third_party/blink/renderer/core/frame/remote_frame_client.h
index b9c8598..dd07bbd 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_client.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_client.h
@@ -58,8 +58,6 @@
 
   virtual void AdvanceFocus(WebFocusType, LocalFrame* source) = 0;
 
-  virtual void VisibilityChanged(bool visible) = 0;
-
   virtual void SetIsInert(bool) = 0;
 
   virtual void SetInheritedEffectiveTouchAction(TouchAction) = 0;
diff --git a/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc b/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
index ffb4e27..156dc6b 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
@@ -166,8 +166,9 @@
                                      WebLocalFrameImpl::FromFrame(source));
 }
 
-void RemoteFrameClientImpl::VisibilityChanged(bool visible) {
-  web_frame_->Client()->VisibilityChanged(visible);
+void RemoteFrameClientImpl::VisibilityChanged(
+    blink::mojom::FrameVisibility visibility) {
+  web_frame_->Client()->VisibilityChanged(visibility);
 }
 
 void RemoteFrameClientImpl::SetIsInert(bool inert) {
diff --git a/third_party/blink/renderer/core/frame/remote_frame_client_impl.h b/third_party/blink/renderer/core/frame/remote_frame_client_impl.h
index 06878012..92d3aa9 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_client_impl.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_client_impl.h
@@ -50,7 +50,7 @@
                          const IntRect& screen_space_rect) override;
   void UpdateRemoteViewportIntersection(const IntRect&, bool) override;
   void AdvanceFocus(WebFocusType, LocalFrame*) override;
-  void VisibilityChanged(bool visible) override;
+  void VisibilityChanged(blink::mojom::FrameVisibility) override;
   void SetIsInert(bool) override;
   void SetInheritedEffectiveTouchAction(TouchAction) override;
   void UpdateRenderThrottlingStatus(bool is_throttled,
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index 5697f4b..a850a44 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -61,6 +61,7 @@
   is_attached_ = true;
   if (ParentFrameView()->IsVisible())
     SetParentVisible(true);
+  UpdateVisibility(true);
 
   SetupRenderThrottling();
   subtree_throttled_ = ParentFrameView()->CanThrottleRendering();
@@ -297,12 +298,12 @@
 
 void RemoteFrameView::Hide() {
   self_visible_ = false;
-  remote_frame_->Client()->VisibilityChanged(false);
+  UpdateVisibility(scroll_visible_);
 }
 
 void RemoteFrameView::Show() {
   self_visible_ = true;
-  remote_frame_->Client()->VisibilityChanged(true);
+  UpdateVisibility(scroll_visible_);
 }
 
 void RemoteFrameView::SetParentVisible(bool visible) {
@@ -312,8 +313,24 @@
   parent_visible_ = visible;
   if (!self_visible_)
     return;
+  UpdateVisibility(scroll_visible_);
+}
 
-  remote_frame_->Client()->VisibilityChanged(self_visible_ && parent_visible_);
+void RemoteFrameView::UpdateVisibility(bool scroll_visible) {
+  blink::mojom::FrameVisibility visibility;
+  scroll_visible_ = scroll_visible;
+  if (self_visible_ && parent_visible_) {
+    visibility = scroll_visible
+                     ? blink::mojom::FrameVisibility::kRenderedInViewport
+                     : blink::mojom::FrameVisibility::kRenderedOutOfViewport;
+  } else {
+    visibility = blink::mojom::FrameVisibility::kNotRendered;
+  }
+
+  if (visibility == visibility_)
+    return;
+  visibility_ = visibility;
+  remote_frame_->Client()->VisibilityChanged(visibility);
 }
 
 void RemoteFrameView::SetupRenderThrottling() {
@@ -327,6 +344,7 @@
   visibility_observer_ = MakeGarbageCollected<ElementVisibilityObserver>(
       target_element, WTF::BindRepeating(
                           [](RemoteFrameView* remote_view, bool is_visible) {
+                            remote_view->UpdateVisibility(is_visible);
                             remote_view->UpdateRenderThrottlingStatus(
                                 !is_visible, remote_view->subtree_throttled_);
                           },
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.h b/third_party/blink/renderer/core/frame/remote_frame_view.h
index 9389744..d80aecf 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_REMOTE_FRAME_VIEW_H_
 
 #include "cc/paint/paint_canvas.h"
+#include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/document_lifecycle.h"
 #include "third_party/blink/renderer/core/frame/frame_view.h"
 #include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h"
@@ -85,6 +86,7 @@
   void UpdateRenderThrottlingStatus(bool hidden, bool subtree_throttled);
   bool CanThrottleRendering() const;
   void SetupRenderThrottling();
+  void UpdateVisibility(bool scroll_visible);
 
   // The properties and handling of the cycle between RemoteFrame
   // and its RemoteFrameView corresponds to that between LocalFrame
@@ -97,6 +99,9 @@
   IntRect frame_rect_;
   bool self_visible_;
   bool parent_visible_;
+  bool scroll_visible_ = true;
+  blink::mojom::FrameVisibility visibility_ =
+      blink::mojom::FrameVisibility::kRenderedInViewport;
 
   Member<ElementVisibilityObserver> visibility_observer_;
   bool subtree_throttled_ = false;
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.idl b/third_party/blink/renderer/core/html/html_iframe_element.idl
index 7f5f9ea..9bea0c3 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.idl
+++ b/third_party/blink/renderer/core/html/html_iframe_element.idl
@@ -43,8 +43,8 @@
 
     // Feature Policy
     [CEReactions, Reflect] attribute DOMString allow;
-    // https://wicg.github.io/feature-policy/#the-policy-object
-    [OriginTrialEnabled=FeaturePolicyJavaScriptInterface] readonly attribute FeaturePolicy featurePolicy;
+    // https://w3c.github.io/webappsec-feature-policy/#the-policy-object
+    [RuntimeEnabled=FeaturePolicyJavaScriptInterface] readonly attribute FeaturePolicy featurePolicy;
 
     [RuntimeEnabled=LazyFrameLoading, CEReactions, Reflect, ReflectOnly=("on", "off", "auto"), ReflectMissing="auto", ReflectInvalid="auto"] attribute DOMString lazyLoad;
 
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index b45129b6..c6b5f3c6 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -3320,12 +3320,16 @@
 
 void HTMLMediaElement::RemoteRouteAvailabilityChanged(
     WebRemotePlaybackAvailability availability) {
-  if (RemotePlaybackClient() &&
-      !RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled()) {
-    // The new remote playback pipeline is using the Presentation API for
-    // remote playback device availability monitoring.
+  // The new remote playback pipeline is using the Presentation API for
+  // remote playback device availability monitoring.
+  // This code is left as is because many tests depend on this path for the
+  // moment, but it will be cleaned up.
+  // TODO(https://crbug.com/927099): Clean up old discovery paths.
+  // TODO(https://crubg.com/927451): Remove test depencencies on
+  // RemoteRouteAvailabilityChanged
+
+  if (RemotePlaybackClient())
     RemotePlaybackClient()->AvailabilityChanged(availability);
-  }
 }
 
 bool HTMLMediaElement::HasRemoteRoutes() const {
@@ -3358,10 +3362,8 @@
 
 void HTMLMediaElement::RemotePlaybackCompatibilityChanged(const WebURL& url,
                                                           bool is_compatible) {
-  if (RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled() &&
-      RemotePlaybackClient()) {
+  if (RemotePlaybackClient())
     RemotePlaybackClient()->SourceChanged(url, is_compatible);
-  }
 }
 
 bool HTMLMediaElement::HasSelectedVideoTrack() {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc
index 846f7b76..bf73a03c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc
@@ -5,10 +5,20 @@
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
 
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 
 namespace blink {
 
+std::ostream& operator<<(std::ostream& out,
+                         const NGCaretNavigator::Position& position) {
+  return out << position.index << "/"
+             << (position.IsBeforeCharacter() ? "BeforeCharacter"
+                                              : "AfterCharacter");
+}
+
 NGCaretNavigator::~NGCaretNavigator() = default;
 
 NGCaretNavigator::NGCaretNavigator(const LayoutBlockFlow& context)
@@ -89,6 +99,68 @@
   return move_direction == MoveDirection::kTowardsLeft;
 }
 
+NGCaretNavigator::Line NGCaretNavigator::ContainingLineOf(
+    unsigned index) const {
+  DCHECK_LT(index, GetText().length());
+  // TODO(xiaochengh): Make it work for multi-col
+  DCHECK(context_.CurrentFragment());
+  unsigned last_line_end = 0;
+  for (const auto child : context_.CurrentFragment()->Children()) {
+    if (!child->IsLineBox())
+      continue;
+    const NGPhysicalLineBoxFragment* line =
+        ToNGPhysicalLineBoxFragment(child.get());
+    DCHECK(line->BreakToken());
+    DCHECK(line->BreakToken()->IsInlineType());
+    const NGInlineBreakToken* token = ToNGInlineBreakToken(line->BreakToken());
+    const unsigned line_end =
+        token->IsFinished() ? GetText().length() : token->TextOffset();
+    if (line_end > index)
+      return {last_line_end, line_end, line->BaseDirection()};
+    last_line_end = line_end;
+  }
+  NOTREACHED();
+  return {};
+}
+
+bool NGCaretNavigator::IsValidCaretPosition(const Position& position) const {
+  unsigned index = position.index;
+  if (position.IsAfterCharacter() && IsLineBreak(index))
+    return false;
+  if (IsCollapsedSpaceByLineWrap(index))
+    return false;
+  if (IsIgnoredInCaretMovement(index))
+    return false;
+  // TODO(xiaochengh): Handle other cases
+  return true;
+}
+
+bool NGCaretNavigator::IsCollapsibleWhitespace(unsigned index) const {
+  DCHECK_LT(index, GetText().length());
+  if (GetText()[index] != kSpaceCharacter)
+    return false;
+  const NGInlineItem& item = GetData().FindItemForTextOffset(index);
+  return item.Style()->CollapseWhiteSpace();
+}
+
+bool NGCaretNavigator::IsLineBreak(unsigned index) const {
+  DCHECK_LT(index, GetText().length());
+  return GetText()[index] == kNewlineCharacter;
+}
+
+bool NGCaretNavigator::IsCollapsedSpaceByLineWrap(unsigned index) const {
+  DCHECK_LT(index, GetText().length());
+  if (!IsCollapsibleWhitespace(index))
+    return false;
+  return index + 1 == ContainingLineOf(index).end_offset;
+}
+
+bool NGCaretNavigator::IsIgnoredInCaretMovement(unsigned index) const {
+  // TODO(xiaochengh): Include floats, out-of-flows and CSS-generated contents.
+  return IsCollapsedSpaceByLineWrap(index) &&
+         index != VisualLastCharacterOf(ContainingLineOf(index));
+}
+
 NGCaretNavigator::Position NGCaretNavigator::LeftEdgeOf(unsigned index) const {
   return EdgeOfInternal(index, MoveDirection::kTowardsLeft);
 }
@@ -117,67 +189,90 @@
   return MoveCharacterInternal(index, MoveDirection::kTowardsRight);
 }
 
-// static
-base::Optional<unsigned> NGCaretNavigator::MoveVisualIndex(
-    unsigned visual_index,
-    unsigned length,
-    MoveDirection move_direction) {
-  if (move_direction == MoveDirection::kTowardsLeft) {
-    if (!visual_index)
-      return base::nullopt;
-    return visual_index - 1;
-  }
-
-  if (visual_index + 1 == length)
-    return base::nullopt;
-  return visual_index + 1;
-}
-
-Vector<int32_t, 32> NGCaretNavigator::IndicesInVisualOrder() const {
+Vector<int32_t, 32> NGCaretNavigator::CharacterIndicesInVisualOrder(
+    const Line& line) const {
   DCHECK(IsBidiEnabled());
 
   Vector<UBiDiLevel, 32> levels;
-  levels.resize(GetText().length());
-  for (unsigned i = 0; i < GetText().length(); ++i)
-    levels[i] = BidiLevelAt(i);
+  levels.ReserveCapacity(line.end_offset - line.start_offset);
+  for (unsigned i = line.start_offset; i < line.end_offset; ++i)
+    levels.push_back(BidiLevelAt(i));
 
-  Vector<int32_t, 32> result;
-  result.resize(levels.size());
-  NGBidiParagraph::IndicesInVisualOrder(levels, &result);
-  return result;
+  Vector<int32_t, 32> indices(levels.size());
+  NGBidiParagraph::IndicesInVisualOrder(levels, &indices);
+
+  for (auto& index : indices)
+    index += line.start_offset;
+  return indices;
 }
 
-unsigned NGCaretNavigator::VisualIndexOf(unsigned index) const {
-  if (!IsBidiEnabled())
-    return index;
+unsigned NGCaretNavigator::VisualMostForwardCharacterOf(
+    const Line& line,
+    MoveDirection direction) const {
+  if (!IsBidiEnabled()) {
+    if (direction == MoveDirection::kTowardsLeft)
+      return line.start_offset;
+    return line.end_offset - 1;
+  }
 
-  const auto indices_in_visual_order = IndicesInVisualOrder();
-  const auto* visual_iterator = std::find(indices_in_visual_order.begin(),
-                                          indices_in_visual_order.end(), index);
-  DCHECK_NE(visual_iterator, indices_in_visual_order.end());
-  return std::distance(indices_in_visual_order.begin(), visual_iterator);
+  const auto indices_in_visual_order = CharacterIndicesInVisualOrder(line);
+  if (direction == MoveDirection::kTowardsLeft)
+    return indices_in_visual_order.front();
+  return indices_in_visual_order.back();
+}
+
+unsigned NGCaretNavigator::VisualFirstCharacterOf(const Line& line) const {
+  return VisualMostForwardCharacterOf(line, IsLtr(line.base_direction)
+                                                ? MoveDirection::kTowardsLeft
+                                                : MoveDirection::kTowardsRight);
+}
+
+unsigned NGCaretNavigator::VisualLastCharacterOf(const Line& line) const {
+  return VisualMostForwardCharacterOf(line, IsLtr(line.base_direction)
+                                                ? MoveDirection::kTowardsRight
+                                                : MoveDirection::kTowardsLeft);
 }
 
 NGCaretNavigator::VisualCharacterMovementResult
 NGCaretNavigator::MoveCharacterInternal(unsigned index,
                                         MoveDirection move_direction) const {
-  DCHECK_LT(index, GetText().length());
-  const unsigned visual_index = VisualIndexOf(index);
-  const TextDirection base_direction = GetData().BaseDirection();
+  const Line line = ContainingLineOf(index);
 
-  const base::Optional<unsigned> maybe_result_visual_index =
-      MoveVisualIndex(visual_index, GetText().length(), move_direction);
-  if (!maybe_result_visual_index.has_value()) {
-    if (TowardsSameDirection(move_direction, base_direction))
-      return {VisualMovementResultType::kAfterContext, base::nullopt};
-    return {VisualMovementResultType::kBeforeContext, base::nullopt};
+  if (index == VisualMostForwardCharacterOf(line, move_direction)) {
+    if (TowardsSameDirection(move_direction, line.base_direction)) {
+      if (line.end_offset == GetText().length())
+        return {VisualMovementResultType::kAfterContext, base::nullopt};
+      const Line next_line = ContainingLineOf(line.end_offset);
+      return {VisualMovementResultType::kWithinContext,
+              VisualFirstCharacterOf(next_line)};
+    }
+
+    if (!line.start_offset)
+      return {VisualMovementResultType::kBeforeContext, base::nullopt};
+    const Line last_line = ContainingLineOf(line.start_offset - 1);
+    return {VisualMovementResultType::kWithinContext,
+            VisualLastCharacterOf(last_line)};
   }
 
-  const unsigned result_visual_index = maybe_result_visual_index.value();
-  const unsigned result_index =
-      IsBidiEnabled() ? IndicesInVisualOrder()[result_visual_index]
-                      : result_visual_index;
-  return {VisualMovementResultType::kWithinContext, result_index};
+  if (!IsBidiEnabled()) {
+    if (move_direction == MoveDirection::kTowardsLeft)
+      return {VisualMovementResultType::kWithinContext, index - 1};
+    return {VisualMovementResultType::kWithinContext, index + 1};
+  }
+
+  Vector<int32_t, 32> indices_in_visual_order =
+      CharacterIndicesInVisualOrder(line);
+  const int32_t* visual_location = std::find(
+      indices_in_visual_order.begin(), indices_in_visual_order.end(), index);
+  DCHECK_NE(visual_location, indices_in_visual_order.end());
+  if (move_direction == MoveDirection::kTowardsLeft) {
+    DCHECK_NE(visual_location, indices_in_visual_order.begin());
+    return {VisualMovementResultType::kWithinContext,
+            *std::prev(visual_location)};
+  }
+  DCHECK_NE(std::next(visual_location), indices_in_visual_order.end());
+  return {VisualMovementResultType::kWithinContext,
+          *std::next(visual_location)};
 }
 
 NGCaretNavigator::VisualCaretMovementResult NGCaretNavigator::LeftPositionOf(
@@ -190,7 +285,8 @@
   return MoveCaretInternal(caret_position, MoveDirection::kTowardsRight);
 }
 
-NGCaretNavigator::VisualCaretMovementResult NGCaretNavigator::MoveCaretInternal(
+NGCaretNavigator::UnvalidatedVisualCaretMovementResult
+NGCaretNavigator::MoveCaretWithoutValidation(
     const Position& caret_position,
     MoveDirection move_direction) const {
   const unsigned index = caret_position.index;
@@ -198,19 +294,45 @@
   if (caret_position == EdgeOfInternal(index, opposite_direction)) {
     // TODO(xiaochengh): Consider grapheme cluster
     return {VisualMovementResultType::kWithinContext,
-            EdgeOfInternal(index, move_direction)};
+            EdgeOfInternal(index, move_direction),
+            !IsIgnoredInCaretMovement(index)};
   }
 
   VisualCharacterMovementResult forward_character =
       MoveCharacterInternal(index, move_direction);
-  if (forward_character.IsWithinContext()) {
-    DCHECK(forward_character.index.has_value());
-    const Position forward_caret =
-        EdgeOfInternal(forward_character.index.value(), opposite_direction);
-    return MoveCaretInternal(forward_caret, move_direction);
-  }
+  if (!forward_character.IsWithinContext())
+    return {forward_character.type};
 
-  return {forward_character.type, base::nullopt};
+  DCHECK(forward_character.index.has_value());
+  const Position forward_caret =
+      EdgeOfInternal(forward_character.index.value(), opposite_direction);
+  return {VisualMovementResultType::kWithinContext, forward_caret};
+}
+
+NGCaretNavigator::VisualCaretMovementResult NGCaretNavigator::MoveCaretInternal(
+    const Position& caret_position,
+    MoveDirection move_direction) const {
+  bool has_passed_character = false;
+  base::Optional<Position> last_position;
+  for (Position runner = caret_position;
+       !has_passed_character || !IsValidCaretPosition(runner);) {
+    const UnvalidatedVisualCaretMovementResult next =
+        MoveCaretWithoutValidation(runner, move_direction);
+    if (next.type != VisualMovementResultType::kWithinContext)
+      return {next.type, base::nullopt};
+
+    // TODO(xiaochengh): Handle editing-ignored contents, e.g., CSS-generated.
+    if (next.has_passed_character)
+      has_passed_character = true;
+
+    runner = next.position.value();
+    last_position = runner;
+
+    // TODO(xiaochengh): Handle the case where we reach a different line with a
+    // different base direction, which occurs with 'unicode-bidi: plain-text'.
+  }
+  DCHECK(last_position.has_value());
+  return {VisualMovementResultType::kWithinContext, last_position.value()};
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h
index 45e58a04..046de98 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h
@@ -18,7 +18,6 @@
 namespace blink {
 
 class LayoutBlockFlow;
-class NGInlineItem;
 struct NGInlineNodeData;
 
 // Hosts the |text_content| of an inline formatting context and provides
@@ -79,7 +78,6 @@
   // - Grapheme clusters
   // - Enterable atomic inlines
   // - Editing-ignored contents
-  // - Multiple lines
 
   enum class VisualMovementResultType {
     kWithinContext,
@@ -126,12 +124,39 @@
   VisualCaretMovementResult RightPositionOf(const Position&) const;
 
  private:
+  // A caret position is invalid if it is:
+  // - kAfter to a line break character.
+  // - Anchored to a collapsible space that's removed by line wrap.
+  // - Anchored to a character that's ignored in caret movement.
+  // TODO(xiaochengh): Handle the the following:
+  // - Enterable atomic inlines
+  bool IsValidCaretPosition(const Position&) const;
+  bool IsLineBreak(unsigned index) const;
+  bool IsCollapsibleWhitespace(unsigned index) const;
+  bool IsCollapsedSpaceByLineWrap(unsigned index) const;
+  bool IsIgnoredInCaretMovement(unsigned index) const;
+
   enum class MoveDirection { kTowardsLeft, kTowardsRight };
   static MoveDirection OppositeDirectionOf(MoveDirection);
   static bool TowardsSameDirection(MoveDirection, TextDirection);
-  static base::Optional<unsigned> MoveVisualIndex(unsigned index,
-                                                  unsigned length,
-                                                  MoveDirection);
+
+  // ------ Line-related functions ------
+
+  // A line contains a consecutive substring of |GetText()|. The lines should
+  // not overlap, and should together cover the entire |GetText()|.
+  struct Line {
+    unsigned start_offset;
+    unsigned end_offset;
+    TextDirection base_direction;
+  };
+  Line ContainingLineOf(unsigned index) const;
+  Vector<int32_t, 32> CharacterIndicesInVisualOrder(const Line&) const;
+  unsigned VisualMostForwardCharacterOf(const Line&,
+                                        MoveDirection direction) const;
+  unsigned VisualLastCharacterOf(const Line&) const;
+  unsigned VisualFirstCharacterOf(const Line&) const;
+
+  // ------ Implementation of public visual movement functions ------
 
   Position EdgeOfInternal(unsigned index, MoveDirection) const;
   VisualCharacterMovementResult MoveCharacterInternal(unsigned index,
@@ -139,15 +164,32 @@
   VisualCaretMovementResult MoveCaretInternal(const Position&,
                                               MoveDirection) const;
 
+  // Performs a "minimal" caret movement to the left/right without validating
+  // the result. The result might be invalid due to, e.g., anchored to an
+  // unallowed character, being visually the same as the input, etc. It's a
+  // subroutine of |MoveCaretInternal|, who keeps calling it until both of the
+  // folliwng are satisfied:
+  // - We've reached a valid caret position.
+  // - During the process, the caret has moved passing a character on which
+  // |IsIgnoredInCaretMovement| is false (indicated by |has_passed_character|).
+  struct UnvalidatedVisualCaretMovementResult {
+    VisualMovementResultType type;
+    base::Optional<Position> position;
+    bool has_passed_character = false;
+  };
+  UnvalidatedVisualCaretMovementResult MoveCaretWithoutValidation(
+      const Position&,
+      MoveDirection) const;
+
   const NGInlineNodeData& GetData() const;
-  const NGInlineItem& GetItem(unsigned index) const;
-  Vector<int32_t, 32> IndicesInVisualOrder() const;
-  unsigned VisualIndexOf(unsigned index) const;
 
   const LayoutBlockFlow& context_;
   DocumentLifecycle::DisallowTransitionScope disallow_transition_;
 };
 
+CORE_EXPORT std::ostream& operator<<(std::ostream&,
+                                     const NGCaretNavigator::Position&);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_CARET_NAVIGATOR_H_
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc
index 582a210..c386e4f 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 
 namespace blink {
 
@@ -252,4 +253,144 @@
   EXPECT_EQ(CaretBefore(5), *RightPositionOf(CaretAfter(8)).position);
 }
 
+// Tests below check caret movement crossing line boundaries
+
+TEST_F(NGCaretNavigatorTest, HardLineBreak) {
+  SetupHtml("container", "<div id=container>abc<br>def</div>");
+
+  EXPECT_TRUE(LeftPositionOf(CaretBefore(0)).IsBeforeContext());
+
+  EXPECT_TRUE(RightPositionOf(CaretAfter(2)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretAfter(2)).position);
+
+  EXPECT_TRUE(RightPositionOf(CaretBefore(3)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretBefore(3)).position);
+
+  EXPECT_TRUE(LeftPositionOf(CaretBefore(4)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(3), *LeftPositionOf(CaretBefore(4)).position);
+
+  EXPECT_TRUE(RightPositionOf(CaretAfter(6)).IsAfterContext());
+}
+
+TEST_F(NGCaretNavigatorTest, SoftLineWrapAtSpace) {
+  SetupHtml("container", "<div id=container style=\"width:0\">abc def</div>");
+
+  EXPECT_TRUE(LeftPositionOf(CaretBefore(0)).IsBeforeContext());
+
+  EXPECT_TRUE(RightPositionOf(CaretAfter(2)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretAfter(2)).position);
+
+  EXPECT_TRUE(RightPositionOf(CaretBefore(3)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretBefore(3)).position);
+
+  EXPECT_TRUE(LeftPositionOf(CaretBefore(4)).IsWithinContext());
+  EXPECT_EQ(CaretAfter(2), *LeftPositionOf(CaretBefore(4)).position);
+
+  EXPECT_TRUE(RightPositionOf(CaretAfter(6)).IsAfterContext());
+}
+
+TEST_F(NGCaretNavigatorTest, BidiAndSoftLineWrapAtSpaceLtr) {
+  LoadAhem();
+  SetupHtml("container",
+            "<div id=container style='font: 10px/10px Ahem; width: 100px'>"
+            "before    &#x05D0;&#x05D1;&#x05D2;&#x05D3; "
+            "&#x05D4;&#x05D5;&#x05D6;&#x05D7;&#x05D8;&#x05D9;"
+            "&#x05DA;&#x05DB;&#x05DC;&#x05DD;&#x05DE;&#x05DF;"
+            "&#x05E0;&#x05E1;&#x05E2;&#x05E3;&#x05E4;&#x05E5;"
+            "</div>");
+
+  // Moving left from "|before DCBA" should be before context
+  EXPECT_TRUE(LeftPositionOf(CaretBefore(0)).IsBeforeContext());
+
+  // Moving right from "before |DCBA" should yield "before D|CBA"
+  EXPECT_TRUE(RightPositionOf(CaretAfter(10)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(10), *RightPositionOf(CaretAfter(10)).position);
+  EXPECT_TRUE(RightPositionOf(CaretAfter(6)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(10), *RightPositionOf(CaretAfter(6)).position);
+
+  // Moving left from "before |DCBA" should yield "before| DCBA"
+  EXPECT_TRUE(LeftPositionOf(CaretAfter(10)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(6), *LeftPositionOf(CaretAfter(10)).position);
+  EXPECT_TRUE(LeftPositionOf(CaretAfter(6)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(6), *LeftPositionOf(CaretAfter(6)).position);
+
+  // Moving right from "before DCBA|" should yield "V|UTSRQPONMLKJIHGFE"
+  EXPECT_TRUE(RightPositionOf(CaretBefore(7)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(29), *RightPositionOf(CaretBefore(7)).position);
+
+  // Moving left from "|VUTSRQPONMLKJIHGFE" should yield "before DCB|A"
+  EXPECT_TRUE(LeftPositionOf(CaretAfter(29)).IsWithinContext());
+  EXPECT_EQ(CaretAfter(7), *LeftPositionOf(CaretAfter(29)).position);
+
+  // Moving right from "VUTSRQPONMLKJIHGFE|" should be after context
+  EXPECT_TRUE(RightPositionOf(CaretBefore(12)).IsAfterContext());
+}
+
+TEST_F(NGCaretNavigatorTest, BidiAndSoftLineWrapAtSpaceRtl) {
+  LoadAhem();
+  SetupHtml(
+      "container",
+      "<div dir=rtl id=container style='font: 10px/10px Ahem; width: 120px'>"
+      "&#x05D0;&#x05D1;&#x05D2;&#x05D3;    after encyclopedia"
+      "</div>");
+
+  // Moving right from "after DCBA|" should be before context
+  EXPECT_TRUE(RightPositionOf(CaretBefore(0)).IsBeforeContext());
+
+  // Moving left from "after| DCBA" should yield "afte|r DCBA"
+  EXPECT_TRUE(LeftPositionOf(CaretAfter(4)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(9), *LeftPositionOf(CaretAfter(4)).position);
+  EXPECT_TRUE(LeftPositionOf(CaretAfter(9)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(9), *LeftPositionOf(CaretAfter(9)).position);
+
+  // Moving right from "after| DCBA" should yield "after |DCBA"
+  EXPECT_TRUE(RightPositionOf(CaretAfter(4)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretAfter(4)).position);
+  EXPECT_TRUE(RightPositionOf(CaretAfter(9)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretAfter(9)).position);
+
+  // Moving left from "|after DCBA" should yield "encyclopedi|a"
+  EXPECT_TRUE(LeftPositionOf(CaretBefore(5)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(22), *LeftPositionOf(CaretBefore(5)).position);
+
+  // Moving right from "encyclopedia|" should yield "a|fter DCBA"
+  EXPECT_TRUE(RightPositionOf(CaretAfter(22)).IsWithinContext());
+  EXPECT_EQ(CaretAfter(5), *RightPositionOf(CaretAfter(22)).position);
+
+  // Moving left from "|encyclopedia" should be after context
+  EXPECT_TRUE(LeftPositionOf(CaretBefore(11)).IsAfterContext());
+}
+
+TEST_F(NGCaretNavigatorTest, SoftLineWrapAtHyphen) {
+  SetupHtml("container", "<div id=container style=\"width:0\">abc-def</div>");
+
+  EXPECT_TRUE(LeftPositionOf(CaretBefore(0)).IsBeforeContext());
+
+  // 3 -> 4
+  EXPECT_TRUE(RightPositionOf(CaretAfter(2)).IsWithinContext());
+  EXPECT_EQ(CaretAfter(3), *RightPositionOf(CaretAfter(2)).position);
+  EXPECT_TRUE(RightPositionOf(CaretBefore(3)).IsWithinContext());
+  EXPECT_EQ(CaretAfter(3), *RightPositionOf(CaretBefore(3)).position);
+
+  // 4 -> 5
+  EXPECT_TRUE(RightPositionOf(CaretAfter(3)).IsWithinContext());
+  EXPECT_EQ(CaretAfter(4), *RightPositionOf(CaretAfter(3)).position);
+  EXPECT_TRUE(RightPositionOf(CaretBefore(4)).IsWithinContext());
+  EXPECT_EQ(CaretAfter(4), *RightPositionOf(CaretBefore(4)).position);
+
+  // 5 -> 4
+  EXPECT_TRUE(LeftPositionOf(CaretBefore(5)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(4), *LeftPositionOf(CaretBefore(5)).position);
+  EXPECT_TRUE(LeftPositionOf(CaretAfter(4)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(4), *LeftPositionOf(CaretAfter(4)).position);
+
+  // 4 -> 3
+  EXPECT_TRUE(LeftPositionOf(CaretBefore(4)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(3), *LeftPositionOf(CaretBefore(4)).position);
+  EXPECT_TRUE(LeftPositionOf(CaretAfter(3)).IsWithinContext());
+  EXPECT_EQ(CaretBefore(3), *LeftPositionOf(CaretAfter(3)).position);
+
+  EXPECT_TRUE(RightPositionOf(CaretAfter(6)).IsAfterContext());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 55bde22..eaf1f49 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -355,6 +355,8 @@
   }
   bool AllowScriptExtensions() override { return false; }
 
+  void VisibilityChanged(blink::mojom::FrameVisibility) override {}
+
   WebCookieJar* CookieJar() const override { return nullptr; }
 
   service_manager::InterfaceProvider* GetInterfaceProvider() override {
@@ -446,7 +448,7 @@
   void UpdateRemoteViewportIntersection(const IntRect& viewport_intersection,
                                         bool occluded_or_obscured) override {}
   void AdvanceFocus(WebFocusType, LocalFrame* source) override {}
-  void VisibilityChanged(bool visible) override {}
+  void VisibilityChanged(blink::mojom::FrameVisibility) override {}
   void SetIsInert(bool) override {}
   void SetInheritedEffectiveTouchAction(TouchAction) override {}
   void UpdateRenderThrottlingStatus(bool is_throttled,
diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc
index f84f3cfc..e503a8b 100644
--- a/third_party/blink/renderer/core/paint/box_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/box_painter_base.cc
@@ -309,6 +309,112 @@
 
 namespace {
 
+// Given the |size| that the whole image should draw at, and the input phase
+// requested by the content, and the space between repeated tiles, return a
+// rectangle with |size| and a location that respects the phase but is no more
+// than one size + space in magnitude. In practice, this means that if there is
+// no repeating the returned rect would contain the destination_offset
+// location. The destination_offset passed here must exactly match the location
+// of the subset in a following call to ComputeSubsetForBackground.
+FloatRect ComputePhaseForBackground(const FloatPoint& destination_offset,
+                                    const FloatSize& size,
+                                    const FloatPoint& phase,
+                                    const FloatSize& spacing) {
+  const FloatSize step_per_tile(size + spacing);
+  return FloatRect(
+      FloatPoint(
+          destination_offset.X() + fmodf(-phase.X(), step_per_tile.Width()),
+          destination_offset.Y() + fmodf(-phase.Y(), step_per_tile.Height())),
+      size);
+}
+
+// Compute the image subset, in intrinsic image coordinates, that gets mapped
+// onto the |subset|, when the whole image would be drawn with phase and size
+// given by |phase_and_size|. Assumes |phase_and_size| contains |subset|. The
+// location of the requested subset should be the painting snapped location, or
+// whatever was used as a destination_offset in ComputePhaseForBackground.
+//
+// It is used to undo the offset added in ComputePhaseForBackground. The size
+// of requested subset should be the unsnapped size so that the computed
+// scale and location in the source image can be correctly determined.
+FloatRect ComputeSubsetForBackground(const FloatRect& phase_and_size,
+                                     const FloatRect& subset,
+                                     const FloatSize& intrinsic_size) {
+  // TODO(schenney): Re-enable this after determining why it fails for
+  // CAP, and maybe other cases.
+  // DCHECK(phase_and_size.Contains(subset));
+
+  const FloatSize scale(phase_and_size.Width() / intrinsic_size.Width(),
+                        phase_and_size.Height() / intrinsic_size.Height());
+  return FloatRect((subset.X() - phase_and_size.X()) / scale.Width(),
+                   (subset.Y() - phase_and_size.Y()) / scale.Height(),
+                   subset.Width() / scale.Width(),
+                   subset.Height() / scale.Height());
+}
+
+// The unsnapped_subset_size should be the target painting area implied by the
+//   content, without any snapping applied. It is necessary to correctly
+//   compute the subset of the source image to paint into the destination.
+// The snapped_paint_rect should be the target destination for painting into.
+// The phase is never snapped.
+// The tile_size is the total image size. The mapping from this size
+//   to the unsnapped_dest_rect size defines the scaling of the image for
+//   sprite computation.
+void DrawTiledBackground(GraphicsContext& context,
+                         Image* image,
+                         const FloatSize& unsnapped_subset_size,
+                         const FloatRect& snapped_paint_rect,
+                         const FloatPoint& phase,
+                         const FloatSize& tile_size,
+                         SkBlendMode op,
+                         const FloatSize& repeat_spacing) {
+  DCHECK(!tile_size.IsEmpty());
+
+  // Use the intrinsic size of the image if it has one, otherwise force the
+  // generated image to be the tile size.
+  FloatSize intrinsic_tile_size(image->Size());
+  FloatSize scale(1, 1);
+  if (image->HasRelativeSize()) {
+    intrinsic_tile_size = tile_size;
+  } else {
+    scale = FloatSize(tile_size.Width() / intrinsic_tile_size.Width(),
+                      tile_size.Height() / intrinsic_tile_size.Height());
+  }
+
+  const FloatRect one_tile_rect = ComputePhaseForBackground(
+      snapped_paint_rect.Location(), tile_size, phase, repeat_spacing);
+
+  // Check and see if a single draw of the image can cover the entire area we
+  // are supposed to tile. The dest_rect_for_subset must use the same
+  // location that was used in ComputePhaseForBackground and the unsnapped
+  // destination rect in order to correctly evaluate the subset size and
+  // location in the presence of border snapping and zoom.
+  FloatRect dest_rect_for_subset(snapped_paint_rect.Location(),
+                                 unsnapped_subset_size);
+  if (one_tile_rect.Contains(dest_rect_for_subset)) {
+    FloatRect visible_src_rect = ComputeSubsetForBackground(
+        one_tile_rect, dest_rect_for_subset, intrinsic_tile_size);
+    // Round to avoid filtering pulling in neighboring pixels, for the
+    // common case of sprite maps.
+    // TODO(schenney): Snapping at this level is a problem for cases where we
+    // might be animating background-position to pan over an image. Ideally we
+    // would either snap only if close to integral, or move snapping
+    // calculations up the stack.
+    visible_src_rect = FloatRect(RoundedIntRect(visible_src_rect));
+    context.DrawImage(image, Image::kSyncDecode, snapped_paint_rect,
+                      &visible_src_rect, op, kDoNotRespectImageOrientation);
+    return;
+  }
+
+  // Note that this tile rect the image's pre-scaled size.
+  FloatRect tile_rect(FloatPoint(), intrinsic_tile_size);
+  // This call takes the unscaled image, applies the given scale, and paints
+  // it into the snapped_dest_rect using phase from one_tile_rect and the
+  // given repeat spacing. Note the phase is already scaled.
+  context.DrawImageTiled(image, snapped_paint_rect, tile_rect, scale,
+                         one_tile_rect.Location(), repeat_spacing, op);
+}
+
 inline bool PaintFastBottomLayer(Node* node,
                                  const PaintInfo& paint_info,
                                  const BoxPainterBase::FillLayerInfo& info,
@@ -359,7 +465,7 @@
 
       // Phase calculation uses the actual painted location, given by the
       // border-snapped destination rect.
-      image_tile = Image::ComputePhaseForBackground(
+      image_tile = ComputePhaseForBackground(
           FloatPoint(geometry.SnappedDestRect().Location()),
           FloatSize(geometry.TileSize()), geometry.Phase(),
           FloatSize(geometry.SpaceSize()));
@@ -417,7 +523,7 @@
   // so snap to integers. This is particuarly important for sprite maps.
   // Calculation up to this point, in LayoutUnits, can lead to small variations
   // from integer size, so it is safe to round without introducing major issues.
-  const FloatRect unrounded_subset = Image::ComputeSubsetForBackground(
+  const FloatRect unrounded_subset = ComputeSubsetForBackground(
       image_tile, dest_rect_for_subset, intrinsic_tile_size);
   FloatRect src_rect = FloatRect(RoundedIntRect(unrounded_subset));
 
@@ -541,17 +647,17 @@
   // NOTE: This method can be called with no image in situations when a bad
   // resource locator is given such as "//:0", so still check for image.
   if (info.should_paint_image && !geometry.SnappedDestRect().IsEmpty() &&
-      image) {
+      !geometry.TileSize().IsEmpty() && image) {
     TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage",
                  "data",
                  inspector_paint_image_event::Data(
                      node, *info.image, FloatRect(image->Rect()),
                      FloatRect(scrolled_paint_rect)));
-    context.DrawTiledImage(image,
-                           FloatSize(geometry.UnsnappedDestRect().Size()),
-                           FloatRect(geometry.SnappedDestRect()),
-                           geometry.Phase(), FloatSize(geometry.TileSize()),
-                           composite_op, FloatSize(geometry.SpaceSize()));
+    DrawTiledBackground(context, image,
+                        FloatSize(geometry.UnsnappedDestRect().Size()),
+                        FloatRect(geometry.SnappedDestRect()), geometry.Phase(),
+                        FloatSize(geometry.TileSize()), composite_op,
+                        FloatSize(geometry.SpaceSize()));
   }
 }
 
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.cc b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
index 2eecda79..2293a21 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
@@ -384,6 +384,8 @@
   flags.setBlendMode(composite_op);
   flags.setColorFilter(sk_ref_sp(context.GetColorFilter()));
   context.DrawRect(dst_rect, flags);
+
+  StartAnimation();
 }
 
 sk_sp<PaintRecord> SVGImage::PaintRecordForContainer(
diff --git a/third_party/blink/renderer/core/timing/performance_observer.cc b/third_party/blink/renderer/core/timing/performance_observer.cc
index 6141b19..546f3be 100644
--- a/third_party/blink/renderer/core/timing/performance_observer.cc
+++ b/third_party/blink/renderer/core/timing/performance_observer.cc
@@ -85,6 +85,7 @@
       callback_(callback),
       performance_(performance),
       filter_options_(PerformanceEntry::kInvalid),
+      type_(PerformanceObserverType::kUnknown),
       is_registered_(false) {
   DCHECK(performance_);
 }
@@ -97,23 +98,64 @@
     return;
   }
 
-  PerformanceEntryTypeMask entry_types = PerformanceEntry::kInvalid;
-  if (observer_init->hasEntryTypes() && observer_init->entryTypes().size()) {
+  if (observer_init->hasEntryTypes()) {
+    if (observer_init->hasType()) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kSyntaxError,
+          "An observe() call MUST NOT include both entryTypes and type.");
+      return;
+    }
+    if (type_ == PerformanceObserverType::kTypeObserver) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kInvalidModificationError,
+          "This observer has performed observe({type:...}, therefore it cannot "
+          "perform observe({entryTypes:...})");
+      return;
+    }
+    type_ = PerformanceObserverType::kEntryTypesObserver;
+    PerformanceEntryTypeMask entry_types = PerformanceEntry::kInvalid;
     const Vector<String>& sequence = observer_init->entryTypes();
     for (const auto& entry_type_string : sequence) {
       entry_types |=
           PerformanceEntry::ToEntryTypeEnum(AtomicString(entry_type_string));
     }
+    if (entry_types == PerformanceEntry::kInvalid) {
+      String message =
+          "The Performance Observer MUST have at least one valid entryType in "
+          "its "
+          "entryTypes attribute.";
+      GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+          kJSMessageSource, kWarningMessageLevel, message));
+      return;
+    }
+    filter_options_ = entry_types;
+  } else {
+    if (!observer_init->hasType()) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kSyntaxError,
+          "An observe() call MUST include either entryTypes or type.");
+      return;
+    }
+    if (type_ == PerformanceObserverType::kEntryTypesObserver) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kInvalidModificationError,
+          "This observer has performed observe({entryTypes:...}, therefore it "
+          "cannot perform observe({type:...})");
+      return;
+    }
+    type_ = PerformanceObserverType::kTypeObserver;
+    PerformanceEntryType entry_type =
+        PerformanceEntry::ToEntryTypeEnum(AtomicString(observer_init->type()));
+    if (entry_type == PerformanceEntry::kInvalid) {
+      String message =
+          "The Performance Observer MUST have a valid entryType in its "
+          "type attribute.";
+      GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+          kJSMessageSource, kWarningMessageLevel, message));
+      return;
+    }
+    filter_options_ |= entry_type;
   }
-  if (entry_types == PerformanceEntry::kInvalid) {
-    String message =
-        "A Performance Observer MUST have at least one valid entryType in its "
-        "entryTypes attribute.";
-    GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel, message));
-    return;
-  }
-  filter_options_ = entry_types;
   if (is_registered_)
     performance_->UpdatePerformanceObserverFilterOptions();
   else
diff --git a/third_party/blink/renderer/core/timing/performance_observer.h b/third_party/blink/renderer/core/timing/performance_observer.h
index c104376..48dd3bed 100644
--- a/third_party/blink/renderer/core/timing/performance_observer.h
+++ b/third_party/blink/renderer/core/timing/performance_observer.h
@@ -55,6 +55,18 @@
   void Trace(blink::Visitor*) override;
 
  private:
+  // This describes the types of parameters that an observer can have in its
+  // observe() function. An observer of type kEntryTypesObserver has already
+  // made a call observe({entryTypes...}) so can only do subsequent observe()
+  // calls with the 'entryTypes' parameter. An observer of type kTypeObserver
+  // has already made a call observe({type...}) so it can only perform
+  // subsequent observe() calls with the 'type' parameter. An observer of type
+  // kUnknown has not called observe().
+  enum class PerformanceObserverType {
+    kEntryTypesObserver,
+    kTypeObserver,
+    kUnknown,
+  };
   void Deliver();
   bool ShouldBeSuspended() const;
 
@@ -63,6 +75,7 @@
   WeakMember<Performance> performance_;
   PerformanceEntryVector performance_entries_;
   PerformanceEntryTypeMask filter_options_;
+  PerformanceObserverType type_;
   bool is_registered_;
 };
 
diff --git a/third_party/blink/renderer/core/timing/performance_observer_init.idl b/third_party/blink/renderer/core/timing/performance_observer_init.idl
index acb2790..08e0440 100644
--- a/third_party/blink/renderer/core/timing/performance_observer_init.idl
+++ b/third_party/blink/renderer/core/timing/performance_observer_init.idl
@@ -5,5 +5,6 @@
 // https://w3c.github.io/performance-timeline/#performanceobserverinit-dictionary
 
 dictionary PerformanceObserverInit {
-    required sequence<DOMString> entryTypes;
+    sequence<DOMString> entryTypes;
+    DOMString type;
 };
diff --git a/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc b/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc
index b55b955..6ebfa142 100644
--- a/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc
+++ b/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc
@@ -473,7 +473,7 @@
 }
 
 static int g_global_descriptor = 0;
-static ThreadIdentifier g_libxml_loader_thread = 0;
+static base::PlatformThreadId g_libxml_loader_thread = 0;
 
 static int MatchFunc(const char*) {
   // Only match loads initiated due to uses of libxml2 from within
diff --git a/third_party/blink/renderer/devtools/front_end/elements/elementsTreeOutline.css b/third_party/blink/renderer/devtools/front_end/elements/elementsTreeOutline.css
index 31e1a8f..7291d01 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/elementsTreeOutline.css
+++ b/third_party/blink/renderer/devtools/front_end/elements/elementsTreeOutline.css
@@ -13,7 +13,7 @@
 .elements-disclosure li {
     /** Keep margin-left & padding-left in sync with ElementsTreeElements.updateDecorators **/
     padding: 0 0 0 14px;
-    margin-top: 1px;
+    border-top: 1px solid transparent;
     margin-left: -2px;
     word-wrap: break-word;
     position: relative;
diff --git a/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc b/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc
index 501686f..983ab58 100644
--- a/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc
@@ -87,22 +87,19 @@
 
 }  // anonymous namespace
 
-media_session::mojom::blink::MediaMetadataPtr
+blink::mojom::blink::SpecMediaMetadataPtr
 MediaMetadataSanitizer::SanitizeAndConvertToMojo(const MediaMetadata* metadata,
                                                  ExecutionContext* context) {
-  media_session::mojom::blink::MediaMetadataPtr mojo_metadata;
   if (!metadata)
-    return mojo_metadata;
+    return blink::mojom::blink::SpecMediaMetadataPtr();
 
-  mojo_metadata = media_session::mojom::blink::MediaMetadata::New();
+  blink::mojom::blink::SpecMediaMetadataPtr mojo_metadata(
+      blink::mojom::blink::SpecMediaMetadata::New());
 
   mojo_metadata->title = metadata->title().Left(kMaxStringLength);
   mojo_metadata->artist = metadata->artist().Left(kMaxStringLength);
   mojo_metadata->album = metadata->album().Left(kMaxStringLength);
 
-  // |source_title_| is populated by content::MediaSessionImpl.
-  mojo_metadata->source_title = g_empty_string16_bit;
-
   for (const MediaImage* image : metadata->artwork()) {
     media_session::mojom::blink::MediaImagePtr mojo_image =
         SanitizeMediaImageAndConvertToMojo(image, context);
diff --git a/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.h b/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.h
index 1c78dab..e37f00a5 100644
--- a/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.h
+++ b/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_MEDIA_METADATA_SANITIZER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_MEDIA_METADATA_SANITIZER_H_
 
-#include "services/media_session/public/mojom/media_session.mojom-blink.h"
+#include "third_party/blink/public/platform/modules/mediasession/media_session.mojom-blink.h"
 
 namespace blink {
 
@@ -16,7 +16,7 @@
  public:
   // Produce the sanitized metadata, which will later be sent to the
   // MediaSession mojo service.
-  static media_session::mojom::blink::MediaMetadataPtr SanitizeAndConvertToMojo(
+  static blink::mojom::blink::SpecMediaMetadataPtr SanitizeAndConvertToMojo(
       const MediaMetadata*,
       ExecutionContext*);
 };
diff --git a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
index bc1154f..eb3674a 100644
--- a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
+++ b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
@@ -245,40 +245,30 @@
 }
 
 void RemotePlayback::PromptInternal() {
-  DCHECK(RuntimeEnabledFeatures::RemotePlaybackBackendEnabled());
-
-  if (RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled()) {
-    PresentationController* controller =
-        PresentationController::FromContext(GetExecutionContext());
-    if (controller && !availability_urls_.IsEmpty()) {
-      controller->GetPresentationService()->StartPresentation(
-          availability_urls_,
-          WTF::Bind(&RemotePlayback::HandlePresentationResponse,
-                    WrapPersistent(this)));
-    } else {
-      // TODO(yuryu): Wrapping PromptCancelled with base::OnceClosure as
-      // InspectorInstrumentation requires a globally unique pointer to track
-      // tasks. We can remove the wrapper if InspectorInstrumentation returns a
-      // task id.
-      base::OnceClosure task =
-          WTF::Bind(&RemotePlayback::PromptCancelled, WrapPersistent(this));
-      std::unique_ptr<int> task_id = std::make_unique<int>(0);
-      probe::AsyncTaskScheduled(GetExecutionContext(), "promptCancelled",
-                                task_id.get());
-      GetExecutionContext()
-          ->GetTaskRunner(TaskType::kMediaElementEvent)
-          ->PostTask(FROM_HERE, WTF::Bind(RunRemotePlaybackTask,
-                                          WrapPersistent(GetExecutionContext()),
-                                          WTF::Passed(std::move(task)),
-                                          WTF::Passed(std::move(task_id))));
-    }
-    return;
+  PresentationController* controller =
+      PresentationController::FromContext(GetExecutionContext());
+  if (controller && !availability_urls_.IsEmpty()) {
+    controller->GetPresentationService()->StartPresentation(
+        availability_urls_,
+        WTF::Bind(&RemotePlayback::HandlePresentationResponse,
+                  WrapPersistent(this)));
+  } else {
+    // TODO(yuryu): Wrapping PromptCancelled with base::OnceClosure as
+    // InspectorInstrumentation requires a globally unique pointer to track
+    // tasks. We can remove the wrapper if InspectorInstrumentation returns a
+    // task id.
+    base::OnceClosure task =
+        WTF::Bind(&RemotePlayback::PromptCancelled, WrapPersistent(this));
+    std::unique_ptr<int> task_id = std::make_unique<int>(0);
+    probe::AsyncTaskScheduled(GetExecutionContext(), "promptCancelled",
+                              task_id.get());
+    GetExecutionContext()
+        ->GetTaskRunner(TaskType::kMediaElementEvent)
+        ->PostTask(FROM_HERE, WTF::Bind(RunRemotePlaybackTask,
+                                        WrapPersistent(GetExecutionContext()),
+                                        WTF::Passed(std::move(task)),
+                                        WTF::Passed(std::move(task_id))));
   }
-
-  if (state_ == WebRemotePlaybackState::kDisconnected)
-    media_element_->RequestRemotePlayback();
-  else
-    media_element_->RequestRemotePlaybackControl();
 }
 
 int RemotePlayback::WatchAvailabilityInternal(
@@ -363,30 +353,26 @@
   switch (state_) {
     case WebRemotePlaybackState::kConnecting:
       DispatchEvent(*Event::Create(event_type_names::kConnecting));
-      if (RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled()) {
-        if (media_element_->IsHTMLVideoElement()) {
-          // TODO(xjz): Pass the remote device name.
-          ToHTMLVideoElement(media_element_)->MediaRemotingStarted(WebString());
-        }
-        media_element_->FlingingStarted();
+      if (media_element_->IsHTMLVideoElement()) {
+        // TODO(xjz): Pass the remote device name.
+        ToHTMLVideoElement(media_element_)->MediaRemotingStarted(WebString());
       }
+      media_element_->FlingingStarted();
       break;
     case WebRemotePlaybackState::kConnected:
       DispatchEvent(*Event::Create(event_type_names::kConnect));
       break;
     case WebRemotePlaybackState::kDisconnected:
       DispatchEvent(*Event::Create(event_type_names::kDisconnect));
-      if (RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled()) {
-        if (media_element_->IsHTMLVideoElement()) {
-          ToHTMLVideoElement(media_element_)
-              ->MediaRemotingStopped(
-                  WebLocalizedString::kMediaRemotingStopNoText);
-        }
-        CleanupConnections();
-        presentation_id_ = "";
-        presentation_url_ = KURL();
-        media_element_->FlingingStopped();
+      if (media_element_->IsHTMLVideoElement()) {
+        ToHTMLVideoElement(media_element_)
+            ->MediaRemotingStopped(
+                WebLocalizedString::kMediaRemotingStopNoText);
       }
+      CleanupConnections();
+      presentation_id_ = "";
+      presentation_url_ = KURL();
+      media_element_->FlingingStopped();
       break;
   }
 
@@ -423,8 +409,6 @@
 
 void RemotePlayback::SourceChanged(const WebURL& source,
                                    bool is_source_supported) {
-  DCHECK(RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled());
-
   if (IsBackgroundAvailabilityMonitoringDisabled())
     return;
 
@@ -482,15 +466,10 @@
   if (state_ == WebRemotePlaybackState::kDisconnected)
     return;
 
-  if (RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled()) {
-    auto* controller =
-        PresentationController::FromContext(GetExecutionContext());
-    if (controller) {
-      controller->GetPresentationService()->CloseConnection(presentation_url_,
-                                                            presentation_id_);
-    }
-  } else {
-    media_element_->RequestRemotePlaybackStop();
+  auto* controller = PresentationController::FromContext(GetExecutionContext());
+  if (controller) {
+    controller->GetPresentationService()->CloseConnection(presentation_url_,
+                                                          presentation_id_);
   }
 }
 
@@ -501,7 +480,6 @@
 
 void RemotePlayback::AvailabilityChanged(
     mojom::blink::ScreenAvailability availability) {
-  DCHECK(RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled());
   DCHECK(is_listening_);
 
   // TODO(avayvod): Use mojom::ScreenAvailability directly once
@@ -531,7 +509,6 @@
 }
 
 const Vector<KURL>& RemotePlayback::Urls() const {
-  DCHECK(RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled());
   // TODO(avayvod): update the URL format and add frame url, mime type and
   // response headers when available.
   return availability_urls_;
@@ -539,7 +516,6 @@
 
 void RemotePlayback::OnConnectionSuccess(
     mojom::blink::PresentationConnectionResultPtr result) {
-  DCHECK(RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled());
   presentation_id_ = std::move(result->presentation_info->id);
   presentation_url_ = std::move(result->presentation_info->url);
 
@@ -558,7 +534,6 @@
 
 void RemotePlayback::OnConnectionError(
     const mojom::blink::PresentationError& error) {
-  DCHECK(RuntimeEnabledFeatures::NewRemotePlaybackPipelineEnabled());
   presentation_id_ = "";
   presentation_url_ = KURL();
   if (error.error_type ==
@@ -603,9 +578,6 @@
 }
 
 void RemotePlayback::StopListeningForAvailability() {
-  if (!RuntimeEnabledFeatures::RemotePlaybackBackendEnabled())
-    return;
-
   if (!is_listening_)
     return;
 
@@ -623,9 +595,6 @@
   if (IsBackgroundAvailabilityMonitoringDisabled())
     return;
 
-  if (!RuntimeEnabledFeatures::RemotePlaybackBackendEnabled())
-    return;
-
   if (is_listening_)
     return;
 
diff --git a/third_party/blink/renderer/modules/webaudio/deferred_task_handler.h b/third_party/blink/renderer/modules/webaudio/deferred_task_handler.h
index 133061d5..fdd3f19 100644
--- a/third_party/blink/renderer/modules/webaudio/deferred_task_handler.h
+++ b/third_party/blink/renderer/modules/webaudio/deferred_task_handler.h
@@ -243,7 +243,7 @@
 
   // Graph locking.
   RecursiveMutex context_graph_mutex_;
-  std::atomic<ThreadIdentifier> audio_thread_;
+  std::atomic<base::PlatformThreadId> audio_thread_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_database.h b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_database.h
index 6d109ed..b132901 100644
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_database.h
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_database.h
@@ -140,7 +140,7 @@
   Mutex authorizer_lock_;
   CrossThreadPersistent<DatabaseAuthorizer> authorizer_;
 
-  ThreadIdentifier opening_thread_;
+  base::PlatformThreadId opening_thread_;
 
   Mutex database_closing_mutex_;
 
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.cc
index 4e7e0c1..4aeea414 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.cc
@@ -75,6 +75,28 @@
                       "WebGL 2.0 Compute (" +
                           String(ContextGL()->GetString(GL_VERSION)) + ")");
     }
+    case GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE:
+    case GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS:
+    case GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS:
+    case GL_MAX_COMBINED_ATOMIC_COUNTERS:
+    case GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS:
+    case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS:
+    case GL_MAX_COMPUTE_SHARED_MEMORY_SIZE:
+    case GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS:
+    case GL_MAX_COMPUTE_UNIFORM_COMPONENTS:
+    case GL_MAX_COMPUTE_UNIFORM_BLOCKS:
+    case GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS:
+    case GL_MAX_COMPUTE_IMAGE_UNIFORMS:
+    case GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS:
+    case GL_MAX_FRAGMENT_ATOMIC_COUNTERS:
+    case GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS:
+    case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS:
+    case GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS:
+    case GL_MAX_VERTEX_ATOMIC_COUNTERS:
+    case GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS:
+      return GetIntParameter(script_state, pname);
+    case GL_MAX_SHADER_STORAGE_BLOCK_SIZE:
+      return GetInt64Parameter(script_state, pname);
 
     default:
       return WebGL2RenderingContextBase::getParameter(script_state, pname);
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.idl b/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.idl
index 4ec4777..4d3e73a 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.idl
+++ b/third_party/blink/renderer/modules/webgl/webgl2_compute_rendering_context_base.idl
@@ -38,8 +38,16 @@
     const GLenum MAX_SHADER_STORAGE_BUFFER_BINDINGS     = 0x90DD;
     const GLenum MAX_SHADER_STORAGE_BLOCK_SIZE          = 0x90DE;
     const GLenum SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT = 0x90DF;
+    const GLenum MAX_COMPUTE_SHARED_MEMORY_SIZE         = 0x8262;
+    const GLenum MAX_COMPUTE_UNIFORM_COMPONENTS         = 0x8263;
     const GLenum MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS     = 0x8264;
     const GLenum MAX_COMPUTE_ATOMIC_COUNTERS            = 0x8265;
+    const GLenum MAX_COMPUTE_WORK_GROUP_INVOCATIONS     = 0x90EB;
+    const GLenum MAX_COMPUTE_UNIFORM_BLOCKS             = 0x91BB;
+    const GLenum MAX_COMPUTE_TEXTURE_IMAGE_UNITS        = 0x91BC;
+    const GLenum MAX_COMPUTE_IMAGE_UNIFORMS             = 0x91BD;
+    const GLenum MAX_COMPUTE_WORK_GROUP_COUNT           = 0x91BE;
+    const GLenum MAX_COMPUTE_WORK_GROUP_SIZE            = 0x91BF;
     const GLenum ATOMIC_COUNTER_BUFFER_INDEX            = 0x9301;
     const GLenum ATOMIC_COUNTER_BUFFER                  = 0x92C0;
     const GLenum ATOMIC_COUNTER_BUFFER_BINDING          = 0x92C1;
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
index 66d8748a..f95cbea7 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
@@ -4703,6 +4703,8 @@
       ContextGL()->GetInteger64i_v(target, index, &value);
       return WebGLAny(script_state, value);
     }
+    case GL_MAX_COMPUTE_WORK_GROUP_COUNT:
+    case GL_MAX_COMPUTE_WORK_GROUP_SIZE:
     case GL_ATOMIC_COUNTER_BUFFER_SIZE:
     case GL_ATOMIC_COUNTER_BUFFER_START:
     case GL_SHADER_STORAGE_BUFFER_SIZE:
@@ -4716,6 +4718,7 @@
       ContextGL()->GetInteger64i_v(target, index, &value);
       return WebGLAny(script_state, value);
     }
+
     default:
       SynthesizeGLError(GL_INVALID_ENUM, "getIndexedParameter",
                         "invalid parameter name");
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 545c96f5..a7c6a23 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -3420,7 +3420,15 @@
       ContextGL()->GetProgramiv(ObjectOrZero(program), pname, &value);
       return WebGLAny(script_state, value);
     case GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
-      if (IsWebGL2OrHigher()) {
+      if (!IsWebGL2OrHigher()) {
+        SynthesizeGLError(GL_INVALID_ENUM, "getProgramParameter",
+                          "invalid parameter name");
+        return ScriptValue::CreateNull(script_state);
+      }
+      ContextGL()->GetProgramiv(ObjectOrZero(program), pname, &value);
+      return WebGLAny(script_state, static_cast<unsigned>(value));
+    case GL_ACTIVE_ATOMIC_COUNTER_BUFFERS:
+      if (context_type_ == Platform::kWebGL2ComputeContextType) {
         ContextGL()->GetProgramiv(ObjectOrZero(program), pname, &value);
         return WebGLAny(script_state, static_cast<unsigned>(value));
       }
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string.h b/third_party/blink/renderer/platform/bindings/parkable_string.h
index 30cdf7b..7372323 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string.h
+++ b/third_party/blink/renderer/platform/bindings/parkable_string.h
@@ -126,7 +126,7 @@
   const unsigned length_;
 
 #if DCHECK_IS_ON()
-  const ThreadIdentifier owning_thread_;
+  const base::PlatformThreadId owning_thread_;
 #endif
 
   void AssertOnValidThread() const {
diff --git a/third_party/blink/renderer/platform/bindings/string_resource.h b/third_party/blink/renderer/platform/bindings/string_resource.h
index 7874392..6cd6989f 100644
--- a/third_party/blink/renderer/platform/bindings/string_resource.h
+++ b/third_party/blink/renderer/platform/bindings/string_resource.h
@@ -108,7 +108,7 @@
 
  private:
 #if DCHECK_IS_ON()
-  WTF::ThreadIdentifier thread_id_;
+  base::PlatformThreadId thread_id_;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(StringResourceBase);
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index ff0142b..4e8379a 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -511,10 +511,6 @@
   RuntimeEnabledFeatures::SetBackgroundVideoTrackOptimizationEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableNewRemotePlaybackPipeline(bool enable) {
-  RuntimeEnabledFeatures::SetNewRemotePlaybackPipelineEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableRemotePlaybackAPI(bool enable) {
   RuntimeEnabledFeatures::SetRemotePlaybackEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index cc494d2..197174a0 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -995,17 +995,17 @@
       std::min(resampling, ImageInterpolationQuality()));
 }
 
-void GraphicsContext::DrawTiledImage(Image* image,
-                                     const FloatSize& unsnapped_subset_size,
-                                     const FloatRect& snapped_paint_rect,
-                                     const FloatPoint& unsnapped_phase,
-                                     const FloatSize& tile_size,
-                                     SkBlendMode op,
-                                     const FloatSize& repeat_spacing) {
+void GraphicsContext::DrawImageTiled(Image* image,
+                                     const FloatRect& dest_rect,
+                                     const FloatRect& src_rect,
+                                     const FloatSize& scale_src_to_dest,
+                                     const FloatPoint& phase,
+                                     const FloatSize& repeat_spacing,
+                                     SkBlendMode op) {
   if (ContextDisabled() || !image)
     return;
-  image->DrawTiledBackground(*this, unsnapped_subset_size, snapped_paint_rect,
-                             unsnapped_phase, tile_size, op, repeat_spacing);
+  image->DrawPattern(*this, src_rect, scale_src_to_dest, phase, op, dest_rect,
+                     repeat_spacing);
   paint_controller_.SetImagePainted();
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.h b/third_party/blink/renderer/platform/graphics/graphics_context.h
index f46176a4..a8dcb080 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.h
@@ -223,14 +223,13 @@
       const FloatRect& src_rect,
       SkBlendMode = SkBlendMode::kSrcOver,
       RespectImageOrientationEnum = kDoNotRespectImageOrientation);
-  // Used for background image
-  void DrawTiledImage(Image*,
-                      const FloatSize& unsnapped_subset_size,
-                      const FloatRect& snapped_paint_rect,
-                      const FloatPoint& unsnapped_phase,
-                      const FloatSize& tile_size,
-                      SkBlendMode = SkBlendMode::kSrcOver,
-                      const FloatSize& repeat_spacing = FloatSize());
+  void DrawImageTiled(Image* image,
+                      const FloatRect& dest_rect,
+                      const FloatRect& src_rect,
+                      const FloatSize& scale_src_to_dest,
+                      const FloatPoint& phase,
+                      const FloatSize& repeat_spacing,
+                      SkBlendMode = SkBlendMode::kSrcOver);
   // Used for border image
   void DrawTiledImage(Image*,
                       const FloatRect& dest_rect,
diff --git a/third_party/blink/renderer/platform/graphics/image.cc b/third_party/blink/renderer/platform/graphics/image.cc
index efa5fa09..dba1d08 100644
--- a/third_party/blink/renderer/platform/graphics/image.cc
+++ b/third_party/blink/renderer/platform/graphics/image.cc
@@ -141,66 +141,6 @@
 }
 
 // TODO(schenney): Lift this code, with the calculations for subsetting the
-// image and the like, up the stack into a BackgroundPainter.
-void Image::DrawTiledBackground(GraphicsContext& ctxt,
-                                const FloatSize& unsnapped_subset_size,
-                                const FloatRect& snapped_paint_rect,
-                                const FloatPoint& phase,
-                                const FloatSize& tile_size,
-                                SkBlendMode op,
-                                const FloatSize& repeat_spacing) {
-  if (tile_size.IsEmpty())
-    return;
-
-  // Use the intrinsic size of the image if it has one, otherwise force the
-  // generated image to be the tile size.
-  FloatSize intrinsic_tile_size(Size());
-  FloatSize scale(1, 1);
-  if (HasRelativeSize()) {
-    intrinsic_tile_size.SetWidth(tile_size.Width());
-    intrinsic_tile_size.SetHeight(tile_size.Height());
-  } else {
-    scale = FloatSize(tile_size.Width() / intrinsic_tile_size.Width(),
-                      tile_size.Height() / intrinsic_tile_size.Height());
-  }
-
-  const FloatRect one_tile_rect = ComputePhaseForBackground(
-      snapped_paint_rect.Location(), tile_size, phase, repeat_spacing);
-
-  // Check and see if a single draw of the image can cover the entire area we
-  // are supposed to tile. The dest_rect_for_subset must use the same
-  // location that was used in ComputePhaseForBackground and the unsnapped
-  // destination rect in order to correctly evaluate the subset size and
-  // location in the presence of border snapping and zoom.
-  FloatRect dest_rect_for_subset(snapped_paint_rect.Location(),
-                                 unsnapped_subset_size);
-  if (one_tile_rect.Contains(dest_rect_for_subset)) {
-    FloatRect visible_src_rect = ComputeSubsetForBackground(
-        one_tile_rect, dest_rect_for_subset, intrinsic_tile_size);
-    // Round to avoid filtering pulling in neighboring pixels, for the
-    // common case of sprite maps.
-    // TODO(schenney): Snapping at this level is a problem for cases where we
-    // might be animating background-position to pan over an image. Ideally we
-    // would either snap only if close to integral, or move snapping
-    // calculations up the stack.
-    visible_src_rect = FloatRect(RoundedIntRect(visible_src_rect));
-    ctxt.DrawImage(this, kSyncDecode, snapped_paint_rect, &visible_src_rect, op,
-                   kDoNotRespectImageOrientation);
-    return;
-  }
-
-  // Note that this tile rect the image's pre-scaled size.
-  FloatRect tile_rect(FloatPoint(), intrinsic_tile_size);
-  // This call takes the unscaled image, applies the given scale, and paints
-  // it into the snapped_dest_rect using phase from one_tile_rect and the
-  // given repeat spacing. Note the phase is already scaled.
-  DrawPattern(ctxt, tile_rect, scale, one_tile_rect.Location(), op,
-              snapped_paint_rect, repeat_spacing);
-
-  StartAnimation();
-}
-
-// TODO(schenney): Lift this code, with the calculations for subsetting the
 // image and the like, up the stack into a border painting class.
 void Image::DrawTiledBorder(GraphicsContext& ctxt,
                             const FloatRect& dst_rect,
@@ -295,8 +235,6 @@
     DrawPattern(ctxt, src_rect, tile_scale_factor, pattern_phase, op, dst_rect,
                 spacing);
   }
-
-  StartAnimation();
 }
 
 namespace {
@@ -412,6 +350,8 @@
 
   context.DrawRect(dest_rect, flags);
 
+  StartAnimation();
+
   if (CurrentFrameIsLazyDecoded()) {
     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
                          "Draw LazyPixelRef", TRACE_EVENT_SCOPE_THREAD,
@@ -454,33 +394,6 @@
   return true;
 }
 
-FloatRect Image::ComputePhaseForBackground(const FloatPoint& destination_offset,
-                                           const FloatSize& size,
-                                           const FloatPoint& phase,
-                                           const FloatSize& spacing) {
-  const FloatSize step_per_tile(size + spacing);
-  return FloatRect(
-      FloatPoint(
-          destination_offset.X() + fmodf(-phase.X(), step_per_tile.Width()),
-          destination_offset.Y() + fmodf(-phase.Y(), step_per_tile.Height())),
-      size);
-}
-
-FloatRect Image::ComputeSubsetForBackground(const FloatRect& phase_and_size,
-                                            const FloatRect& subset,
-                                            const FloatSize& intrinsic_size) {
-  // TODO(schenney): Re-enable this after determining why it fails for
-  // CAP, and maybe other cases.
-  // DCHECK(phase_and_size.Contains(subset));
-
-  const FloatSize scale(phase_and_size.Width() / intrinsic_size.Width(),
-                        phase_and_size.Height() / intrinsic_size.Height());
-  return FloatRect((subset.X() - phase_and_size.X()) / scale.Width(),
-                   (subset.Y() - phase_and_size.Y()) / scale.Height(),
-                   subset.Width() / scale.Width(),
-                   subset.Height() / scale.Height());
-}
-
 SkBitmap Image::AsSkBitmapForCurrentFrame(
     RespectImageOrientationEnum should_respect_image_orientation) {
   PaintImage paint_image = PaintImageForCurrentFrame();
diff --git a/third_party/blink/renderer/platform/graphics/image.h b/third_party/blink/renderer/platform/graphics/image.h
index 490c63f..a6e42e8 100644
--- a/third_party/blink/renderer/platform/graphics/image.h
+++ b/third_party/blink/renderer/platform/graphics/image.h
@@ -223,33 +223,6 @@
     return nullptr;
   }
 
-  // Given the |size| that the whole image should draw at, and the
-  // input phase requested by the content, and the space between repeated tiles,
-  // return a rectangle with |size| and a location that respects
-  // the phase but is no more than one size + space in magnitude. In practice,
-  // this means that if there is no repeating the returned rect would contain
-  // the destination_offset location. The destination_offset passed here must
-  // exactly match the location of the subset in a following call to
-  // ComputeSubsetForBackground.
-  static FloatRect ComputePhaseForBackground(
-      const FloatPoint& destination_offset,
-      const FloatSize& size,
-      const FloatPoint& phase,
-      const FloatSize& spacing);
-
-  // Compute the image subset, in intrinsic image coordinates, that gets mapped
-  // onto the |subset|, when the whole image would be drawn with phase
-  // and size given by |phase_and_size|. Assumes
-  // |phase_and_size| contains |subset|. The location
-  // of the requested subset should be the painting snapped location, or
-  // whatever was used as a destination_offset in ComputePhaseForBackground.
-  // It is used to undo the offset added in ComputePhaseForBackground. The size
-  // of requested subset should be the unsnapped size so that the computed
-  // scale and location in the source image can be correctly determined.
-  static FloatRect ComputeSubsetForBackground(const FloatRect& phase_and_size,
-                                              const FloatRect& subset,
-                                              const FloatSize& intrinsic_size);
-
   virtual sk_sp<PaintRecord> PaintRecordForContainer(
       const KURL& url,
       const IntSize& container_size,
@@ -278,22 +251,6 @@
  protected:
   Image(ImageObserver* = nullptr, bool is_multipart = false);
 
-  // The unsnapped_subset_size should be the target painting area implied by the
-  //   content, without any snapping applied. It is necessary to correctly
-  //   compute the subset of the source image to paint into the destination.
-  // The snapped_paint_rect should be the target destination for painting into.
-  // The phase is never snapped.
-  // The tile_size is the total image size. The mapping from this size
-  //   to the unsnapped_dest_rect size defines the scaling of the image for
-  //   sprite computation.
-  void DrawTiledBackground(GraphicsContext&,
-                           const FloatSize& unsnapped_subset_size,
-                           const FloatRect& snapped_paint_rect,
-                           const FloatPoint& phase,
-                           const FloatSize& tile_size,
-                           SkBlendMode,
-                           const FloatSize& repeat_spacing);
-
   void DrawTiledBorder(GraphicsContext&,
                        const FloatRect& dst_rect,
                        const FloatRect& src_rect,
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 00282d6..a7bca2b 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -260,7 +260,7 @@
   bool CheckThread() const { return thread_ == CurrentThread(); }
 
   ThreadHeap& Heap() const { return *heap_; }
-  ThreadIdentifier ThreadId() const { return thread_; }
+  base::PlatformThreadId ThreadId() const { return thread_; }
 
   // When ThreadState is detaching from non-main thread its
   // heap is expected to be empty (because it is going away).
@@ -667,7 +667,7 @@
   static uint8_t main_thread_state_storage_[];
 
   std::unique_ptr<ThreadHeap> heap_;
-  ThreadIdentifier thread_;
+  base::PlatformThreadId thread_;
   std::unique_ptr<PersistentRegion> persistent_region_;
   std::unique_ptr<PersistentRegion> weak_persistent_region_;
   intptr_t* start_of_stack_;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a6cdb4f6..6eaa00ff 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -485,9 +485,7 @@
     },
     {
       name: "FeaturePolicyJavaScriptInterface",
-      implied_by: ["ExperimentalProductivityFeatures"],
-      origin_trial_feature_name: "FeaturePolicyJSAPI",
-      status: "experimental"
+      status: "stable"
     },
     {
       name: "FeaturePolicyReporting",
@@ -1466,7 +1464,7 @@
     },
     {
       name: "WebXR",
-      origin_trial_feature_name: "WebXRDeviceM69",
+      origin_trial_feature_name: "WebXRDeviceM73",
       status: "experimental",
     },
     // Subset of the Gamepad extensions used for early WebXR implementations
diff --git a/third_party/blink/renderer/platform/supplementable.h b/third_party/blink/renderer/platform/supplementable.h
index c88d7238..d0222bf 100644
--- a/third_party/blink/renderer/platform/supplementable.h
+++ b/third_party/blink/renderer/platform/supplementable.h
@@ -222,8 +222,8 @@
 
 #if DCHECK_IS_ON()
  private:
-  ThreadIdentifier attached_thread_id_;
-  ThreadIdentifier creation_thread_id_;
+  base::PlatformThreadId attached_thread_id_;
+  base::PlatformThreadId creation_thread_id_;
 #endif
 };
 
diff --git a/third_party/blink/renderer/platform/timer.h b/third_party/blink/renderer/platform/timer.h
index 92bfbbb1..ae0457d 100644
--- a/third_party/blink/renderer/platform/timer.h
+++ b/third_party/blink/renderer/platform/timer.h
@@ -102,7 +102,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> web_task_runner_;
 
 #if DCHECK_IS_ON()
-  ThreadIdentifier thread_;
+  base::PlatformThreadId thread_;
 #endif
   base::WeakPtrFactory<TimerBase> weak_ptr_factory_;
 
diff --git a/third_party/blink/renderer/platform/wtf/std_lib_extras.h b/third_party/blink/renderer/platform/wtf/std_lib_extras.h
index 4c5bf0e..bf2d8f0b 100644
--- a/third_party/blink/renderer/platform/wtf/std_lib_extras.h
+++ b/third_party/blink/renderer/platform/wtf/std_lib_extras.h
@@ -153,7 +153,7 @@
   InstanceStorage<WrapperType> instance_;
 #if DCHECK_IS_ON()
   bool safely_initialized_;
-  ThreadIdentifier thread_;
+  base::PlatformThreadId thread_;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(StaticSingleton);
diff --git a/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h b/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h
index 6a1dd19..ef2d9a8f 100644
--- a/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h
+++ b/third_party/blink/renderer/platform/wtf/thread_restriction_verifier.h
@@ -99,7 +99,7 @@
 
   bool shared_;
 
-  ThreadIdentifier owning_thread_;
+  base::PlatformThreadId owning_thread_;
 };
 
 }  // namespace WTF
diff --git a/third_party/blink/renderer/platform/wtf/thread_specific.h b/third_party/blink/renderer/platform/wtf/thread_specific.h
index 31a7d75..44179d69 100644
--- a/third_party/blink/renderer/platform/wtf/thread_specific.h
+++ b/third_party/blink/renderer/platform/wtf/thread_specific.h
@@ -87,10 +87,7 @@
   // longer has a graceful shutdown sequence. Be careful to call this function
   // (which can be re-entrant) while the pointer is still set, to avoid lazily
   // allocating WTFThreadData after it is destroyed.
-  // This check cannot be performed using WTF::IsMainThread, as it uses TLS to
-  // store thread information, and we can't rely on the destruction order of
-  // TLS variables.
-  if (CurrentThread() == g_main_thread_identifier)
+  if (IsMainThread())
     return;
 
   // The memory was allocated via Partitions::FastZeroedMalloc, and then the
diff --git a/third_party/blink/renderer/platform/wtf/threading.cc b/third_party/blink/renderer/platform/wtf/threading.cc
index 51dd1b47..07eb8a7 100644
--- a/third_party/blink/renderer/platform/wtf/threading.cc
+++ b/third_party/blink/renderer/platform/wtf/threading.cc
@@ -8,8 +8,8 @@
 
 namespace WTF {
 
-ThreadIdentifier CurrentThread() {
-  thread_local ThreadIdentifier g_id = base::PlatformThread::CurrentId();
+base::PlatformThreadId CurrentThread() {
+  thread_local base::PlatformThreadId g_id = base::PlatformThread::CurrentId();
   return g_id;
 }
 
diff --git a/third_party/blink/renderer/platform/wtf/threading.h b/third_party/blink/renderer/platform/wtf/threading.h
index 4f7aedac..ad6c743 100644
--- a/third_party/blink/renderer/platform/wtf/threading.h
+++ b/third_party/blink/renderer/platform/wtf/threading.h
@@ -40,13 +40,11 @@
 
 namespace WTF {
 
-using ThreadIdentifier = base::PlatformThreadId;
-
 // Initializes global state required by |currentThread|.
 // Needs to be called once during program execution, before |currentThread|.
 WTF_EXPORT void InitializeCurrentThread();
 
-WTF_EXPORT ThreadIdentifier CurrentThread();
+WTF_EXPORT base::PlatformThreadId CurrentThread();
 
 #if DCHECK_IS_ON()
 WTF_EXPORT bool IsBeforeThreadCreated();
@@ -55,7 +53,6 @@
 
 }  // namespace WTF
 
-using WTF::ThreadIdentifier;
 using WTF::CurrentThread;
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_THREADING_H_
diff --git a/third_party/blink/renderer/platform/wtf/wtf.cc b/third_party/blink/renderer/platform/wtf/wtf.cc
index 1d85de8b..e6cfcb8 100644
--- a/third_party/blink/renderer/platform/wtf/wtf.cc
+++ b/third_party/blink/renderer/platform/wtf/wtf.cc
@@ -47,16 +47,7 @@
 
 bool g_initialized;
 void (*g_call_on_main_thread_function)(MainThreadFunction, void*);
-ThreadIdentifier g_main_thread_identifier;
-
-#if defined(COMPONENT_BUILD) && defined(OS_WIN)
-static thread_local bool g_is_main_thread = false;
-bool IsMainThread() {
-  return g_is_main_thread;
-}
-#else
-thread_local bool g_is_main_thread = false;
-#endif
+base::PlatformThreadId g_main_thread_identifier;
 
 namespace internal {
 
@@ -66,13 +57,16 @@
 
 }  // namespace internal
 
+bool IsMainThread() {
+  return CurrentThread() == g_main_thread_identifier;
+}
+
 void Initialize(void (*call_on_main_thread_function)(MainThreadFunction,
                                                      void*)) {
   // WTF, and Blink in general, cannot handle being re-initialized.
   // Make that explicit here.
   CHECK(!g_initialized);
   g_initialized = true;
-  g_is_main_thread = true;
   g_main_thread_identifier = CurrentThread();
 
   WTFThreadData::Initialize();
diff --git a/third_party/blink/renderer/platform/wtf/wtf.h b/third_party/blink/renderer/platform/wtf/wtf.h
index f9a53868..a8f024d6 100644
--- a/third_party/blink/renderer/platform/wtf/wtf.h
+++ b/third_party/blink/renderer/platform/wtf/wtf.h
@@ -31,7 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_H_
 
-#include "build/build_config.h"
 #include "third_party/blink/renderer/platform/wtf/compiler.h"
 #include "third_party/blink/renderer/platform/wtf/threading.h"
 #include "third_party/blink/renderer/platform/wtf/wtf_export.h"
@@ -39,22 +38,12 @@
 namespace WTF {
 
 typedef void MainThreadFunction(void*);
-WTF_EXPORT extern ThreadIdentifier g_main_thread_identifier;
+WTF_EXPORT extern base::PlatformThreadId g_main_thread_identifier;
 
 // This function must be called exactly once from the main thread before using
 // anything else in WTF.
 WTF_EXPORT void Initialize(void (*)(MainThreadFunction, void*));
-
-// thread_local variables can't be exported on Windows, so we use an extra
-// function call on component builds.
-#if defined(COMPONENT_BUILD) && defined(OS_WIN)
 WTF_EXPORT bool IsMainThread();
-#else
-WTF_EXPORT extern thread_local bool g_is_main_thread;
-inline bool IsMainThread() {
-  return g_is_main_thread;
-}
-#endif
 
 namespace internal {
 void CallOnMainThread(MainThreadFunction*, void* context);
diff --git a/third_party/blink/renderer/platform/wtf/wtf_thread_data.h b/third_party/blink/renderer/platform/wtf/wtf_thread_data.h
index 21213473..58e7197 100644
--- a/third_party/blink/renderer/platform/wtf/wtf_thread_data.h
+++ b/third_party/blink/renderer/platform/wtf/wtf_thread_data.h
@@ -54,7 +54,7 @@
 
   ICUConverterWrapper& CachedConverterICU() { return *cached_converter_icu_; }
 
-  ThreadIdentifier ThreadId() const { return thread_id_; }
+  base::PlatformThreadId ThreadId() const { return thread_id_; }
 
   // Must be called on the main thread before any callers to wtfThreadData().
   static void Initialize();
@@ -67,7 +67,7 @@
   std::unique_ptr<AtomicStringTable> atomic_string_table_;
   std::unique_ptr<ICUConverterWrapper> cached_converter_icu_;
 
-  ThreadIdentifier thread_id_;
+  base::PlatformThreadId thread_id_;
 
 #if defined(OS_WIN) && defined(COMPILER_MSVC)
   size_t thread_stack_size_ = 0u;
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 5690db5..a1003bc 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -40,6 +40,7 @@
             'base::MakeRefCounted',
             'base::Optional',
             'base::OptionalOrNullptr',
+            'base::PlatformThreadId',
             'base::RefCountedData',
             'base::RunLoop',
             'base::CreateSequencedTaskRunnerWithTraits',
diff --git a/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_runner_unittest.py b/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_runner_unittest.py
index e7c4bf0..50751bd 100644
--- a/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_runner_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/controllers/web_test_runner_unittest.py
@@ -27,6 +27,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+import sys
 import unittest
 
 from blinkpy.common.host_mock import MockHost
@@ -79,6 +80,9 @@
         self._should_have_http_lock = http_lock
 
 
+# TODO(crbug.com/926841): Debug running this test on Swarming on Windows.
+# Ensure that all child processes are always cleaned up.
+@unittest.skipIf(sys.platform == 'win32', 'may not clean up child processes')
 class WebTestRunnerTests(unittest.TestCase):
 
     # pylint: disable=protected-access
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index fbaee64..fb37c03 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -125,6 +125,10 @@
 # This was disabled by sheriff on Dec 31. Also failing on Win7, will need more investigation.
 crbug.com/891427 [ Mac Win7 ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-listbox.html [ Pass Failure Timeout Crash ]
 
+# Unexpected failures (due to leaks?)
+crbug.com/927454 [ Linux ] external/wpt/pointerevents/pointerevent_pointerleave_descendant_over.html [ Pass Failure ]
+crbug.com/927454 [ Linux ] external/wpt/pointerevents/pointerevent_lostpointercapture_is_first.html [ Pass Failure ]
+
 # ====== Site Isolation failures from here ======
 # See also third_party/blink/web_tests/virtual/not-site-per-process/README.md
 #
@@ -142,7 +146,6 @@
 crbug.com/771003 http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
 crbug.com/771003 virtual/outofblink-cors-ns/http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
 crbug.com/771003 virtual/outofblink-cors/http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
-crbug.com/771003 virtual/feature-policy-for-sandbox/http/tests/security/mixedContent/insecure-iframe-in-main-frame.html [ Failure ]
 
 # Tests temporarily disabled with Site Isolation - known differences in product
 # behavior (either accepted for the long-term, or for the short-term):
@@ -154,7 +157,6 @@
 crbug.com/669083 http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Failure ]
 crbug.com/669083 virtual/outofblink-cors-ns/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Failure ]
 crbug.com/669083 virtual/outofblink-cors/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Failure ]
-crbug.com/669083 virtual/feature-policy-for-sandbox/http/tests/security/frameNavigation/xss-DENIED-top-navigation-without-user-gesture.html [ Failure ]
 crbug.com/886588 external/wpt/dom/events/EventListener-addEventListener.sub.window.html [ Failure ]
 
 # Tests temporarily disabled with Site Isolation - uninvestigated bugs:
@@ -170,7 +172,6 @@
 crbug.com/793127 http/tests/security/upgrade-insecure-requests/iframe-upgrade.https.html [ Crash ]
 crbug.com/793127 virtual/outofblink-cors-ns/http/tests/security/upgrade-insecure-requests/iframe-upgrade.https.html [ Crash ]
 crbug.com/793127 virtual/outofblink-cors/http/tests/security/upgrade-insecure-requests/iframe-upgrade.https.html [ Crash ]
-crbug.com/793127 virtual/feature-policy-for-sandbox/http/tests/security/upgrade-insecure-requests/iframe-upgrade.https.html [ Crash ]
 crbug.com/801992 http/tests/misc/iframe-script-modify-attr.html [ Pass Crash ]
 crbug.com/807675 http/tests/images/image-decode-in-frame.html [ Crash Failure ]
 crbug.com/819800 external/wpt/payment-request/allowpaymentrequest/setting-allowpaymentrequest-timing.https.sub.html [ Failure ]
@@ -1789,26 +1790,10 @@
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_forward_line_br.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_forward_line_range.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_forward_line_small_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_03_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_04_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_17_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_17_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_18_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_18_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_19_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_19_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_20_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_20_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_21_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_21_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_22_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_22_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_23_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_23_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_24_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_24_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_41_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_42_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_right_character_in_mixed_bidi.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_01_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_01_rtl_multi_line.html [ Failure Crash ]
@@ -1828,26 +1813,10 @@
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_08_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_09_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_09_rtl_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_03_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_04_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_17_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_17_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_18_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_18_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_19_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_19_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_20_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_20_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_21_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_21_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_22_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_22_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_23_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_23_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_24_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_24_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_41_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_42_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_01_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_01_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_02_ltr_multi_line.html [ Failure Crash ]
@@ -2057,7 +2026,6 @@
 crbug.com/518883 crbug.com/390452 http/tests/security/isolatedWorld/media-query-wrapper-leaks.html [ Failure Pass Timeout ]
 crbug.com/518883 crbug.com/390452 virtual/outofblink-cors/http/tests/security/isolatedWorld/media-query-wrapper-leaks.html [ Failure Pass Timeout ]
 crbug.com/518883 crbug.com/390452 virtual/outofblink-cors-ns/http/tests/security/isolatedWorld/media-query-wrapper-leaks.html [ Failure Pass Timeout ]
-crbug.com/518883 crbug.com/390452 virtual/feature-policy-for-sandbox/http/tests/security/isolatedWorld/media-query-wrapper-leaks.html [ Failure Pass Timeout ]
 crbug.com/518987 http/tests/xmlhttprequest/navigation-abort-detaches-frame.html [ Pass Timeout ]
 crbug.com/518987 virtual/outofblink-cors/http/tests/xmlhttprequest/navigation-abort-detaches-frame.html [ Pass Timeout ]
 crbug.com/518987 virtual/outofblink-cors-ns/http/tests/xmlhttprequest/navigation-abort-detaches-frame.html [ Pass Timeout ]
@@ -2459,10 +2427,6 @@
 crbug.com/528062 [ Win ] virtual/outofblink-cors-ns/http/tests/security/contentSecurityPolicy/cached-frame-csp.html [ Failure ]
 crbug.com/528062 [ Win ] virtual/outofblink-cors-ns/http/tests/security/xssAuditor/cached-frame.html [ Failure ]
 crbug.com/528062 [ Win ] virtual/outofblink-cors-ns/http/tests/security/xssAuditor/chunked-big-script.html [ Failure ]
-crbug.com/528062 [ Win ] virtual/feature-policy-for-sandbox/http/tests/security/XFrameOptions/x-frame-options-cached.html [ Failure ]
-crbug.com/528062 [ Win ] virtual/feature-policy-for-sandbox/http/tests/security/contentSecurityPolicy/cached-frame-csp.html [ Failure ]
-crbug.com/528062 [ Win ] virtual/feature-policy-for-sandbox/http/tests/security/xssAuditor/cached-frame.html [ Failure ]
-crbug.com/528062 [ Win ] virtual/feature-policy-for-sandbox/http/tests/security/xssAuditor/chunked-big-script.html [ Failure ]
 
 # When drawing subpixel smoothed glyphs, CoreGraphics will fake bold the glyphs.
 # In this configuration, the pixel smoothed glyphs will be created from subpixel smoothed glyphs.
@@ -2744,7 +2708,6 @@
 crbug.com/501659 http/tests/security/xss-DENIED-xml-external-entity.xhtml [ Failure ]
 crbug.com/501659 virtual/outofblink-cors/http/tests/security/xss-DENIED-xml-external-entity.xhtml [ Failure ]
 crbug.com/501659 virtual/outofblink-cors-ns/http/tests/security/xss-DENIED-xml-external-entity.xhtml [ Failure ]
-crbug.com/501659 virtual/feature-policy-for-sandbox/http/tests/security/xss-DENIED-xml-external-entity.xhtml [ Failure ]
 crbug.com/501659 fast/css/stylesheet-candidate-nodes-crash.xhtml [ Failure ]
 
 crbug.com/545140 [ Mac ] fast/encoding/denormalised-voiced-japanese-chars.html [ Failure ]
@@ -2869,7 +2832,6 @@
 crbug.com/763830 http/tests/security/cors-rfc1918/ [ Skip ]
 crbug.com/763830 virtual/outofblink-cors/http/tests/security/cors-rfc1918/ [ Skip ]
 crbug.com/763830 virtual/outofblink-cors-ns/http/tests/security/cors-rfc1918/ [ Skip ]
-crbug.com/763830 virtual/feature-policy-for-sandbox/http/tests/security/cors-rfc1918/ [ Skip ]
 
 crbug.com/831729 external/wpt/event-timing/event-timing-crossiframe.html [ Timeout ]
 crbug.com/831729 external/wpt/event-timing/event-timing-observer-manual.html [ Skip ]
@@ -3881,6 +3843,8 @@
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/the-page/body-margin-2j.html [ Failure ]
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/the-page/body-margin-2k.html [ Failure ]
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/the-page/body-margin-2l.html [ Failure ]
+crbug.com/626703 external/wpt/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html [ Timeout ]
+crbug.com/626703 external/wpt/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-fonts/font-synthesis-01.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-fonts/font-synthesis-02.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-fonts/font-synthesis-03.html [ Failure ]
@@ -4393,7 +4357,6 @@
 crbug.com/610835 http/tests/security/XFrameOptions/x-frame-options-deny-multiple-clients.html [ Failure Pass ]
 crbug.com/610835 virtual/outofblink-cors/http/tests/security/XFrameOptions/x-frame-options-deny-multiple-clients.html [ Failure Pass ]
 crbug.com/610835 virtual/outofblink-cors-ns/http/tests/security/XFrameOptions/x-frame-options-deny-multiple-clients.html [ Failure Pass ]
-crbug.com/610835 virtual/feature-policy-for-sandbox/http/tests/security/XFrameOptions/x-frame-options-deny-multiple-clients.html [ Failure Pass ]
 
 # Added 2016-12-12
 crbug.com/673539 [ Linux ] css3/filters/effect-contrast-hw.html [ Pass Failure ]
@@ -4571,8 +4534,6 @@
 crbug.com/724027 virtual/outofblink-cors/http/tests/security/contentSecurityPolicy/source-list-parsing-04.html [ Skip ]
 crbug.com/724027 virtual/outofblink-cors-ns/http/tests/security/contentSecurityPolicy/directive-parsing-03.html [ Skip ]
 crbug.com/724027 virtual/outofblink-cors-ns/http/tests/security/contentSecurityPolicy/source-list-parsing-04.html [ Skip ]
-crbug.com/724027 virtual/feature-policy-for-sandbox/http/tests/security/contentSecurityPolicy/directive-parsing-03.html [ Skip ]
-crbug.com/724027 virtual/feature-policy-for-sandbox/http/tests/security/contentSecurityPolicy/source-list-parsing-04.html [ Skip ]
 
 # Sheriff failures 2017-05-16
 crbug.com/722212 fast/events/pointerevents/mouse-pointer-event-properties.html [ Failure Timeout Pass ]
@@ -4672,7 +4633,6 @@
 crbug.com/708994 http/tests/security/cross-frame-mouse-source-capabilities.html [ Timeout Pass ]
 crbug.com/708994 virtual/outofblink-cors/http/tests/security/cross-frame-mouse-source-capabilities.html [ Timeout Pass ]
 crbug.com/708994 virtual/outofblink-cors-ns/http/tests/security/cross-frame-mouse-source-capabilities.html [ Timeout Pass ]
-crbug.com/708994 virtual/feature-policy-for-sandbox/http/tests/security/cross-frame-mouse-source-capabilities.html [ Timeout Pass ]
 
 crbug.com/745887 [ Mac ] fast/frames/sandboxed-iframe-plugins.html [ Failure Pass ]
 crbug.com/745887 [ Win ] fast/frames/sandboxed-iframe-plugins.html [ Failure Pass ]
@@ -5874,9 +5834,6 @@
 # Sheriff 2019-01-11
 crbug.com/921038 virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/legacy-performance-memory-counters-enabled.html [ Skip ]
 
-# Fails until document.featurePolicy is available in stable
-crbug.com/917070 virtual/stable/webexposed/feature-policy-features.html [ Failure ]
-
 # Mac doesn't support lowLatency Canvas Contexts.
 crbug.com/922218 [ Mac ] fast/canvas-api/canvas-lowlatency-getContext.html [ Failure ]
 
@@ -6013,19 +5970,6 @@
 # WebXR feature policy feature name in Chrome doesn't match spec.
 crbug.com/924670 external/wpt/webvr/webvr-supported-by-feature-policy.html [ Failure ]
 
-# Feature Policy for Sandbox currently causes these tests to fail.
-crbug.com/926245 virtual/feature-policy-for-sandbox/http/tests/security/contentSecurityPolicy/sandbox-empty-subframe.html [ Failure ]
-crbug.com/926245 virtual/feature-policy-for-sandbox/http/tests/security/contentSecurityPolicy/sandbox-empty.html [ Failure ]
-crbug.com/926245 virtual/feature-policy-for-sandbox/http/tests/security/contentSecurityPolicy/sandbox-in-http-header-control.html [ Failure ]
-crbug.com/926245 virtual/feature-policy-for-sandbox/http/tests/security/contentSecurityPolicy/sandbox-invalid-header.html [ Failure ]
-crbug.com/926247 virtual/feature-policy-for-sandbox/http/tests/navigation/new-window-sandboxed-iframe.html [ Failure ]
-crbug.com/926247 virtual/feature-policy-for-sandbox/http/tests/security/popup-allowed-by-sandbox-is-sandboxed.html [ Failure ]
-crbug.com/926248 virtual/feature-policy-for-sandbox/mhtml/page_with_css_and_js_ie.mht [ Failure ]
-crbug.com/926248 virtual/feature-policy-for-sandbox/mhtml/page_with_css_and_js_unmht.mht [ Failure ]
-crbug.com/926248 virtual/feature-policy-for-sandbox/mhtml/page_with_javascript.mht [ Failure ]
-crbug.com/926248 virtual/feature-policy-for-sandbox/mhtml/transfer_encoding_8bit.mht [ Failure ]
-crbug.com/926249 virtual/feature-policy-for-sandbox/http/tests/security/sandbox-iframe-blocks-modals.php [ Failure ]
-
 # Sheriff 2019-01-25
 crbug.com/925325 [ Mac ] storage/indexeddb/index-population.html [ Pass Failure ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index d3e1231..0c1c5bd 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1013,20 +1013,5 @@
     "prefix": "bidi-caret-affinity",
     "base": "editing/selection/modify_move",
     "args": ["--enable-blink-features=BidiCaretAffinity,EditingNG"]
-  },
-  {
-    "prefix": "feature-policy-for-sandbox",
-    "base": "http/tests/navigation",
-    "args": ["--enable-blink-features=FeaturePolicyForSandbox"]
-  },
-  {
-    "prefix": "feature-policy-for-sandbox",
-    "base": "http/tests/security",
-    "args": ["--enable-blink-features=FeaturePolicyForSandbox"]
-  },
-  {
-    "prefix": "feature-policy-for-sandbox",
-    "base": "mhtml",
-    "args": ["--enable-blink-features=FeaturePolicyForSandbox"]
   }
 ]
diff --git a/third_party/blink/web_tests/editing/input/remove_frame_on_set_composition.html b/third_party/blink/web_tests/editing/input/remove_frame_on_set_composition.html
new file mode 100644
index 0000000..a9358def
--- /dev/null
+++ b/third_party/blink/web_tests/editing/input/remove_frame_on_set_composition.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
+<script>
+selection_test(
+  '<iframe id=child></iframe>',
+  selection => {
+    const child = selection.document.getElementById('child');
+    const childDocument = child.contentDocument;
+    const textarea = childDocument.createElement('textarea');
+    childDocument.body.appendChild(textarea);
+    textarea.addEventListener("input", () => child.remove());
+    textarea.focus();
+
+    assert_own_property(
+        window, 'textInputController',
+        'This test requires textInputController to simulate IME operations');
+    textInputController.setComposition("foo");
+  },
+  '',
+  'SetComposition should not crash when event handler removes frame');
+</script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_03_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_03_rtl.html
index 1dc0c737..2350f12 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_03_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_03_rtl.html
@@ -3,22 +3,30 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|<br>abc</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><br>|abc</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><br>abc|</div>'
+      : '<div contenteditable dir="rtl"><br>|abc</div>',
   '3-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><br>|abc</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><br>ab|c</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><br>|abc</div>'
+      : '<div contenteditable dir="rtl"><br>ab|c</div>',
   '3-1 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><br>a|bc</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><br>abc|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><br>|abc</div>'
+      : '<div contenteditable dir="rtl"><br>abc|</div>',
   '3-2 rtl left character');
 
 selection_test(
@@ -30,6 +38,8 @@
 selection_test(
   '<div contenteditable dir="rtl"><br>abc|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><br>abc|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><br>ab|c</div>'
+      : '<div contenteditable dir="rtl"><br>abc|</div>',
   '3-4 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_04_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_04_ltr.html
index 557b896..44a42da 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_04_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_04_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|<br>\u05D0\u05D1\u05D2</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -12,7 +14,9 @@
 selection_test(
   '<div contenteditable dir="ltr"><br>|\u05D0\u05D1\u05D2</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|<br>\u05D0\u05D1\u05D2</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><br>\u05D0|\u05D1\u05D2</div>'
+      : '<div contenteditable dir="ltr">|<br>\u05D0\u05D1\u05D2</div>',
   '4-1 ltr left character');
 
 selection_test(
@@ -24,12 +28,16 @@
 selection_test(
   '<div contenteditable dir="ltr"><br>\u05D0\u05D1|\u05D2</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><br>|\u05D0\u05D1\u05D2</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><br>\u05D0\u05D1\u05D2|</div>'
+      : '<div contenteditable dir="ltr"><br>|\u05D0\u05D1\u05D2</div>',
   '4-3 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr"><br>\u05D0\u05D1\u05D2|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><br>\u05D0|\u05D1\u05D2</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|<br>\u05D0\u05D1\u05D2</div>'
+      : '<div contenteditable dir="ltr"><br>\u05D0|\u05D1\u05D2</div>',
   '4-4 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_17_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_17_ltr.html
index cf63c58..8e68af5 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_17_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_17_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -48,25 +50,33 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-7 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before  |  \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-8 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before   | \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-9 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    |\u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-10 ltr left character');
 
 selection_test(
@@ -84,19 +94,25 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-13 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-14 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0|\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-15 ltr left character');
 
 selection_test(
@@ -156,12 +172,16 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-25 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0|\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0|\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-26 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_17_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_17_rtl.html
index 5bd1afe..f6a5ded 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_17_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_17_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">befor|e    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">befor|e    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">b|efore    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-1 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_18_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_18_ltr.html
index 815ebd7..097da3b8 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_18_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_18_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">\u05DC|\u05E4\u05E0\u05D9    after encyclopedia</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   '18-0 ltr left character');
 
 selection_test(
@@ -24,7 +28,9 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0|\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   '18-3 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_18_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_18_rtl.html
index 6ef28dc..016ad93 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_18_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_18_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -36,31 +38,41 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
   '18-5 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9  |  after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
   '18-6 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9   | after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
   '18-7 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    |after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
   '18-8 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
   '18-9 rtl left character');
 
 selection_test(
@@ -84,19 +96,25 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
   '18-13 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>',
   '18-14 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after e|ncyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
   '18-15 rtl left character');
 
 selection_test(
@@ -162,6 +180,8 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
   '18-26 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_19_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_19_ltr.html
index 2194fbb..676c5b3 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_19_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_19_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -48,25 +50,33 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-7 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before  |  \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-8 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before   | \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-9 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    |\u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-10 ltr left character');
 
 selection_test(
@@ -84,19 +94,25 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-13 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-14 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0|\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-15 ltr left character');
 
 selection_test(
@@ -156,12 +172,16 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-25 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0|\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0|\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-26 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_19_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_19_rtl.html
index 1b08caf..1f31ae2d 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_19_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_19_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">befor|e    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">befor|e    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">b|efore    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-1 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_20_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_20_ltr.html
index 2b181cc8..6e8bc1b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_20_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_20_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">\u05DC|\u05E4\u05E0\u05D9    after encyclopedia</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   '20-0 ltr left character');
 
 selection_test(
@@ -24,7 +28,9 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0|\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   '20-3 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_20_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_20_rtl.html
index 9ece180..bd47600 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_20_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_20_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -36,31 +38,41 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
   '20-5 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9  |  after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
   '20-6 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9   | after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
   '20-7 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    |after encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
   '20-8 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
   '20-9 rtl left character');
 
 selection_test(
@@ -84,19 +96,25 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
   '20-13 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>',
   '20-14 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after e|ncyclopedia</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
   '20-15 rtl left character');
 
 selection_test(
@@ -162,6 +180,8 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
   '20-26 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_21_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_21_ltr.html
index 6ffa4ea..63beaec7 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_21_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_21_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -54,7 +56,9 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9|\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-8 ltr left character');
 
 selection_test(
@@ -90,19 +94,25 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6|\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-14 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9|\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9|\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-15 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE|\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-16 ltr left character');
 
 selection_test(
@@ -120,7 +130,9 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA|\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-19 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_21_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_21_rtl.html
index 0a3605c..435f069 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_21_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_21_rtl.html
@@ -4,17 +4,22 @@
 <script src="../../assert_selection.js"></script>
 <script>
 const isMac = navigator.platform.indexOf('Mac') === 0;
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 100px;">This i|s \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This i|s \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 100px;">T|his is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-1 rtl left character');
 
 selection_test(
@@ -134,17 +139,23 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 th|e boxes.</div>'
-  : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>',
+  usesBidiAffinity
+      ? (isMac
+          ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>'
+          : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.|</div>')
+      : (isMac
+          ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 th|e boxes.</div>'
+          : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>'),
   '21-21 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 t|he boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>'
-  : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes|.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>'
+      : (isMac
+          ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>'
+          : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes|.</div>'),
   '21-22 rtl left character');
 
 selection_test(
@@ -157,7 +168,9 @@
   '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 th|e boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 th|e boxes.</div>',
   '21-24 rtl left character');
 
@@ -165,7 +178,9 @@
   '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.|</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>',
   '21-25 rtl left character');
 
@@ -173,7 +188,9 @@
   '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the b|oxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes|.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes|.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>',
   '21-26 rtl left character');
 
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_22_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_22_ltr.html
index d8529378..f006a6b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_22_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_22_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -54,7 +56,9 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9|\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-8 ltr left character');
 
 selection_test(
@@ -90,19 +94,25 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6|\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-14 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9|\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9|\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-15 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE|\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-16 ltr left character');
 
 selection_test(
@@ -120,7 +130,9 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA|\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-19 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_22_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_22_rtl.html
index b22d368..f83df320 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_22_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_22_rtl.html
@@ -4,17 +4,22 @@
 <script src="../../assert_selection.js"></script>
 <script>
 const isMac = navigator.platform.indexOf('Mac') === 0;
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 100px;">This i|s \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This i|s \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">T|his is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-1 rtl left character');
 
 selection_test(
@@ -134,17 +139,23 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 th|e boxes.</div>'
-  : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>',
+  usesBidiAffinity
+      ? (isMac
+          ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>'
+          : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.|</div>')
+      : (isMac
+          ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 th|e boxes.</div>'
+          : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>'),
   '22-21 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 t|he boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
-  isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>'
-  : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes|.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>'
+      : (isMac
+          ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>'
+          : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes|.</div>'),
   '22-22 rtl left character');
 
 selection_test(
@@ -157,7 +168,9 @@
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 th|e boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 th|e boxes.</div>',
   '22-24 rtl left character');
 
@@ -165,7 +178,9 @@
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.|</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>',
   '22-25 rtl left character');
 
@@ -173,7 +188,9 @@
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the b|oxes.</div>',
   selection => selection.modify('move', 'left', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes|.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes|.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>',
   '22-26 rtl left character');
 
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_41_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_41_ltr.html
index b47614cc..dae274a 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_41_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_41_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">|abc<!-- -->\n<!-- -->def</div>',
   selection => selection.modify('move', 'left', 'character'),
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_41_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_41_rtl.html
index 48e1ff9..4f92a6a7 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_41_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_41_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">|abc<!-- -->\n<!-- -->def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">ab|c \n def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc \n def|</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">ab|c \n def</div>',
   '41-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">a|bc<!-- -->\n<!-- -->def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc| \n def</div>',
+  usesBidiAffinity
+      ?  '<div contenteditable dir="rtl" style="white-space: pre;">|abc \n def</div>'
+      :  '<div contenteditable dir="rtl" style="white-space: pre;">abc| \n def</div>',
   '41-1 rtl left character');
 
 selection_test(
@@ -24,31 +30,41 @@
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">abc|<!-- -->\n<!-- -->def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc \n |def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc \n def|</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">abc \n |def</div>',
   '41-3 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">abc<!-- -->|\n<!-- -->def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc \n |def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc \n def|</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">abc \n |def</div>',
   '41-4 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">abc<!-- -->\n|<!-- -->def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc \n de|f</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc \n| def</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">abc \n de|f</div>',
   '41-5 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">abc<!-- -->\n<!-- -->|def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc \n de|f</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc \n |def</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">abc \n de|f</div>',
   '41-6 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">abc<!-- -->\n<!-- -->d|ef</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc \n def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc \n |def</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">abc \n def|</div>',
   '41-7 rtl left character');
 
 selection_test(
@@ -60,6 +76,8 @@
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">abc<!-- -->\n<!-- -->def|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc \n def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc \n de|f</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">abc \n def|</div>',
   '41-9 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_42_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_42_ltr.html
index d925b1f..143d2c4 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_42_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_42_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">|\u05D0\u05D1\u05D2<!-- -->\n<!-- -->\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">|\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0|\u05D1\u05D2 \n \u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">|\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5</div>',
   '42-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1|\u05D2<!-- -->\n<!-- -->\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">|\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2| \n \u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">|\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5</div>',
   '42-2 ltr left character');
 
 selection_test(
@@ -36,13 +42,17 @@
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2<!-- -->\n|<!-- -->\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2| \n \u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3|\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2| \n \u05D3\u05D4\u05D5</div>',
   '42-5 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2<!-- -->\n<!-- -->|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2| \n \u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3|\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2| \n \u05D3\u05D4\u05D5</div>',
   '42-6 ltr left character');
 
 selection_test(
@@ -54,12 +64,16 @@
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2<!-- -->\n<!-- -->\u05D3\u05D4|\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n |\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5|</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n |\u05D3\u05D4\u05D5</div>',
   '42-8 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2<!-- -->\n<!-- -->\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3|\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2| \n \u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3|\u05D4\u05D5</div>',
   '42-9 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_42_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_42_rtl.html
index f55d5ba..28376ae 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_42_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_42_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">|\u05D0\u05D1\u05D2<!-- -->\n<!-- -->\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_03_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_03_rtl.html
index ff83714..6c9b03cb 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_03_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_03_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|<br>abc</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -12,7 +14,9 @@
 selection_test(
   '<div contenteditable dir="rtl"><br>|abc</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|<br>abc</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><br>a|bc</div>'
+      : '<div contenteditable dir="rtl">|<br>abc</div>',
   '3-1 rtl right character');
 
 selection_test(
@@ -24,12 +28,16 @@
 selection_test(
   '<div contenteditable dir="rtl"><br>ab|c</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><br>|abc</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><br>abc|</div>'
+      : '<div contenteditable dir="rtl"><br>|abc</div>',
   '3-3 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl"><br>abc|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><br>a|bc</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|<br>abc</div>'
+      : '<div contenteditable dir="rtl"><br>a|bc</div>',
   '3-4 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_04_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_04_ltr.html
index 7ff29cb..d5a704c1 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_04_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_04_ltr.html
@@ -3,22 +3,30 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|<br>\u05D0\u05D1\u05D2</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><br>|\u05D0\u05D1\u05D2</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><br>\u05D0\u05D1\u05D2|</div>'
+      : '<div contenteditable dir="ltr"><br>|\u05D0\u05D1\u05D2</div>',
   '4-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><br>|\u05D0\u05D1\u05D2</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><br>\u05D0\u05D1|\u05D2</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><br>|\u05D0\u05D1\u05D2</div>'
+      : '<div contenteditable dir="ltr"><br>\u05D0\u05D1|\u05D2</div>',
   '4-1 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><br>\u05D0|\u05D1\u05D2</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><br>\u05D0\u05D1\u05D2|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><br>|\u05D0\u05D1\u05D2</div>'
+      : '<div contenteditable dir="ltr"><br>\u05D0\u05D1\u05D2|</div>',
   '4-2 ltr right character');
 
 selection_test(
@@ -30,6 +38,8 @@
 selection_test(
   '<div contenteditable dir="ltr"><br>\u05D0\u05D1\u05D2|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><br>\u05D0\u05D1\u05D2|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><br>\u05D0\u05D1|\u05D2</div>'
+      : '<div contenteditable dir="ltr"><br>\u05D0\u05D1\u05D2|</div>',
   '4-4 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_17_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_17_ltr.html
index 2ade4e5..7b1217d 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_17_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_17_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -48,31 +50,41 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-7 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before  |  \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-8 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before   | \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-9 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    |\u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-10 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-11 ltr right character');
 
 selection_test(
@@ -90,19 +102,25 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-14 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>',
   '17-15 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0|\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
   '17-16 ltr right character');
 
 selection_test(
@@ -162,6 +180,8 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
   '17-26 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_17_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_17_rtl.html
index d32b488..b783dcd 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_17_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_17_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">b|efore    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-0 rtl right character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">befor|e    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '17-5 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_18_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_18_ltr.html
index 1a8d244..ecd04a59 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_18_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_18_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0|\u05D9    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0|\u05D9    after encyclopedia</div>',
   '18-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 120px;">\u05DC|\u05E4\u05E0\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
   '18-1 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_18_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_18_rtl.html
index c3b74718..5cb9e0a 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_18_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_18_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -36,25 +38,33 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
   '18-5 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9  |  after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
   '18-6 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9   | after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
   '18-7 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    |after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
   '18-8 rtl right character');
 
 selection_test(
@@ -78,19 +88,25 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>',
   '18-12 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>',
   '18-13 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after e|ncyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
   '18-14 rtl right character');
 
 selection_test(
@@ -156,12 +172,16 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
   '18-25 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after e|ncyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after e|ncyclopedia</div>',
   '18-26 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_19_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_19_ltr.html
index 3954c06..b442a94 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_19_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_19_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -48,31 +50,41 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-7 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before  |  \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-8 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before   | \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-9 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    |\u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-10 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    \u05D0|\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before |   \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-11 ltr right character');
 
 selection_test(
@@ -90,19 +102,25 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9| \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8|\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-14 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>',
   '19-15 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0|\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 |\u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
   '19-16 ltr right character');
 
 selection_test(
@@ -162,6 +180,8 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9|\u05D4</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4|</div>',
   '19-26 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_19_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_19_rtl.html
index cc423bbf..ded2aab 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_19_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_19_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">b|efore    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-0 rtl right character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">befor|e    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">before|    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">|before    \u05D0\u05D7\u05E8\u05D9 \u05D0\u05E0\u05E6\u05D9\u05E7\u05DC\u05D5\u05E4\u05D3\u05D9\u05D4</div>',
   '19-5 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_20_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_20_ltr.html
index cae48c1..bdc5128 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_20_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_20_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0|\u05D9    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0|\u05D9    after encyclopedia</div>',
   '20-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 120px;">\u05DC|\u05E4\u05E0\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>'
+      : '<div contenteditable dir="ltr" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
   '20-1 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_20_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_20_rtl.html
index d303e55..e91c37e 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_20_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_20_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">|\u05DC\u05E4\u05E0\u05D9    after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -36,25 +38,33 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
   '20-5 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9  |  after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
   '20-6 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9   | after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
   '20-7 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    |after encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>',
   '20-8 rtl right character');
 
 selection_test(
@@ -78,19 +88,25 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    afte|r encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9 |   after encyclopedia</div>',
   '20-12 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9|    after encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>',
   '20-13 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after e|ncyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after| encyclopedia</div>',
   '20-14 rtl right character');
 
 selection_test(
@@ -156,12 +172,16 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedi|a</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after |encyclopedia</div>',
   '20-25 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after encyclopedia|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after e|ncyclopedia</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    a|fter encyclopedia</div>'
+      : '<div contenteditable dir="rtl" style="width: 120px;">\u05DC\u05E4\u05E0\u05D9    after e|ncyclopedia</div>',
   '20-26 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_21_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_21_ltr.html
index cba98b84..f487061 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_21_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_21_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -54,13 +56,17 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6|\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA|\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6|\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-8 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9|\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-9 ltr right character');
 
 selection_test(
@@ -96,19 +102,25 @@
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6|\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-15 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA|\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA|\u05E8 the boxes.</div>',
   '21-16 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE|\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>',
   '21-17 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_21_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_21_rtl.html
index 487a33c..70795fa8 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_21_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_21_rtl.html
@@ -4,11 +4,14 @@
 <script src="../../assert_selection.js"></script>
 <script>
 const isMac = navigator.platform.indexOf('Mac') === 0;
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
 
 selection_test(
   '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">T|his is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-0 rtl right character');
 
 selection_test(
@@ -44,7 +47,9 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 100px;">This i|s \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '21-6 rtl right character');
 
 selection_test(
@@ -134,7 +139,9 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 t|he boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>',
   '21-21 rtl right character');
 
 selection_test(
@@ -147,7 +154,9 @@
   '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 th|e boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>',
   '21-23 rtl right character');
 
@@ -155,7 +164,9 @@
   '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 t|he boxes.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 t|he boxes.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>',
   '21-24 rtl right character');
 
@@ -163,7 +174,9 @@
   '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the b|oxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the b|oxes.</div>',
   '21-25 rtl right character');
 
@@ -188,9 +201,11 @@
 selection_test(
   '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>'
-  : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes|.</div>'
+      : (isMac
+          ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>'
+          : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>'),
   '21-29 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_22_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_22_ltr.html
index c297174d..2ed70af 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_22_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_22_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -54,13 +56,17 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6|\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA|\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6|\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-8 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">This is \u05D9|\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is |\u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-9 ltr right character');
 
 selection_test(
@@ -96,19 +102,25 @@
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8| \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6|\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-15 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA|\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA|\u05E8 the boxes.</div>',
   '22-16 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE|\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 |\u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="ltr" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>',
   '22-17 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_22_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_22_rtl.html
index 53a53ef..7780052 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_22_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_22_rtl.html
@@ -4,11 +4,14 @@
 <script src="../../assert_selection.js"></script>
 <script>
 const isMac = navigator.platform.indexOf('Mac') === 0;
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
 
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">T|his is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-0 rtl right character');
 
 selection_test(
@@ -44,7 +47,9 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This i|s \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is| \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">|This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes.</div>',
   '22-6 rtl right character');
 
 selection_test(
@@ -134,7 +139,9 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 t|he boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>',
   '22-21 rtl right character');
 
 selection_test(
@@ -147,7 +154,9 @@
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 th|e boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>',
   '22-23 rtl right character');
 
@@ -155,7 +164,9 @@
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 t|he boxes.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8| the boxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 t|he boxes.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>',
   '22-24 rtl right character');
 
@@ -163,7 +174,9 @@
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>',
   selection => selection.modify('move', 'right', 'character'),
   isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>'
+  ? (usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the b|oxes.</div>'
+      : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the| boxes.</div>')
   : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the b|oxes.</div>',
   '22-25 rtl right character');
 
@@ -188,9 +201,11 @@
 selection_test(
   '<div contenteditable dir="rtl" contenteditable style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxe|s.</div>',
   selection => selection.modify('move', 'right', 'character'),
-  isMac
-  ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>'
-  : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the boxes|.</div>'
+      : (isMac
+          ? '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 the |boxes.</div>'
+          : '<div contenteditable dir="rtl" style="width: 100px;">This is \u05D9\u05D5\u05EA\u05E8 \u05E6\u05E8 \u05DE\u05D9\u05EA\u05E8 |the boxes.</div>'),
   '22-29 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_41_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_41_ltr.html
index f418457..11be489 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_41_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_41_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">|abc<!-- -->\n<!-- -->def</div>',
   selection => selection.modify('move', 'right', 'character'),
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_41_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_41_rtl.html
index c9023032..d13a0c8 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_41_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_41_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">|abc<!-- -->\n<!-- -->def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">|abc \n def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">a|bc \n def</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">|abc \n def</div>',
   '41-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">ab|c<!-- -->\n<!-- -->def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">|abc \n def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc| \n def</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">|abc \n def</div>',
   '41-2 rtl right character');
 
 selection_test(
@@ -36,13 +42,17 @@
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">abc<!-- -->\n|<!-- -->def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc| \n def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc \n d|ef</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">abc| \n def</div>',
   '41-5 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">abc<!-- -->\n<!-- -->|def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc| \n def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc \n d|ef</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">abc| \n def</div>',
   '41-6 rtl right character');
 
 selection_test(
@@ -54,12 +64,16 @@
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">abc<!-- -->\n<!-- -->de|f</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc \n |def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc \n def|</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">abc \n |def</div>',
   '41-8 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">abc<!-- -->\n<!-- -->def|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl" style="white-space: pre;">abc \n d|ef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl" style="white-space: pre;">abc| \n def</div>'
+      : '<div contenteditable dir="rtl" style="white-space: pre;">abc \n d|ef</div>',
   '41-9 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_42_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_42_ltr.html
index 482bfc6..b9aa4dbd 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_42_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_42_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">|\u05D0\u05D1\u05D2<!-- -->\n<!-- -->\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1|\u05D2 \n \u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5|</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1|\u05D2 \n \u05D3\u05D4\u05D5</div>',
   '42-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0|\u05D1\u05D2<!-- -->\n<!-- -->\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2| \n \u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">|\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2| \n \u05D3\u05D4\u05D5</div>',
   '42-1 ltr right character');
 
 selection_test(
@@ -24,31 +30,41 @@
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2|<!-- -->\n<!-- -->\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n |\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5|</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n |\u05D3\u05D4\u05D5</div>',
   '42-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2<!-- -->|\n<!-- -->\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n |\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5|</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n |\u05D3\u05D4\u05D5</div>',
   '42-4 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2<!-- -->\n|<!-- -->\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4|\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n| \u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4|\u05D5</div>',
   '42-5 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2<!-- -->\n<!-- -->|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4|\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n |\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4|\u05D5</div>',
   '42-6 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2<!-- -->\n<!-- -->\u05D3|\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n |\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5|</div>',
   '42-7 ltr right character');
 
 selection_test(
@@ -60,6 +76,8 @@
 selection_test(
   '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2<!-- -->\n<!-- -->\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4|\u05D5</div>'
+      : '<div contenteditable dir="ltr" style="white-space: pre;">\u05D0\u05D1\u05D2 \n \u05D3\u05D4\u05D5|</div>',
   '42-9 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_42_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_42_rtl.html
index 795de77..cd09342 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_42_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_42_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl" style="white-space: pre;">|\u05D0\u05D1\u05D2<!-- -->\n<!-- -->\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index 7aa93ae..cc638a3f 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -34703,6 +34703,18 @@
      {}
     ]
    ],
+   "css/css-backgrounds/child-move-reveals-parent-background.html": [
+    [
+     "/css/css-backgrounds/child-move-reveals-parent-background.html",
+     [
+      [
+       "/css/css-backgrounds/child-move-reveals-parent-background-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-backgrounds/css-border-radius-001.html": [
     [
      "/css/css-backgrounds/css-border-radius-001.html",
@@ -125794,6 +125806,11 @@
      {}
     ]
    ],
+   "css/css-backgrounds/child-move-reveals-parent-background-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-backgrounds/justfortest.html": [
     [
      {}
@@ -184619,6 +184636,26 @@
      {}
     ]
    ],
+   "streams/writable-streams/aborting.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "streams/writable-streams/aborting.any.serviceworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "streams/writable-streams/aborting.any.sharedworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "streams/writable-streams/aborting.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "streams/writable-streams/properties.any-expected.txt": [
     [
      {}
@@ -186449,117 +186486,117 @@
      {}
     ]
    ],
-   "wasm/serialization/incrementer.wasm": [
+   "wasm/serialization/module/incrementer.wasm": [
     [
      {}
     ]
    ],
-   "wasm/serialization/nested-worker-success-sharedworker-expected.txt": [
+   "wasm/serialization/module/nested-worker-success-sharedworker-expected.txt": [
     [
      {}
     ]
    ],
-   "wasm/serialization/nested-worker-success.any.sharedworker-expected.txt": [
+   "wasm/serialization/module/nested-worker-success.any.sharedworker-expected.txt": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/blank.html": [
+   "wasm/serialization/module/resources/blank.html": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/broadcastchannel-iframe.html": [
+   "wasm/serialization/module/resources/broadcastchannel-iframe.html": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/broadcastchannel-sharedworker.js": [
+   "wasm/serialization/module/resources/broadcastchannel-sharedworker.js": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/broadcastchannel-worker.js": [
+   "wasm/serialization/module/resources/broadcastchannel-worker.js": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/create-empty-wasm-module.js": [
+   "wasm/serialization/module/resources/create-empty-wasm-module.js": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/echo-iframe.html": [
+   "wasm/serialization/module/resources/echo-iframe.html": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/echo-worker.js": [
+   "wasm/serialization/module/resources/echo-worker.js": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/incrementer-iframe-domain.sub.html": [
+   "wasm/serialization/module/resources/incrementer-iframe-domain.sub.html": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/incrementer-iframe.html": [
+   "wasm/serialization/module/resources/incrementer-iframe.html": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/incrementer-popup.html": [
+   "wasm/serialization/module/resources/incrementer-popup.html": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/incrementer-worker-with-channel.js": [
+   "wasm/serialization/module/resources/incrementer-worker-with-channel.js": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/incrementer-worker.js": [
+   "wasm/serialization/module/resources/incrementer-worker.js": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/incrementer.wasm": [
+   "wasm/serialization/module/resources/incrementer.wasm": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/nested-iframe-1.html": [
+   "wasm/serialization/module/resources/nested-iframe-1.html": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/nested-iframe-2.html": [
+   "wasm/serialization/module/resources/nested-iframe-2.html": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/nested-iframe-3.html": [
+   "wasm/serialization/module/resources/nested-iframe-3.html": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/nested-iframe-4-incrementer.html": [
+   "wasm/serialization/module/resources/nested-iframe-4-incrementer.html": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/serviceworker-failure.js": [
+   "wasm/serialization/module/resources/serviceworker-failure.js": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/sharedworker-failure.js": [
+   "wasm/serialization/module/resources/sharedworker-failure.js": [
     [
      {}
     ]
    ],
-   "wasm/serialization/resources/test-incrementer.js": [
+   "wasm/serialization/module/resources/test-incrementer.js": [
     [
      {}
     ]
@@ -238881,6 +238918,18 @@
      {}
     ]
    ],
+   "html/semantics/document-metadata/the-link-element/link-multiple-error-events.html": [
+    [
+     "/html/semantics/document-metadata/the-link-element/link-multiple-error-events.html",
+     {}
+    ]
+   ],
+   "html/semantics/document-metadata/the-link-element/link-multiple-load-events.html": [
+    [
+     "/html/semantics/document-metadata/the-link-element/link-multiple-load-events.html",
+     {}
+    ]
+   ],
    "html/semantics/document-metadata/the-link-element/link-rel-attribute.html": [
     [
      "/html/semantics/document-metadata/the-link-element/link-rel-attribute.html",
@@ -281517,99 +281566,99 @@
      {}
     ]
    ],
-   "wasm/serialization/broadcastchannel-success-and-failure.html": [
+   "wasm/serialization/module/broadcastchannel-success-and-failure.html": [
     [
-     "/wasm/serialization/broadcastchannel-success-and-failure.html",
+     "/wasm/serialization/module/broadcastchannel-success-and-failure.html",
      {}
     ]
    ],
-   "wasm/serialization/broadcastchannel-success.html": [
+   "wasm/serialization/module/broadcastchannel-success.html": [
     [
-     "/wasm/serialization/broadcastchannel-success.html",
+     "/wasm/serialization/module/broadcastchannel-success.html",
      {}
     ]
    ],
-   "wasm/serialization/identity-not-preserved.html": [
+   "wasm/serialization/module/identity-not-preserved.html": [
     [
-     "/wasm/serialization/identity-not-preserved.html",
+     "/wasm/serialization/module/identity-not-preserved.html",
      {}
     ]
    ],
-   "wasm/serialization/nested-worker-success.any.js": [
+   "wasm/serialization/module/nested-worker-success.any.js": [
     [
-     "/wasm/serialization/nested-worker-success.any.sharedworker.html",
+     "/wasm/serialization/module/nested-worker-success.any.sharedworker.html",
      {}
     ],
     [
-     "/wasm/serialization/nested-worker-success.any.worker.html",
+     "/wasm/serialization/module/nested-worker-success.any.worker.html",
      {}
     ]
    ],
-   "wasm/serialization/no-transferring.html": [
+   "wasm/serialization/module/no-transferring.html": [
     [
-     "/wasm/serialization/no-transferring.html",
+     "/wasm/serialization/module/no-transferring.html",
      {}
     ]
    ],
-   "wasm/serialization/serialization-via-history.html": [
+   "wasm/serialization/module/serialization-via-history.html": [
     [
-     "/wasm/serialization/serialization-via-history.html",
+     "/wasm/serialization/module/serialization-via-history.html",
      {}
     ]
    ],
-   "wasm/serialization/serialization-via-idb.any.js": [
+   "wasm/serialization/module/serialization-via-idb.any.js": [
     [
-     "/wasm/serialization/serialization-via-idb.any.html",
+     "/wasm/serialization/module/serialization-via-idb.any.html",
      {}
     ],
     [
-     "/wasm/serialization/serialization-via-idb.any.worker.html",
+     "/wasm/serialization/module/serialization-via-idb.any.worker.html",
      {}
     ]
    ],
-   "wasm/serialization/serialization-via-notifications-api.any.js": [
+   "wasm/serialization/module/serialization-via-notifications-api.any.js": [
     [
-     "/wasm/serialization/serialization-via-notifications-api.any.html",
+     "/wasm/serialization/module/serialization-via-notifications-api.any.html",
      {}
     ],
     [
-     "/wasm/serialization/serialization-via-notifications-api.any.worker.html",
+     "/wasm/serialization/module/serialization-via-notifications-api.any.worker.html",
      {}
     ]
    ],
-   "wasm/serialization/window-domain-success.sub.html": [
+   "wasm/serialization/module/window-domain-success.sub.html": [
     [
-     "/wasm/serialization/window-domain-success.sub.html",
+     "/wasm/serialization/module/window-domain-success.sub.html",
      {}
     ]
    ],
-   "wasm/serialization/window-messagechannel-success.html": [
+   "wasm/serialization/module/window-messagechannel-success.html": [
     [
-     "/wasm/serialization/window-messagechannel-success.html",
+     "/wasm/serialization/module/window-messagechannel-success.html",
      {}
     ]
    ],
-   "wasm/serialization/window-serviceworker-failure.https.html": [
+   "wasm/serialization/module/window-serviceworker-failure.https.html": [
     [
-     "/wasm/serialization/window-serviceworker-failure.https.html",
+     "/wasm/serialization/module/window-serviceworker-failure.https.html",
      {}
     ]
    ],
-   "wasm/serialization/window-sharedworker-failure.html": [
+   "wasm/serialization/module/window-sharedworker-failure.html": [
     [
-     "/wasm/serialization/window-sharedworker-failure.html",
+     "/wasm/serialization/module/window-sharedworker-failure.html",
      {}
     ]
    ],
-   "wasm/serialization/window-similar-but-cross-origin-success.sub.html": [
+   "wasm/serialization/module/window-similar-but-cross-origin-success.sub.html": [
     [
-     "/wasm/serialization/window-similar-but-cross-origin-success.sub.html",
+     "/wasm/serialization/module/window-similar-but-cross-origin-success.sub.html",
      {}
     ]
    ],
-   "wasm/serialization/window-simple-success.html": [
+   "wasm/serialization/module/window-simple-success.html": [
     [
-     "/wasm/serialization/window-simple-success.html",
+     "/wasm/serialization/module/window-simple-success.html",
      {}
     ]
    ],
@@ -330738,6 +330787,14 @@
    "8a48fe357b9f4cab09d7e59e28f0560cf2235e41",
    "support"
   ],
+  "css/css-backgrounds/child-move-reveals-parent-background-ref.html": [
+   "10324966edb042c1c7298ce22dad76766c2a777b",
+   "support"
+  ],
+  "css/css-backgrounds/child-move-reveals-parent-background.html": [
+   "e369eccd07f3c7c4146b1a419d5b110ff6d0eb7c",
+   "reftest"
+  ],
   "css/css-backgrounds/color-behind-images.htm": [
    "170380aa8e12ad2685e31d507851de352b90d1f8",
    "visual"
@@ -334403,7 +334460,7 @@
    "testharness"
   ],
   "css/css-env/supports-script.tentative.html": [
-   "ec9b6d0dfe61f91d8e8869e71cd170f45c6631f3",
+   "7ab4db23a373ac5cea9e217b2dc3b15fab12fbb0",
    "testharness"
   ],
   "css/css-env/syntax.tentative.html": [
@@ -358271,39 +358328,39 @@
    "reftest"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-cj-loose.html": [
-   "cf7c5a44415f3514598bbf386ac6fadebd63a2c5",
+   "def6adf0da7d6e35a7a88d086d3df19cfaca7b3e",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-cj-normal.html": [
-   "7c701c8546d3d057d9fdf65c12bd4a43ac68dc64",
+   "bc204f5377d9a8477e81d7215148612f4805ddc8",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-cj-strict.html": [
-   "9fe18046fcd897eb3c7acd66785e9d8afea7075c",
+   "cdb8398248238c6266c883db8aeaaa243013694a",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-cpm-loose.html": [
-   "6120e62cc9b39b0197b171b51b6e45937f5a1684",
+   "c932cb4fa3f5b01284f4e4ae2b0fc0637d0b12a5",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-cpm-normal.html": [
-   "1e1d4bb683b5c87d609418640d6bd000f2545d9c",
+   "7816fb53b0bca5cf52672802fb005be89c9f3b8b",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-cpm-strict.html": [
-   "ddf3c2cc9609e650a5f3816a61a5eb6acd71633d",
+   "833e45e6b58ac2aee90967a706a3ae82d122a224",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-hyphens-loose.html": [
-   "647bfc8a675942d299647c32e3730fe61d2afa33",
+   "fbb13ec8c34e43e57273e3b826598f9cfaa84ef2",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-hyphens-normal.html": [
-   "5e22458f9351669b38d90632f475ca25682db2bb",
+   "dc22d6161f08ed57baec7ed41f0f62cb1222327e",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-hyphens-strict.html": [
-   "3db8bb8804daade32849334e21cea38e758671fa",
+   "cd5d5aa939535f5b52f579c35e2bfa1730918875",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-in-loose-expected.txt": [
@@ -358311,39 +358368,39 @@
    "support"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-in-loose.html": [
-   "09c5758d79aa386817ac2e7e6c7a162399f8790d",
+   "235d38177cbb91cf6f6f3f4d512593516409f770",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-in-normal.html": [
-   "8bf3aa9016d1f213f340a5c4a7fa0a09a8b6ea7d",
+   "7bdedd5a82f8ce37cb2ea57f24efd9058eebf8ef",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-in-strict.html": [
-   "3c14e183d84bc048954a9474b07aae01e87f12b9",
+   "58c05bb0868a0154c8caabb9bb744786c59a527e",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-iteration-loose.html": [
-   "dbf77a0f806410c8d6fb95c3ce260a57c5716a21",
+   "49f51f4b7d29107a20b31d81131459cf86d84d9d",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-iteration-normal.html": [
-   "ecdcdc31733b3b765fbb087a5c22c1dfde98531a",
+   "55f54ea84b193a9595287baa12231c7fa3ae754d",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-iteration-strict.html": [
-   "3b2a2ee9eb19b49c17c3ead23cdae9d84ef36961",
+   "1c2cd9a5d430fbc3df261b107c47614477f52fae",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-po-loose.html": [
-   "795cb0230b6eaed306a621a7b9ef7c2cb626a04f",
+   "4d79a1227801f47b317d5b18e117b38a8bc65d86",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-po-normal.html": [
-   "7c591eb936a13a8a4de8a83ec5d40fe07de183b2",
+   "eb3003c27212b637df0b5f390447d5c3ee62928b",
    "testharness"
   ],
   "css/css-text/i18n/ja/css-text-line-break-ja-po-strict.html": [
-   "c41ea732a8919954ebd77fe76c883f9e6379f08e",
+   "08e16f26321bab7ca03f2cfe6eddd0c324d765dc",
    "testharness"
   ],
   "css/css-text/i18n/reference/css3-text-line-break-opclns-001-ref.html": [
@@ -359063,27 +359120,27 @@
    "support"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-cpm-loose.html": [
-   "b9f81eeecf5b0341da049f6f1b0eabec29b4f601",
+   "4044e10666d15573fa4d7b3d455b956895eae19e",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-cpm-normal.html": [
-   "7d2af701422cad0e054aead632ae24c2dae9ffe9",
+   "01096b26bae5e195788d6f87f55bb267c73cd938",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-cpm-strict.html": [
-   "069212e628e376276159795c1da4335c76e6f141",
+   "8460aaa0de7137999b9f2ca89435d9e71ccb6902",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-hyphens-loose.html": [
-   "70ebdc0898b19e8af303f2a7c3769f8305946ca6",
+   "724f77692a47b6c3fa9d97955cd5f1f1ce446924",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-hyphens-normal.html": [
-   "8fd631efb3f2dcad9a44fbef8fb722a27dc1cd25",
+   "e89c9b8b85cf4a5f404965e777d4bfeeb9a98186",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-hyphens-strict.html": [
-   "91f261018b174d352a50829cc9e830201893d660",
+   "b9585d77aba7c830fc95ecaab51115931acf85dd",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-in-loose-expected.txt": [
@@ -359091,39 +359148,39 @@
    "support"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-in-loose.html": [
-   "4f163719ae7ef7d95d360c68db9e84da660bef5b",
+   "e7686617a0a893f2280efe794095776961cce164",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-in-normal.html": [
-   "bb0df2e8fc1dea46c97f7c76bcaab59631374b5c",
+   "a9409cfe8d7aab5b8308fca9b56d6addf6f30a83",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-in-strict.html": [
-   "965d5133fab10083f6772502f7e4b7855dedfa07",
+   "defdf223074b76ef4783bb63c7bafbd226927b67",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-iteration-loose.html": [
-   "72c476cdce7db0d2e97cdb4a6c38ccf53590bf64",
+   "64f821925c01fa471ac31c20f802b75a349c24a4",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-iteration-normal.html": [
-   "92ac2e020911c3d6dc2b9a9df6c7816e07d46521",
+   "a7f66ea2b6b027ec8afcffc3983e6e5f939b9d02",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-iteration-strict.html": [
-   "22ac4baf03bf59f9f0a4623bcd83a004a33f1db9",
+   "0b1ca2a6c16beaa6f557add655b9d12a5833bb12",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-po-loose.html": [
-   "0e0280505fa2219b6c0ac7a1e0ef0838e72cc228",
+   "04a0f4d620f4a450db84fc4cea627191f0b840f4",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-po-normal.html": [
-   "490e4067a4042b81304d8b6c4925da7371142762",
+   "5a52cf853e29e7c056e6f092252d2bbefc2e8704",
    "testharness"
   ],
   "css/css-text/i18n/zh/css-text-line-break-zh-po-strict.html": [
-   "ec44c093a32decdc7bd39b77d7409fff36b62f4b",
+   "d85b74a54056166cedbf04f694d681ef95fff9b4",
    "testharness"
   ],
   "css/css-text/inheritance-expected.txt": [
@@ -380315,7 +380372,7 @@
    "support"
   ],
   "css/cssom/CSS.html": [
-   "7d558f04466ffad77e36f8aaa90abd220a24c27b",
+   "ba048c58acaac5cd2550fb21cb9b6e9ee8fb0bb1",
    "testharness"
   ],
   "css/cssom/CSSKeyframeRule.html": [
@@ -407682,6 +407739,14 @@
    "a809cc44b19796e50b7564b852a3234cca7e51f0",
    "testharness"
   ],
+  "html/semantics/document-metadata/the-link-element/link-multiple-error-events.html": [
+   "ea7e496ae8ea36f4241f565714952f4ac2c1968a",
+   "testharness"
+  ],
+  "html/semantics/document-metadata/the-link-element/link-multiple-load-events.html": [
+   "24cd5ba701184308d7fe28195119552d6431b87d",
+   "testharness"
+  ],
   "html/semantics/document-metadata/the-link-element/link-rel-attribute.html": [
    "14d06227ac862be169c2c1745fd6ec8913836394",
    "testharness"
@@ -447258,10 +447323,26 @@
    "2bf9eabed8410c9352a70163c8f40e25811dfd0f",
    "testharness"
   ],
+  "streams/writable-streams/aborting.any-expected.txt": [
+   "f2ded616cec362b3d8e95eea45ad9e15a7fdd01f",
+   "support"
+  ],
   "streams/writable-streams/aborting.any.js": [
-   "18fb58edf6de41d5d6208997d94fb5ed9e00a1e0",
+   "ea47a55fa9ff61cdc2f0ac3caca1e98c7b2c719d",
    "testharness"
   ],
+  "streams/writable-streams/aborting.any.serviceworker-expected.txt": [
+   "f2ded616cec362b3d8e95eea45ad9e15a7fdd01f",
+   "support"
+  ],
+  "streams/writable-streams/aborting.any.sharedworker-expected.txt": [
+   "f2ded616cec362b3d8e95eea45ad9e15a7fdd01f",
+   "support"
+  ],
+  "streams/writable-streams/aborting.any.worker-expected.txt": [
+   "f2ded616cec362b3d8e95eea45ad9e15a7fdd01f",
+   "support"
+  ],
   "streams/writable-streams/bad-strategies.any.js": [
    "d67ee6b5039dc98e5093aef0c3f2820462112a4c",
    "testharness"
@@ -447299,7 +447380,7 @@
    "testharness"
   ],
   "streams/writable-streams/general.any.js": [
-   "253909f27b42406f099f4539311fde0280a482fa",
+   "96670448a9da68ce54d1f3ecf8b2ba38b2673c73",
    "testharness"
   ],
   "streams/writable-streams/properties.any-expected.txt": [
@@ -450910,151 +450991,151 @@
    "8316dcfbc89bd02073e4e08db1bee7f65d37e86c",
    "support"
   ],
-  "wasm/serialization/broadcastchannel-success-and-failure.html": [
+  "wasm/serialization/module/broadcastchannel-success-and-failure.html": [
    "0d11cc595be2d16ad795be8199ed2ae7abe79974",
    "testharness"
   ],
-  "wasm/serialization/broadcastchannel-success.html": [
+  "wasm/serialization/module/broadcastchannel-success.html": [
    "cd5f8d0b56a19148dbd01b4218869f1f0c3526fd",
    "testharness"
   ],
-  "wasm/serialization/identity-not-preserved.html": [
+  "wasm/serialization/module/identity-not-preserved.html": [
    "24bb3b16d8c50600a634d62d4c48c49dfb3b120e",
    "testharness"
   ],
-  "wasm/serialization/incrementer.wasm": [
+  "wasm/serialization/module/incrementer.wasm": [
    "47afcdef2a2812acccecd0f203d30d3023593f3d",
    "support"
   ],
-  "wasm/serialization/nested-worker-success-sharedworker-expected.txt": [
+  "wasm/serialization/module/nested-worker-success-sharedworker-expected.txt": [
    "7b70ea298989ba1aeafd5cadeff2dd50b97c56e2",
    "support"
   ],
-  "wasm/serialization/nested-worker-success.any.js": [
+  "wasm/serialization/module/nested-worker-success.any.js": [
    "5388ebcc39b22946957250004577a1966c264a5a",
    "testharness"
   ],
-  "wasm/serialization/nested-worker-success.any.sharedworker-expected.txt": [
+  "wasm/serialization/module/nested-worker-success.any.sharedworker-expected.txt": [
    "7b70ea298989ba1aeafd5cadeff2dd50b97c56e2",
    "support"
   ],
-  "wasm/serialization/no-transferring.html": [
+  "wasm/serialization/module/no-transferring.html": [
    "a0bf11f01dd459b2e3abeb249f725e1e05d1532f",
    "testharness"
   ],
-  "wasm/serialization/resources/blank.html": [
+  "wasm/serialization/module/resources/blank.html": [
    "a3c3a4689a62b45b1e429f6b7a94690e556a1259",
    "support"
   ],
-  "wasm/serialization/resources/broadcastchannel-iframe.html": [
+  "wasm/serialization/module/resources/broadcastchannel-iframe.html": [
    "83e347b5cb35c92aa3cd96263a68b56af366f0e3",
    "support"
   ],
-  "wasm/serialization/resources/broadcastchannel-sharedworker.js": [
+  "wasm/serialization/module/resources/broadcastchannel-sharedworker.js": [
    "310e0e9358446acaec0f13d8e2fb4437316953c2",
    "support"
   ],
-  "wasm/serialization/resources/broadcastchannel-worker.js": [
+  "wasm/serialization/module/resources/broadcastchannel-worker.js": [
    "76a8177060498547ab1661319c20d5d5288cd96f",
    "support"
   ],
-  "wasm/serialization/resources/create-empty-wasm-module.js": [
+  "wasm/serialization/module/resources/create-empty-wasm-module.js": [
    "7326710c9e47d756bbdab1ead2303b108b8f04db",
    "support"
   ],
-  "wasm/serialization/resources/echo-iframe.html": [
+  "wasm/serialization/module/resources/echo-iframe.html": [
    "c4fd5824a1c617c21fe8b92483b388d586edf06e",
    "support"
   ],
-  "wasm/serialization/resources/echo-worker.js": [
+  "wasm/serialization/module/resources/echo-worker.js": [
    "cbbde8a73c8c2a63cc97cbe2b6cd7c6d81585b5c",
    "support"
   ],
-  "wasm/serialization/resources/incrementer-iframe-domain.sub.html": [
+  "wasm/serialization/module/resources/incrementer-iframe-domain.sub.html": [
    "d2d18de49950c2508a69545ad95a937898b04532",
    "support"
   ],
-  "wasm/serialization/resources/incrementer-iframe.html": [
+  "wasm/serialization/module/resources/incrementer-iframe.html": [
    "5c8bc0735e207a7c18f12d578276ae3c3b999da5",
    "support"
   ],
-  "wasm/serialization/resources/incrementer-popup.html": [
+  "wasm/serialization/module/resources/incrementer-popup.html": [
    "660e472b27c086068edeb7fd2bcade536c4bd5e9",
    "support"
   ],
-  "wasm/serialization/resources/incrementer-worker-with-channel.js": [
+  "wasm/serialization/module/resources/incrementer-worker-with-channel.js": [
    "0323b3e52e75e894ae40ffc68e904ffc81ded024",
    "support"
   ],
-  "wasm/serialization/resources/incrementer-worker.js": [
+  "wasm/serialization/module/resources/incrementer-worker.js": [
    "1779ceea520ccfd07da6d595d8a34be62de89428",
    "support"
   ],
-  "wasm/serialization/resources/incrementer.wasm": [
+  "wasm/serialization/module/resources/incrementer.wasm": [
    "47afcdef2a2812acccecd0f203d30d3023593f3d",
    "support"
   ],
-  "wasm/serialization/resources/nested-iframe-1.html": [
+  "wasm/serialization/module/resources/nested-iframe-1.html": [
    "fe93cc0c4b0fe5b86bf1a12de84fb3fc48ea08a5",
    "support"
   ],
-  "wasm/serialization/resources/nested-iframe-2.html": [
+  "wasm/serialization/module/resources/nested-iframe-2.html": [
    "fad52ce9de3977c077b5a22e72ee7b23837ea302",
    "support"
   ],
-  "wasm/serialization/resources/nested-iframe-3.html": [
+  "wasm/serialization/module/resources/nested-iframe-3.html": [
    "7971022b2cdc315d598761a3694838494c2884a8",
    "support"
   ],
-  "wasm/serialization/resources/nested-iframe-4-incrementer.html": [
+  "wasm/serialization/module/resources/nested-iframe-4-incrementer.html": [
    "f419f4bc36cdffafa665e333a7e7bced3d153585",
    "support"
   ],
-  "wasm/serialization/resources/serviceworker-failure.js": [
+  "wasm/serialization/module/resources/serviceworker-failure.js": [
    "39796f9d94a39d2a13ed832544ce781373a20655",
    "support"
   ],
-  "wasm/serialization/resources/sharedworker-failure.js": [
+  "wasm/serialization/module/resources/sharedworker-failure.js": [
    "854c70b9e84e6e6fb1c59f64a06a79646a122576",
    "support"
   ],
-  "wasm/serialization/resources/test-incrementer.js": [
+  "wasm/serialization/module/resources/test-incrementer.js": [
    "65cb33227a37376c1a0134275d5079d442b443a9",
    "support"
   ],
-  "wasm/serialization/serialization-via-history.html": [
+  "wasm/serialization/module/serialization-via-history.html": [
    "35dc17b6701fadf920ce251ec6c63da1c26b6570",
    "testharness"
   ],
-  "wasm/serialization/serialization-via-idb.any.js": [
+  "wasm/serialization/module/serialization-via-idb.any.js": [
    "1d861c3d3aa1072b1c90332fec7ac993d3b59552",
    "testharness"
   ],
-  "wasm/serialization/serialization-via-notifications-api.any.js": [
+  "wasm/serialization/module/serialization-via-notifications-api.any.js": [
    "84105651d3b53192f453b9f16bb85163165495cb",
    "testharness"
   ],
-  "wasm/serialization/window-domain-success.sub.html": [
-   "51d4c5cb0ea0c0c5cf69530876c2f7c19bb3830a",
+  "wasm/serialization/module/window-domain-success.sub.html": [
+   "07360d8264df01e20123697bf2e635fc66ebacfe",
    "testharness"
   ],
-  "wasm/serialization/window-messagechannel-success.html": [
+  "wasm/serialization/module/window-messagechannel-success.html": [
    "e686c8113561d94e860a774771aa69b974696716",
    "testharness"
   ],
-  "wasm/serialization/window-serviceworker-failure.https.html": [
+  "wasm/serialization/module/window-serviceworker-failure.https.html": [
    "97c5a1decdb85317930508ece8f306fb80880ca2",
    "testharness"
   ],
-  "wasm/serialization/window-sharedworker-failure.html": [
+  "wasm/serialization/module/window-sharedworker-failure.html": [
    "667e985a30b53c0ecadfd4c68f6217b87a7a5b98",
    "testharness"
   ],
-  "wasm/serialization/window-similar-but-cross-origin-success.sub.html": [
-   "070cf0a49a8f0c0ede81b6751e727b44f36c0043",
+  "wasm/serialization/module/window-similar-but-cross-origin-success.sub.html": [
+   "a615547de09634632c1115180534bea5594835e3",
    "testharness"
   ],
-  "wasm/serialization/window-simple-success.html": [
+  "wasm/serialization/module/window-simple-success.html": [
    "6f2ccf465e93a160c73df548fc58774a5040f0e6",
    "testharness"
   ],
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe-type.any.js b/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe-type.any.js
new file mode 100644
index 0000000..5cdac97
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe-type.any.js
@@ -0,0 +1,54 @@
+// META: script=performanceobservers.js
+
+test(() => {
+  const obs = new PerformanceObserver(() =>{});
+  obs.observe({entryTypes: ["mark"]});
+  assert_throws('InvalidModificationError', function () {
+    obs.observe({type: "measure"});
+  });
+}, "Calling observe() with entryTypes and then type should throw an InvalidModificationError");
+
+test(() => {
+  const obs = new PerformanceObserver(() =>{});
+  obs.observe({type: "mark"});
+  assert_throws('InvalidModificationError', function () {
+    obs.observe({entryTypes: ["measure"]});
+  });
+}, "Calling observe() with type and then entryTypes should throw an InvalidModificationError");
+
+test(() => {
+  const obs = new PerformanceObserver(() =>{});
+  assert_throws(new SyntaxError(), function () {
+    obs.observe({type: "mark", entryTypes: ["measure"]});
+  });
+}, "Calling observe() with type and entryTypes should throw a SyntaxError");
+
+test(function () {
+  const obs = new PerformanceObserver(() =>{});
+  // Definitely not an entry type.
+  obs.observe({type: "this-cannot-match-an-entryType"});
+  // Close to an entry type, but not quite.
+  obs.observe({type: "marks"});
+}, "Passing in unknown values to type does throw an exception.");
+
+async_test(function (t) {
+  let observedMark = false;
+  let observedMeasure = false;
+  const observer = new PerformanceObserver(
+    t.step_func(function (entryList, obs) {
+      observedMark |= entryList.getEntries().filter(
+        entry => entry.entryType === 'mark').length;
+      observedMeasure |= entryList.getEntries().filter(
+        entry => entry.entryType === 'measure').length
+      // Only conclude the test once we receive both entries!
+      if (observedMark && observedMeasure) {
+        observer.disconnect();
+        t.done();
+      }
+    })
+  );
+  observer.observe({type: "mark"});
+  observer.observe({type: "measure"});
+  self.performance.mark("mark1");
+  self.performance.measure("measure1");
+}, "observe() with different type values stacks.");
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe.any.js b/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe.any.js
index 8520c26..6a673db 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe.any.js
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/po-observe.any.js
@@ -2,13 +2,13 @@
 
   test(function () {
     var obs = new PerformanceObserver(function () { return true; });
-    assert_throws(new TypeError(), function () {
+    assert_throws(new SyntaxError(), function () {
       obs.observe({});
     });
-    assert_throws(new TypeError(), function () {
+    assert_throws(new SyntaxError(), function () {
       obs.observe({entryType: []});
     });
-  }, "no entryTypes throws a TypeError");
+  }, "no 'type' or 'entryTypes' throws a SyntaxError");
   test(function () {
     var obs = new PerformanceObserver(function () { return true; });
     assert_throws(new TypeError(), function () {
@@ -19,13 +19,13 @@
   test(function () {
     var obs = new PerformanceObserver(function () { return true; });
     obs.observe({entryTypes: []});
-  }, "Empty sequence entryTypes is a no-op");
+  }, "Empty sequence entryTypes does not throw an exception.");
 
   test(function () {
     var obs = new PerformanceObserver(function () { return true; });
     obs.observe({entryTypes: ["this-cannot-match-an-entryType"]});
     obs.observe({entryTypes: ["marks","navigate", "resources"]});
-  }, "Unknown entryTypes are no-op");
+  }, "Unknown entryTypes do not throw an exception.");
 
   test(function () {
     var obs = new PerformanceObserver(function () { return true; });
diff --git a/third_party/blink/web_tests/external/wpt/portals/portals-cross-origin-load.sub.html b/third_party/blink/web_tests/external/wpt/portals/portals-cross-origin-load.sub.html
new file mode 100644
index 0000000..f860ac5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/portals-cross-origin-load.sub.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+  promise_test(async () => {
+    var bc = new BroadcastChannel("portals-cross-origin-load");
+    var receiveMessage = new Promise((resolve, reject) => {
+      bc.onmessage = e => {
+        bc.close();
+        resolve();
+      }
+    });
+    var portal = document.createElement("portal");
+    portal.src = "http://{{hosts[alt][www]}}:{{ports[http][0]}}/portals/resources/portal-cross-origin.sub.html";
+    document.body.appendChild(portal);
+    return receiveMessage;
+  });
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/portals/resources/portal-cross-origin.sub.html b/third_party/blink/web_tests/external/wpt/portals/resources/portal-cross-origin.sub.html
new file mode 100644
index 0000000..145ab5a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/resources/portal-cross-origin.sub.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<body>
+  <script>
+    var iframe = document.createElement("iframe");
+    iframe.src = "http://{{host}}:{{ports[http][0]}}/portals/resources/portal-forward-with-broadcast.html?broadcastchannel=portals-cross-origin-load";
+    iframe.onload = e => {
+      iframe.contentWindow.postMessage("loaded", "*");
+    }
+    document.body.appendChild(iframe);
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/portals/resources/portal-forward-with-broadcast.html b/third_party/blink/web_tests/external/wpt/portals/resources/portal-forward-with-broadcast.html
new file mode 100644
index 0000000..39bda69
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/resources/portal-forward-with-broadcast.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<body>
+  <script>
+    function forwardMessage(e) {
+      let broadcastChannel = new BroadcastChannel(new URL(location).searchParams.get('broadcastchannel'));
+      try {
+        broadcastChannel.postMessage(e.data);
+      } finally {
+        broadcastChannel.close();
+      }
+    }
+    window.addEventListener("message", forwardMessage);
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any-expected.txt b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any-expected.txt
new file mode 100644
index 0000000..f2ded61
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any-expected.txt
@@ -0,0 +1,59 @@
+This is a testharness.js-based test.
+Found 55 tests; 54 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Aborting a WritableStream before it starts should cause the writer's unsettled ready promise to reject
+PASS Aborting a WritableStream should cause the writer's fulfilled ready promise to reset to a rejected one
+PASS abort() on a released writer rejects
+PASS Aborting a WritableStream immediately prevents future writes
+PASS Aborting a WritableStream prevents further writes after any that are in progress
+PASS Fulfillment value of ws.abort() call must be undefined even if the underlying sink returns a non-undefined value
+PASS WritableStream if sink's abort throws, the promise returned by writer.abort() rejects
+PASS WritableStream if sink's abort throws, the promise returned by multiple writer.abort()s is the same and rejects
+PASS WritableStream if sink's abort throws, the promise returned by ws.abort() rejects
+PASS WritableStream if sink's abort throws, for an abort performed during a write, the promise returned by ws.abort() rejects
+PASS Aborting a WritableStream passes through the given reason
+PASS Aborting a WritableStream puts it in an errored state with the error passed to abort()
+PASS Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied
+PASS Closing but then immediately aborting a WritableStream causes the stream to error
+PASS Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt
+PASS Aborting a WritableStream after it is closed is a no-op
+PASS WritableStream should NOT call underlying sink's close if no abort is supplied (historical)
+PASS returning a thenable from abort() should work
+PASS .closed should not resolve before fulfilled write()
+PASS .closed should not resolve before rejected write(); write() error should not overwrite abort() error
+PASS writes should be satisfied in order when aborting
+PASS writes should be satisfied in order after rejected write when aborting
+PASS close() should reject with abort reason why abort() is first error
+PASS underlying abort() should not be called until underlying write() completes
+PASS underlying abort() should not be called if underlying close() has started
+PASS if underlying close() has started and then rejects, the abort() and close() promises should reject with the underlying close rejection reason
+PASS an abort() that happens during a write() should trigger the underlying abort() even with a close() queued
+PASS if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error
+PASS writer close() promise should resolve before abort() promise
+PASS writer.ready should reject on controller error without waiting for underlying write
+PASS writer.abort() while there is an in-flight write, and then finish the write with rejection
+PASS writer.abort(), controller.error() while there is an in-flight write, and then finish the write
+PASS writer.abort(), controller.error() while there is an in-flight close, and then finish the close
+PASS controller.error(), writer.abort() while there is an in-flight write, and then finish the write
+PASS controller.error(), writer.abort() while there is an in-flight close, and then finish the close
+PASS releaseLock() while aborting should reject the original closed promise
+PASS releaseLock() during delayed async abort() should reject the writer.closed promise
+PASS sink abort() should not be called until sink start() is done
+PASS if start attempts to error the controller after abort() has been called, then it should lose
+PASS stream abort() promise should still resolve if sink start() rejects
+PASS writer abort() during sink start() should replace the writer.ready promise synchronously
+PASS promises returned from other writer methods should be rejected when writer abort() happens during sink start()
+PASS abort() should succeed despite rejection from write
+PASS abort() should be rejected with the rejection returned from close()
+PASS a rejecting sink.write() should not prevent sink.abort() from being called
+PASS when start errors after stream abort(), underlying sink abort() should be called anyway
+PASS when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined
+PASS when calling abort() twice on the same stream, but sequentially so so there's no pending abort the second time, both should fulfill with undefined
+PASS calling abort() on an errored stream should fulfill with undefined
+PASS sink abort() should not be called if stream was erroring due to controller.error() before abort() was called
+PASS sink abort() should not be called if stream was erroring due to bad strategy before abort() was called
+PASS abort with no arguments should set the stored error to undefined
+PASS abort with an undefined argument should set the stored error to undefined
+PASS abort with a string argument should set the stored error to that argument
+FAIL abort on a locked stream should reject promise_test: Unhandled rejection with value: undefined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.js b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.js
index 18fb58e..ea47a55 100644
--- a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.js
+++ b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.js
@@ -1368,3 +1368,10 @@
                               e => assert_equals(e, 'string argument', 'e should be \'string argument\''));
   });
 }, 'abort with a string argument should set the stored error to that argument');
+
+promise_test(t => {
+  const ws = new WritableStream();
+  const writer = ws.getWriter();
+  return promise_rejects(t, new TypeError(), ws.abort(), 'abort should reject')
+    .then(() => writer.ready);
+}, 'abort on a locked stream should reject');
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.serviceworker-expected.txt
new file mode 100644
index 0000000..f2ded61
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.serviceworker-expected.txt
@@ -0,0 +1,59 @@
+This is a testharness.js-based test.
+Found 55 tests; 54 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Aborting a WritableStream before it starts should cause the writer's unsettled ready promise to reject
+PASS Aborting a WritableStream should cause the writer's fulfilled ready promise to reset to a rejected one
+PASS abort() on a released writer rejects
+PASS Aborting a WritableStream immediately prevents future writes
+PASS Aborting a WritableStream prevents further writes after any that are in progress
+PASS Fulfillment value of ws.abort() call must be undefined even if the underlying sink returns a non-undefined value
+PASS WritableStream if sink's abort throws, the promise returned by writer.abort() rejects
+PASS WritableStream if sink's abort throws, the promise returned by multiple writer.abort()s is the same and rejects
+PASS WritableStream if sink's abort throws, the promise returned by ws.abort() rejects
+PASS WritableStream if sink's abort throws, for an abort performed during a write, the promise returned by ws.abort() rejects
+PASS Aborting a WritableStream passes through the given reason
+PASS Aborting a WritableStream puts it in an errored state with the error passed to abort()
+PASS Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied
+PASS Closing but then immediately aborting a WritableStream causes the stream to error
+PASS Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt
+PASS Aborting a WritableStream after it is closed is a no-op
+PASS WritableStream should NOT call underlying sink's close if no abort is supplied (historical)
+PASS returning a thenable from abort() should work
+PASS .closed should not resolve before fulfilled write()
+PASS .closed should not resolve before rejected write(); write() error should not overwrite abort() error
+PASS writes should be satisfied in order when aborting
+PASS writes should be satisfied in order after rejected write when aborting
+PASS close() should reject with abort reason why abort() is first error
+PASS underlying abort() should not be called until underlying write() completes
+PASS underlying abort() should not be called if underlying close() has started
+PASS if underlying close() has started and then rejects, the abort() and close() promises should reject with the underlying close rejection reason
+PASS an abort() that happens during a write() should trigger the underlying abort() even with a close() queued
+PASS if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error
+PASS writer close() promise should resolve before abort() promise
+PASS writer.ready should reject on controller error without waiting for underlying write
+PASS writer.abort() while there is an in-flight write, and then finish the write with rejection
+PASS writer.abort(), controller.error() while there is an in-flight write, and then finish the write
+PASS writer.abort(), controller.error() while there is an in-flight close, and then finish the close
+PASS controller.error(), writer.abort() while there is an in-flight write, and then finish the write
+PASS controller.error(), writer.abort() while there is an in-flight close, and then finish the close
+PASS releaseLock() while aborting should reject the original closed promise
+PASS releaseLock() during delayed async abort() should reject the writer.closed promise
+PASS sink abort() should not be called until sink start() is done
+PASS if start attempts to error the controller after abort() has been called, then it should lose
+PASS stream abort() promise should still resolve if sink start() rejects
+PASS writer abort() during sink start() should replace the writer.ready promise synchronously
+PASS promises returned from other writer methods should be rejected when writer abort() happens during sink start()
+PASS abort() should succeed despite rejection from write
+PASS abort() should be rejected with the rejection returned from close()
+PASS a rejecting sink.write() should not prevent sink.abort() from being called
+PASS when start errors after stream abort(), underlying sink abort() should be called anyway
+PASS when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined
+PASS when calling abort() twice on the same stream, but sequentially so so there's no pending abort the second time, both should fulfill with undefined
+PASS calling abort() on an errored stream should fulfill with undefined
+PASS sink abort() should not be called if stream was erroring due to controller.error() before abort() was called
+PASS sink abort() should not be called if stream was erroring due to bad strategy before abort() was called
+PASS abort with no arguments should set the stored error to undefined
+PASS abort with an undefined argument should set the stored error to undefined
+PASS abort with a string argument should set the stored error to that argument
+FAIL abort on a locked stream should reject promise_test: Unhandled rejection with value: undefined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.sharedworker-expected.txt
new file mode 100644
index 0000000..f2ded61
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.sharedworker-expected.txt
@@ -0,0 +1,59 @@
+This is a testharness.js-based test.
+Found 55 tests; 54 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Aborting a WritableStream before it starts should cause the writer's unsettled ready promise to reject
+PASS Aborting a WritableStream should cause the writer's fulfilled ready promise to reset to a rejected one
+PASS abort() on a released writer rejects
+PASS Aborting a WritableStream immediately prevents future writes
+PASS Aborting a WritableStream prevents further writes after any that are in progress
+PASS Fulfillment value of ws.abort() call must be undefined even if the underlying sink returns a non-undefined value
+PASS WritableStream if sink's abort throws, the promise returned by writer.abort() rejects
+PASS WritableStream if sink's abort throws, the promise returned by multiple writer.abort()s is the same and rejects
+PASS WritableStream if sink's abort throws, the promise returned by ws.abort() rejects
+PASS WritableStream if sink's abort throws, for an abort performed during a write, the promise returned by ws.abort() rejects
+PASS Aborting a WritableStream passes through the given reason
+PASS Aborting a WritableStream puts it in an errored state with the error passed to abort()
+PASS Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied
+PASS Closing but then immediately aborting a WritableStream causes the stream to error
+PASS Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt
+PASS Aborting a WritableStream after it is closed is a no-op
+PASS WritableStream should NOT call underlying sink's close if no abort is supplied (historical)
+PASS returning a thenable from abort() should work
+PASS .closed should not resolve before fulfilled write()
+PASS .closed should not resolve before rejected write(); write() error should not overwrite abort() error
+PASS writes should be satisfied in order when aborting
+PASS writes should be satisfied in order after rejected write when aborting
+PASS close() should reject with abort reason why abort() is first error
+PASS underlying abort() should not be called until underlying write() completes
+PASS underlying abort() should not be called if underlying close() has started
+PASS if underlying close() has started and then rejects, the abort() and close() promises should reject with the underlying close rejection reason
+PASS an abort() that happens during a write() should trigger the underlying abort() even with a close() queued
+PASS if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error
+PASS writer close() promise should resolve before abort() promise
+PASS writer.ready should reject on controller error without waiting for underlying write
+PASS writer.abort() while there is an in-flight write, and then finish the write with rejection
+PASS writer.abort(), controller.error() while there is an in-flight write, and then finish the write
+PASS writer.abort(), controller.error() while there is an in-flight close, and then finish the close
+PASS controller.error(), writer.abort() while there is an in-flight write, and then finish the write
+PASS controller.error(), writer.abort() while there is an in-flight close, and then finish the close
+PASS releaseLock() while aborting should reject the original closed promise
+PASS releaseLock() during delayed async abort() should reject the writer.closed promise
+PASS sink abort() should not be called until sink start() is done
+PASS if start attempts to error the controller after abort() has been called, then it should lose
+PASS stream abort() promise should still resolve if sink start() rejects
+PASS writer abort() during sink start() should replace the writer.ready promise synchronously
+PASS promises returned from other writer methods should be rejected when writer abort() happens during sink start()
+PASS abort() should succeed despite rejection from write
+PASS abort() should be rejected with the rejection returned from close()
+PASS a rejecting sink.write() should not prevent sink.abort() from being called
+PASS when start errors after stream abort(), underlying sink abort() should be called anyway
+PASS when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined
+PASS when calling abort() twice on the same stream, but sequentially so so there's no pending abort the second time, both should fulfill with undefined
+PASS calling abort() on an errored stream should fulfill with undefined
+PASS sink abort() should not be called if stream was erroring due to controller.error() before abort() was called
+PASS sink abort() should not be called if stream was erroring due to bad strategy before abort() was called
+PASS abort with no arguments should set the stored error to undefined
+PASS abort with an undefined argument should set the stored error to undefined
+PASS abort with a string argument should set the stored error to that argument
+FAIL abort on a locked stream should reject promise_test: Unhandled rejection with value: undefined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.worker-expected.txt
new file mode 100644
index 0000000..f2ded61
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.worker-expected.txt
@@ -0,0 +1,59 @@
+This is a testharness.js-based test.
+Found 55 tests; 54 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS Aborting a WritableStream before it starts should cause the writer's unsettled ready promise to reject
+PASS Aborting a WritableStream should cause the writer's fulfilled ready promise to reset to a rejected one
+PASS abort() on a released writer rejects
+PASS Aborting a WritableStream immediately prevents future writes
+PASS Aborting a WritableStream prevents further writes after any that are in progress
+PASS Fulfillment value of ws.abort() call must be undefined even if the underlying sink returns a non-undefined value
+PASS WritableStream if sink's abort throws, the promise returned by writer.abort() rejects
+PASS WritableStream if sink's abort throws, the promise returned by multiple writer.abort()s is the same and rejects
+PASS WritableStream if sink's abort throws, the promise returned by ws.abort() rejects
+PASS WritableStream if sink's abort throws, for an abort performed during a write, the promise returned by ws.abort() rejects
+PASS Aborting a WritableStream passes through the given reason
+PASS Aborting a WritableStream puts it in an errored state with the error passed to abort()
+PASS Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied
+PASS Closing but then immediately aborting a WritableStream causes the stream to error
+PASS Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt
+PASS Aborting a WritableStream after it is closed is a no-op
+PASS WritableStream should NOT call underlying sink's close if no abort is supplied (historical)
+PASS returning a thenable from abort() should work
+PASS .closed should not resolve before fulfilled write()
+PASS .closed should not resolve before rejected write(); write() error should not overwrite abort() error
+PASS writes should be satisfied in order when aborting
+PASS writes should be satisfied in order after rejected write when aborting
+PASS close() should reject with abort reason why abort() is first error
+PASS underlying abort() should not be called until underlying write() completes
+PASS underlying abort() should not be called if underlying close() has started
+PASS if underlying close() has started and then rejects, the abort() and close() promises should reject with the underlying close rejection reason
+PASS an abort() that happens during a write() should trigger the underlying abort() even with a close() queued
+PASS if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error
+PASS writer close() promise should resolve before abort() promise
+PASS writer.ready should reject on controller error without waiting for underlying write
+PASS writer.abort() while there is an in-flight write, and then finish the write with rejection
+PASS writer.abort(), controller.error() while there is an in-flight write, and then finish the write
+PASS writer.abort(), controller.error() while there is an in-flight close, and then finish the close
+PASS controller.error(), writer.abort() while there is an in-flight write, and then finish the write
+PASS controller.error(), writer.abort() while there is an in-flight close, and then finish the close
+PASS releaseLock() while aborting should reject the original closed promise
+PASS releaseLock() during delayed async abort() should reject the writer.closed promise
+PASS sink abort() should not be called until sink start() is done
+PASS if start attempts to error the controller after abort() has been called, then it should lose
+PASS stream abort() promise should still resolve if sink start() rejects
+PASS writer abort() during sink start() should replace the writer.ready promise synchronously
+PASS promises returned from other writer methods should be rejected when writer abort() happens during sink start()
+PASS abort() should succeed despite rejection from write
+PASS abort() should be rejected with the rejection returned from close()
+PASS a rejecting sink.write() should not prevent sink.abort() from being called
+PASS when start errors after stream abort(), underlying sink abort() should be called anyway
+PASS when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined
+PASS when calling abort() twice on the same stream, but sequentially so so there's no pending abort the second time, both should fulfill with undefined
+PASS calling abort() on an errored stream should fulfill with undefined
+PASS sink abort() should not be called if stream was erroring due to controller.error() before abort() was called
+PASS sink abort() should not be called if stream was erroring due to bad strategy before abort() was called
+PASS abort with no arguments should set the stored error to undefined
+PASS abort with an undefined argument should set the stored error to undefined
+PASS abort with a string argument should set the stored error to that argument
+FAIL abort on a locked stream should reject promise_test: Unhandled rejection with value: undefined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/general.any.js b/third_party/blink/web_tests/external/wpt/streams/writable-streams/general.any.js
index 253909f2..9667044 100644
--- a/third_party/blink/web_tests/external/wpt/streams/writable-streams/general.any.js
+++ b/third_party/blink/web_tests/external/wpt/streams/writable-streams/general.any.js
@@ -268,3 +268,10 @@
   assert_true(sub.extraFunction(),
               'extraFunction() should be present on Subclass object');
 }, 'Subclassing WritableStream should work');
+
+test(() => {
+  const ws = new WritableStream();
+  assert_false(ws.locked, 'stream should not be locked');
+  ws.getWriter();
+  assert_true(ws.locked, 'stream should be locked');
+}, 'the locked getter should return true if the stream has a writer');
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/js/helpers.js b/third_party/blink/web_tests/external/wpt/webaudio/js/helpers.js
index 5970b7b..fbbfc8e 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/js/helpers.js
+++ b/third_party/blink/web_tests/external/wpt/webaudio/js/helpers.js
@@ -159,13 +159,8 @@
     function testOnNormalContext(callback) {
       function testOutput(nodeToInspect, expectedBuffers, callback) {
         testLength = 0;
-        // While the spec allows ScriptProcessorNode's to have no outputs, it's
-        // not well specified, and Chrome requires the ScriptProcessorNode to
-        // be connected (directly or indirectly) to the destination.  So make it
-        // so.  This doesn't affect the tests using the function because the
-        // ScriptProcessorNode is what verifies the test.
-        var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels, 1);
-        nodeToInspect.connect(sp).connect(context.destination);
+        var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels, 0);
+        nodeToInspect.connect(sp);
         sp.onaudioprocess = function(e) {
           var expectedBuffer = expectedBuffers.shift();
           testLength += expectedBuffer.length;
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html
index 1b5531b..62d90da1 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html
@@ -10,17 +10,13 @@
     var ac = new AudioContext();
     var analyser = ac.createAnalyser();
     var constant = ac.createConstantSource();
-    // Chrome requires a ScriptProcessorNode to be connected to destination
-    // (directly or indirectly), so create the node with one output and connect
-    // it to the destination.  This doesn't affect the test because the
-    // ScriptProcessorNode itself verifies the results.
-    var sp = ac.createScriptProcessor(2048, 1, 1);
+    var sp = ac.createScriptProcessor(2048, 1, 0);
 
     constant.offset.value = 0.0;
 
     constant.connect(analyser).connect(ac.destination);
 
-    constant.connect(sp).connect(ac.destination);
+    constant.connect(sp);
 
     var buf = new Float32Array(analyser.frequencyBinCount);
     var iteration_count = 10;
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/xr/webxr-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/xr/webxr-origin-trial-interfaces.html
index 141273c..a1e86b7 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/xr/webxr-origin-trial-interfaces.html
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/xr/webxr-origin-trial-interfaces.html
@@ -1,30 +1,38 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<!-- Generate token with the command:
-tools/origin_trials/generate_token.py http://127.0.0.1:8000 WebXRDeviceM69 --expire-timestamp=2000000000
-
-To test whether the token is working, run virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/xr tests.
--->
-<title>WebXRDeviceM69 - interfaces exposed by origin trial</title>
+<title>WebXR - interfaces exposed by origin trial</title>
 <script src="../../../resources/testharness.js"></script>
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../../resources/origin-trials-helper.js"></script>
 <script>
-
-let token = "AkvrKmuIjbDoP4zBBuZLWVMJLzFCV+2l8Iv2RPYCSbeSjFRRidSbIgW41p+jnCcOukYZ3tE4ZvQsR6qNhiIW5QoAAABWeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiV2ViWFJEZXZpY2VNNjkiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=";
-
 let properties_to_check = {'Navigator': ['xr']};
 
-// Skip this test if flags are not set properly.
-if(!self.internals.runtimeFlags.webXREnabled) {
+// Can only run these two tests if webXR is not enabled via a Chrome flag.
+// That is only the case when running this in a virtual test suite (by default,
+// runtime enabled features are on for layout tests).
+// To run in virtual test suite:
+// tools/run_web_tests.py virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed
+if (!self.internals.runtimeFlags.webXREnabled) {
   test(t => {
     OriginTrialsHelper.check_properties_missing(this, properties_to_check);
   }, "WebXR's entrypoint properties are not available without a token.");
+
+  // The WebXRDeviceM69 token has been disabled due to breaking webxr API
+  // changes made in M73.
+  // generated with command
+  // tools/origin_trials/generate_token.py http://127.0.0.1:8000 WebXRDeviceM69 --expire-timestamp=2000000000
+  let token_m69 = "AkvrKmuIjbDoP4zBBuZLWVMJLzFCV+2l8Iv2RPYCSbeSjFRRidSbIgW41p+jnCcOukYZ3tE4ZvQsR6qNhiIW5QoAAABWeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiV2ViWFJEZXZpY2VNNjkiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=";
+  OriginTrialsHelper.add_token(token_m69);
+  test(t => {
+    OriginTrialsHelper.check_properties_missing(this, properties_to_check);
+  }, "WebXR's entrypoint properties are not available with WebXRDeviceM69 token.");
 }
 
-OriginTrialsHelper.add_token(token);
-
+// generated with command
+// tools/origin_trials/generate_token.py http://127.0.0.1:8000 WebXRDeviceM73 --expire-timestamp=2000000000
+let token_m73 = "AkdUKG/76uPyi1gvtP+q4o8XF9C6DWpF45h6xzMHwBFS+cfXrgo0zMHkA1T9ovuz+VVtxacaS/dc8F8JeWpcqAoAAABWeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiV2ViWFJEZXZpY2VNNzMiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=";
+OriginTrialsHelper.add_token(token_m73);
 test(t => {
   OriginTrialsHelper.check_properties(this, properties_to_check);
-}, "WebXR's entrypoint properties are available.");
+}, "WebXR's entrypoint properties are available with WebXRDeviceM73 token.");
 </script>
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index c150db9..685a749 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -1531,7 +1531,15 @@
     attribute MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS
     attribute MAX_COMPUTE_ATOMIC_COUNTERS
     attribute MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS
+    attribute MAX_COMPUTE_IMAGE_UNIFORMS
     attribute MAX_COMPUTE_SHADER_STORAGE_BLOCKS
+    attribute MAX_COMPUTE_SHARED_MEMORY_SIZE
+    attribute MAX_COMPUTE_TEXTURE_IMAGE_UNITS
+    attribute MAX_COMPUTE_UNIFORM_BLOCKS
+    attribute MAX_COMPUTE_UNIFORM_COMPONENTS
+    attribute MAX_COMPUTE_WORK_GROUP_COUNT
+    attribute MAX_COMPUTE_WORK_GROUP_INVOCATIONS
+    attribute MAX_COMPUTE_WORK_GROUP_SIZE
     attribute MAX_CUBE_MAP_TEXTURE_SIZE
     attribute MAX_DRAW_BUFFERS
     attribute MAX_ELEMENTS_INDICES
diff --git a/third_party/blink/web_tests/virtual/feature-policy-for-sandbox/http/tests/navigation/README.txt b/third_party/blink/web_tests/virtual/feature-policy-for-sandbox/http/tests/navigation/README.txt
deleted file mode 100644
index 2ff197f5..0000000
--- a/third_party/blink/web_tests/virtual/feature-policy-for-sandbox/http/tests/navigation/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-This directory is for running navigation tests with the FeaturePolicyForSandbox
-runtime flag.
diff --git a/third_party/blink/web_tests/virtual/feature-policy-for-sandbox/http/tests/security/README.txt b/third_party/blink/web_tests/virtual/feature-policy-for-sandbox/http/tests/security/README.txt
deleted file mode 100644
index b71a013..0000000
--- a/third_party/blink/web_tests/virtual/feature-policy-for-sandbox/http/tests/security/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-This directory is for running tests with the FeaturePolicyForSandbox
-runtime flag.
diff --git a/third_party/blink/web_tests/virtual/feature-policy-for-sandbox/mhtml/README.txt b/third_party/blink/web_tests/virtual/feature-policy-for-sandbox/mhtml/README.txt
deleted file mode 100644
index b71a013..0000000
--- a/third_party/blink/web_tests/virtual/feature-policy-for-sandbox/mhtml/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-This directory is for running tests with the FeaturePolicyForSandbox
-runtime flag.
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index 8fd0d0bd..00675d1 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -569,6 +569,7 @@
     property contentDocument
     property contentWindow
     property csp
+    property featurePolicy
     property frameBorder
     property getSVGDocument
     property height
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 507a7e2..76b3144d 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
@@ -1173,6 +1173,7 @@
     getter documentURI
     getter domain
     getter embeds
+    getter featurePolicy
     getter fgColor
     getter firstElementChild
     getter fonts
@@ -2467,6 +2468,7 @@
     getter contentDocument
     getter contentWindow
     getter csp
+    getter featurePolicy
     getter frameBorder
     getter height
     getter longDesc
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 557a346..73d387f 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -1578,7 +1578,15 @@
 [Worker]     attribute MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS
 [Worker]     attribute MAX_COMPUTE_ATOMIC_COUNTERS
 [Worker]     attribute MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS
+[Worker]     attribute MAX_COMPUTE_IMAGE_UNIFORMS
 [Worker]     attribute MAX_COMPUTE_SHADER_STORAGE_BLOCKS
+[Worker]     attribute MAX_COMPUTE_SHARED_MEMORY_SIZE
+[Worker]     attribute MAX_COMPUTE_TEXTURE_IMAGE_UNITS
+[Worker]     attribute MAX_COMPUTE_UNIFORM_BLOCKS
+[Worker]     attribute MAX_COMPUTE_UNIFORM_COMPONENTS
+[Worker]     attribute MAX_COMPUTE_WORK_GROUP_COUNT
+[Worker]     attribute MAX_COMPUTE_WORK_GROUP_INVOCATIONS
+[Worker]     attribute MAX_COMPUTE_WORK_GROUP_SIZE
 [Worker]     attribute MAX_CUBE_MAP_TEXTURE_SIZE
 [Worker]     attribute MAX_DRAW_BUFFERS
 [Worker]     attribute MAX_ELEMENTS_INDICES
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index f2b4067e..443981b 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -8219,7 +8219,15 @@
     attribute MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS
     attribute MAX_COMPUTE_ATOMIC_COUNTERS
     attribute MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS
+    attribute MAX_COMPUTE_IMAGE_UNIFORMS
     attribute MAX_COMPUTE_SHADER_STORAGE_BLOCKS
+    attribute MAX_COMPUTE_SHARED_MEMORY_SIZE
+    attribute MAX_COMPUTE_TEXTURE_IMAGE_UNITS
+    attribute MAX_COMPUTE_UNIFORM_BLOCKS
+    attribute MAX_COMPUTE_UNIFORM_COMPONENTS
+    attribute MAX_COMPUTE_WORK_GROUP_COUNT
+    attribute MAX_COMPUTE_WORK_GROUP_INVOCATIONS
+    attribute MAX_COMPUTE_WORK_GROUP_SIZE
     attribute MAX_CUBE_MAP_TEXTURE_SIZE
     attribute MAX_DRAW_BUFFERS
     attribute MAX_ELEMENTS_INDICES
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index eab29218..02f0b257 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -1449,7 +1449,15 @@
 [Worker]     attribute MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS
 [Worker]     attribute MAX_COMPUTE_ATOMIC_COUNTERS
 [Worker]     attribute MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS
+[Worker]     attribute MAX_COMPUTE_IMAGE_UNIFORMS
 [Worker]     attribute MAX_COMPUTE_SHADER_STORAGE_BLOCKS
+[Worker]     attribute MAX_COMPUTE_SHARED_MEMORY_SIZE
+[Worker]     attribute MAX_COMPUTE_TEXTURE_IMAGE_UNITS
+[Worker]     attribute MAX_COMPUTE_UNIFORM_BLOCKS
+[Worker]     attribute MAX_COMPUTE_UNIFORM_COMPONENTS
+[Worker]     attribute MAX_COMPUTE_WORK_GROUP_COUNT
+[Worker]     attribute MAX_COMPUTE_WORK_GROUP_INVOCATIONS
+[Worker]     attribute MAX_COMPUTE_WORK_GROUP_SIZE
 [Worker]     attribute MAX_CUBE_MAP_TEXTURE_SIZE
 [Worker]     attribute MAX_DRAW_BUFFERS
 [Worker]     attribute MAX_ELEMENTS_INDICES
diff --git a/third_party/inspector_protocol/README.chromium b/third_party/inspector_protocol/README.chromium
index f5d2c21e..b6eafdd 100644
--- a/third_party/inspector_protocol/README.chromium
+++ b/third_party/inspector_protocol/README.chromium
@@ -2,7 +2,7 @@
 Short Name: inspector_protocol
 URL: https://chromium.googlesource.com/deps/inspector_protocol/
 Version: 0
-Revision: 9cba74155ff34f0695c1f975ee087049204d3dc5
+Revision: d6a19bda35159094ad1fbfd6d81ce8fdc043943b
 License: BSD
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/inspector_protocol/check_protocol_compatibility.py b/third_party/inspector_protocol/check_protocol_compatibility.py
index e23bd702..d2df244 100755
--- a/third_party/inspector_protocol/check_protocol_compatibility.py
+++ b/third_party/inspector_protocol/check_protocol_compatibility.py
@@ -45,6 +45,7 @@
 #
 # Adding --show_changes to the command line prints out a list of valid public API changes.
 
+from __future__ import print_function
 import copy
 import os.path
 import optparse
@@ -475,9 +476,9 @@
     if arg_options.show_changes:
         changes = compare_schemas(domains, baseline_domains, True)
         if len(changes) > 0:
-            print "  Public changes since %s:" % version
+            print("  Public changes since %s:" % version)
             for change in changes:
-                print "    %s" % change
+                print("    %s" % change)
 
     if arg_options.stamp:
         with open(arg_options.stamp, 'a') as _:
diff --git a/third_party/inspector_protocol/code_generator.py b/third_party/inspector_protocol/code_generator.py
index edf8c4de..bee9cce 100755
--- a/third_party/inspector_protocol/code_generator.py
+++ b/third_party/inspector_protocol/code_generator.py
@@ -33,14 +33,14 @@
         def json_object_hook(object_dict):
             items = [(k, os.path.join(config_base, v) if k == "path" else v) for (k, v) in object_dict.items()]
             items = [(k, os.path.join(output_base, v) if k == "output" else v) for (k, v) in items]
-            keys, values = zip(*items)
+            keys, values = list(zip(*items))
             return collections.namedtuple('X', keys)(*values)
         return json.loads(data, object_hook=json_object_hook)
 
     def init_defaults(config_tuple, path, defaults):
         keys = list(config_tuple._fields)  # pylint: disable=E1101
         values = [getattr(config_tuple, k) for k in keys]
-        for i in xrange(len(keys)):
+        for i in range(len(keys)):
             if hasattr(values[i], "_fields"):
                 values[i] = init_defaults(values[i], path + "." + keys[i], defaults)
         for optional in defaults:
@@ -134,7 +134,7 @@
 
 
 def to_snake_case(name):
-    return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", name, sys.maxint).lower()
+    return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", name, sys.maxsize).lower()
 
 
 def to_method_case(config, name):
@@ -664,14 +664,14 @@
     # Make gyp / make generatos happy, otherwise make rebuilds world.
     inputs_ts = max(map(os.path.getmtime, inputs))
     up_to_date = True
-    for output_file in outputs.iterkeys():
+    for output_file in outputs.keys():
         if not os.path.exists(output_file) or os.path.getmtime(output_file) < inputs_ts:
             up_to_date = False
             break
     if up_to_date:
         sys.exit()
 
-    for file_name, content in outputs.iteritems():
+    for file_name, content in outputs.items():
         out_file = open(file_name, "w")
         out_file.write(content)
         out_file.close()
diff --git a/third_party/inspector_protocol/encoding/cbor.cc b/third_party/inspector_protocol/encoding/cbor.cc
index fcdc8ff..105ccf31 100644
--- a/third_party/inspector_protocol/encoding/cbor.cc
+++ b/third_party/inspector_protocol/encoding/cbor.cc
@@ -48,6 +48,19 @@
 static constexpr uint8_t kInitialByteForDouble =
     EncodeInitialByte(MajorType::SIMPLE_VALUE, 27);
 
+// TAG 24 indicates that what follows is a byte string which is
+// encoded in CBOR format. We use this as a wrapper for
+// maps and arrays, allowing us to skip them, because the
+// byte string carries its size (byte length).
+// https://tools.ietf.org/html/rfc7049#section-2.4.4.1
+static constexpr uint8_t kInitialByteForEnvelope =
+    EncodeInitialByte(MajorType::TAG, 24);
+// The initial byte for a byte string with at most 2^32 bytes
+// of payload. This is used for envelope encoding, even if
+// the byte string is shorter.
+static constexpr uint8_t kInitialByteFor32BitLengthByteString =
+    EncodeInitialByte(MajorType::BYTE_STRING, 26);
+
 // See RFC 7049 Section 2.2.1, indefinite length arrays / maps have additional
 // info = 31.
 static constexpr uint8_t kInitialByteIndefiniteLengthArray =
@@ -73,7 +86,7 @@
 template <typename T>
 void WriteBytesMostSignificantByteFirst(T v, std::vector<uint8_t>* out) {
   for (int shift_bytes = sizeof(T) - 1; shift_bytes >= 0; --shift_bytes)
-    out->push_back(0xff & v >> (shift_bytes * 8));
+    out->push_back(0xff & (v >> (shift_bytes * 8)));
 }
 }  // namespace
 
@@ -215,6 +228,11 @@
 // (kInitialByteForDouble) plus the 64 bits of payload for its value.
 constexpr int kEncodedDoubleSize = 1 + sizeof(uint64_t);
 
+// An envelope is encoded with a specific initial byte
+// (kInitialByteForEnvelope), plus the start byte for a BYTE_STRING with a 32
+// bit wide length, plus a 32 bit length for that string.
+constexpr int kEncodedEnvelopeHeaderSize = 1 + 1 + sizeof(uint32_t);
+
 void EncodeDouble(double value, std::vector<uint8_t>* out) {
   // The additional_info=27 indicates 64 bits for the double follow.
   // See RFC 7049 Section 2.3, Table 1.
@@ -227,25 +245,62 @@
   WriteBytesMostSignificantByteFirst<uint64_t>(reinterpret.to_uint64, out);
 }
 
+void EnvelopeEncoder::EncodeStart(std::vector<uint8_t>* out) {
+  assert(byte_size_pos_ == 0);
+  out->push_back(kInitialByteForEnvelope);
+  out->push_back(kInitialByteFor32BitLengthByteString);
+  byte_size_pos_ = out->size();
+  out->resize(out->size() + sizeof(uint32_t));
+}
+
+bool EnvelopeEncoder::EncodeStop(std::vector<uint8_t>* out) {
+  assert(byte_size_pos_ != 0);
+  // The byte size is the size of the payload, that is, all the
+  // bytes that were written past the byte size position itself.
+  uint64_t byte_size = out->size() - (byte_size_pos_ + sizeof(uint32_t));
+  // We store exactly 4 bytes, so at most INT32MAX, with most significant
+  // byte first.
+  if (byte_size > std::numeric_limits<uint32_t>::max()) return false;
+  for (int shift_bytes = sizeof(uint32_t) - 1; shift_bytes >= 0;
+       --shift_bytes) {
+    (*out)[byte_size_pos_++] = 0xff & (byte_size >> (shift_bytes * 8));
+  }
+  return true;
+}
+
 namespace {
-class JsonToCBOREncoder : public JsonParserHandler {
+class JSONToCBOREncoder : public JSONParserHandler {
  public:
-  JsonToCBOREncoder(std::vector<uint8_t>* out, Status* status)
+  JSONToCBOREncoder(std::vector<uint8_t>* out, Status* status)
       : out_(out), status_(status) {
     *status_ = Status();
   }
 
   void HandleObjectBegin() override {
+    envelopes_.emplace_back();
+    envelopes_.back().EncodeStart(out_);
     out_->push_back(kInitialByteIndefiniteLengthMap);
   }
 
-  void HandleObjectEnd() override { out_->push_back(kStopByte); };
+  void HandleObjectEnd() override {
+    out_->push_back(kStopByte);
+    assert(!envelopes_.empty());
+    envelopes_.back().EncodeStop(out_);
+    envelopes_.pop_back();
+  };
 
   void HandleArrayBegin() override {
+    envelopes_.emplace_back();
+    envelopes_.back().EncodeStart(out_);
     out_->push_back(kInitialByteIndefiniteLengthArray);
   }
 
-  void HandleArrayEnd() override { out_->push_back(kStopByte); };
+  void HandleArrayEnd() override {
+    out_->push_back(kStopByte);
+    assert(!envelopes_.empty());
+    envelopes_.back().EncodeStop(out_);
+    envelopes_.pop_back();
+  };
 
   void HandleString16(std::vector<uint16_t> chars) override {
     for (uint16_t ch : chars) {
@@ -286,26 +341,27 @@
 
  private:
   std::vector<uint8_t>* out_;
+  std::vector<EnvelopeEncoder> envelopes_;
   Status* status_;
 };
 }  // namespace
 
-std::unique_ptr<JsonParserHandler> NewJsonToCBOREncoder(
+std::unique_ptr<JSONParserHandler> NewJSONToCBOREncoder(
     std::vector<uint8_t>* out, Status* status) {
-  return std::make_unique<JsonToCBOREncoder>(out, status);
+  return std::make_unique<JSONToCBOREncoder>(out, status);
 }
 
 namespace {
 // Below are three parsing routines for CBOR, which cover enough
 // to roundtrip JSON messages.
 bool ParseMap(int32_t stack_depth, CBORTokenizer* tokenizer,
-              JsonParserHandler* out);
+              JSONParserHandler* out);
 bool ParseArray(int32_t stack_depth, CBORTokenizer* tokenizer,
-                JsonParserHandler* out);
+                JSONParserHandler* out);
 bool ParseValue(int32_t stack_depth, CBORTokenizer* tokenizer,
-                JsonParserHandler* out);
+                JSONParserHandler* out);
 
-void ParseUTF16String(CBORTokenizer* tokenizer, JsonParserHandler* out) {
+void ParseUTF16String(CBORTokenizer* tokenizer, JSONParserHandler* out) {
   std::vector<uint16_t> value;
   span<uint8_t> rep = tokenizer->GetString16WireRep();
   for (std::ptrdiff_t ii = 0; ii < rep.size(); ii += 2)
@@ -315,7 +371,7 @@
 }
 
 // For now this method only covers US-ASCII. Later, we may allow UTF8.
-bool ParseASCIIString(CBORTokenizer* tokenizer, JsonParserHandler* out) {
+bool ParseASCIIString(CBORTokenizer* tokenizer, JSONParserHandler* out) {
   assert(tokenizer->TokenTag() == CBORTokenTag::STRING8);
   std::vector<uint16_t> value16;
   for (uint8_t ch : tokenizer->GetString8()) {
@@ -334,12 +390,15 @@
 }
 
 bool ParseValue(int32_t stack_depth, CBORTokenizer* tokenizer,
-                JsonParserHandler* out) {
+                JSONParserHandler* out) {
   if (stack_depth > kStackLimit) {
     out->HandleError(
         Status{Error::CBOR_STACK_LIMIT_EXCEEDED, tokenizer->Status().pos});
     return false;
   }
+  // Skip past the envelope to get to what's inside.
+  if (tokenizer->TokenTag() == CBORTokenTag::ENVELOPE)
+    tokenizer->EnterEnvelope();
   switch (tokenizer->TokenTag()) {
     case CBORTokenTag::ERROR:
       out->HandleError(tokenizer->Status());
@@ -394,7 +453,7 @@
 // ParseArray may only be called after an indefinite length array has been
 // detected.
 bool ParseArray(int32_t stack_depth, CBORTokenizer* tokenizer,
-                JsonParserHandler* out) {
+                JSONParserHandler* out) {
   assert(tokenizer->TokenTag() == CBORTokenTag::ARRAY_START);
   tokenizer->Next();
   out->HandleArrayBegin();
@@ -420,7 +479,7 @@
 // ParseArray may only be called after an indefinite length array has been
 // detected.
 bool ParseMap(int32_t stack_depth, CBORTokenizer* tokenizer,
-              JsonParserHandler* out) {
+              JSONParserHandler* out) {
   assert(tokenizer->TokenTag() == CBORTokenTag::MAP_START);
   out->HandleObjectBegin();
   tokenizer->Next();
@@ -453,16 +512,29 @@
 }
 }  // namespace
 
-void ParseCBOR(span<uint8_t> bytes, JsonParserHandler* json_out) {
-  CBORTokenizer tokenizer(bytes);
-  if (tokenizer.TokenTag() == CBORTokenTag::DONE) {
+void ParseCBOR(span<uint8_t> bytes, JSONParserHandler* json_out) {
+  if (bytes.empty()) {
     json_out->HandleError(Status{Error::CBOR_NO_INPUT, 0});
     return;
   }
-  if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) {
+  if (bytes[0] != kInitialByteForEnvelope) {
     json_out->HandleError(Status{Error::CBOR_INVALID_START_BYTE, 0});
     return;
   }
+  CBORTokenizer tokenizer(bytes);
+  if (tokenizer.TokenTag() == CBORTokenTag::ERROR) {
+    json_out->HandleError(tokenizer.Status());
+    return;
+  }
+  // We checked for the envelope start byte above, so the tokenizer
+  // must agree here, since it's not an error.
+  assert(tokenizer.TokenTag() == CBORTokenTag::ENVELOPE);
+  tokenizer.EnterEnvelope();
+  if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) {
+    json_out->HandleError(
+        Status{Error::CBOR_MAP_START_EXPECTED, tokenizer.Status().pos});
+    return;
+  }
   if (!ParseMap(/*stack_depth=*/1, &tokenizer, json_out)) return;
   if (tokenizer.TokenTag() == CBORTokenTag::DONE) return;
   if (tokenizer.TokenTag() == CBORTokenTag::ERROR) {
@@ -474,7 +546,7 @@
 }
 
 CBORTokenizer::CBORTokenizer(span<uint8_t> bytes) : bytes_(bytes) {
-  ReadNextToken();
+  ReadNextToken(/*enter_envelope=*/false);
 }
 CBORTokenizer::~CBORTokenizer() {}
 
@@ -483,7 +555,12 @@
 void CBORTokenizer::Next() {
   if (token_tag_ == CBORTokenTag::ERROR || token_tag_ == CBORTokenTag::DONE)
     return;
-  ReadNextToken();
+  ReadNextToken(/*enter_envelope=*/false);
+}
+
+void CBORTokenizer::EnterEnvelope() {
+  assert(token_tag_ == CBORTokenTag::ENVELOPE);
+  ReadNextToken(/*enter_envelope=*/true);
 }
 
 Status CBORTokenizer::Status() const { return status_; }
@@ -528,9 +605,13 @@
       token_start_internal_value_);
 }
 
-void CBORTokenizer::ReadNextToken() {
-  status_.pos =
-      status_.pos == Status::npos() ? 0 : status_.pos + token_byte_length_;
+void CBORTokenizer::ReadNextToken(bool enter_envelope) {
+  if (enter_envelope) {
+    status_.pos += kEncodedEnvelopeHeaderSize;
+  } else {
+    status_.pos =
+        status_.pos == Status::npos() ? 0 : status_.pos + token_byte_length_;
+  }
   status_.error = Error::OK;
   if (status_.pos >= bytes_.size()) {
     token_tag_ = CBORTokenTag::DONE;
@@ -576,6 +657,30 @@
       SetToken(CBORTokenTag::DOUBLE, kEncodedDoubleSize);
       return;
     }
+    case kInitialByteForEnvelope: {  // ENVELOPE
+      if (status_.pos + kEncodedEnvelopeHeaderSize > bytes_.size()) {
+        SetError(Error::CBOR_INVALID_ENVELOPE);
+        return;
+      }
+      // The envelope must be a byte string with 32 bit length.
+      if (bytes_[status_.pos + 1] != kInitialByteFor32BitLengthByteString) {
+        SetError(Error::CBOR_INVALID_ENVELOPE);
+        return;
+      }
+      // Read the length of the byte string.
+      token_start_internal_value_ = ReadBytesMostSignificantByteFirst<uint32_t>(
+          bytes_.subspan(status_.pos + 2));
+      // Make sure the payload is contained within the message.
+      if (token_start_internal_value_ + kEncodedEnvelopeHeaderSize +
+              status_.pos >
+          size_t(bytes_.size())) {
+        SetError(Error::CBOR_INVALID_ENVELOPE);
+        return;
+      }
+      SetToken(CBORTokenTag::ENVELOPE,
+               kEncodedEnvelopeHeaderSize + token_start_internal_value_);
+      return;
+    }
     default: {
       span<uint8_t> remainder =
           bytes_.subspan(status_.pos, bytes_.size() - status_.pos);
diff --git a/third_party/inspector_protocol/encoding/cbor.h b/third_party/inspector_protocol/encoding/cbor.h
index 6e7aa34..bf92a59 100644
--- a/third_party/inspector_protocol/encoding/cbor.h
+++ b/third_party/inspector_protocol/encoding/cbor.h
@@ -17,7 +17,13 @@
 // The binary encoding for the inspector protocol follows the CBOR specification
 // (RFC 7049). Additional constraints:
 // - Only indefinite length maps and arrays are supported.
-// - At the top level, a message must be an indefinite length map.
+// - Maps and arrays are wrapped with an envelope, that is, a
+//   CBOR tag with value 24 followed by a byte string specifying
+//   the byte length of the enclosed map / array. The byte string
+//   must use a 32 bit wide length.
+// - At the top level, a message must be an indefinite length map
+//   wrapped by an envelope.
+// - Maximal size for messages is 2^32 (4 GB).
 // - For scalars, we support only the int32_t range, encoded as
 //   UNSIGNED/NEGATIVE (major types 0 / 1).
 // - UTF16 strings, including with unbalanced surrogate pairs, are encoded
@@ -50,18 +56,39 @@
 // with additional info = 27, followed by 8 bytes in big endian.
 void EncodeDouble(double value, std::vector<uint8_t>* out);
 
+// An envelope indicates the byte length of a wrapped item.
+// We use this for maps and array, which allows the decoder
+// to skip such (nested) values whole sale.
+// It's implemented as a CBOR tag (major type 6) with additional
+// info = 24, followed by a byte string with a 32 bit length value;
+// so the maximal structure that we can wrap is 2^32 bits long.
+// See also: https://tools.ietf.org/html/rfc7049#section-2.4.4.1
+class EnvelopeEncoder {
+ public:
+  // Emits the envelope start bytes and records the position for the
+  // byte size in |byte_size_pos_|. Also emits empty bytes for the
+  // byte sisze so that encoding can continue.
+  void EncodeStart(std::vector<uint8_t>* out);
+  // This records the current size in |out| at position byte_size_pos_.
+  // Returns true iff successful.
+  bool EncodeStop(std::vector<uint8_t>* out);
+
+ private:
+  uint64_t byte_size_pos_ = 0;
+};
+
 // This can be used to convert from JSON to CBOR, by passing the
 // return value to the routines in json_parser.h.  The handler will encode into
 // |out|, and iff an error occurs it will set |status| to an error and clear
 // |out|. Otherwise, |status.ok()| will be |true|.
-std::unique_ptr<JsonParserHandler> NewJsonToCBOREncoder(
+std::unique_ptr<JSONParserHandler> NewJSONToCBOREncoder(
     std::vector<uint8_t>* out, Status* status);
 
 // Parses a CBOR encoded message from |bytes|, sending JSON events to
 // |json_out|. If an error occurs, sends |out->HandleError|, and parsing stops.
 // The client is responsible for discarding the already received information in
 // that case.
-void ParseCBOR(span<uint8_t> bytes, JsonParserHandler* json_out);
+void ParseCBOR(span<uint8_t> bytes, JSONParserHandler* json_out);
 
 // Tags for the tokens within a CBOR message that CBORStream understands.
 // Note that this is not the same terminology as the CBOR spec (RFC 7049),
@@ -93,6 +120,12 @@
   ARRAY_START,
   // Ends a map or an array.
   STOP,
+  // An envelope indicator, wrapping a map or array.
+  // Internally this carries the byte length of the wrapped
+  // map or array. While CBORTokenizer::Next() will read / skip the entire
+  // envelope, CBORTokenizer::EnterEnvelope() reads the tokens
+  // inside of it.
+  ENVELOPE,
   // We've reached the end there is nothing else to read.
   DONE,
 };
@@ -113,6 +146,11 @@
 
   // Advances to the next token.
   void Next();
+  // Can only be called if TokenTag() == CBORTokenTag::ENVELOPE.
+  // While Next() would skip past the entire envelope / what it's
+  // wrapping, EnterEnvelope positions the cursor inside of the envelope,
+  // letting the client explore the nested structure.
+  void EnterEnvelope();
 
   // If TokenTag() is CBORTokenTag::ERROR, then Status().error describes
   // the error more precisely; otherwise it'll be set to Error::OK.
@@ -139,7 +177,7 @@
   span<uint8_t> GetBinary() const;
 
  private:
-  void ReadNextToken();
+  void ReadNextToken(bool enter_envelope);
   void SetToken(CBORTokenTag token, int64_t token_byte_length);
   void SetError(Error error);
 
diff --git a/third_party/inspector_protocol/encoding/cbor_test.cc b/third_party/inspector_protocol/encoding/cbor_test.cc
index 7f46c7d4..1b928f1 100644
--- a/third_party/inspector_protocol/encoding/cbor_test.cc
+++ b/third_party/inspector_protocol/encoding/cbor_test.cc
@@ -404,7 +404,7 @@
 }
 
 //
-// NewJsonToCBOREncoder
+// NewJSONToCBOREncoder
 //
 void EncodeSevenBitStringForTest(const std::string& key,
                                  std::vector<uint8_t>* out) {
@@ -414,14 +414,14 @@
       out);
 }
 
-TEST(JsonToCborEncoderTest, SevenBitStrings) {
+TEST(JSONToCBOREncoderTest, SevenBitStrings) {
   // When a string can be represented as 7 bit ASCII, the encoder will use the
   // STRING (major Type 3) type, so the actual characters end up as bytes on the
   // wire.
   std::vector<uint8_t> encoded;
   Status status;
-  std::unique_ptr<JsonParserHandler> encoder =
-      NewJsonToCBOREncoder(&encoded, &status);
+  std::unique_ptr<JSONParserHandler> encoder =
+      NewJSONToCBOREncoder(&encoded, &status);
   std::vector<uint16_t> utf16 = {'f', 'o', 'o'};
   encoder->HandleString16(utf16);
   EXPECT_EQ(Error::OK, status.error);
@@ -433,7 +433,7 @@
 }
 
 TEST(JsonCborRoundtrip, EncodingDecoding) {
-  // Hits all the cases except binary and error in JsonParserHandler, first
+  // Hits all the cases except binary and error in JSONParserHandler, first
   // parsing a JSON message into CBOR, then parsing it back from CBOR into JSON.
   std::string json =
       "{"
@@ -447,12 +447,16 @@
       "}";
   std::vector<uint8_t> encoded;
   Status status;
-  std::unique_ptr<JsonParserHandler> encoder =
-      NewJsonToCBOREncoder(&encoded, &status);
+  std::unique_ptr<JSONParserHandler> encoder =
+      NewJSONToCBOREncoder(&encoded, &status);
   span<uint8_t> ascii_in(reinterpret_cast<const uint8_t*>(json.data()),
                          json.size());
-  parseJSONChars(GetLinuxDevPlatform(), ascii_in, encoder.get());
-  std::vector<uint8_t> expected;
+  ParseJSONChars(GetLinuxDevPlatform(), ascii_in, encoder.get());
+  std::vector<uint8_t> expected = {
+      0xd8,            // envelope
+      0x5a,            // byte string with 32 bit length
+      0,    0, 0, 94,  // length is 94 bytes
+  };
   expected.push_back(0xbf);  // indef length map start
   EncodeSevenBitStringForTest("string", &expected);
   // This is followed by the encoded string for "Hello, 🌎."
@@ -474,6 +478,11 @@
   EncodeSevenBitStringForTest("null", &expected);
   expected.push_back(7 << 5 | 22);  // RFC 7049 Section 2.3, Table 2: null
   EncodeSevenBitStringForTest("array", &expected);
+  expected.push_back(0xd8);  // envelope
+  expected.push_back(0x5a);  // byte string with 32 bit length
+  // the length is 5 bytes (that's up to end indef length array below).
+  for (uint8_t ch : std::array<uint8_t, 4>{{0, 0, 0, 5}})
+    expected.push_back(ch);
   expected.push_back(0x9f);  // RFC 7049 Section 2.2.1, indef length array start
   expected.push_back(1);     // Three UNSIGNED values (easy since Major Type 0)
   expected.push_back(2);
@@ -485,8 +494,8 @@
 
   // And now we roundtrip, decoding the message we just encoded.
   std::string decoded;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &decoded, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &decoded, &status);
   ParseCBOR(span<uint8_t>(encoded.data(), encoded.size()), json_writer.get());
   EXPECT_EQ(Error::OK, status.error);
   EXPECT_EQ(json, decoded);
@@ -501,22 +510,22 @@
     SCOPED_TRACE(std::string("example: ") + json);
     std::vector<uint8_t> encoded;
     Status status;
-    std::unique_ptr<JsonParserHandler> encoder =
-        NewJsonToCBOREncoder(&encoded, &status);
+    std::unique_ptr<JSONParserHandler> encoder =
+        NewJSONToCBOREncoder(&encoded, &status);
     span<uint8_t> ascii_in(reinterpret_cast<const uint8_t*>(json.data()),
                            json.size());
-    parseJSONChars(GetLinuxDevPlatform(), ascii_in, encoder.get());
+    ParseJSONChars(GetLinuxDevPlatform(), ascii_in, encoder.get());
     std::string decoded;
-    std::unique_ptr<JsonParserHandler> json_writer =
-        NewJsonWriter(GetLinuxDevPlatform(), &decoded, &status);
+    std::unique_ptr<JSONParserHandler> json_writer =
+        NewJSONWriter(GetLinuxDevPlatform(), &decoded, &status);
     ParseCBOR(span<uint8_t>(encoded.data(), encoded.size()), json_writer.get());
     EXPECT_EQ(Error::OK, status.error);
     EXPECT_EQ(json, decoded);
   }
 }
 
-TEST(JsonToCborEncoderTest, HelloWorldBinary_WithTripToJson) {
-  // The JsonParserHandler::HandleBinary is a special case: The JSON parser will
+TEST(JSONToCBOREncoderTest, HelloWorldBinary_WithTripToJson) {
+  // The JSONParserHandler::HandleBinary is a special case: The JSON parser will
   // never call this method, because JSON does not natively support the binary
   // type. So, we can't fully roundtrip. However, the other direction works:
   // binary will be rendered in JSON, as a base64 string. So, we make calls to
@@ -525,8 +534,8 @@
   // world.".
   std::vector<uint8_t> encoded;
   Status status;
-  std::unique_ptr<JsonParserHandler> encoder =
-      NewJsonToCBOREncoder(&encoded, &status);
+  std::unique_ptr<JSONParserHandler> encoder =
+      NewJSONToCBOREncoder(&encoded, &status);
   encoder->HandleObjectBegin();
   // Emit a key.
   encoder->HandleString16(std::vector<uint16_t>{'f', 'o', 'o'});
@@ -539,8 +548,8 @@
 
   // Now drive the json writer via the CBOR decoder.
   std::string decoded;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &decoded, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &decoded, &status);
   ParseCBOR(span<uint8_t>(encoded.data(), encoded.size()), json_writer.get());
   EXPECT_EQ(Error::OK, status.error);
   EXPECT_EQ(Status::npos(), status.pos);
@@ -552,20 +561,22 @@
 // ParseCBOR
 //
 TEST(ParseCBORTest, ParseEmptyCBORMessage) {
-  // Just an indefinite length map that's empty (0xff = stop byte).
-  std::vector<uint8_t> in = {0xbf, 0xff};
+  // An envelope starting with 0xd8, 0x5a, with the byte length
+  // of 2, containing a map that's empty (0xbf for map
+  // start, and 0xff for map end).
+  std::vector<uint8_t> in = {0xd8, 0x5a, 0, 0, 0, 2, 0xbf, 0xff};
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(in.data(), in.size()), json_writer.get());
   EXPECT_EQ(Error::OK, status.error);
   EXPECT_EQ("{}", out);
 }
 
 TEST(ParseCBORTest, ParseCBORHelloWorld) {
-  std::vector<uint8_t> bytes;
-
+  const uint8_t kPayloadLen = 27;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen};
   bytes.push_back(0xbf);                       // start indef length map.
   EncodeSevenBitStringForTest("msg", &bytes);  // key: msg
   // Now write the value, the familiar "Hello, 🌎." where the globe is expressed
@@ -576,11 +587,12 @@
             ',', 0, ' ', 0, 0x3c, 0xd8, 0x0e, 0xdf, '.', 0}})
     bytes.push_back(ch);
   bytes.push_back(0xff);  // stop byte
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
 
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::OK, status.error);
   EXPECT_EQ("{\"msg\":\"Hello, \\ud83c\\udf0e.\"}", out);
@@ -590,8 +602,8 @@
   std::vector<uint8_t> in = {};
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(in.data(), in.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_NO_INPUT, status.error);
   EXPECT_EQ("", out);
@@ -599,13 +611,13 @@
 
 TEST(ParseCBORTest, InvalidStartByteError) {
   // Here we test that some actual json, which usually starts with {,
-  // is not considered CBOR. CBOR messages must start with 0xbf, the
-  // indefinite length map start byte.
+  // is not considered CBOR. CBOR messages must start with 0x5a, the
+  // envelope start byte.
   std::string json = "{\"msg\": \"Hello, world.\"}";
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       json_writer.get());
@@ -614,12 +626,15 @@
 }
 
 TEST(ParseCBORTest, UnexpectedEofExpectedValueError) {
-  std::vector<uint8_t> bytes = {0xbf};         // The byte for starting a map.
+  constexpr uint8_t kPayloadLen = 5;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};                             // map start
   EncodeSevenBitStringForTest("key", &bytes);  // A key; so value would be next.
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE, status.error);
   EXPECT_EQ(int64_t(bytes.size()), status.pos);
@@ -627,14 +642,17 @@
 }
 
 TEST(ParseCBORTest, UnexpectedEofInArrayError) {
-  std::vector<uint8_t> bytes = {0xbf};  // The byte for starting a map.
+  constexpr uint8_t kPayloadLen = 8;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};  // The byte for starting a map.
   EncodeSevenBitStringForTest("array",
                               &bytes);  // A key; so value would be next.
   bytes.push_back(0x9f);  // byte for indefinite length array start.
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_UNEXPECTED_EOF_IN_ARRAY, status.error);
   EXPECT_EQ(int64_t(bytes.size()), status.pos);
@@ -642,40 +660,52 @@
 }
 
 TEST(ParseCBORTest, UnexpectedEofInMapError) {
-  std::vector<uint8_t> bytes = {0xbf};  // The byte for starting a map.
+  constexpr uint8_t kPayloadLen = 1;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};  // The byte for starting a map.
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_UNEXPECTED_EOF_IN_MAP, status.error);
-  EXPECT_EQ(1, status.pos);
+  EXPECT_EQ(7, status.pos);
   EXPECT_EQ("", out);
 }
 
 TEST(ParseCBORTest, InvalidMapKeyError) {
-  // The byte for starting a map, followed by a byte representing null.
-  // null is not a valid map key.
-  std::vector<uint8_t> bytes = {0xbf, 7 << 5 | 22};
+  constexpr uint8_t kPayloadLen = 2;
+  std::vector<uint8_t> bytes = {0xd8,       0x5a, 0,
+                                0,          0,    kPayloadLen,  // envelope
+                                0xbf,                           // map start
+                                7 << 5 | 22};  // null (not a valid map key)
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_INVALID_MAP_KEY, status.error);
-  EXPECT_EQ(1, status.pos);
+  EXPECT_EQ(7, status.pos);
   EXPECT_EQ("", out);
 }
 
 std::vector<uint8_t> MakeNestedCBOR(int depth) {
   std::vector<uint8_t> bytes;
+  std::vector<EnvelopeEncoder> envelopes;
   for (int ii = 0; ii < depth; ++ii) {
+    envelopes.emplace_back();
+    envelopes.back().EncodeStart(&bytes);
     bytes.push_back(0xbf);  // indef length map start
     EncodeSevenBitStringForTest("key", &bytes);
   }
   EncodeSevenBitStringForTest("innermost_value", &bytes);
-  for (int ii = 0; ii < depth; ++ii)
+  for (int ii = 0; ii < depth; ++ii) {
     bytes.push_back(0xff);  // stop byte, finishes map.
+    envelopes.back().EncodeStop(&bytes);
+    envelopes.pop_back();
+  }
   return bytes;
 }
 
@@ -684,8 +714,8 @@
     std::vector<uint8_t> bytes = MakeNestedCBOR(3);
     std::string out;
     Status status;
-    std::unique_ptr<JsonParserHandler> json_writer =
-        NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+    std::unique_ptr<JSONParserHandler> json_writer =
+        NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
     ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
     EXPECT_EQ(Error::OK, status.error);
     EXPECT_EQ(Status::npos(), status.pos);
@@ -695,49 +725,57 @@
     std::vector<uint8_t> bytes = MakeNestedCBOR(1000);
     std::string out;
     Status status;
-    std::unique_ptr<JsonParserHandler> json_writer =
-        NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+    std::unique_ptr<JSONParserHandler> json_writer =
+        NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
     ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
     EXPECT_EQ(Error::OK, status.error);
     EXPECT_EQ(Status::npos(), status.pos);
   }
 
   // We just want to know the length of one opening map so we can compute
-  // where the error is encountered.
-  std::vector<uint8_t> opening_segment = {0xbf};
-  EncodeSevenBitStringForTest("key", &opening_segment);
+  // where the error is encountered. So we look at a small example and find
+  // the second envelope start.
+  std::vector<uint8_t> small_example = MakeNestedCBOR(3);
+  int64_t opening_segment_size = 1;  // Start after the first envelope start.
+  while (opening_segment_size < int64_t(small_example.size()) &&
+         small_example[opening_segment_size] != 0xd8)
+    opening_segment_size++;
 
   {  // Depth 1001: limit exceeded.
     std::vector<uint8_t> bytes = MakeNestedCBOR(1001);
     std::string out;
     Status status;
-    std::unique_ptr<JsonParserHandler> json_writer =
-        NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+    std::unique_ptr<JSONParserHandler> json_writer =
+        NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
     ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
     EXPECT_EQ(Error::CBOR_STACK_LIMIT_EXCEEDED, status.error);
-    EXPECT_EQ(int64_t(opening_segment.size()) * 1001, status.pos);
+    EXPECT_EQ(opening_segment_size * 1001, status.pos);
   }
   {  // Depth 1200: still limit exceeded, and at the same pos as for 1001
     std::vector<uint8_t> bytes = MakeNestedCBOR(1200);
     std::string out;
     Status status;
-    std::unique_ptr<JsonParserHandler> json_writer =
-        NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+    std::unique_ptr<JSONParserHandler> json_writer =
+        NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
     ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
     EXPECT_EQ(Error::CBOR_STACK_LIMIT_EXCEEDED, status.error);
-    EXPECT_EQ(int64_t(opening_segment.size()) * 1001, status.pos);
+    EXPECT_EQ(opening_segment_size * 1001, status.pos);
   }
 }
 
 TEST(ParseCBORTest, UnsupportedValueError) {
-  std::vector<uint8_t> bytes = {0xbf};  // start indef length map.
+  constexpr uint8_t kPayloadLen = 6;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};                             // map start
   EncodeSevenBitStringForTest("key", &bytes);
   int64_t error_pos = bytes.size();
   bytes.push_back(6 << 5 | 5);  // tags aren't supported yet.
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
+
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_UNSUPPORTED_VALUE, status.error);
   EXPECT_EQ(error_pos, status.pos);
@@ -745,7 +783,9 @@
 }
 
 TEST(ParseCBORTest, InvalidString16Error) {
-  std::vector<uint8_t> bytes = {0xbf};  // start indef length map.
+  constexpr uint8_t kPayloadLen = 11;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};                             // map start
   EncodeSevenBitStringForTest("key", &bytes);
   int64_t error_pos = bytes.size();
   // a BYTE_STRING of length 5 as value; since we interpret these as string16,
@@ -753,10 +793,11 @@
   // 5 isn't divisible by 2.
   bytes.push_back(2 << 5 | 5);
   for (int ii = 0; ii < 5; ++ii) bytes.push_back(' ');
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_INVALID_STRING16, status.error);
   EXPECT_EQ(error_pos, status.pos);
@@ -764,16 +805,19 @@
 }
 
 TEST(ParseCBORTest, InvalidString8Error) {
-  std::vector<uint8_t> bytes = {0xbf};  // start indef length map.
+  constexpr uint8_t kPayloadLen = 6;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};                             // map start
   EncodeSevenBitStringForTest("key", &bytes);
   int64_t error_pos = bytes.size();
   // a STRING of length 5 as value, but we're at the end of the bytes array
   // so it can't be decoded successfully.
   bytes.push_back(3 << 5 | 5);
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_INVALID_STRING8, status.error);
   EXPECT_EQ(error_pos, status.pos);
@@ -781,17 +825,20 @@
 }
 
 TEST(ParseCBORTest, String8MustBe7BitError) {
-  std::vector<uint8_t> bytes = {0xbf};  // start indef length map.
+  constexpr uint8_t kPayloadLen = 11;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};                             // map start
   EncodeSevenBitStringForTest("key", &bytes);
   int64_t error_pos = bytes.size();
   // a STRING of length 5 as value, with a payload that has bytes outside
   // 7 bit (> 0x7f).
   bytes.push_back(3 << 5 | 5);
   for (int ii = 0; ii < 5; ++ii) bytes.push_back(0xf0);
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_STRING8_MUST_BE_7BIT, status.error);
   EXPECT_EQ(error_pos, status.pos);
@@ -799,7 +846,9 @@
 }
 
 TEST(ParseCBORTest, InvalidBinaryError) {
-  std::vector<uint8_t> bytes = {0xbf};  // start indef length map.
+  constexpr uint8_t kPayloadLen = 9;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};                             // map start
   EncodeSevenBitStringForTest("key", &bytes);
   int64_t error_pos = bytes.size();
   bytes.push_back(6 << 5 | 22);  // base64 hint for JSON; indicates binary
@@ -807,10 +856,11 @@
   // Just two garbage bytes, not enough for the binary.
   bytes.push_back(0x31);
   bytes.push_back(0x23);
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_INVALID_BINARY, status.error);
   EXPECT_EQ(error_pos, status.pos);
@@ -818,17 +868,20 @@
 }
 
 TEST(ParseCBORTest, InvalidDoubleError) {
-  std::vector<uint8_t> bytes = {0xbf};  // start indef length map.
+  constexpr uint8_t kPayloadLen = 8;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};                             // map start
   EncodeSevenBitStringForTest("key", &bytes);
   int64_t error_pos = bytes.size();
   bytes.push_back(7 << 5 | 27);  // initial byte for double
   // Just two garbage bytes, not enough to represent an actual double.
   bytes.push_back(0x31);
   bytes.push_back(0x23);
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_INVALID_DOUBLE, status.error);
   EXPECT_EQ(error_pos, status.pos);
@@ -836,17 +889,20 @@
 }
 
 TEST(ParseCBORTest, InvalidSignedError) {
-  std::vector<uint8_t> bytes = {0xbf};  // start indef length map.
+  constexpr uint8_t kPayloadLen = 14;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};                             // map start
   EncodeSevenBitStringForTest("key", &bytes);
   int64_t error_pos = bytes.size();
   // uint64_t max is a perfectly fine value to encode as CBOR unsigned,
   // but we don't support this since we only cover the int32_t range.
   cbor_internals::WriteTokenStart(cbor_internals::MajorType::UNSIGNED,
                                   std::numeric_limits<uint64_t>::max(), &bytes);
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_INVALID_INT32, status.error);
   EXPECT_EQ(error_pos, status.pos);
@@ -854,7 +910,9 @@
 }
 
 TEST(ParseCBORTest, TrailingJunk) {
-  std::vector<uint8_t> bytes = {0xbf};  // start indef length map.
+  constexpr uint8_t kPayloadLen = 35;
+  std::vector<uint8_t> bytes = {0xd8, 0x5a, 0, 0, 0, kPayloadLen,  // envelope
+                                0xbf};                             // map start
   EncodeSevenBitStringForTest("key", &bytes);
   EncodeSevenBitStringForTest("value", &bytes);
   bytes.push_back(0xff);  // Up to here, it's a perfectly fine msg.
@@ -863,10 +921,11 @@
 
   cbor_internals::WriteTokenStart(cbor_internals::MajorType::UNSIGNED,
                                   std::numeric_limits<uint64_t>::max(), &bytes);
+  EXPECT_EQ(kPayloadLen, bytes.size() - 6);
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> json_writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> json_writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get());
   EXPECT_EQ(Error::CBOR_TRAILING_JUNK, status.error);
   EXPECT_EQ(error_pos, status.pos);
diff --git a/third_party/inspector_protocol/encoding/json_parser.cc b/third_party/inspector_protocol/encoding/json_parser.cc
index 333275b..82bd184 100644
--- a/third_party/inspector_protocol/encoding/json_parser.cc
+++ b/third_party/inspector_protocol/encoding/json_parser.cc
@@ -36,7 +36,7 @@
 template <typename Char>
 class JsonParser {
  public:
-  JsonParser(const Platform* platform, JsonParserHandler* handler)
+  JsonParser(const Platform* platform, JSONParserHandler* handler)
       : platform_(platform), handler_(handler) {}
 
   void Parse(const Char* start, size_t length) {
@@ -567,18 +567,18 @@
   const Char* start_pos_ = nullptr;
   bool error_ = false;
   const Platform* platform_;
-  JsonParserHandler* handler_;
+  JSONParserHandler* handler_;
 };
 }  // namespace
 
-void parseJSONChars(const Platform* platform, span<uint8_t> chars,
-                    JsonParserHandler* handler) {
+void ParseJSONChars(const Platform* platform, span<uint8_t> chars,
+                    JSONParserHandler* handler) {
   JsonParser<uint8_t> parser(platform, handler);
   parser.Parse(chars.data(), chars.size());
 }
 
-void parseJSONChars(const Platform* platform, span<uint16_t> chars,
-                    JsonParserHandler* handler) {
+void ParseJSONChars(const Platform* platform, span<uint16_t> chars,
+                    JSONParserHandler* handler) {
   JsonParser<uint16_t> parser(platform, handler);
   parser.Parse(chars.data(), chars.size());
 }
diff --git a/third_party/inspector_protocol/encoding/json_parser.h b/third_party/inspector_protocol/encoding/json_parser.h
index 1241f26..e5a8b03 100644
--- a/third_party/inspector_protocol/encoding/json_parser.h
+++ b/third_party/inspector_protocol/encoding/json_parser.h
@@ -13,10 +13,10 @@
 
 namespace inspector_protocol {
 // JSON parsing routines.
-void parseJSONChars(const Platform* deps, span<uint8_t> chars,
-                    JsonParserHandler* handler);
-void parseJSONChars(const Platform* deps, span<uint16_t> chars,
-                    JsonParserHandler* handler);
+void ParseJSONChars(const Platform* deps, span<uint8_t> chars,
+                    JSONParserHandler* handler);
+void ParseJSONChars(const Platform* deps, span<uint16_t> chars,
+                    JSONParserHandler* handler);
 }  // namespace inspector_protocol
 
 #endif  // INSPECTOR_PROTOCOL_ENCODING_JSON_PARSER_H_
diff --git a/third_party/inspector_protocol/encoding/json_parser_handler.h b/third_party/inspector_protocol/encoding/json_parser_handler.h
index 95b9ea8..c501d99f 100644
--- a/third_party/inspector_protocol/encoding/json_parser_handler.h
+++ b/third_party/inspector_protocol/encoding/json_parser_handler.h
@@ -11,9 +11,9 @@
 
 namespace inspector_protocol {
 // Handler interface for JSON parser events. See also json_parser.h.
-class JsonParserHandler {
+class JSONParserHandler {
  public:
-  virtual ~JsonParserHandler() = default;
+  virtual ~JSONParserHandler() = default;
   virtual void HandleObjectBegin() = 0;
   virtual void HandleObjectEnd() = 0;
   virtual void HandleArrayBegin() = 0;
diff --git a/third_party/inspector_protocol/encoding/json_parser_test.cc b/third_party/inspector_protocol/encoding/json_parser_test.cc
index cee0eac..38b0826b 100644
--- a/third_party/inspector_protocol/encoding/json_parser_test.cc
+++ b/third_party/inspector_protocol/encoding/json_parser_test.cc
@@ -12,7 +12,7 @@
 #include "linux_dev_platform.h"
 
 namespace inspector_protocol {
-class Log : public JsonParserHandler {
+class Log : public JSONParserHandler {
  public:
   void HandleObjectBegin() override { log_ << "object begin\n"; }
 
@@ -62,7 +62,7 @@
 
 TEST_F(JsonParserTest, SimpleDictionary) {
   std::string json = "{\"foo\": 42}";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -77,7 +77,7 @@
 
 TEST_F(JsonParserTest, NestedDictionary) {
   std::string json = "{\"foo\": {\"bar\": {\"baz\": 1}, \"bar2\": 2}}";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -100,7 +100,7 @@
 
 TEST_F(JsonParserTest, Doubles) {
   std::string json = "{\"foo\": 3.1415, \"bar\": 31415e-4}";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -118,7 +118,7 @@
 TEST_F(JsonParserTest, Unicode) {
   // Globe character. 0xF0 0x9F 0x8C 0x8E in utf8, 0xD83C 0xDF0E in utf16.
   std::string json = "{\"msg\": \"Hello, \\uD83C\\uDF0E.\"}";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -138,7 +138,7 @@
   // We provide the moon with json escape, but the earth as utf16 input.
   // Either way they arrive as utf8 (after decoding in log_.str()).
   base::string16 json = base::UTF8ToUTF16("{\"space\": \"🌎 \\uD83C\\uDF19.\"}");
-  parseJSONChars(GetLinuxDevPlatform(),
+  ParseJSONChars(GetLinuxDevPlatform(),
                  span<uint16_t>(reinterpret_cast<const uint16_t*>(json.data()),
                                 json.size()),
                  &log_);
@@ -167,7 +167,7 @@
       "\"3 byte\":\"屋\","
       "\"4 byte\":\"🌎\""
       "}";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -191,7 +191,7 @@
   std::string json = "{\"foo\": 3.1415} junk";
   int64_t junk_idx = json.find("junk");
   EXPECT_GT(junk_idx, 0);
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -212,7 +212,7 @@
   // kStackLimit is 1000 (see json_parser.cc). First let's
   // try with a small nested example.
   std::string json_3 = MakeNestedJson(3);
-  parseJSONChars(GetLinuxDevPlatform(),
+  ParseJSONChars(GetLinuxDevPlatform(),
                  span<uint8_t>(reinterpret_cast<const uint8_t*>(json_3.data()),
                                json_3.size()),
                  &log_);
@@ -233,7 +233,7 @@
   // Now with kStackLimit (1000).
   log_ = Log();
   std::string json_limit = MakeNestedJson(1000);
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json_limit.data()),
                     json_limit.size()),
@@ -242,7 +242,7 @@
   // Now with kStackLimit + 1 (1001) - it exceeds in the innermost instance.
   log_ = Log();
   std::string exceeded = MakeNestedJson(1001);
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(exceeded.data()),
                     exceeded.size()),
@@ -252,7 +252,7 @@
   // Now way past the limit. Still, the point of exceeding is 1001.
   log_ = Log();
   std::string far_out = MakeNestedJson(10000);
-  parseJSONChars(GetLinuxDevPlatform(),
+  ParseJSONChars(GetLinuxDevPlatform(),
                  span<uint8_t>(reinterpret_cast<const uint8_t*>(far_out.data()),
                                far_out.size()),
                  &log_);
@@ -262,7 +262,7 @@
 
 TEST_F(JsonParserTest, NoInputError) {
   std::string json = "";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -273,7 +273,7 @@
 
 TEST_F(JsonParserTest, InvalidTokenError) {
   std::string json = "|";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -285,7 +285,7 @@
 TEST_F(JsonParserTest, InvalidNumberError) {
   // Mantissa exceeds max (the constant used here is int64_t max).
   std::string json = "1E9223372036854775807";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -297,7 +297,7 @@
 TEST_F(JsonParserTest, InvalidStringError) {
   // \x22 is an unsupported escape sequence
   std::string json = "\"foo\\x22\"";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -308,7 +308,7 @@
 
 TEST_F(JsonParserTest, UnexpectedArrayEndError) {
   std::string json = "[1,2,]";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -319,7 +319,7 @@
 
 TEST_F(JsonParserTest, CommaOrArrayEndExpectedError) {
   std::string json = "[1,2 2";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -332,7 +332,7 @@
 TEST_F(JsonParserTest, StringLiteralExpectedError) {
   // There's an error because the key bar, a string, is not terminated.
   std::string json = "{\"foo\": 3.1415, \"bar: 31415e-4}";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -343,7 +343,7 @@
 
 TEST_F(JsonParserTest, ColonExpectedError) {
   std::string json = "{\"foo\", 42}";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -354,7 +354,7 @@
 
 TEST_F(JsonParserTest, UnexpectedObjectEndError) {
   std::string json = "{\"foo\": 42, }";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -366,7 +366,7 @@
 TEST_F(JsonParserTest, CommaOrObjectEndExpectedError) {
   // The second separator should be a comma.
   std::string json = "{\"foo\": 3.1415: \"bar\": 0}";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
@@ -378,7 +378,7 @@
 
 TEST_F(JsonParserTest, ValueExpectedError) {
   std::string json = "}";
-  parseJSONChars(
+  ParseJSONChars(
       GetLinuxDevPlatform(),
       span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()),
       &log_);
diff --git a/third_party/inspector_protocol/encoding/json_std_string_writer.cc b/third_party/inspector_protocol/encoding/json_std_string_writer.cc
index 37c8424..6e899e16 100644
--- a/third_party/inspector_protocol/encoding/json_std_string_writer.cc
+++ b/third_party/inspector_protocol/encoding/json_std_string_writer.cc
@@ -81,7 +81,7 @@
 }
 
 // Implements a handler for JSON parser events to emit a JSON string.
-class Writer : public JsonParserHandler {
+class Writer : public JSONParserHandler {
  public:
   Writer(Platform* platform, std::string* out, Status* status)
       : platform_(platform), out_(out), status_(status) {
@@ -207,7 +207,7 @@
 };
 }  // namespace
 
-std::unique_ptr<JsonParserHandler> NewJsonWriter(Platform* platform,
+std::unique_ptr<JSONParserHandler> NewJSONWriter(Platform* platform,
                                                  std::string* out,
                                                  Status* status) {
   return std::make_unique<Writer>(platform, out, status);
diff --git a/third_party/inspector_protocol/encoding/json_std_string_writer.h b/third_party/inspector_protocol/encoding/json_std_string_writer.h
index 33250bbf..b2c9b634 100644
--- a/third_party/inspector_protocol/encoding/json_std_string_writer.h
+++ b/third_party/inspector_protocol/encoding/json_std_string_writer.h
@@ -17,7 +17,7 @@
 // Except for calling the HandleError routine at any time, the client
 // code must call the Handle* methods in an order in which they'd occur
 // in valid JSON; otherwise we may crash (the code uses assert).
-std::unique_ptr<JsonParserHandler> NewJsonWriter(Platform* platform,
+std::unique_ptr<JSONParserHandler> NewJSONWriter(Platform* platform,
                                                  std::string* out,
                                                  Status* status);
 }  // namespace inspector_protocol
diff --git a/third_party/inspector_protocol/encoding/json_std_string_writer_test.cc b/third_party/inspector_protocol/encoding/json_std_string_writer_test.cc
index 7c45c65..0baa4d5 100644
--- a/third_party/inspector_protocol/encoding/json_std_string_writer_test.cc
+++ b/third_party/inspector_protocol/encoding/json_std_string_writer_test.cc
@@ -19,8 +19,8 @@
 TEST(JsonStdStringWriterTest, HelloWorld) {
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   writer->HandleObjectBegin();
   writer->HandleString16(UTF16String("msg1"));
   writer->HandleString16(UTF16String("Hello, 🌎."));
@@ -54,14 +54,14 @@
 }
 
 TEST(JsonStdStringWriterTest, BinaryEncodedAsJsonString) {
-  // The encoder emits binary submitted to JsonParserHandler::HandleBinary
+  // The encoder emits binary submitted to JSONParserHandler::HandleBinary
   // as base64. The following three examples are taken from
   // https://en.wikipedia.org/wiki/Base64.
   {
     std::string out;
     Status status;
-    std::unique_ptr<JsonParserHandler> writer =
-        NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+    std::unique_ptr<JSONParserHandler> writer =
+        NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
     writer->HandleBinary({'M', 'a', 'n'});
     EXPECT_TRUE(status.ok());
     EXPECT_EQ("\"TWFu\"", out);
@@ -69,8 +69,8 @@
   {
     std::string out;
     Status status;
-    std::unique_ptr<JsonParserHandler> writer =
-        NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+    std::unique_ptr<JSONParserHandler> writer =
+        NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
     writer->HandleBinary({'M', 'a'});
     EXPECT_TRUE(status.ok());
     EXPECT_EQ("\"TWE=\"", out);
@@ -78,8 +78,8 @@
   {
     std::string out;
     Status status;
-    std::unique_ptr<JsonParserHandler> writer =
-        NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+    std::unique_ptr<JSONParserHandler> writer =
+        NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
     writer->HandleBinary({'M'});
     EXPECT_TRUE(status.ok());
     EXPECT_EQ("\"TQ==\"", out);
@@ -87,8 +87,8 @@
   {  // "Hello, world.", verified with base64decode.org.
     std::string out;
     Status status;
-    std::unique_ptr<JsonParserHandler> writer =
-        NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+    std::unique_ptr<JSONParserHandler> writer =
+        NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
     writer->HandleBinary(
         {'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '.'});
     EXPECT_TRUE(status.ok());
@@ -101,8 +101,8 @@
   // status and clears the output.
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> writer =
-      NewJsonWriter(GetLinuxDevPlatform(), &out, &status);
+  std::unique_ptr<JSONParserHandler> writer =
+      NewJSONWriter(GetLinuxDevPlatform(), &out, &status);
   writer->HandleObjectBegin();
   writer->HandleString16(UTF16String("msg1"));
   writer->HandleError(Status{Error::JSON_PARSER_VALUE_EXPECTED, 42});
@@ -139,8 +139,8 @@
 
   std::string out;
   Status status;
-  std::unique_ptr<JsonParserHandler> writer =
-      NewJsonWriter(&platform, &out, &status);
+  std::unique_ptr<JSONParserHandler> writer =
+      NewJSONWriter(&platform, &out, &status);
   writer->HandleArrayBegin();
   writer->HandleDouble(.1);
   writer->HandleDouble(-.7);
diff --git a/third_party/inspector_protocol/encoding/status.h b/third_party/inspector_protocol/encoding/status.h
index b5c5f609..bd20f4f 100644
--- a/third_party/inspector_protocol/encoding/status.h
+++ b/third_party/inspector_protocol/encoding/status.h
@@ -28,19 +28,21 @@
 
   CBOR_INVALID_INT32 = 0x0e,
   CBOR_INVALID_DOUBLE = 0x0f,
-  CBOR_INVALID_STRING8 = 0x10,
-  CBOR_INVALID_STRING16 = 0x11,
-  CBOR_INVALID_BINARY = 0x12,
-  CBOR_UNSUPPORTED_VALUE = 0x13,
-  CBOR_NO_INPUT = 0x14,
-  CBOR_INVALID_START_BYTE = 0x15,
-  CBOR_UNEXPECTED_EOF_EXPECTED_VALUE = 0x16,
-  CBOR_UNEXPECTED_EOF_IN_ARRAY = 0x17,
-  CBOR_UNEXPECTED_EOF_IN_MAP = 0x18,
-  CBOR_INVALID_MAP_KEY = 0x19,
-  CBOR_STACK_LIMIT_EXCEEDED = 0x1a,
-  CBOR_STRING8_MUST_BE_7BIT = 0x1b,
-  CBOR_TRAILING_JUNK = 0x1c,
+  CBOR_INVALID_ENVELOPE = 0x10,
+  CBOR_INVALID_STRING8 = 0x11,
+  CBOR_INVALID_STRING16 = 0x12,
+  CBOR_INVALID_BINARY = 0x13,
+  CBOR_UNSUPPORTED_VALUE = 0x14,
+  CBOR_NO_INPUT = 0x15,
+  CBOR_INVALID_START_BYTE = 0x16,
+  CBOR_UNEXPECTED_EOF_EXPECTED_VALUE = 0x17,
+  CBOR_UNEXPECTED_EOF_IN_ARRAY = 0x18,
+  CBOR_UNEXPECTED_EOF_IN_MAP = 0x19,
+  CBOR_INVALID_MAP_KEY = 0x1a,
+  CBOR_STACK_LIMIT_EXCEEDED = 0x1b,
+  CBOR_STRING8_MUST_BE_7BIT = 0x1c,
+  CBOR_TRAILING_JUNK = 0x1d,
+  CBOR_MAP_START_EXPECTED = 0x1e,
 };
 
 // A status value with position that can be copied. The default status
diff --git a/third_party/inspector_protocol/lib/Maybe_h.template b/third_party/inspector_protocol/lib/Maybe_h.template
index 15626ab..68b166f 100644
--- a/third_party/inspector_protocol/lib/Maybe_h.template
+++ b/third_party/inspector_protocol/lib/Maybe_h.template
@@ -83,7 +83,7 @@
 template<>
 class Maybe<bool> : public MaybeBase<bool> {
 public:
-    Maybe() { }
+    Maybe() { m_value = false; }
     Maybe(bool value) : MaybeBase(value) { }
     Maybe(Maybe&& other) IP_NOEXCEPT : MaybeBase(std::move(other)) {}
     using MaybeBase::operator=;
@@ -92,7 +92,7 @@
 template<>
 class Maybe<int> : public MaybeBase<int> {
 public:
-    Maybe() { }
+    Maybe() { m_value = 0; }
     Maybe(int value) : MaybeBase(value) { }
     Maybe(Maybe&& other) IP_NOEXCEPT : MaybeBase(std::move(other)) {}
     using MaybeBase::operator=;
@@ -101,7 +101,7 @@
 template<>
 class Maybe<double> : public MaybeBase<double> {
 public:
-    Maybe() { }
+    Maybe() { m_value = 0; }
     Maybe(double value) : MaybeBase(value) { }
     Maybe(Maybe&& other) IP_NOEXCEPT : MaybeBase(std::move(other)) {}
     using MaybeBase::operator=;
diff --git a/third_party/inspector_protocol/pdl.py b/third_party/inspector_protocol/pdl.py
index 652e99c..43111e9 100644
--- a/third_party/inspector_protocol/pdl.py
+++ b/third_party/inspector_protocol/pdl.py
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from __future__ import print_function
 import collections
 import json
 import os.path
@@ -159,7 +160,7 @@
             enumliterals.append(trimLine)
             continue
 
-        print 'Error in %s:%s, illegal token: \t%s' % (file_name, i, line)
+        print('Error in %s:%s, illegal token: \t%s' % (file_name, i, line))
         sys.exit(1)
     return protocol
 
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index 79996f4..a3d9262 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Wednesday January 16 2019
+Date: Thursday January 31 2019
 Branch: master
-Commit: 9ecc0e779a29281e5698451bfd1b3ebe8f053bfd
+Commit: cde3da57b9a18636026531694ca76671894d1dce
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/libvpx_srcs.gni b/third_party/libvpx/libvpx_srcs.gni
index 1b76cc9..376acb9 100644
--- a/third_party/libvpx/libvpx_srcs.gni
+++ b/third_party/libvpx/libvpx_srcs.gni
@@ -188,6 +188,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_aq_360.c",
@@ -664,6 +666,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_aq_360.c",
@@ -1143,6 +1147,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_aq_360.c",
@@ -1507,6 +1513,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_denoiser_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_error_neon.c",
@@ -1909,6 +1917,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_aq_360.c",
@@ -2352,6 +2362,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_denoiser_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_error_neon.c",
@@ -2762,6 +2774,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_denoiser_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_frame_scale_neon.c",
@@ -3200,6 +3214,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_dct_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_denoiser_neon.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/arm/neon/vp9_frame_scale_neon.c",
@@ -3597,6 +3613,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_aq_360.c",
@@ -3937,6 +3955,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_aq_360.c",
@@ -4276,6 +4296,8 @@
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_detokenize.h",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.c",
   "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_dsubexp.h",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.c",
+  "//third_party/libvpx/source/libvpx/vp9/decoder/vp9_job_queue.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.c",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_alt_ref_aq.h",
   "//third_party/libvpx/source/libvpx/vp9/encoder/vp9_aq_360.c",
diff --git a/third_party/libvpx/source/config/ios/arm-neon/vpx_config.asm b/third_party/libvpx/source/config/ios/arm-neon/vpx_config.asm
index beb3351..8a79f63d 100644
--- a/third_party/libvpx/source/config/ios/arm-neon/vpx_config.asm
+++ b/third_party/libvpx/source/config/ios/arm-neon/vpx_config.asm
@@ -3,7 +3,6 @@
 
 	.set WIDE_REFERENCE, 0
 	.set ARCHITECTURE, 5
-	.set DO1STROUNDING, 0
 	.syntax unified
 .set ARCH_ARM ,  1
 .set ARCH_MIPS ,  0
diff --git a/third_party/libvpx/source/config/ios/arm64/vpx_config.asm b/third_party/libvpx/source/config/ios/arm64/vpx_config.asm
index 2a02bb3a..ab563f8a 100644
--- a/third_party/libvpx/source/config/ios/arm64/vpx_config.asm
+++ b/third_party/libvpx/source/config/ios/arm64/vpx_config.asm
@@ -3,7 +3,6 @@
 
 	.set WIDE_REFERENCE, 0
 	.set ARCHITECTURE, 5
-	.set DO1STROUNDING, 0
 	.syntax unified
 .set ARCH_ARM ,  1
 .set ARCH_MIPS ,  0
diff --git a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_config.asm b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_config.asm
index 1733c41..241f370 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_config.asm
+++ b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_config.asm
@@ -1,6 +1,5 @@
 @ This file was created from a .asm file
 @  using the ads2gas.pl script.
-	.equ DO1STROUNDING, 0
 	.syntax unified
 .equ ARCH_ARM ,  1
 .equ ARCH_MIPS ,  0
diff --git a/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_config.asm b/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_config.asm
index 75e9f0f..225e0896 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_config.asm
+++ b/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_config.asm
@@ -1,6 +1,5 @@
 @ This file was created from a .asm file
 @  using the ads2gas.pl script.
-	.equ DO1STROUNDING, 0
 	.syntax unified
 .equ ARCH_ARM ,  1
 .equ ARCH_MIPS ,  0
diff --git a/third_party/libvpx/source/config/linux/arm-neon/vpx_config.asm b/third_party/libvpx/source/config/linux/arm-neon/vpx_config.asm
index 9887adf..34101dab 100644
--- a/third_party/libvpx/source/config/linux/arm-neon/vpx_config.asm
+++ b/third_party/libvpx/source/config/linux/arm-neon/vpx_config.asm
@@ -1,6 +1,5 @@
 @ This file was created from a .asm file
 @  using the ads2gas.pl script.
-	.equ DO1STROUNDING, 0
 	.syntax unified
 .equ ARCH_ARM ,  1
 .equ ARCH_MIPS ,  0
diff --git a/third_party/libvpx/source/config/linux/arm/vpx_config.asm b/third_party/libvpx/source/config/linux/arm/vpx_config.asm
index 13377ed..b9450db 100644
--- a/third_party/libvpx/source/config/linux/arm/vpx_config.asm
+++ b/third_party/libvpx/source/config/linux/arm/vpx_config.asm
@@ -1,6 +1,5 @@
 @ This file was created from a .asm file
 @  using the ads2gas.pl script.
-	.equ DO1STROUNDING, 0
 	.syntax unified
 .equ ARCH_ARM ,  1
 .equ ARCH_MIPS ,  0
diff --git a/third_party/libvpx/source/config/linux/arm64-highbd/vpx_config.asm b/third_party/libvpx/source/config/linux/arm64-highbd/vpx_config.asm
index 8863b2c..f728cb2 100644
--- a/third_party/libvpx/source/config/linux/arm64-highbd/vpx_config.asm
+++ b/third_party/libvpx/source/config/linux/arm64-highbd/vpx_config.asm
@@ -1,6 +1,5 @@
 @ This file was created from a .asm file
 @  using the ads2gas.pl script.
-	.equ DO1STROUNDING, 0
 	.syntax unified
 .equ ARCH_ARM ,  1
 .equ ARCH_MIPS ,  0
diff --git a/third_party/libvpx/source/config/linux/arm64/vpx_config.asm b/third_party/libvpx/source/config/linux/arm64/vpx_config.asm
index d4de564..648ea113 100644
--- a/third_party/libvpx/source/config/linux/arm64/vpx_config.asm
+++ b/third_party/libvpx/source/config/linux/arm64/vpx_config.asm
@@ -1,6 +1,5 @@
 @ This file was created from a .asm file
 @  using the ads2gas.pl script.
-	.equ DO1STROUNDING, 0
 	.syntax unified
 .equ ARCH_ARM ,  1
 .equ ARCH_MIPS ,  0
diff --git a/third_party/libvpx/source/config/linux/generic/vpx_config.asm b/third_party/libvpx/source/config/linux/generic/vpx_config.asm
index 9bae4f7..20c7b9b 100644
--- a/third_party/libvpx/source/config/linux/generic/vpx_config.asm
+++ b/third_party/libvpx/source/config/linux/generic/vpx_config.asm
@@ -1,6 +1,5 @@
 @ This file was created from a .asm file
 @  using the ads2gas.pl script.
-	.equ DO1STROUNDING, 0
 	.syntax unified
 .equ ARCH_ARM ,  0
 .equ ARCH_MIPS ,  0
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index 5e0b583..b279009 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -2,7 +2,7 @@
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  7
 #define VERSION_PATCH  0
-#define VERSION_EXTRA  "1676-g9ecc0e779a"
+#define VERSION_EXTRA  "1741-gcde3da57b9"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.7.0-1676-g9ecc0e779a"
-#define VERSION_STRING      " v1.7.0-1676-g9ecc0e779a"
+#define VERSION_STRING_NOSP "v1.7.0-1741-gcde3da57b9"
+#define VERSION_STRING      " v1.7.0-1741-gcde3da57b9"
diff --git a/third_party/libvpx/source/config/win/arm64/vpx_config.asm b/third_party/libvpx/source/config/win/arm64/vpx_config.asm
index f0976ca7..5eeb841 100644
--- a/third_party/libvpx/source/config/win/arm64/vpx_config.asm
+++ b/third_party/libvpx/source/config/win/arm64/vpx_config.asm
@@ -3,7 +3,6 @@
 
 	.set WIDE_REFERENCE, 0
 	.set ARCHITECTURE, 5
-	.set DO1STROUNDING, 0
 	.syntax unified
 .set ARCH_ARM ,  1
 .set ARCH_MIPS ,  0
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index cb09cb6..99c531c 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -5393,6 +5393,11 @@
   <int value="1" label="Stale"/>
 </enum>
 
+<enum name="BooleanStarsSalientImageFound">
+  <int value="0" label="Salient image was not found."/>
+  <int value="1" label="Salient image was found."/>
+</enum>
+
 <enum name="BooleanStartedCompleted">
   <int value="0" label="Started"/>
   <int value="1" label="Completed"/>
@@ -31797,6 +31802,7 @@
   <int value="7533886" label="disable-offer-store-unmasked-wallet-cards"/>
   <int value="10458238" label="disable-print-preview-simplify"/>
   <int value="11698808" label="enable-dom-distiller-button-animation"/>
+  <int value="19629326" label="OmniboxExperimentalKeywordMode:enabled"/>
   <int value="19815558" label="EnableSettingsShortcutSearch:disabled"/>
   <int value="23556595" label="MarkHttpAs:enabled"/>
   <int value="26875005" label="disable-explicit-dma-fences"/>
@@ -31896,6 +31902,7 @@
   <int value="201343576" label="enable-password-change-support:enabled"/>
   <int value="203239167" label="ImprovedGeoLanguageData:enabled"/>
   <int value="203776499" label="enable-virtual-keyboard-overscroll"/>
+  <int value="212489101" label="AutofillAssistantChromeEntry:enabled"/>
   <int value="215328738" label="ImprovedGeoLanguageData:disabled"/>
   <int value="217455219" label="SyncStandaloneTransport:enabled"/>
   <int value="218890378" label="ManualSaving:disabled"/>
@@ -32542,6 +32549,7 @@
   <int value="1330264457" label="OmniboxUIExperimentVerticalLayout:disabled"/>
   <int value="1332120969" label="ChromeDuet:disabled"/>
   <int value="1333847867" label="NoScriptPreviews:enabled"/>
+  <int value="1338356182" label="AutofillAssistantChromeEntry:disabled"/>
   <int value="1338864675" label="EnableTouchableAppContextMenu:disabled"/>
   <int value="1339426771" label="TopSitesFromSiteEngagement:disabled"/>
   <int value="1340690624" label="WebPaymentsMethodSectionOrderV2:disabled"/>
@@ -32739,6 +32747,7 @@
   <int value="1677167062" label="AutomaticPasswordGeneration:enabled"/>
   <int value="1677258310" label="DragAppsInTabletMode:disabled"/>
   <int value="1679558835" label="ArcAvailableForChildAccount:enabled"/>
+  <int value="1688075820" label="OmniboxExperimentalKeywordMode:disabled"/>
   <int value="1689123607" label="enable-app-link"/>
   <int value="1689183477" label="enable-merge-key-char-events"/>
   <int value="1690837904" label="save-previous-document-resources"/>
@@ -50376,6 +50385,14 @@
   <int value="6" label="Omnibox"/>
 </enum>
 
+<enum name="StarsPromoActions">
+  <int value="0" label="Displayed">
+    The promo panel has been presented to the user.
+  </int>
+  <int value="1" label="Dismissed">The user selected the not now button.</int>
+  <int value="2" label="Completed">The user selected the sign-in button.</int>
+</enum>
+
 <enum name="StartupProfilingFinishReason">
   <int value="0" label="Done (all metrics gathered)"/>
   <int value="1"
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 2132b39..2d67d05 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -42001,6 +42001,15 @@
   </summary>
 </histogram>
 
+<histogram name="Installer.PowerwashCount" units="powerwashes">
+  <owner>zeuthen@chromium.org</owner>
+  <summary>
+    The number of times a Chrome OS device has been powerwashed (factory reset)
+    without subsequently going through recovery and/or changing the dev mode
+    switch. Reported once after each powerwash.
+  </summary>
+</histogram>
+
 <histogram name="Installer.RebootToNewPartitionAttempt" units="count"
     expires_after="2019-01-30">
   <owner>zeuthen@chromium.org</owner>
@@ -42116,6 +42125,15 @@
   </summary>
 </histogram>
 
+<histogram name="Installer.UpdateTime" units="seconds"
+    expires_after="2019-01-30">
+  <owner>zeuthen@chromium.org</owner>
+  <summary>
+    The time in seconds it took to update a Chrome OS system -- from completing
+    an update check to reboot pending.
+  </summary>
+</histogram>
+
 <histogram name="Installer.UpdateURLSwitches" units="count"
     expires_after="2019-01-30">
   <owner>zeuthen@chromium.org</owner>
@@ -110576,6 +110594,44 @@
   <summary>Amount of time taken to serialize a call stack profile.</summary>
 </histogram>
 
+<histogram name="Stars.BookmarksBar_Active_Clip_Count"
+    expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when the bookmarks bar view is activated.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Clipper_Folio_Selected_Depth_Count"
+    expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the depth of a folio when it is selected in the clipper.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Clipper_Folio_Selected_Visible_Folio_Count"
+    expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the number of folios visible when a folio is selected in the clipper.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Clipper_Open_Folio_Count" expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of folios when the clipper is opened.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Folio_Active_Clip_Count" expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when the folio view is activated.
+  </summary>
+</histogram>
+
 <histogram name="Stars.Goog_Related" units="%" expires_after="2019-01-30">
   <owner>yefim@chromium.org</owner>
   <summary>
@@ -110610,10 +110666,58 @@
   </summary>
 </histogram>
 
+<histogram name="Stars.Launch_Bookmark_BookmarksBar_Clip_Count"
+    expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when a bookmark is launched from the
+    bookmarks bar view.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Launch_Bookmark_Folio_Clip_Count"
+    expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when a bookmark is launched from the folio
+    view.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Launch_Bookmark_Search_Clip_Count"
+    expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when a bookmark is launched from the search
+    view.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Launch_Bookmark_SmartGroup_Clip_Count"
+    expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when a bookmark is launched from the smart
+    group view.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Launch_Bookmark_Timeline_Clip_Count"
+    expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when a bookmark is launched from the
+    timeline view.
+  </summary>
+</histogram>
+
 <histogram name="Stars.LaunchLocation" enum="StarsLaunchLocation"
     expires_after="2019-01-30">
-  <owner>ianwen@chromium.org</owner>
-  <summary>Logs a UI location from which a bookmark is launched.</summary>
+  <owner>lpromero@chromium.org</owner>
+  <summary>
+    Logs every time a bookmark is launched from an assortment of different UI
+    surfaces with Stars, the new bookmarks UI.
+  </summary>
 </histogram>
 
 <histogram name="Stars.No_Images_Snippets" units="%" expires_after="2019-01-30">
@@ -110633,6 +110737,66 @@
   </summary>
 </histogram>
 
+<histogram name="Stars.Number_Of_Nodes" expires_after="2019-01-30">
+  <owner>yefim@chromium.org</owner>
+  <summary>
+    Logs number of bookmark nodes every time stars extension is loaded.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Profile_Active_Clip_Count" expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when the profile view is activated.
+  </summary>
+</histogram>
+
+<histogram name="Stars.PromoActions" enum="StarsPromoActions"
+    expires_after="2019-01-30">
+  <owner>jbbegue@chromium.org</owner>
+  <summary>
+    Count the actions performed by the user on the stars promo panel currently
+    only on android and ios.
+  </summary>
+</histogram>
+
+<histogram name="Stars.SalientImageFound" enum="BooleanStarsSalientImageFound"
+    expires_after="2019-01-30">
+  <owner>lpromero@chromium.org</owner>
+  <summary>
+    A boolean that indicates if a salient image was found for a displayed
+    bookmark. It is recorded every single time a bookmark is displayed. That
+    way, this histogram shows the proportion of bookmarks the user sees with an
+    image. This is used only with Stars, the new bookmarks UI.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Search_Active_Clip_Count" expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when the search view is activated.
+  </summary>
+</histogram>
+
+<histogram name="Stars.SmartGroup_Active_Clip_Count" expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when the smart group view is activated.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Timeline_Active_Clip_Count" expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>
+    Logs the approx number of clips when the timeline view is activated.
+  </summary>
+</histogram>
+
+<histogram name="Stars.Version" expires_after="2019-01-30">
+  <owner>accamed@google.com</owner>
+  <summary>Logs the extension version the user is using.</summary>
+</histogram>
+
 <histogram name="Startup.AfterStartupTaskCount">
   <owner>michaeln@chromium.org</owner>
   <summary>
diff --git a/tools/perf/contrib/leak_detection/page_sets.py b/tools/perf/contrib/leak_detection/page_sets.py
index ec1b2b0..0130e0c 100644
--- a/tools/perf/contrib/leak_detection/page_sets.py
+++ b/tools/perf/contrib/leak_detection/page_sets.py
@@ -117,7 +117,8 @@
       'https://www.blizzard.com',
       'https://ign.com/',
       'https://www.yelp.com/',
-      'https://gizmodo.com/',
+      # Times out waiting for HasReachedQuiescence - crbug.com/927427
+      # 'https://gizmodo.com/',
       'https://www.gsmarena.com/',
       'https://www.theverge.com/',
       'https://www.nlm.nih.gov/',
diff --git a/tools/perf/process_perf_results.py b/tools/perf/process_perf_results.py
index 2146fae..fb92d52 100755
--- a/tools/perf/process_perf_results.py
+++ b/tools/perf/process_perf_results.py
@@ -48,6 +48,15 @@
 DATA_FORMAT_HISTOGRAMS = 'histograms'
 DATA_FORMAT_UNKNOWN = 'unknown'
 
+# See https://crbug.com/923564.
+# We want to switch over to using histograms for everything, but converting from
+# the format output by gtest perf tests to histograms has introduced several
+# problems. So, only perform the conversion on tests that are whitelisted and
+# are okay with potentially encountering issues.
+GTEST_CONVERSION_WHITELIST = [
+  'xr.vr.common_perftests',
+]
+
 
 def _GetMachineGroup(build_properties):
   machine_group = None
@@ -95,6 +104,13 @@
       buildbucket['build'].get('bucket') == 'luci.chrome.ci'):
     is_luci = True
 
+  if is_luci and _is_gtest(json_to_upload) and (
+      name in GTEST_CONVERSION_WHITELIST):
+    path_util.AddTracingToPath()
+    from tracing.value import gtest_json_converter
+    gtest_json_converter.ConvertGtestJsonFile(json_to_upload)
+    _data_format_cache[json_to_upload] = DATA_FORMAT_HISTOGRAMS
+
   if 'build' in buildbucket:
     args += [
       '--project', buildbucket['build'].get('project'),
diff --git a/ui/base/ime/BUILD.gn b/ui/base/ime/BUILD.gn
index 47fb630..d2e351d 100644
--- a/ui/base/ime/BUILD.gn
+++ b/ui/base/ime/BUILD.gn
@@ -41,6 +41,18 @@
     ":text_input_types",
     "//skia",
   ]
+
+  if (is_chromeos || use_ozone) {
+    sources += [
+      "character_composer.cc",
+      "character_composer.h",
+    ]
+    deps += [
+      "//ui/events:dom_keycode_converter",
+      "//ui/events:events",
+      "//ui/events:events_base",
+    ]
+  }
 }
 
 jumbo_component("ime") {
@@ -173,14 +185,6 @@
     ]
   }
 
-  if (is_chromeos || use_ozone) {
-    sources += [
-      "character_composer.cc",
-      "character_composer.h",
-    ]
-    deps += [ "//ui/events:dom_keycode_converter" ]
-  }
-
   if (!toolkit_views && !use_aura) {
     sources -= [
       "input_method_factory.cc",
diff --git a/ui/base/ime/character_composer.h b/ui/base/ime/character_composer.h
index b3845c3..9f7967e 100644
--- a/ui/base/ime/character_composer.h
+++ b/ui/base/ime/character_composer.h
@@ -12,7 +12,7 @@
 
 #include "base/macros.h"
 #include "base/strings/string_util.h"
-#include "ui/base/ime/ui_base_ime_export.h"
+#include "ui/base/ime/ui_base_ime_types_export.h"
 #include "ui/events/keycodes/dom/dom_key.h"
 
 namespace ui {
@@ -20,7 +20,7 @@
 
 // A class to recognize compose and dead key sequence.
 // Outputs composed character.
-class UI_BASE_IME_EXPORT CharacterComposer {
+class UI_BASE_IME_TYPES_EXPORT CharacterComposer {
  public:
   using ComposeBuffer = std::vector<DomKey>;
 
diff --git a/ui/events/keycodes/platform_key_map_win_unittest.cc b/ui/events/keycodes/platform_key_map_win_unittest.cc
index da2459c..34f13dc 100644
--- a/ui/events/keycodes/platform_key_map_win_unittest.cc
+++ b/ui/events/keycodes/platform_key_map_win_unittest.cc
@@ -408,10 +408,10 @@
   }
 }
 
-INSTANTIATE_TEST_CASE_P(VerifyAltGraph,
-                        AltGraphModifierTest,
-                        ::testing::Values(KEYBOARD_LAYOUT_ENGLISH_US,
-                                          KEYBOARD_LAYOUT_FRENCH));
+INSTANTIATE_TEST_SUITE_P(VerifyAltGraph,
+                         AltGraphModifierTest,
+                         ::testing::Values(KEYBOARD_LAYOUT_ENGLISH_US,
+                                           KEYBOARD_LAYOUT_FRENCH));
 
 }  // namespace
 
diff --git a/ui/ozone/platform/scenic/scenic_screen.cc b/ui/ozone/platform/scenic/scenic_screen.cc
index e7658c5c..170db83 100644
--- a/ui/ozone/platform/scenic/scenic_screen.cc
+++ b/ui/ozone/platform/scenic/scenic_screen.cc
@@ -91,13 +91,13 @@
 }
 
 gfx::Point ScenicScreen::GetCursorScreenPoint() const {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return gfx::Point();
 }
 
 gfx::AcceleratedWidget ScenicScreen::GetAcceleratedWidgetAtScreenPoint(
     const gfx::Point& point) const {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return gfx::kNullAcceleratedWidget;
 }
 
diff --git a/ui/ozone/platform/scenic/scenic_surface_factory.cc b/ui/ozone/platform/scenic/scenic_surface_factory.cc
index cce2266..5bca937 100644
--- a/ui/ozone/platform/scenic/scenic_surface_factory.cc
+++ b/ui/ozone/platform/scenic/scenic_surface_factory.cc
@@ -69,7 +69,7 @@
                         gfx::Size size,
                         gfx::BufferFormat format)
       : size_(size), format_(format) {
-    NOTIMPLEMENTED();
+    NOTIMPLEMENTED_LOG_ONCE();
   }
 
   bool AreDmaBufFdsValid() const override { return false; }
diff --git a/ui/ozone/platform/scenic/scenic_window.cc b/ui/ozone/platform/scenic/scenic_window.cc
index 5403f8e..60537dc 100644
--- a/ui/ozone/platform/scenic/scenic_window.cc
+++ b/ui/ozone/platform/scenic/scenic_window.cc
@@ -121,15 +121,15 @@
 }
 
 void ScenicWindow::SetCapture() {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void ScenicWindow::ReleaseCapture() {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 bool ScenicWindow::HasCapture() const {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
   return false;
 }
 
@@ -154,11 +154,11 @@
 }
 
 void ScenicWindow::SetCursor(PlatformCursor cursor) {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void ScenicWindow::MoveCursorTo(const gfx::Point& location) {
-  NOTIMPLEMENTED();
+  NOTIMPLEMENTED_LOG_ONCE();
 }
 
 void ScenicWindow::ConfineCursorToBounds(const gfx::Rect& bounds) {
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index 39b326f..29a47cd 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -192,6 +192,8 @@
     "test/test_pointer.h",
     "test/test_positioner.cc",
     "test/test_positioner.h",
+    "test/test_region.cc",
+    "test/test_region.h",
     "test/test_seat.cc",
     "test/test_seat.h",
     "test/test_touch.cc",
@@ -202,6 +204,7 @@
 
   deps = [
     "//base:base",
+    "//skia",
     "//testing/gmock",
     "//third_party/wayland:wayland_server",
     "//third_party/wayland-protocols:linux_dmabuf_protocol",
diff --git a/ui/ozone/platform/wayland/test/mock_surface.cc b/ui/ozone/platform/wayland/test/mock_surface.cc
index 7f266b69..bab5b77 100644
--- a/ui/ozone/platform/wayland/test/mock_surface.cc
+++ b/ui/ozone/platform/wayland/test/mock_surface.cc
@@ -16,6 +16,18 @@
   GetUserDataAs<MockSurface>(resource)->Attach(buffer_resource, x, y);
 }
 
+void SetOpaqueRegion(wl_client* client,
+                     wl_resource* resource,
+                     wl_resource* region) {
+  GetUserDataAs<MockSurface>(resource)->SetOpaqueRegion(region);
+}
+
+void SetInputRegion(wl_client* client,
+                    wl_resource* resource,
+                    wl_resource* region) {
+  GetUserDataAs<MockSurface>(resource)->SetInputRegion(region);
+}
+
 void Damage(wl_client* client,
             wl_resource* resource,
             int32_t x,
@@ -32,16 +44,16 @@
 }  // namespace
 
 const struct wl_surface_interface kMockSurfaceImpl = {
-    &DestroyResource,  // destroy
-    &Attach,           // attach
-    &Damage,           // damage
-    nullptr,           // frame
-    nullptr,           // set_opaque_region
-    nullptr,           // set_input_region
-    &Commit,           // commit
-    nullptr,           // set_buffer_transform
-    nullptr,           // set_buffer_scale
-    nullptr,           // damage_buffer
+    DestroyResource,  // destroy
+    Attach,           // attach
+    Damage,           // damage
+    nullptr,          // frame
+    SetOpaqueRegion,  // set_opaque_region
+    SetInputRegion,   // set_input_region
+    Commit,           // commit
+    nullptr,          // set_buffer_transform
+    nullptr,          // set_buffer_scale
+    nullptr,          // 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 a4ba746..1140ce2 100644
--- a/ui/ozone/platform/wayland/test/mock_surface.h
+++ b/ui/ozone/platform/wayland/test/mock_surface.h
@@ -27,6 +27,8 @@
   static MockSurface* FromResource(wl_resource* resource);
 
   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_METHOD4(Damage,
                void(int32_t x, int32_t y, int32_t width, int32_t height));
   MOCK_METHOD0(Commit, void());
diff --git a/ui/ozone/platform/wayland/test/test_compositor.cc b/ui/ozone/platform/wayland/test/test_compositor.cc
index a8195d9..0d22217 100644
--- a/ui/ozone/platform/wayland/test/test_compositor.cc
+++ b/ui/ozone/platform/wayland/test/test_compositor.cc
@@ -6,8 +6,10 @@
 
 #include <wayland-server-core.h>
 
+#include "base/logging.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/test/server_object.h"
+#include "ui/ozone/platform/wayland/test/test_region.h"
 
 namespace wl {
 
@@ -16,7 +18,6 @@
 constexpr uint32_t kCompositorVersion = 4;
 
 void CreateSurface(wl_client* client, wl_resource* resource, uint32_t id) {
-  auto* compositor = GetUserDataAs<TestCompositor>(resource);
   wl_resource* surface_resource = wl_resource_create(
       client, &wl_surface_interface, wl_resource_get_version(resource), id);
   if (!surface_resource) {
@@ -25,14 +26,23 @@
   }
   SetImplementation(surface_resource, &kMockSurfaceImpl,
                     std::make_unique<MockSurface>(surface_resource));
+
+  auto* compositor = GetUserDataAs<TestCompositor>(resource);
   compositor->AddSurface(GetUserDataAs<MockSurface>(surface_resource));
 }
 
+void CreateRegion(wl_client* client, wl_resource* resource, uint32_t id) {
+  wl_resource* region_resource =
+      wl_resource_create(client, &wl_region_interface, 1, id);
+  SetImplementation(region_resource, &kTestWlRegionImpl,
+                    std::make_unique<TestRegion>());
+}
+
 }  // namespace
 
 const struct wl_compositor_interface kTestCompositorImpl = {
-    &CreateSurface,  // create_surface
-    nullptr,         // create_region
+    CreateSurface,  // create_surface
+    CreateRegion,   // create_region
 };
 
 TestCompositor::TestCompositor()
diff --git a/ui/ozone/platform/wayland/test/test_region.cc b/ui/ozone/platform/wayland/test/test_region.cc
new file mode 100644
index 0000000..ff1fbd0c
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_region.cc
@@ -0,0 +1,43 @@
+// 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/test/test_region.h"
+
+#include <wayland-server-core.h>
+
+#include "ui/ozone/platform/wayland/test/server_object.h"
+
+namespace wl {
+
+namespace {
+
+void Destroy(wl_client* client, wl_resource* resource) {
+  wl_resource_destroy(resource);
+}
+
+void Add(wl_client* client,
+         wl_resource* resource,
+         int32_t x,
+         int32_t y,
+         int32_t width,
+         int32_t height) {
+  GetUserDataAs<SkRegion>(resource)->op(SkIRect::MakeXYWH(x, y, width, height),
+                                        SkRegion::kUnion_Op);
+}
+
+static void Subtract(wl_client* client,
+                     wl_resource* resource,
+                     int32_t x,
+                     int32_t y,
+                     int32_t width,
+                     int32_t height) {
+  GetUserDataAs<SkRegion>(resource)->op(SkIRect::MakeXYWH(x, y, width, height),
+                                        SkRegion::kDifference_Op);
+}
+
+}  // namespace
+
+const struct wl_region_interface kTestWlRegionImpl = {Destroy, Add, Subtract};
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_region.h b/ui/ozone/platform/wayland/test/test_region.h
new file mode 100644
index 0000000..a0dd582
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_region.h
@@ -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.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_REGION_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_REGION_H_
+
+#include <wayland-server-protocol-core.h>
+
+#include "third_party/skia/include/core/SkRegion.h"
+
+namespace wl {
+
+extern const struct wl_region_interface kTestWlRegionImpl;
+
+using TestRegion = SkRegion;
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_REGION_H_
diff --git a/ui/views/mus/desktop_window_tree_host_mus.cc b/ui/views/mus/desktop_window_tree_host_mus.cc
index b550b31..f48d31b 100644
--- a/ui/views/mus/desktop_window_tree_host_mus.cc
+++ b/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -309,6 +309,25 @@
     Hide();
 }
 
+void DesktopWindowTreeHostMus::UpdateMinAndMaxSize() {
+  gfx::Size min_size = content_window()->delegate()->GetMinimumSize();
+  gfx::Size max_size = content_window()->delegate()->GetMaximumSize();
+  if (min_size_ == min_size && max_size_ == max_size)
+    return;
+  min_size_ = min_size;
+  max_size_ = max_size;
+  // Setting the property to |window()| to propagate those properties to the
+  // window server.
+  if (min_size_ == gfx::Size())
+    window()->ClearProperty(aura::client::kMinimumSize);
+  else
+    window()->SetProperty(aura::client::kMinimumSize, new gfx::Size(min_size_));
+  if (max_size_ == gfx::Size())
+    window()->ClearProperty(aura::client::kMaximumSize);
+  else
+    window()->SetProperty(aura::client::kMaximumSize, new gfx::Size(max_size_));
+}
+
 void DesktopWindowTreeHostMus::Init(const Widget::InitParams& params) {
   const bool translucent =
       MusClient::ShouldMakeWidgetWindowsTranslucent(params);
@@ -529,6 +548,9 @@
   // otherwise focus goes to window().
   content_window()->Show();
 
+  if (show_state != ui::SHOW_STATE_MINIMIZED)
+    UpdateMinAndMaxSize();
+
   if (notify_visibility_change)
     native_widget_delegate_->OnNativeWidgetVisibilityChanged(true);
 
@@ -873,6 +895,7 @@
   if (widget->widget_delegate())
     behavior = widget->widget_delegate()->GetResizeBehavior();
   window()->SetProperty(aura::client::kResizeBehaviorKey, behavior);
+  UpdateMinAndMaxSize();
 }
 
 bool DesktopWindowTreeHostMus::ShouldUpdateWindowTransparency() const {
@@ -985,6 +1008,7 @@
     if (!max_size_in_pixels.IsEmpty())
       size.SetToMin(max_size_in_pixels);
     final_bounds_in_pixels.set_size(size);
+    UpdateMinAndMaxSize();
   }
   WindowTreeHostMus::SetBoundsInPixels(final_bounds_in_pixels,
                                        local_surface_id_allocation);
diff --git a/ui/views/mus/desktop_window_tree_host_mus.h b/ui/views/mus/desktop_window_tree_host_mus.h
index 4674a08..a95e38e 100644
--- a/ui/views/mus/desktop_window_tree_host_mus.h
+++ b/ui/views/mus/desktop_window_tree_host_mus.h
@@ -80,6 +80,9 @@
   // WindowObserver::OnWindowVisibilityChanged().
   void OnWindowTreeHostWindowVisibilityChanged(bool visible);
 
+  // Checks the minimum and the maximum size and notifies to the window service.
+  void UpdateMinAndMaxSize();
+
   // DesktopWindowTreeHost:
   void Init(const Widget::InitParams& params) override;
   void OnNativeWidgetCreated(const Widget::InitParams& params) override;
@@ -196,6 +199,10 @@
   // a change in the visibility of window().
   bool is_updating_window_visibility_ = false;
 
+  // The maximum size and the minimum size of the window.
+  gfx::Size max_size_;
+  gfx::Size min_size_;
+
   // aura::WindowObserver on window().
   std::unique_ptr<WindowTreeHostWindowObserver>
       window_tree_host_window_observer_;
diff --git a/ui/views/mus/desktop_window_tree_host_mus_unittest.cc b/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
index d4b6ac1..a0e976c 100644
--- a/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
+++ b/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
@@ -933,6 +933,83 @@
   transient_child->RemoveObserver(&observer);
 }
 
+class StaticSizedWidgetDelegate : public WidgetDelegateView {
+ public:
+  StaticSizedWidgetDelegate(const gfx::Size& min_size,
+                            const gfx::Size& max_size)
+      : min_size_(min_size), max_size_(max_size) {}
+  ~StaticSizedWidgetDelegate() override = default;
+
+  void SetMinMaxSize(const gfx::Size& min_size, const gfx::Size& max_size) {
+    min_size_ = min_size;
+    max_size_ = max_size;
+  }
+
+ private:
+  // View:
+  gfx::Size GetMinimumSize() const override { return min_size_; }
+  gfx::Size GetMaximumSize() const override { return max_size_; }
+
+  gfx::Size min_size_;
+  gfx::Size max_size_;
+
+  DISALLOW_COPY_AND_ASSIGN(StaticSizedWidgetDelegate);
+};
+
+TEST_F(DesktopWindowTreeHostMusTest, MinMaxSize) {
+  gfx::Size min_size(100, 100);
+  gfx::Size max_size(200, 200);
+  auto* delegate = new StaticSizedWidgetDelegate(min_size, max_size);
+  std::unique_ptr<Widget> widget = CreateWidget(delegate);
+  aura::Window* window = widget->GetNativeWindow()->GetRootWindow();
+
+  // min/max sizes are not yet set.
+  EXPECT_FALSE(window->GetProperty(aura::client::kMinimumSize));
+  EXPECT_FALSE(window->GetProperty(aura::client::kMaximumSize));
+
+  widget->Show();
+  EXPECT_EQ(min_size, *window->GetProperty(aura::client::kMinimumSize));
+  EXPECT_EQ(max_size, *window->GetProperty(aura::client::kMaximumSize));
+
+  // Changing the min/max size isn't propagated immediately.
+  gfx::Size min_size2(120, 130);
+  gfx::Size max_size2(190, 180);
+  delegate->SetMinMaxSize(min_size2, max_size2);
+  EXPECT_EQ(min_size, *window->GetProperty(aura::client::kMinimumSize));
+  EXPECT_EQ(max_size, *window->GetProperty(aura::client::kMaximumSize));
+
+  // Propagated when the widget gets resized.
+  widget->SetBounds(gfx::Rect(0, 0, 150, 150));
+  EXPECT_EQ(min_size2, *window->GetProperty(aura::client::kMinimumSize));
+  EXPECT_EQ(max_size2, *window->GetProperty(aura::client::kMaximumSize));
+
+  delegate->SetMinMaxSize(min_size, max_size);
+  // SizeConstraintsChanged should cause the update of min/max size.
+  widget->OnSizeConstraintsChanged();
+  EXPECT_EQ(min_size, *window->GetProperty(aura::client::kMinimumSize));
+  EXPECT_EQ(max_size, *window->GetProperty(aura::client::kMaximumSize));
+
+  // Re-show should propagate the information.
+  delegate->SetMinMaxSize(min_size2, max_size2);
+  widget->Hide();
+  widget->Show();
+  EXPECT_EQ(min_size2, *window->GetProperty(aura::client::kMinimumSize));
+  EXPECT_EQ(max_size2, *window->GetProperty(aura::client::kMaximumSize));
+
+  // If not changed, properties shouldn't be updated.
+  gfx::Size* min_ptr = window->GetProperty(aura::client::kMinimumSize);
+  gfx::Size* max_ptr = window->GetProperty(aura::client::kMaximumSize);
+  widget->OnSizeConstraintsChanged();
+  EXPECT_EQ(min_ptr, window->GetProperty(aura::client::kMinimumSize));
+  EXPECT_EQ(max_ptr, window->GetProperty(aura::client::kMaximumSize));
+
+  // If there are no limits, properties should be cleared.
+  delegate->SetMinMaxSize(gfx::Size(), gfx::Size());
+  widget->OnSizeConstraintsChanged();
+  EXPECT_FALSE(window->GetProperty(aura::client::kMinimumSize));
+  EXPECT_FALSE(window->GetProperty(aura::client::kMaximumSize));
+}
+
 // DesktopWindowTreeHostMusTest with --force-device-scale-factor=1.25.
 class DesktopWindowTreeHostMusTestFractionalDPI
     : public DesktopWindowTreeHostMusTest {
diff --git a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html
index 4bcb282..edf7f63 100644
--- a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html
+++ b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.html
@@ -12,8 +12,6 @@
   <template>
     <style include="cr-shared-style">
       :host {
-        /* TODO(crbug/925209): GG 900 in light mode seems overly dark. */
-        --cr-search-field-border-color: var(--cr-primary-text-color);
         display: flex;
         user-select: none;
       }
@@ -41,7 +39,7 @@
         --cr-input-error-display: none;
         --cr-input-input: {
           height: 100%;
-          border-bottom: 1px solid var(--cr-search-field-border-color);
+          border-bottom: 1px solid var(--cr-secondary-text-color);
         }
         --cr-input-padding-end: 0;
         --cr-input-padding-start: 0;
@@ -60,7 +58,6 @@
          * background of the container cr-input to white). Can we just not set
          * these rules? */
         --cr-input-background-color: inherit;
-        --cr-search-field-border-color: var(--cr-secondary-text-color);
       }
 
       :host([has-search-text]) cr-input {
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
index 8c3df8bf..5b8ba4f 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html
@@ -14,13 +14,13 @@
         --cr-toolbar-height: 56px;
         --paper-icon-button-ink-color: white;
         align-items: center;
+        /* TODO(dbeam): default background-color instead of external styling. */
         color: #fff;
         display: flex;
         height: var(--cr-toolbar-height);
       }
 
       :host-context([dark]) {
-        border-bottom: var(--cr-separator-line);
         color: var(--cr-secondary-text-color);
       }
 
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html
index 217cc5a..e750064 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html
@@ -11,7 +11,7 @@
   <template>
     <style include="cr-icons paper-button-style">
       :host {
-        background: white;
+        background-color: white;
         border-bottom: 1px solid var(--google-grey-300);
         bottom: 0;
         color: var(--google-grey-900);
@@ -29,10 +29,10 @@
       }
 
       :host-context([dark]) {
-        background: var(--google-grey-900);
+        background-color: var(--google-grey-900);
         background-image: linear-gradient(rgba(255, 255, 255, .04),
                                           rgba(255, 255, 255, .04));
-        border-bottom-color: var(--google-grey-refresh-700);
+        border-bottom-color: var(--cr-separator-color);
         color: var(--cr-secondary-text-color);
       }
 
diff --git a/ui/webui/resources/cr_elements/paper_button_style_css.html b/ui/webui/resources/cr_elements/paper_button_style_css.html
index 608cf8a1..e1a7665181 100644
--- a/ui/webui/resources/cr_elements/paper_button_style_css.html
+++ b/ui/webui/resources/cr_elements/paper_button_style_css.html
@@ -80,7 +80,7 @@
         color: var(--text-color);
         flex-shrink: 0;
         font-weight: 500;
-        height: 32px;
+        height: var(--cr-button-height);
         margin: 0;
         padding: 8px 16px;
         text-decoration: none;
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html
index fc8a660..0650ade 100644
--- a/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -34,6 +34,8 @@
     --google-blue-refresh-500-rgb: 66, 133, 244;  /* #4285f4 */
     --google-blue-refresh-500: rgb(var(--google-blue-refresh-500-rgb));
 
+    --google-green-refresh-300-rgb: 129, 201, 149;  /* #81c995 */
+    --google-green-refresh-300: rgb(var(--google-green-refresh-300-rgb));
     --google-green-refresh-700-rgb: 24, 128, 56;  /* #188038 */
     --google-green-refresh-700: rgb(var(--google-green-refresh-700-rgb));
 
@@ -111,6 +113,7 @@
     }
 
     --cr-button-edge-spacing: 12px;
+    --cr-button-height: 32px;
 
     /* Spacing between policy (controlledBy) indicator and control. */
     --cr-controlled-by-spacing: 24px;
diff --git a/ui/webui/resources/css/md_colors.css b/ui/webui/resources/css/md_colors.css
index 4166c6422..28a2dc1 100644
--- a/ui/webui/resources/css/md_colors.css
+++ b/ui/webui/resources/css/md_colors.css
@@ -2,23 +2,17 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-:root {
+html {
   /* This is a custom, Chrome-specific color that does not have a --paper or
    * --google equivalent. */
   --md-background-color: rgb(248, 249, 250);
   --md-loading-message-color: #6e6e6e;
-  /* This is --google-blue-700, rewritten as a native custom property for speed.
-   */
+  /* --google-blue-700, rewritten as a native custom property for speed. */
   --md-toolbar-color: rgb(51, 103, 214);
 }
 
-html[dark],
-:host-context(html[dark]) {
+html[dark] {
   --md-background-color: rgb(32, 33, 36);  /* --google-grey-900 */
   --md-loading-message-color: #9AA0A6;  /* --google-grey-refresh-500 */
-  --md-toolbar-border-color: rgb(95, 99, 104);  /* --google-grey-refresh-700 */
-  /* Dark mode doesn't currently have a different toolbar background color.
-   * Instead, it uses a 1px grey border at the bottom. We set it just in case
-   * opaqueness is required for some reason. */
-  --md-toolbar-color: var(--md-background-color);
+  --md-toolbar-color: rgba(255, 255, 255, .04);
 }