diff --git a/DEPS b/DEPS
index dc4abdd1..419c4057 100644
--- a/DEPS
+++ b/DEPS
@@ -138,11 +138,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'e08b443f9bda453c016d0cc4f73ad93352d539f3',
+  'skia_revision': 'b9ca863abadf980efa142c6ed7c83a42e547d2ce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'ab5fb228bcaf387cc72dfd93cdc62c1db4867ebf',
+  'v8_revision': 'd705884e5e49d607bb5695aec6892b828d67aa6c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -150,7 +150,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': '8b227a3b707d676fab6186384378c35ef3fefc14',
+  'angle_revision': 'c5c937e1e8bdaa4c0a02483af81e5fecd280ec9a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -158,7 +158,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': '215adebfd4d0bfc67e018eebcaffd6cf581657e7',
+  'pdfium_revision': '99cfd6bbb447e1143c2717cb274a07141400c7c5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -201,7 +201,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': '26377fc5ea907d3242cfdb8e1b4b9af11bbb486e',
+  'catapult_revision': '4a5c435597fa9808dc2a1019cae412916e5c120c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -807,7 +807,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ee08c785ae2217380beef4122ae9fe13e9e20755',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '90b7c4838a288a12e8eedff7571366422cf2c1bc',
       'condition': 'checkout_linux',
   },
 
@@ -822,7 +822,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'dfcfcfc36723154f1c574d1885b98d698b2587ac',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'd8ac0c37a3b2b13e26c458cc6efcceebbe52b3c7',
       'condition': 'checkout_linux',
   },
 
@@ -832,7 +832,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b1a3e411d38e2a8da3ba7ec81a4b9b95cde0993e',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '53ea429070b1d8c359f90309fd340b26d149a143',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1187,7 +1187,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '2c923646c7d3e0694acedb656b2e1cacf46bb6dd',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'c47d62b1e095a28a1e0e941a14ee7ebaa8ceb3e5',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1355,7 +1355,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6f0b34abee8dba611c253738d955c59f703c147a',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e3cc4895c26c3cc6ab901ac236ba355aa49e5c35',
+    Var('webrtc_git') + '/src.git' + '@' + '775c02ea0cd2684cd8ec5055dda7f2c2e882758d',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1396,7 +1396,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@16559b057d78e87b73f2e98258deb87fd192d27e',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a682bfc0ae99fc88dca3f01cd498a990695ac12d',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java
index f3b69c4..668db70 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java
@@ -14,6 +14,7 @@
 import com.android.webview.chromium.WebViewDelegateFactory.WebViewDelegate;
 
 import org.chromium.android_webview.AwContentsClient;
+import org.chromium.android_webview.AwHistogramRecorder;
 import org.chromium.android_webview.AwRenderProcess;
 import org.chromium.android_webview.AwSafeBrowsingResponse;
 import org.chromium.android_webview.AwWebResourceResponse;
@@ -102,6 +103,11 @@
                 result = mWebViewClient.shouldOverrideUrlLoading(mWebView, request.url);
             }
             if (TRACE) Log.i(TAG, "shouldOverrideUrlLoading result=" + result);
+
+            // Record UMA for shouldOverrideUrlLoading.
+            AwHistogramRecorder.recordCallbackInvocation(
+                    AwHistogramRecorder.WebViewCallbackType.SHOULD_OVERRIDE_URL_LOADING);
+
             return result;
         } finally {
             TraceEvent.end("WebViewContentsClientAdapter.shouldOverrideUrlLoading");
@@ -121,6 +127,11 @@
             } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                 ApiHelperForM.onPageCommitVisible(mWebViewClient, mWebView, url);
             }
+
+            // Record UMA for onPageCommitVisible.
+            AwHistogramRecorder.recordCallbackInvocation(
+                    AwHistogramRecorder.WebViewCallbackType.ON_PAGE_COMMIT_VISIBLE);
+
             // Otherwise, the API does not exist, so do nothing.
         } finally {
             TraceEvent.end("WebViewContentsClientAdapter.onPageCommitVisible");
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
index c1ba2d99d..bded5066 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewContentsClientAdapter.java
@@ -41,6 +41,7 @@
 import org.chromium.android_webview.AwContentsClient;
 import org.chromium.android_webview.AwContentsClientBridge;
 import org.chromium.android_webview.AwGeolocationPermissions;
+import org.chromium.android_webview.AwHistogramRecorder;
 import org.chromium.android_webview.AwHttpAuthHandler;
 import org.chromium.android_webview.AwRenderProcessGoneDetail;
 import org.chromium.android_webview.AwWebResourceResponse;
@@ -311,6 +312,10 @@
             TraceEvent.begin("WebViewContentsClientAdapter.onLoadResource");
             if (TRACE) Log.i(TAG, "onLoadResource=" + url);
             mWebViewClient.onLoadResource(mWebView, url);
+
+            // Record UMA for onLoadResource.
+            AwHistogramRecorder.recordCallbackInvocation(
+                    AwHistogramRecorder.WebViewCallbackType.ON_LOAD_RESOURCE);
         } finally {
             TraceEvent.end("WebViewContentsClientAdapter.onLoadResource");
         }
@@ -408,6 +413,11 @@
             TraceEvent.begin("WebViewContentsClientAdapter.onPageStarted");
             if (TRACE) Log.i(TAG, "onPageStarted=" + url);
             mWebViewClient.onPageStarted(mWebView, url, mWebView.getFavicon());
+
+            // Record UMA for onPageStarted.
+            AwHistogramRecorder.recordCallbackInvocation(
+                    AwHistogramRecorder.WebViewCallbackType.ON_PAGE_STARTED);
+
         } finally {
             TraceEvent.end("WebViewContentsClientAdapter.onPageStarted");
         }
@@ -423,6 +433,10 @@
             if (TRACE) Log.i(TAG, "onPageFinished=" + url);
             mWebViewClient.onPageFinished(mWebView, url);
 
+            // Record UMA for onPageFinished.
+            AwHistogramRecorder.recordCallbackInvocation(
+                    AwHistogramRecorder.WebViewCallbackType.ON_PAGE_FINISHED);
+
             // See b/8208948
             // This fakes an onNewPicture callback after onPageFinished to allow
             // CTS tests to run in an un-flaky manner. This is required as the
diff --git a/android_webview/java/src/org/chromium/android_webview/AwHistogramRecorder.java b/android_webview/java/src/org/chromium/android_webview/AwHistogramRecorder.java
index 17b44fc5..499bb221 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwHistogramRecorder.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwHistogramRecorder.java
@@ -19,15 +19,23 @@
     @IntDef({WebViewCallbackType.ON_RECEIVED_LOGIN_REQUEST,
             WebViewCallbackType.ON_RECEIVED_CLIENT_CERT_REQUEST,
             WebViewCallbackType.ON_RECEIVED_HTTP_AUTH_REQUEST,
-            WebViewCallbackType.ON_DOWNLOAD_START})
-    @interface WebViewCallbackType {
+            WebViewCallbackType.ON_DOWNLOAD_START, WebViewCallbackType.ON_PAGE_STARTED,
+            WebViewCallbackType.ON_PAGE_FINISHED, WebViewCallbackType.ON_LOAD_RESOURCE,
+            WebViewCallbackType.ON_PAGE_COMMIT_VISIBLE,
+            WebViewCallbackType.SHOULD_OVERRIDE_URL_LOADING})
+    public @interface WebViewCallbackType {
         // These values are used for UMA. Don't reuse or reorder values.
         // If you add something, update NUM_ENTRIES.
         int ON_RECEIVED_LOGIN_REQUEST = 0;
         int ON_RECEIVED_CLIENT_CERT_REQUEST = 1;
         int ON_RECEIVED_HTTP_AUTH_REQUEST = 2;
         int ON_DOWNLOAD_START = 3;
-        int NUM_ENTRIES = 4;
+        int ON_PAGE_STARTED = 4;
+        int ON_PAGE_FINISHED = 5;
+        int ON_LOAD_RESOURCE = 6;
+        int ON_PAGE_COMMIT_VISIBLE = 7;
+        int SHOULD_OVERRIDE_URL_LOADING = 8;
+        int NUM_ENTRIES = 9;
     }
 
     // not meant to be instantiated
diff --git a/ash/accessibility/accessibility_highlight_controller.cc b/ash/accessibility/accessibility_highlight_controller.cc
index 03d406f..99b7f79 100644
--- a/ash/accessibility/accessibility_highlight_controller.cc
+++ b/ash/accessibility/accessibility_highlight_controller.cc
@@ -12,7 +12,6 @@
 #include "ash/shell.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/text_input_client.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/event.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/core/cursor_manager.h"
@@ -49,10 +48,7 @@
   Shell::Get()->AddPreTargetHandler(this);
   Shell::Get()->cursor_manager()->AddObserver(this);
 
-  // In-process ash uses the InputMethod shared between ash and browser. Mash
-  // receives caret updates from the browser over mojo.
-  if (!::features::IsMultiProcessMash())
-    GetSharedInputMethod()->AddObserver(this);
+  GetSharedInputMethod()->AddObserver(this);
 }
 
 AccessibilityHighlightController::~AccessibilityHighlightController() {
@@ -62,8 +58,7 @@
   controller->HideCaretRing();
   controller->HideCursorRing();
 
-  if (!::features::IsMultiProcessMash())
-    GetSharedInputMethod()->RemoveObserver(this);
+  GetSharedInputMethod()->RemoveObserver(this);
   Shell::Get()->cursor_manager()->RemoveObserver(this);
   Shell::Get()->RemovePreTargetHandler(this);
 }
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc b/ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc
index 1c33baf..77c11de 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc
+++ b/ash/components/shortcut_viewer/views/keyboard_shortcut_view.cc
@@ -207,6 +207,14 @@
   return "KeyboardShortcutView";
 }
 
+ax::mojom::Role KeyboardShortcutView::GetAccessibleWindowRole() {
+  return ax::mojom::Role::kWindow;
+}
+
+base::string16 KeyboardShortcutView::GetAccessibleWindowTitle() const {
+  return l10n_util::GetStringUTF16(IDS_KSV_TITLE);
+}
+
 bool KeyboardShortcutView::AcceleratorPressed(
     const ui::Accelerator& accelerator) {
   DCHECK_EQ(ui::VKEY_W, accelerator.key_code());
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_view.h b/ash/components/shortcut_viewer/views/keyboard_shortcut_view.h
index bcd14be..9a983dfb6 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_view.h
+++ b/ash/components/shortcut_viewer/views/keyboard_shortcut_view.h
@@ -45,6 +45,8 @@
 
   // views::View:
   const char* GetClassName() const override;
+  ax::mojom::Role GetAccessibleWindowRole() override;
+  base::string16 GetAccessibleWindowTitle() const override;
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
   void Layout() override;
   gfx::Size CalculatePreferredSize() const override;
diff --git a/ash/display/display_color_manager.cc b/ash/display/display_color_manager.cc
index b892ba78..345bee3 100644
--- a/ash/display/display_color_manager.cc
+++ b/ash/display/display_color_manager.cc
@@ -19,7 +19,6 @@
 #include "base/threading/scoped_blocking_call.h"
 #include "components/quirks/quirks_manager.h"
 #include "third_party/qcms/src/qcms.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/display/types/display_snapshot.h"
@@ -324,12 +323,6 @@
     return false;
   }
 
-  // TODO: enable QuirksManager for mash. http://crbug.com/728748. Some tests
-  // don't create the Shell when running this code, hence the
-  // Shell::HasInstance() conditional.
-  if (Shell::HasInstance() && features::IsMultiProcessMash())
-    return false;
-
   const bool valid_product_code =
       display->product_code() != display::DisplaySnapshot::kInvalidProductCode;
   // TODO(mcasas): correct UMA s/Id/Code/, https://crbug.com/821393.
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc
index 7364e2a..b54e556f 100644
--- a/ash/display/window_tree_host_manager.cc
+++ b/ash/display/window_tree_host_manager.cc
@@ -824,19 +824,15 @@
   AshWindowTreeHost* ash_host =
       AshWindowTreeHost::Create(params_with_bounds).release();
   aura::WindowTreeHost* host = ash_host->AsWindowTreeHost();
-  // Out-of-process ash uses the IME mojo service. In-process ash uses a single
-  // input method shared between ash and browser code.
-  if (!::features::IsMultiProcessMash()) {
-    DCHECK(!host->has_input_method());
-    if (!input_method_) {  // Singleton input method instance for Ash.
-      input_method_ = ui::CreateInputMethod(this, host->GetAcceleratedWidget());
-      // Makes sure the input method is focused by default when created, because
-      // Ash uses singleton InputMethod and it won't call OnFocus/OnBlur when
-      // the active window changed.
-      input_method_->OnFocus();
-    }
-    host->SetSharedInputMethod(input_method_.get());
+  DCHECK(!host->has_input_method());
+  if (!input_method_) {  // Singleton input method instance for Ash.
+    input_method_ = ui::CreateInputMethod(this, host->GetAcceleratedWidget());
+    // Makes sure the input method is focused by default when created, because
+    // Ash uses singleton InputMethod and it won't call OnFocus/OnBlur when
+    // the active window changed.
+    input_method_->OnFocus();
   }
+  host->SetSharedInputMethod(input_method_.get());
 
   host->window()->SetName(base::StringPrintf(
       "%sRootWindow-%d", params_with_bounds.offscreen ? "Offscreen" : "",
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index 9142611..c9bf89f 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -376,20 +376,12 @@
     case ui::ET_SCROLL_FLING_START:
       Drop(translated_target, *translated_event.get());
       break;
-    case ui::ET_GESTURE_END:
-      // This case occurs when IsUsingWindowService() is true and the user
-      // presses, pauses, and releases a touch without any movement between.
-      // That gesture should be interpreted as a long tap and show a menu, etc.
-      // Classic Ash handles this scenario below via ET_GESTURE_LONG_TAP, while
-      // Mash handles it in DragDropControllerMus::OnPerformDragDropCompleted.
-      DoDragCancel(kTouchCancelAnimationDuration);
-      break;
     case ui::ET_GESTURE_LONG_TAP:
       // Ideally we would want to just forward this long tap event to the
       // |drag_source_window_|. However, webkit does not accept events while a
       // drag drop is still in progress. The drag drop ends only when the nested
       // message loop ends. Due to this stupidity, we have to defer forwarding
-      // the long tap. This only occurs when IsUsingWindowService() is false.
+      // the long tap.
       pending_long_tap_.reset(new ui::GestureEvent(
           *event,
           static_cast<aura::Window*>(drag_drop_tracker_->capture_window()),
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 40e8947..ba615ba 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -44,7 +44,6 @@
 #include "components/user_manager/user_type.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/base/user_activity/user_activity_detector.h"
 #include "ui/base/user_activity/user_activity_observer.h"
 #include "ui/display/display.h"
@@ -967,8 +966,6 @@
     for (int i = 0; i < 3; ++i)
       system_info_->AddChildView(create_info_label());
   }
-  if (::features::IsSingleProcessMash())
-    system_info_->AddChildView(create_info_label());
 
   if (show)
     system_info_->SetVisible(true);
@@ -982,8 +979,6 @@
   update_label(0, os_version_label_text);
   update_label(1, enterprise_info_text);
   update_label(2, bluetooth_name);
-  if (::features::IsSingleProcessMash())
-    update_label(3, "SingleProcessMash");
 
   LayoutTopHeader();
 }
diff --git a/ash/main.cc b/ash/main.cc
deleted file mode 100644
index 79a08b1..0000000
--- a/ash/main.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/ash_service.h"
-#include "base/feature_list.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/path_service.h"
-#include "base/run_loop.h"
-#include "base/task/single_thread_task_executor.h"
-#include "ui/base/material_design/material_design_controller.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_features.h"
-
-// This path is only hit in testing, not production. Production launches ash by
-// way of the utility process, which does not use this.
-void ServiceMain(service_manager::mojom::ServiceRequest request) {
-  logging::SetLogPrefix("ash");
-  // Load ash resources and strings.
-  // TODO: investigate nuking ash_service_resources and use the same resources
-  // that are used when AshService is launched via the utility process.
-  base::FilePath path;
-  base::PathService::Get(base::DIR_MODULE, &path);
-  base::FilePath ash_resources =
-      path.Append(FILE_PATH_LITERAL("ash_service_resources.pak"));
-  ui::ResourceBundle::InitSharedInstanceWithPakPath(ash_resources);
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  if (ui::ResourceBundle::IsScaleFactorSupported(ui::SCALE_FACTOR_200P)) {
-    base::FilePath ash_resources_200 =
-        path.Append(FILE_PATH_LITERAL("ash_service_resources_200.pak"));
-    rb.AddDataPackFromPath(ash_resources_200, ui::SCALE_FACTOR_200P);
-  }
-
-  // AshService has a distinct code path when running out of process. The out of
-  // process code path is triggered by way of |kMash|. As this code is only run
-  // when ash is out of process it has to force |kMash| to be enabled
-  // (otherwise AshService thinks it's running in process, which does not make
-  // sense, nor work).
-  DCHECK(base::FeatureList::GetInstance());
-  std::string enabled_features;
-  std::string disabled_features;
-  base::FeatureList::GetInstance()->GetCommandLineFeatureOverrides(
-      &enabled_features, &disabled_features);
-  if (!enabled_features.empty())
-    enabled_features += ",";
-  enabled_features += features::kMash.name;
-  // Disable SingleProcessMash, even if it's on by default in the test suite.
-  if (!disabled_features.empty())
-    disabled_features += ",";
-  disabled_features += features::kSingleProcessMash.name;
-  // This code path is really only for testing (production code uses the utility
-  // process to launch AshService), so it's ok to use a for-testing function.
-  base::FeatureList::ClearInstanceForTesting();
-  CHECK(base::FeatureList::InitializeInstance(enabled_features,
-                                              disabled_features));
-  CHECK(base::FeatureList::IsEnabled(features::kMash));
-  CHECK(!base::FeatureList::IsEnabled(features::kSingleProcessMash));
-
-  ui::MaterialDesignController::Initialize();
-
-  base::SingleThreadTaskExecutor main_task_executor(
-      base::MessagePump::Type::UI);
-  ash::AshService(std::move(request)).RunUntilTermination();
-}
diff --git a/ash/metrics/demo_session_metrics_recorder.cc b/ash/metrics/demo_session_metrics_recorder.cc
index 25450e76..a4067dd 100644
--- a/ash/metrics/demo_session_metrics_recorder.cc
+++ b/ash/metrics/demo_session_metrics_recorder.cc
@@ -145,18 +145,10 @@
   if (app_id == extension_misc::kChromeAppId)
     return DemoModeApp::kBrowser;
 
-  auto is_default = [](const std::string& app_id) {
-    if (!features::IsMultiProcessMash())
-      return app_id.empty();
-
-    return base::StartsWith(app_id, ShelfWindowWatcher::kDefaultShelfIdPrefix,
-                            base::CompareCase::SENSITIVE);
-  };
-
   // If the window is the "browser" type, having an app ID other than the
   // default indicates a hosted/bookmark app.
   if (app_type == AppType::CHROME_APP ||
-      (app_type == AppType::BROWSER && !is_default(app_id))) {
+      (app_type == AppType::BROWSER && !app_id.empty())) {
     return GetAppFromAppId(app_id);
   }
 
diff --git a/ash/shelf/shelf_window_watcher.cc b/ash/shelf/shelf_window_watcher.cc
index 7b36a92e..7f2c6b5 100644
--- a/ash/shelf/shelf_window_watcher.cc
+++ b/ash/shelf/shelf_window_watcher.cc
@@ -19,7 +19,6 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/resources/grit/ui_resources.h"
 #include "ui/wm/public/activation_client.h"
 
@@ -27,26 +26,12 @@
 namespace {
 
 // Returns the window's shelf item type property value.
-// MultiProcessMash also experimentally returns TYPE_DIALOG for some windows.
 ShelfItemType GetShelfItemType(aura::Window* window) {
-  if (features::IsMultiProcessMash() &&
-      window->GetProperty(kShelfItemTypeKey) == TYPE_UNDEFINED &&
-      window->type() == aura::client::WINDOW_TYPE_NORMAL) {
-    return TYPE_DIALOG;
-  }
   return static_cast<ShelfItemType>(window->GetProperty(kShelfItemTypeKey));
 }
 
 // Returns the window's shelf id property value.
-// MultiProcessMash also experimentally sets and returns default ids values.
 ShelfID GetShelfID(aura::Window* window) {
-  if (features::IsMultiProcessMash() && !window->GetProperty(kShelfIDKey)) {
-    static int id = 0;
-    const ash::ShelfID shelf_id(ShelfWindowWatcher::kDefaultShelfIdPrefix +
-                                std::to_string(id++));
-    window->SetProperty(kShelfIDKey, shelf_id.Serialize());
-    return shelf_id;
-  }
   return ShelfID::Deserialize(window->GetProperty(kShelfIDKey));
 }
 
@@ -116,20 +101,6 @@
     aura::Window* window,
     const void* key,
     intptr_t old) {
-  // ShelfIDs should rarely change beyond replacing Mash temporary defaults.
-  // Support ShelfID changes by removing the item; it will be re-added below.
-  if (features::IsMultiProcessMash() && key == kShelfIDKey &&
-      window_watcher_->user_windows_with_items_.count(window) > 0) {
-    ShelfID old_id = ShelfID::Deserialize(reinterpret_cast<std::string*>(old));
-    ShelfID new_id = ShelfID::Deserialize(window->GetProperty(kShelfIDKey));
-    if (old_id != new_id && !old_id.IsNull() && !new_id.IsNull() &&
-        window_watcher_->model_->ItemIndexByID(old_id) >= 0) {
-      window_watcher_->user_windows_with_items_.erase(window);
-      const int index = window_watcher_->model_->ItemIndexByID(old_id);
-      window_watcher_->model_->RemoveItemAt(index);
-    }
-  }
-
   if (key == kShelfIDKey && window == wm::GetActiveWindow()) {
     window_watcher_->model_->SetActiveShelfID(
         ShelfID::Deserialize(window->GetProperty(kShelfIDKey)));
diff --git a/ash/shelf/shelf_window_watcher.h b/ash/shelf/shelf_window_watcher.h
index 0eca483..4e5764c 100644
--- a/ash/shelf/shelf_window_watcher.h
+++ b/ash/shelf/shelf_window_watcher.h
@@ -20,9 +20,6 @@
 // ShelfWindowWatcher manages ShelfItems for dialogs in the default container
 // with valid ShelfItemType and ShelfID window properties (ie. task manager).
 // ShelfWindowWatcher also tracks the active shelf item via window activation.
-//
-// Some windows are experimentally given default properties in MultiProcessMash.
-// TODO(crbug.com/722496|887156): Resolve, KSV etc. tracking approach for Mash.
 class ShelfWindowWatcher : public ::wm::ActivationChangeObserver,
                            public ShellObserver {
  public:
diff --git a/ash/shell.cc b/ash/shell.cc
index 9e42f75..ecc719e6 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -845,10 +845,8 @@
 
   shell_delegate_.reset();
 
-  if (!::features::IsMultiProcessMash()) {
-    // Must be shut down after detachable_base_handler_.
-    chromeos::HammerdClient::Shutdown();
-  }
+  // Must be shut down after detachable_base_handler_.
+  chromeos::HammerdClient::Shutdown();
 
   for (auto& observer : shell_observers_)
     observer.OnShellDestroyed();
@@ -1156,8 +1154,7 @@
   user_metrics_recorder_->OnShellInitialized();
 
   // Initialize the D-Bus bus and services for ash.
-  if (!::features::IsMultiProcessMash())
-    ash_dbus_helper_ = AshDBusHelper::CreateWithExistingBus(dbus_bus);
+  ash_dbus_helper_ = AshDBusHelper::CreateWithExistingBus(dbus_bus);
   ash_dbus_services_ = std::make_unique<AshDBusServices>(dbus_bus.get());
 
   // By this point ash shell should have initialized its D-Bus signal
diff --git a/ash/shell/content/embedded_browser.cc b/ash/shell/content/embedded_browser.cc
index 9127df26..47301df8 100644
--- a/ash/shell/content/embedded_browser.cc
+++ b/ash/shell/content/embedded_browser.cc
@@ -10,7 +10,6 @@
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "content/public/browser/web_contents.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/layout/fill_layout.h"
@@ -37,9 +36,6 @@
 
  private:
   HostWidget(gfx::NativeWindow native_window) {
-    // Note this does not work under multi process mash.
-    DCHECK(!features::IsMultiProcessMash());
-
     SetLayoutManager(std::make_unique<views::FillLayout>());
 
     auto* widget = new views::Widget;
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index 2323d44..ea384b0b 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -37,7 +37,6 @@
 #include "ui/base/ime/text_input_client.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/range/range.h"
@@ -80,12 +79,8 @@
 
 // Returns true if the current input context type is password.
 bool IsInPasswordInputContext() {
-  // Avoid getting IMEBridge instance if ash is not in browser.
-  // This is to temporarily mute the crash (http://crbug.com/867084).
-  // TODO(shuchen): This will be eventually be solved by the Mojo-based IMF.
-  return !::features::IsMultiProcessMash() &&
-         ui::IMEBridge::Get()->GetCurrentInputContext().type ==
-             ui::TEXT_INPUT_TYPE_PASSWORD;
+  return ui::IMEBridge::Get()->GetCurrentInputContext().type ==
+         ui::TEXT_INPUT_TYPE_PASSWORD;
 }
 
 class ImeMenuLabel : public views::Label {
diff --git a/base/android/library_loader/library_prefetcher_unittest.cc b/base/android/library_loader/library_prefetcher_unittest.cc
index 697cd07..67ae24c 100644
--- a/base/android/library_loader/library_prefetcher_unittest.cc
+++ b/base/android/library_loader/library_prefetcher_unittest.cc
@@ -22,7 +22,7 @@
 const size_t kPageSize = 4096;
 }  // namespace
 
-TEST(NativeLibraryPrefetcherTest, DISABLED_TestPercentageOfResidentCode) {
+TEST(NativeLibraryPrefetcherTest, TestPercentageOfResidentCode) {
   size_t length = 4 * kPageSize;
   auto shared_region = base::WritableSharedMemoryRegion::Create(length);
   ASSERT_TRUE(shared_region.IsValid());
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index dd6c1ac..a3c2135 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8910186499721502448
\ No newline at end of file
+8910159406036687024
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 8355f76c6..3116fec2 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8910186980098289872
\ No newline at end of file
+8910161519679062064
\ No newline at end of file
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 786cd2b..b34ea29 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -132,6 +132,8 @@
     "layers/texture_layer_client.h",
     "layers/texture_layer_impl.cc",
     "layers/texture_layer_impl.h",
+    "layers/tile_size_calculator.cc",
+    "layers/tile_size_calculator.h",
     "layers/touch_action_region.cc",
     "layers/touch_action_region.h",
     "layers/ui_resource_layer.cc",
diff --git a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
index a1f8b53f..261141d 100644
--- a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
+++ b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
@@ -95,7 +95,7 @@
     return base_client_->CreateTile(info);
   }
 
-  gfx::Size CalculateTileSize(const gfx::Size& content_bounds) const override {
+  gfx::Size CalculateTileSize(const gfx::Size& content_bounds) override {
     return base_client_->CalculateTileSize(content_bounds);
   }
 
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 1bae1c75..644a34b 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -48,23 +48,6 @@
 // tiling's scale if the desired scale is within this ratio.
 const float kSnapToExistingTilingRatio = 1.2f;
 
-// Even for really wide viewports, at some point GPU raster should use
-// less than 4 tiles to fill the viewport. This is set to 256 as a
-// sane minimum for now, but we might want to tune this for low-end.
-const int kMinHeightForGpuRasteredTile = 256;
-
-// When making odd-sized tiles, round them up to increase the chances
-// of using the same tile size.
-const int kTileRoundUp = 64;
-
-// Round GPU default tile sizes to a multiple of 32. This helps prevent
-// rounding errors during compositing.
-const int kGpuDefaultTileRoundUp = 32;
-
-// For performance reasons and to support compressed tile textures, tile
-// width and height should be an even multiple of 4 in size.
-const int kTileMinimalAlignment = 4;
-
 // Large contents scale can cause overflow issues. Cap the ideal contents scale
 // by this constant, since scales larger than this are usually not correct or
 // their scale doesn't matter as long as it's large. Content scales usually
@@ -92,55 +75,6 @@
                    static_cast<int>(rb - ry));
 }
 
-// This function converts the given |device_pixels_size| to the expected size
-// of content which was generated to fill it at 100%.  This takes into account
-// the ceil operations that occur as device pixels are converted to/from DIPs
-// (content size must be a whole number of DIPs).
-gfx::Size ApplyDsfAdjustment(gfx::Size device_pixels_size, float dsf) {
-  gfx::Size content_size_in_dips =
-      gfx::ScaleToCeiledSize(device_pixels_size, 1.0f / dsf);
-  gfx::Size content_size_in_dps =
-      gfx::ScaleToCeiledSize(content_size_in_dips, dsf);
-  return content_size_in_dps;
-}
-
-// For GPU rasterization, we pick an ideal tile size using the viewport so we
-// don't need any settings. The current approach uses 4 tiles to cover the
-// viewport vertically.
-gfx::Size CalculateGpuTileSize(const gfx::Size& base_tile_size,
-                               const gfx::Size& content_bounds,
-                               const gfx::Size& max_tile_size) {
-  int tile_width = base_tile_size.width();
-
-  // Increase the height proportionally as the width decreases, and pad by our
-  // border texels to make the tiles exactly match the viewport.
-  int divisor = 4;
-  if (content_bounds.width() <= base_tile_size.width() / 2)
-    divisor = 2;
-  if (content_bounds.width() <= base_tile_size.width() / 4)
-    divisor = 1;
-  int tile_height =
-      MathUtil::UncheckedRoundUp(base_tile_size.height(), divisor) / divisor;
-
-  // Grow default sizes to account for overlapping border texels.
-  tile_width += 2 * PictureLayerTiling::kBorderTexels;
-  tile_height += 2 * PictureLayerTiling::kBorderTexels;
-
-  // Round GPU default tile sizes to a multiple of kGpuDefaultTileAlignment.
-  // This helps prevent rounding errors in our CA path. https://crbug.com/632274
-  tile_width = MathUtil::UncheckedRoundUp(tile_width, kGpuDefaultTileRoundUp);
-  tile_height = MathUtil::UncheckedRoundUp(tile_height, kGpuDefaultTileRoundUp);
-
-  tile_height = std::max(tile_height, kMinHeightForGpuRasteredTile);
-
-  if (!max_tile_size.IsEmpty()) {
-    tile_width = std::min(tile_width, max_tile_size.width());
-    tile_height = std::min(tile_height, max_tile_size.height());
-  }
-
-  return gfx::Size(tile_width, tile_height);
-}
-
 }  // namespace
 
 PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl,
@@ -164,7 +98,8 @@
       nearest_neighbor_(false),
       use_transformed_rasterization_(false),
       is_directly_composited_image_(false),
-      can_use_lcd_text_(true) {
+      can_use_lcd_text_(true),
+      tile_size_calculator_(this) {
   layer_tree_impl()->RegisterPictureLayerImpl(this);
 }
 
@@ -952,90 +887,9 @@
   return false;
 }
 
-gfx::Size PictureLayerImpl::CalculateTileSize(
-    const gfx::Size& content_bounds) const {
-  int max_texture_size = layer_tree_impl()->max_texture_size();
-
-  if (mask_type_ == Layer::LayerMaskType::SINGLE_TEXTURE_MASK) {
-    // Masks are not tiled, so if we can't cover the whole mask with one tile,
-    // we shouldn't have such a tiling at all.
-    DCHECK_LE(content_bounds.width(), max_texture_size);
-    DCHECK_LE(content_bounds.height(), max_texture_size);
-    return content_bounds;
-  }
-
-  int default_tile_width = 0;
-  int default_tile_height = 0;
-  if (layer_tree_impl()->use_gpu_rasterization()) {
-    gfx::Size max_tile_size =
-        layer_tree_impl()->settings().max_gpu_raster_tile_size;
-
-    // Calculate |base_tile_size based| on |gpu_raster_max_texture_size_|,
-    // adjusting for ceil operations that may occur due to DSF.
-    gfx::Size base_tile_size = ApplyDsfAdjustment(
-        gpu_raster_max_texture_size_, layer_tree_impl()->device_scale_factor());
-
-    // Set our initial size assuming a |base_tile_size| equal to our
-    // |viewport_size|.
-    gfx::Size default_tile_size =
-        CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size);
-
-    // Use half-width GPU tiles when the content_width is greater than our
-    // calculated tile size.
-    if (content_bounds.width() > default_tile_size.width()) {
-      // Divide width by 2 and round up.
-      base_tile_size.set_width((base_tile_size.width() + 1) / 2);
-      default_tile_size =
-          CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size);
-    }
-
-    default_tile_width = default_tile_size.width();
-    default_tile_height = default_tile_size.height();
-  } else {
-    // For CPU rasterization we use tile-size settings.
-    const LayerTreeSettings& settings = layer_tree_impl()->settings();
-    int max_untiled_content_width = settings.max_untiled_layer_size.width();
-    int max_untiled_content_height = settings.max_untiled_layer_size.height();
-    default_tile_width = settings.default_tile_size.width();
-    default_tile_height = settings.default_tile_size.height();
-
-    // If the content width is small, increase tile size vertically.
-    // If the content height is small, increase tile size horizontally.
-    // If both are less than the untiled-size, use a single tile.
-    if (content_bounds.width() < default_tile_width)
-      default_tile_height = max_untiled_content_height;
-    if (content_bounds.height() < default_tile_height)
-      default_tile_width = max_untiled_content_width;
-    if (content_bounds.width() < max_untiled_content_width &&
-        content_bounds.height() < max_untiled_content_height) {
-      default_tile_height = max_untiled_content_height;
-      default_tile_width = max_untiled_content_width;
-    }
-  }
-
-  int tile_width = default_tile_width;
-  int tile_height = default_tile_height;
-
-  // Clamp the tile width/height to the content width/height to save space.
-  if (content_bounds.width() < default_tile_width) {
-    tile_width = std::min(tile_width, content_bounds.width());
-    tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileRoundUp);
-    tile_width = std::min(tile_width, default_tile_width);
-  }
-  if (content_bounds.height() < default_tile_height) {
-    tile_height = std::min(tile_height, content_bounds.height());
-    tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileRoundUp);
-    tile_height = std::min(tile_height, default_tile_height);
-  }
-
-  // Ensure that tile width and height are properly aligned.
-  tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileMinimalAlignment);
-  tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileMinimalAlignment);
-
-  // Under no circumstance should we be larger than the max texture size.
-  tile_width = std::min(tile_width, max_texture_size);
-  tile_height = std::min(tile_height, max_texture_size);
-  return gfx::Size(tile_width, tile_height);
+gfx::Size PictureLayerImpl::CalculateTileSize(const gfx::Size& content_bounds) {
+  content_bounds_ = content_bounds;
+  return tile_size_calculator_.CalculateTileSize();
 }
 
 void PictureLayerImpl::GetContentsResourceId(
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index 40fab0ce..6d564e1 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -15,6 +15,7 @@
 #include "cc/cc_export.h"
 #include "cc/layers/layer.h"
 #include "cc/layers/layer_impl.h"
+#include "cc/layers/tile_size_calculator.h"
 #include "cc/paint/image_id.h"
 #include "cc/tiles/picture_layer_tiling.h"
 #include "cc/tiles/picture_layer_tiling_set.h"
@@ -60,7 +61,7 @@
 
   // PictureLayerTilingClient overrides.
   std::unique_ptr<Tile> CreateTile(const Tile::CreateInfo& info) override;
-  gfx::Size CalculateTileSize(const gfx::Size& content_bounds) const override;
+  gfx::Size CalculateTileSize(const gfx::Size& content_bounds) override;
   const Region* GetPendingInvalidation() override;
   const PictureLayerTiling* GetPendingOrActiveTwinTiling(
       const PictureLayerTiling* tiling) const override;
@@ -74,6 +75,11 @@
   void set_gpu_raster_max_texture_size(gfx::Size gpu_raster_max_texture_size) {
     gpu_raster_max_texture_size_ = gpu_raster_max_texture_size;
   }
+
+  gfx::Size gpu_raster_max_texture_size() {
+    return gpu_raster_max_texture_size_;
+  }
+
   using PaintWorkletRecordMap =
       base::flat_map<scoped_refptr<PaintWorkletInput>, sk_sp<PaintRecord>>;
   void UpdateRasterSource(
@@ -138,6 +144,8 @@
     return paint_worklet_records_;
   }
 
+  gfx::Size content_bounds() { return content_bounds_; }
+
  protected:
   PictureLayerImpl(LayerTreeImpl* tree_impl,
                    int id,
@@ -231,6 +239,9 @@
   // asynchronously on a worklet thread, triggered from
   // |LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation|.
   PaintWorkletRecordMap paint_worklet_records_;
+
+  gfx::Size content_bounds_;
+  TileSizeCalculator tile_size_calculator_;
 };
 
 }  // namespace cc
diff --git a/cc/layers/tile_size_calculator.cc b/cc/layers/tile_size_calculator.cc
new file mode 100644
index 0000000..6c071ca
--- /dev/null
+++ b/cc/layers/tile_size_calculator.cc
@@ -0,0 +1,221 @@
+// 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 "cc/layers/tile_size_calculator.h"
+
+#include "cc/base/math_util.h"
+#include "cc/layers/picture_layer_impl.h"
+#include "cc/trees/layer_tree_impl.h"
+
+namespace cc {
+namespace {
+// Even for really wide viewports, at some point GPU raster should use
+// less than 4 tiles to fill the viewport. This is set to 256 as a
+// sane minimum for now, but we might want to tune this for low-end.
+const int kMinHeightForGpuRasteredTile = 256;
+
+// When making odd-sized tiles, round them up to increase the chances
+// of using the same tile size.
+const int kTileRoundUp = 64;
+
+// Round GPU default tile sizes to a multiple of 32. This helps prevent
+// rounding errors during compositing.
+const int kGpuDefaultTileRoundUp = 32;
+
+// For performance reasons and to support compressed tile textures, tile
+// width and height should be an even multiple of 4 in size.
+const int kTileMinimalAlignment = 4;
+
+// This function converts the given |device_pixels_size| to the expected size
+// of content which was generated to fill it at 100%.  This takes into account
+// the ceil operations that occur as device pixels are converted to/from DIPs
+// (content size must be a whole number of DIPs).
+gfx::Size ApplyDsfAdjustment(const gfx::Size& device_pixels_size, float dsf) {
+  gfx::Size content_size_in_dips =
+      gfx::ScaleToCeiledSize(device_pixels_size, 1.0f / dsf);
+  gfx::Size content_size_in_dps =
+      gfx::ScaleToCeiledSize(content_size_in_dips, dsf);
+  return content_size_in_dps;
+}
+
+// For GPU rasterization, we pick an ideal tile size using the viewport so we
+// don't need any settings. The current approach uses 4 tiles to cover the
+// viewport vertically.
+gfx::Size CalculateGpuTileSize(const gfx::Size& base_tile_size,
+                               const gfx::Size& content_bounds,
+                               const gfx::Size& max_tile_size) {
+  int tile_width = base_tile_size.width();
+
+  // Increase the height proportionally as the width decreases, and pad by our
+  // border texels to make the tiles exactly match the viewport.
+  int divisor = 4;
+  if (content_bounds.width() <= base_tile_size.width() / 2)
+    divisor = 2;
+  if (content_bounds.width() <= base_tile_size.width() / 4)
+    divisor = 1;
+  int tile_height =
+      MathUtil::UncheckedRoundUp(base_tile_size.height(), divisor) / divisor;
+
+  // Grow default sizes to account for overlapping border texels.
+  tile_width += 2 * PictureLayerTiling::kBorderTexels;
+  tile_height += 2 * PictureLayerTiling::kBorderTexels;
+
+  // Round GPU default tile sizes to a multiple of kGpuDefaultTileAlignment.
+  // This helps prevent rounding errors in our CA path. https://crbug.com/632274
+  tile_width = MathUtil::UncheckedRoundUp(tile_width, kGpuDefaultTileRoundUp);
+  tile_height = MathUtil::UncheckedRoundUp(tile_height, kGpuDefaultTileRoundUp);
+
+  tile_height = std::max(tile_height, kMinHeightForGpuRasteredTile);
+
+  if (!max_tile_size.IsEmpty()) {
+    tile_width = std::min(tile_width, max_tile_size.width());
+    tile_height = std::min(tile_height, max_tile_size.height());
+  }
+
+  return gfx::Size(tile_width, tile_height);
+}
+
+}  // namespace
+
+// AffectingParams.
+bool TileSizeCalculator::AffectingParams::operator==(
+    const AffectingParams& other) {
+  return max_texture_size == other.max_texture_size &&
+         use_gpu_rasterization == other.use_gpu_rasterization &&
+         device_scale_factor == other.device_scale_factor &&
+         max_tile_size == other.max_tile_size &&
+         gpu_raster_max_texture_size == other.gpu_raster_max_texture_size &&
+         max_untiled_layer_size == other.max_untiled_layer_size &&
+         default_tile_size == other.default_tile_size &&
+         layer_content_bounds == other.layer_content_bounds;
+}
+
+// TileSizeCalculator.
+TileSizeCalculator::TileSizeCalculator(PictureLayerImpl* layer_impl)
+    : layer_impl_(layer_impl) {}
+
+bool TileSizeCalculator::IsAffectingParamsChanged() {
+  AffectingParams new_params = GetAffectingParams();
+
+  if (affecting_params_ == new_params)
+    return false;
+
+  affecting_params_ = new_params;
+  return true;
+}
+
+TileSizeCalculator::AffectingParams TileSizeCalculator::GetAffectingParams() {
+  AffectingParams params;
+  LayerTreeImpl* layer_tree_impl = layer_impl()->layer_tree_impl();
+  params.max_texture_size = layer_tree_impl->max_texture_size();
+  params.use_gpu_rasterization = layer_tree_impl->use_gpu_rasterization();
+  params.max_tile_size = layer_tree_impl->settings().max_gpu_raster_tile_size;
+  params.gpu_raster_max_texture_size =
+      layer_impl()->gpu_raster_max_texture_size();
+  params.device_scale_factor = layer_tree_impl->device_scale_factor();
+  params.max_untiled_layer_size =
+      layer_tree_impl->settings().max_untiled_layer_size;
+  params.default_tile_size = layer_tree_impl->settings().default_tile_size;
+  params.layer_content_bounds = layer_impl()->content_bounds();
+  return params;
+}
+
+gfx::Size TileSizeCalculator::CalculateTileSize() {
+  gfx::Size content_bounds = layer_impl()->content_bounds();
+
+  if (layer_impl()->mask_type() == Layer::LayerMaskType::SINGLE_TEXTURE_MASK) {
+    // Masks are not tiled, so if we can't cover the whole mask with one tile,
+    // we shouldn't have such a tiling at all.
+    DCHECK_LE(content_bounds.width(),
+              layer_impl()->layer_tree_impl()->max_texture_size());
+    DCHECK_LE(content_bounds.height(),
+              layer_impl()->layer_tree_impl()->max_texture_size());
+    return content_bounds;
+  }
+
+  // If |affecting_params_| is already computed and not changed, return
+  // pre-calculated tile size.
+  if (!IsAffectingParamsChanged())
+    return tile_size_;
+
+  int default_tile_width = 0;
+  int default_tile_height = 0;
+  if (affecting_params_.use_gpu_rasterization) {
+    gfx::Size max_tile_size = affecting_params_.max_tile_size;
+
+    // Calculate |base_tile_size| based on |gpu_raster_max_texture_size|,
+    // adjusting for ceil operations that may occur due to DSF.
+    gfx::Size base_tile_size =
+        ApplyDsfAdjustment(affecting_params_.gpu_raster_max_texture_size,
+                           affecting_params_.device_scale_factor);
+
+    // Set our initial size assuming a |base_tile_size| equal to our
+    // |viewport_size|.
+    gfx::Size default_tile_size =
+        CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size);
+
+    // Use half-width GPU tiles when the content_width is greater than our
+    // calculated tile size.
+    if (content_bounds.width() > default_tile_size.width()) {
+      // Divide width by 2 and round up.
+      base_tile_size.set_width((base_tile_size.width() + 1) / 2);
+      default_tile_size =
+          CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size);
+    }
+
+    default_tile_width = default_tile_size.width();
+    default_tile_height = default_tile_size.height();
+  } else {
+    // For CPU rasterization we use tile-size settings.
+    int max_untiled_content_width =
+        affecting_params_.max_untiled_layer_size.width();
+    int max_untiled_content_height =
+        affecting_params_.max_untiled_layer_size.height();
+    default_tile_width = affecting_params_.default_tile_size.width();
+    default_tile_height = affecting_params_.default_tile_size.height();
+
+    // If the content width is small, increase tile size vertically.
+    // If the content height is small, increase tile size horizontally.
+    // If both are less than the untiled-size, use a single tile.
+    if (content_bounds.width() < default_tile_width)
+      default_tile_height = max_untiled_content_height;
+    if (content_bounds.height() < default_tile_height)
+      default_tile_width = max_untiled_content_width;
+    if (content_bounds.width() < max_untiled_content_width &&
+        content_bounds.height() < max_untiled_content_height) {
+      default_tile_height = max_untiled_content_height;
+      default_tile_width = max_untiled_content_width;
+    }
+  }
+
+  int tile_width = default_tile_width;
+  int tile_height = default_tile_height;
+
+  // Clamp the tile width/height to the content width/height to save space.
+  if (content_bounds.width() < default_tile_width) {
+    tile_width = std::min(tile_width, content_bounds.width());
+    tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileRoundUp);
+    tile_width = std::min(tile_width, default_tile_width);
+  }
+  if (content_bounds.height() < default_tile_height) {
+    tile_height = std::min(tile_height, content_bounds.height());
+    tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileRoundUp);
+    tile_height = std::min(tile_height, default_tile_height);
+  }
+
+  // Ensure that tile width and height are properly aligned.
+  tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileMinimalAlignment);
+  tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileMinimalAlignment);
+
+  // Under no circumstance should we be larger than the max texture size.
+  tile_width = std::min(tile_width, affecting_params_.max_texture_size);
+  tile_height = std::min(tile_height, affecting_params_.max_texture_size);
+
+  // Store the calculated tile size.
+  tile_size_ = gfx::Size(tile_width, tile_height);
+
+  return tile_size_;
+}
+
+}  // namespace cc
diff --git a/cc/layers/tile_size_calculator.h b/cc/layers/tile_size_calculator.h
new file mode 100644
index 0000000..cd455b92
--- /dev/null
+++ b/cc/layers/tile_size_calculator.h
@@ -0,0 +1,50 @@
+// 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 CC_LAYERS_TILE_SIZE_CALCULATOR_H_
+#define CC_LAYERS_TILE_SIZE_CALCULATOR_H_
+
+#include "cc/cc_export.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace cc {
+
+class PictureLayerImpl;
+
+// This class calculates the tile size only when the |affecting_params_|
+// or |content_bounds_| is changed.
+class CC_EXPORT TileSizeCalculator {
+ public:
+  explicit TileSizeCalculator(PictureLayerImpl* layer_impl);
+
+  gfx::Size CalculateTileSize();
+
+ private:
+  struct AffectingParams {
+    int max_texture_size = 0;
+    bool use_gpu_rasterization = false;
+    float device_scale_factor = 0.0f;
+    gfx::Size max_tile_size;
+    gfx::Size gpu_raster_max_texture_size;
+    gfx::Size max_untiled_layer_size;
+    gfx::Size default_tile_size;
+    gfx::Size layer_content_bounds;
+
+    bool operator==(const AffectingParams& other);
+  };
+
+  PictureLayerImpl* layer_impl() const { return layer_impl_; }
+  AffectingParams GetAffectingParams();
+  bool IsAffectingParamsChanged();
+
+  PictureLayerImpl* layer_impl_;
+
+  AffectingParams affecting_params_;
+
+  gfx::Size tile_size_;
+};
+
+}  // namespace cc
+
+#endif  // CC_LAYERS_TILE_SIZE_CALCULATOR_H_
diff --git a/cc/test/fake_picture_layer_impl.cc b/cc/test/fake_picture_layer_impl.cc
index 73f05851..d6b54221 100644
--- a/cc/test/fake_picture_layer_impl.cc
+++ b/cc/test/fake_picture_layer_impl.cc
@@ -60,7 +60,7 @@
 }
 
 gfx::Size FakePictureLayerImpl::CalculateTileSize(
-    const gfx::Size& content_bounds) const {
+    const gfx::Size& content_bounds) {
   if (fixed_tile_size_.IsEmpty()) {
     return PictureLayerImpl::CalculateTileSize(content_bounds);
   }
diff --git a/cc/test/fake_picture_layer_impl.h b/cc/test/fake_picture_layer_impl.h
index bf1d81ec..703821cd 100644
--- a/cc/test/fake_picture_layer_impl.h
+++ b/cc/test/fake_picture_layer_impl.h
@@ -85,7 +85,7 @@
   void PushPropertiesTo(LayerImpl* layer_impl) override;
   void AppendQuads(viz::RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
-  gfx::Size CalculateTileSize(const gfx::Size& content_bounds) const override;
+  gfx::Size CalculateTileSize(const gfx::Size& content_bounds) override;
 
   void DidBecomeActive() override;
   size_t did_become_active_call_count() {
diff --git a/cc/test/fake_picture_layer_tiling_client.cc b/cc/test/fake_picture_layer_tiling_client.cc
index d2637665..7acb267e 100644
--- a/cc/test/fake_picture_layer_tiling_client.cc
+++ b/cc/test/fake_picture_layer_tiling_client.cc
@@ -49,7 +49,7 @@
 }
 
 gfx::Size FakePictureLayerTilingClient::CalculateTileSize(
-    const gfx::Size& /* content_bounds */) const {
+    const gfx::Size& /* content_bounds */) {
   return tile_size_;
 }
 
diff --git a/cc/test/fake_picture_layer_tiling_client.h b/cc/test/fake_picture_layer_tiling_client.h
index aa38db905..2ebdf1d 100644
--- a/cc/test/fake_picture_layer_tiling_client.h
+++ b/cc/test/fake_picture_layer_tiling_client.h
@@ -29,7 +29,7 @@
 
   // PictureLayerTilingClient implementation.
   std::unique_ptr<Tile> CreateTile(const Tile::CreateInfo& info) override;
-  gfx::Size CalculateTileSize(const gfx::Size& content_bounds) const override;
+  gfx::Size CalculateTileSize(const gfx::Size& content_bounds) override;
   bool HasValidTilePriorities() const override;
 
   void SetTileSize(const gfx::Size& tile_size);
diff --git a/cc/tiles/picture_layer_tiling.h b/cc/tiles/picture_layer_tiling.h
index 4f66d99..417fb14 100644
--- a/cc/tiles/picture_layer_tiling.h
+++ b/cc/tiles/picture_layer_tiling.h
@@ -40,8 +40,7 @@
   // Create a tile at the given content_rect (in the contents scale of the
   // tiling) This might return null if the client cannot create such a tile.
   virtual std::unique_ptr<Tile> CreateTile(const Tile::CreateInfo& info) = 0;
-  virtual gfx::Size CalculateTileSize(
-    const gfx::Size& content_bounds) const = 0;
+  virtual gfx::Size CalculateTileSize(const gfx::Size& content_bounds) = 0;
   // This invalidation region defines the area (if any, it can by null) that
   // tiles can not be shared between pending and active trees.
   virtual const Region* GetPendingInvalidation() = 0;
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 329983c..1516c9b2 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -170,7 +170,7 @@
           "$root_out_dir/locales/en-US.pak",
           "$root_out_dir/locales/fr.pak",
         ]
-        if (enable_hidpi || enable_mus) {
+        if (enable_hidpi) {
           data += [ "$root_out_dir/chrome_200_percent.pak" ]
         }
       }
@@ -373,20 +373,6 @@
       sources += [ "app/chrome_exe_main_aura.cc" ]
     }
   }
-
-  # TODO(jonross): remove this once telemetry can differentiate between linux
-  # and linux chromiumos.
-  if (enable_mus) {
-    chrome_binary("chrome_test") {
-      # Note that the output name needs to end with "chrome", because that's
-      # what telemetry test-runner expects.
-      output_name = "test_chrome"
-      sources = []
-      if (!is_win && use_aura) {
-        sources += [ "app/chrome_exe_main_aura.cc" ]
-      }
-    }
-  }
 }  # !is_android && !is_mac
 
 if (is_win) {
diff --git a/chrome/VERSION b/chrome/VERSION
index 128a7e8..0edb812 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=77
 MINOR=0
-BUILD=3831
+BUILD=3832
 PATCH=0
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 8e39e99..6f17ceb 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -65,6 +65,7 @@
   java_files = [
     "java/src/org/chromium/chrome/browser/autofill_assistant/AbstractListObserver.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantKeyboardCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java",
@@ -75,6 +76,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryFactoryImpl.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/EditDistance.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/SizeListenableLinearLayout.java",
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_bottom_sheet_content.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_bottom_sheet_content.xml
index 40e8182..1496545 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_bottom_sheet_content.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_bottom_sheet_content.xml
@@ -2,7 +2,7 @@
 <!-- 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. -->
-<org.chromium.chrome.browser.autofill_assistant.SizeListenableLinearLayout
+<LinearLayout
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/autofill_assistant"
@@ -62,4 +62,4 @@
             android:clipChildren="false"
             android:clipToPadding="false" />
     </org.chromium.chrome.browser.widget.FadingEdgeScrollView>
-</org.chromium.chrome.browser.autofill_assistant.SizeListenableLinearLayout>
+</LinearLayout>
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_onboarding.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_onboarding.xml
index 95df51e..8ff7526 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_onboarding.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_onboarding.xml
@@ -3,124 +3,127 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<LinearLayout
+<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/assistant_onboarding"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingTop="12dp"
-    android:paddingBottom="14dp"
-    android:paddingStart="24dp"
-    android:paddingEnd="24dp"
-    android:gravity="center_horizontal"
-    android:orientation="vertical">
-
-    <!-- Image background -->
-    <ImageView
-        android:id="@+id/onboarding_image"
-        tools:ignore="contentDescription"
-        android:layout_width="250dp"
-        android:layout_height="wrap_content"
-        android:scaleType="centerCrop"
-        android:src="@drawable/autofill_assistant_onboarding_bg"
-        android:layout_marginTop="12dp" />
-
-    <!-- "Try Google Assistant in Chrome" -->
-    <ImageView
-        android:id="@+id/onboarding_try_assistant"
-        tools:ignore="contentDescription"
-        android:layout_width="185dp"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="16dp"
-        android:src="@drawable/autofill_assistant_onboarding_try" />
-
-    <!-- Subtitle (e.g., 'Google Assistant saves you time...')-->
+    android:layout_height="wrap_content">
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="1dp"
-            android:layout_weight="1"/>
-        <!-- The subtitle takes 4/6 of the screen width. -->
-        <TextView
-            android:id="@+id/onboarding_subtitle"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="4"
-            android:layout_marginTop="24dp"
-            android:textAppearance="@style/TextAppearance.AssistantBlackBody"
-            android:gravity="center_horizontal"
-            android:text="@string/autofill_assistant_init_message" />
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="1dp"
-            android:layout_weight="1"/>
-    </LinearLayout>
-
-    <!-- Separator -->
-    <LinearLayout
-        android:id="@+id/onboarding_separator"
-        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="24dp"
+        android:paddingTop="12dp"
+        android:paddingBottom="14dp"
+        android:paddingStart="24dp"
+        android:paddingEnd="24dp"
         android:gravity="center_horizontal"
-        android:orientation="horizontal"
-        android:weightSum="1.0">
-        <View style="@style/HorizontalDivider"
-            android:layout_width="0dp"
-            android:layout_height="1dp"
-            android:layout_weight="0.33"/>
-    </LinearLayout>
-    <Space android:layout_width="0dp" android:layout_height="24dp"/>
+        android:orientation="vertical">
 
-    <!-- Terms and Conditions message and link -->
-    <LinearLayout
-        android:layout_height="wrap_content"
+        <!-- Image background -->
+        <ImageView
+            android:id="@+id/onboarding_image"
+            tools:ignore="contentDescription"
+            android:layout_width="250dp"
+            android:layout_height="wrap_content"
+            android:scaleType="centerCrop"
+            android:src="@drawable/autofill_assistant_onboarding_bg"
+            android:layout_marginTop="12dp" />
+
+        <!-- "Try Google Assistant in Chrome" -->
+        <ImageView
+            android:id="@+id/onboarding_try_assistant"
+            tools:ignore="contentDescription"
+            android:layout_width="185dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dp"
+            android:src="@drawable/autofill_assistant_onboarding_try" />
+
+        <!-- Subtitle (e.g., 'Google Assistant saves you time...')-->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="1dp"
+                android:layout_weight="1"/>
+            <!-- The subtitle takes 4/6 of the screen width. -->
+            <TextView
+                android:id="@+id/onboarding_subtitle"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="4"
+                android:layout_marginTop="24dp"
+                android:textAppearance="@style/TextAppearance.AssistantBlackBody"
+                android:gravity="center_horizontal"
+                android:text="@string/autofill_assistant_init_message" />
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="1dp"
+                android:layout_weight="1"/>
+        </LinearLayout>
+
+        <!-- Separator -->
+        <LinearLayout
+            android:id="@+id/onboarding_separator"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="24dp"
+            android:gravity="center_horizontal"
+            android:orientation="horizontal"
+            android:weightSum="1.0">
+            <View style="@style/HorizontalDivider"
+                android:layout_width="0dp"
+                android:layout_height="1dp"
+                android:layout_weight="0.33"/>
+        </LinearLayout>
+        <Space android:layout_width="0dp" android:layout_height="24dp"/>
+
+        <!-- Terms and Conditions message and link -->
+        <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:paddingBottom="9dp">
+            <TextView
+                android:id="@+id/google_terms_message"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="0dp"
+                android:layout_gravity="center"
+                android:textAppearance="@style/TextAppearance.AssistantBlackCaption"
+                android:text="@string/autofill_assistant_google_terms_description" />
+        </LinearLayout>
+
+        <!-- Layout for the buttons -->
+        <Space android:layout_width="0dp" android:layout_height="18dp"/>
+        <LinearLayout
         android:layout_width="match_parent"
-        android:paddingBottom="9dp">
-        <TextView
-            android:id="@+id/google_terms_message"
+        android:layout_height="wrap_content"
+        android:gravity="bottom|center"
+        android:orientation="horizontal">
+
+        <!-- 'No' button -->
+        <org.chromium.ui.widget.ButtonCompat
+            android:id="@+id/button_init_not_ok"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_margin="0dp"
-            android:layout_gravity="center"
-            android:textAppearance="@style/TextAppearance.AssistantBlackCaption"
-            android:text="@string/autofill_assistant_google_terms_description" />
+            android:singleLine="true"
+            android:gravity="center"
+            android:text="@string/cancel"
+            style="@style/TextButton" />
+
+        <Space
+            android:layout_weight="1"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"/>
+
+        <!-- 'Yes' button -->
+        <org.chromium.ui.widget.ButtonCompat
+            android:id="@+id/button_init_ok"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:gravity="center"
+            android:text="@string/init_ok"
+            style="FilledButton.Flat" />
+        </LinearLayout>
     </LinearLayout>
-
-    <!-- Layout for the buttons -->
-    <Space android:layout_width="0dp" android:layout_height="18dp"/>
-    <LinearLayout
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:gravity="bottom|center"
-    android:orientation="horizontal">
-
-    <!-- 'No' button -->
-    <org.chromium.ui.widget.ButtonCompat
-        android:id="@+id/button_init_not_ok"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:singleLine="true"
-        android:gravity="center"
-        android:text="@string/cancel"
-        style="@style/TextButton" />
-
-    <Space
-        android:layout_weight="1"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"/>
-
-    <!-- 'Yes' button -->
-    <org.chromium.ui.widget.ButtonCompat
-        android:id="@+id/button_init_ok"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:singleLine="true"
-        android:gravity="center"
-        android:text="@string/init_ok"
-        style="@style/FilledButton.Flat" />
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</ScrollView>
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index 8761ac5..1396985 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
-import android.content.Context;
 import android.support.annotation.Nullable;
 import android.transition.ChangeBounds;
 import android.transition.Fade;
@@ -16,7 +15,6 @@
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
-import org.chromium.base.Callback;
 import org.chromium.base.ObserverList;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.autofill_assistant.R;
@@ -30,8 +28,6 @@
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
 import org.chromium.chrome.browser.autofill_assistant.infobox.AssistantInfoBoxCoordinator;
-import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
-import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState;
 import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestModel;
 import org.chromium.chrome.browser.compositor.CompositorViewResizer;
@@ -52,6 +48,7 @@
     private final AssistantModel mModel;
     private final BottomSheetController mBottomSheetController;
     private final AssistantBottomSheetContent mContent;
+    private final ScrollView mScrollableContent;
 
     // Child coordinators.
     private final AssistantHeaderCoordinator mHeaderCoordinator;
@@ -79,7 +76,22 @@
             ChromeActivity activity, AssistantModel model, BottomSheetController controller) {
         mModel = model;
         mBottomSheetController = controller;
-        mContent = new AssistantBottomSheetContent(activity);
+
+        BottomSheet.BottomSheetContent currentSheetContent =
+                controller.getBottomSheet().getCurrentSheetContent();
+        if (currentSheetContent instanceof AssistantBottomSheetContent) {
+            mContent = (AssistantBottomSheetContent) currentSheetContent;
+        } else {
+            mContent = new AssistantBottomSheetContent(activity);
+        }
+
+        // Replace or set the content to the actual Autofill Assistant views.
+        ViewGroup bottomBarView = (ViewGroup) LayoutInflater.from(activity).inflate(
+                R.layout.autofill_assistant_bottom_sheet_content, /* root= */ null);
+        mScrollableContent = bottomBarView.findViewById(R.id.scrollable_content);
+        ViewGroup scrollableContentContainer =
+                mScrollableContent.findViewById(R.id.scrollable_content_container);
+        mContent.setContent(bottomBarView, mScrollableContent);
 
         // Set up animations. We need to setup them before initializing the child coordinators as we
         // want our observers to be triggered before the coordinators/view binders observers.
@@ -90,8 +102,8 @@
         setupAnimations(model, rootView);
 
         // Instantiate child components.
-        mHeaderCoordinator = new AssistantHeaderCoordinator(
-                activity, mContent.mBottomBarView, model.getHeaderModel());
+        mHeaderCoordinator =
+                new AssistantHeaderCoordinator(activity, bottomBarView, model.getHeaderModel());
         mInfoBoxCoordinator = new AssistantInfoBoxCoordinator(activity, model.getInfoBoxModel());
         mDetailsCoordinator = new AssistantDetailsCoordinator(activity, model.getDetailsModel());
         mPaymentRequestCoordinator =
@@ -103,7 +115,7 @@
                 new AssistantActionsCarouselCoordinator(activity, model.getActionsModel());
         BottomSheet bottomSheet = controller.getBottomSheet();
         mPeekHeightCoordinator = new AssistantPeekHeightCoordinator(activity, this, bottomSheet,
-                mContent.mToolbarView, mContent.mBottomBarView, mSuggestionsCoordinator.getView(),
+                mContent.getToolbarView(), bottomBarView, mSuggestionsCoordinator.getView(),
                 mActionsCoordinator.getView(), AssistantPeekHeightCoordinator.PeekMode.HANDLE);
 
         // We don't want to animate the carousels children views as they are already animated by the
@@ -113,12 +125,12 @@
 
         // Add child views to bottom bar container. We put all child views in the scrollable
         // container, except the actions and suggestions.
-        mContent.mScrollableContentContainer.addView(mInfoBoxCoordinator.getView());
-        mContent.mScrollableContentContainer.addView(mDetailsCoordinator.getView());
-        mContent.mScrollableContentContainer.addView(mPaymentRequestCoordinator.getView());
-        mContent.mScrollableContentContainer.addView(mFormCoordinator.getView());
-        mContent.mBottomBarView.addView(mSuggestionsCoordinator.getView());
-        mContent.mBottomBarView.addView(mActionsCoordinator.getView());
+        scrollableContentContainer.addView(mInfoBoxCoordinator.getView());
+        scrollableContentContainer.addView(mDetailsCoordinator.getView());
+        scrollableContentContainer.addView(mPaymentRequestCoordinator.getView());
+        scrollableContentContainer.addView(mFormCoordinator.getView());
+        bottomBarView.addView(mSuggestionsCoordinator.getView());
+        bottomBarView.addView(mActionsCoordinator.getView());
 
         // Set children top margins to have a spacing between them.
         int childSpacing = activity.getResources().getDimensionPixelSize(
@@ -142,31 +154,9 @@
         setHorizontalMargins(mDetailsCoordinator.getView());
         setHorizontalMargins(mFormCoordinator.getView());
 
-        View bottomSheetContainer = bottomSheet.findViewById(R.id.bottom_sheet_content);
         bottomSheet.addObserver(new EmptyBottomSheetObserver() {
             @Override
-            public void onSheetClosed(int reason) {
-                // When scrolling with y < peekHeight, the BottomSheet will make the content
-                // invisible. This is a workaround to prevent that as our toolbar is transparent and
-                // we want to sheet content to stay visible.
-                if ((bottomSheet.getSheetState() == BottomSheet.SheetState.SCROLLING
-                            || bottomSheet.getSheetState() == BottomSheet.SheetState.PEEK)
-                        && bottomSheet.getCurrentSheetContent() == mContent
-                        && model.get(AssistantModel.VISIBLE)) {
-                    bottomSheetContainer.setVisibility(View.VISIBLE);
-                }
-            }
-
-            @Override
             public void onSheetStateChanged(int newState) {
-                if (newState == BottomSheet.SheetState.PEEK
-                        && bottomSheet.getCurrentSheetContent() == mContent) {
-                    // When in the peek state, the BottomSheet hides the content view. We override
-                    // that because we artificially increase the height of the transparent toolbar
-                    // to show parts of the content view.
-                    bottomSheetContainer.setVisibility(View.VISIBLE);
-                }
-
                 maybeShowHeaderChip();
             }
 
@@ -200,13 +190,13 @@
         // (i.e. details shadow and carousel cancel button shadow) but we need to clip the children
         // when the ScrollView is scrollable, otherwise scrolled content will overlap with the
         // header and carousels.
-        ScrollView scrollView = mContent.mScrollableContent;
+        ScrollView scrollView = mScrollableContent;
         scrollView.addOnLayoutChangeListener(
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                     boolean canScroll =
                             scrollView.canScrollVertically(-1) || scrollView.canScrollVertically(1);
-                    mContent.mScrollableContent.setClipChildren(canScroll);
-                    mContent.mBottomBarView.setClipChildren(canScroll);
+                    mScrollableContent.setClipChildren(canScroll);
+                    bottomBarView.setClipChildren(canScroll);
                 });
     }
 
@@ -264,38 +254,9 @@
         mHeaderCoordinator.destroy();
     }
 
-    /**
-     * Show the onboarding screen and call {@code callback} with {@code true} if the user agreed to
-     * proceed, false otherwise.
-     */
-    public void showOnboarding(String experimentIds, Callback<Boolean> callback) {
-        mModel.getHeaderModel().set(AssistantHeaderModel.VISIBLE, false);
-
-        // Show overlay to prevent user from interacting with the page during onboarding.
-        mModel.getOverlayModel().set(AssistantOverlayModel.STATE, AssistantOverlayState.FULL);
-
-        AssistantOnboardingCoordinator.show(experimentIds, mContent.mBottomBarView.getContext(),
-                mContent.mScrollableContentContainer, accepted -> {
-                    if (!accepted) {
-                        callback.onResult(false);
-                        return;
-                    }
-
-                    mModel.getHeaderModel().set(AssistantHeaderModel.VISIBLE, true);
-
-                    // Hide overlay.
-                    mModel.getOverlayModel().set(
-                            AssistantOverlayModel.STATE, AssistantOverlayState.HIDDEN);
-
-                    callback.onResult(true);
-                });
-    }
-
     /** Request showing the Assistant bottom bar view and expand the sheet. */
     public void showAndExpand() {
-        if (mBottomSheetController.requestShowContent(mContent, /* animate= */ true)) {
-            mBottomSheetController.expandSheet();
-        }
+        BottomSheetUtils.showContentAndExpand(mBottomSheetController, mContent);
     }
 
     /** Hide the Assistant bottom bar view. */
@@ -318,7 +279,7 @@
 
     @Override
     public void setShowOnlyCarousels(boolean showOnlyCarousels) {
-        mContent.mScrollableContent.setVisibility(showOnlyCarousels ? View.GONE : View.VISIBLE);
+        mScrollableContent.setVisibility(showOnlyCarousels ? View.GONE : View.VISIBLE);
     }
 
     @Override
@@ -396,102 +357,4 @@
     public void removeObserver(Observer observer) {
         mSizeObservers.removeObserver(observer);
     }
-
-    // TODO(crbug.com/806868): Move this class at the top of the file once it is a static class.
-    private static class AssistantBottomSheetContent implements BottomSheet.BottomSheetContent {
-        private final ViewGroup mToolbarView;
-        private final SizeListenableLinearLayout mBottomBarView;
-        private final ScrollView mScrollableContent;
-        private final LinearLayout mScrollableContentContainer;
-
-        public AssistantBottomSheetContent(Context context) {
-            mToolbarView = (ViewGroup) LayoutInflater.from(context).inflate(
-                    R.layout.autofill_assistant_bottom_sheet_toolbar, /* root= */ null);
-            mBottomBarView = (SizeListenableLinearLayout) LayoutInflater.from(context).inflate(
-                    R.layout.autofill_assistant_bottom_sheet_content, /* root= */ null);
-            mScrollableContent = mBottomBarView.findViewById(R.id.scrollable_content);
-            mScrollableContentContainer =
-                    mScrollableContent.findViewById(R.id.scrollable_content_container);
-        }
-
-        @Override
-        public View getContentView() {
-            return mBottomBarView;
-        }
-
-        @Nullable
-        @Override
-        public View getToolbarView() {
-            return mToolbarView;
-        }
-
-        @Override
-        public int getVerticalScrollOffset() {
-            return mScrollableContent.getScrollY();
-        }
-
-        @Override
-        public void destroy() {}
-
-        @Override
-        public int getPriority() {
-            return BottomSheet.ContentPriority.HIGH;
-        }
-
-        @Override
-        public boolean swipeToDismissEnabled() {
-            return false;
-        }
-
-        @Override
-        public boolean isPeekStateEnabled() {
-            return true;
-        }
-
-        @Override
-        public boolean wrapContentEnabled() {
-            return true;
-        }
-
-        @Override
-        public boolean hasCustomLifecycle() {
-            return true;
-        }
-
-        @Override
-        public boolean hasCustomScrimLifecycle() {
-            return true;
-        }
-
-        @Override
-        public boolean setContentSizeListener(@Nullable BottomSheet.ContentSizeListener listener) {
-            mBottomBarView.setContentSizeListener(listener);
-            return true;
-        }
-
-        @Override
-        public boolean hideOnScroll() {
-            return false;
-        }
-
-        @Override
-        public int getSheetContentDescriptionStringId() {
-            return R.string.autofill_assistant_sheet_content_description;
-        }
-
-        @Override
-        public int getSheetHalfHeightAccessibilityStringId() {
-            return R.string.autofill_assistant_sheet_half_height;
-        }
-
-        @Override
-        public int getSheetFullHeightAccessibilityStringId() {
-            return R.string.autofill_assistant_sheet_full_height;
-        }
-
-        @Override
-        public int getSheetClosedAccessibilityStringId() {
-            return R.string.autofill_assistant_sheet_closed;
-        }
-    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java
new file mode 100644
index 0000000..bbfb174
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java
@@ -0,0 +1,130 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ScrollView;
+
+import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
+
+/**
+ * The {@link BottomSheet.BottomSheetContent} for the Autofill Assistant. It supports notifying the
+ * BottomSheet when its size changes and allows to dynamically set its scrollable content (in
+ * practice, this allows to replace the onboarding by the actual Autofill Assistant content).
+ */
+class AssistantBottomSheetContent implements BottomSheet.BottomSheetContent {
+    private final View mToolbarView;
+    private final SizeListenableLinearLayout mContentView;
+    @Nullable
+    private ScrollView mContentScrollableView;
+
+    public AssistantBottomSheetContent(Context context) {
+        mToolbarView = LayoutInflater.from(context).inflate(
+                R.layout.autofill_assistant_bottom_sheet_toolbar, /* root= */ null);
+        mContentView = new SizeListenableLinearLayout(context);
+        mContentView.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+    }
+
+    public void setContent(View content, ScrollView scrollableView) {
+        if (mContentView.getChildCount() > 0) {
+            mContentView.removeAllViews();
+        }
+
+        content.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        mContentView.addView(content);
+        mContentScrollableView = scrollableView;
+    }
+
+    @Override
+    public View getContentView() {
+        return mContentView;
+    }
+
+    @Nullable
+    @Override
+    public View getToolbarView() {
+        return mToolbarView;
+    }
+
+    @Override
+    public int getVerticalScrollOffset() {
+        if (mContentScrollableView != null) {
+            return mContentScrollableView.getScrollY();
+        }
+
+        return 0;
+    }
+
+    @Override
+    public boolean setContentSizeListener(@Nullable BottomSheet.ContentSizeListener listener) {
+        mContentView.setContentSizeListener(listener);
+        return true;
+    }
+
+    @Override
+    public void destroy() {}
+
+    @Override
+    public int getPriority() {
+        return BottomSheet.ContentPriority.HIGH;
+    }
+
+    @Override
+    public boolean swipeToDismissEnabled() {
+        return false;
+    }
+
+    @Override
+    public boolean isPeekStateEnabled() {
+        return true;
+    }
+
+    @Override
+    public boolean wrapContentEnabled() {
+        return true;
+    }
+
+    @Override
+    public boolean hasCustomLifecycle() {
+        return true;
+    }
+
+    @Override
+    public boolean hasCustomScrimLifecycle() {
+        return true;
+    }
+
+    @Override
+    public boolean hideOnScroll() {
+        return false;
+    }
+
+    @Override
+    public int getSheetContentDescriptionStringId() {
+        return R.string.autofill_assistant_sheet_content_description;
+    }
+
+    @Override
+    public int getSheetHalfHeightAccessibilityStringId() {
+        return R.string.autofill_assistant_sheet_half_height;
+    }
+
+    @Override
+    public int getSheetFullHeightAccessibilityStringId() {
+        return R.string.autofill_assistant_sheet_full_height;
+    }
+
+    @Override
+    public int getSheetClosedAccessibilityStringId() {
+        return R.string.autofill_assistant_sheet_closed;
+    }
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index f8db8e8..11fc9d43 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -4,8 +4,9 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
+import android.support.annotation.Nullable;
+
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -16,35 +17,31 @@
  * sub-components and shutting down the Autofill Assistant.
  */
 class AssistantCoordinator {
-    interface Delegate {
-        /** Completely stop the Autofill Assistant. */
-        void stop(@DropOutReason int reason);
-
-        // TODO(crbug.com/806868): Move onboarding and snackbar out of this class and remove the
-        // delegate.
-    }
-
     private static final String FEEDBACK_CATEGORY_TAG =
             "com.android.chrome.USER_INITIATED_FEEDBACK_REPORT_AUTOFILL_ASSISTANT";
 
     private final ChromeActivity mActivity;
-    private final Delegate mDelegate;
 
     private final AssistantModel mModel;
     private AssistantBottomBarCoordinator mBottomBarCoordinator;
     private final AssistantKeyboardCoordinator mKeyboardCoordinator;
     private final AssistantOverlayCoordinator mOverlayCoordinator;
 
-    AssistantCoordinator(
-            ChromeActivity activity, Delegate delegate, BottomSheetController controller) {
+    AssistantCoordinator(ChromeActivity activity, BottomSheetController controller,
+            @Nullable AssistantOverlayCoordinator overlayCoordinator) {
         mActivity = activity;
-        mDelegate = delegate;
-        mModel = new AssistantModel();
 
-        // Instantiate child components.
+        if (overlayCoordinator != null) {
+            mModel = new AssistantModel(overlayCoordinator.getModel());
+            mOverlayCoordinator = overlayCoordinator;
+        } else {
+            mModel = new AssistantModel();
+            mOverlayCoordinator =
+                    new AssistantOverlayCoordinator(activity, mModel.getOverlayModel());
+        }
+
         mBottomBarCoordinator = new AssistantBottomBarCoordinator(activity, mModel, controller);
         mKeyboardCoordinator = new AssistantKeyboardCoordinator(activity, mModel);
-        mOverlayCoordinator = new AssistantOverlayCoordinator(activity, mModel.getOverlayModel());
 
         activity.getCompositorViewHolder().addCompositorViewResizer(mBottomBarCoordinator);
         mModel.setVisible(true);
@@ -63,20 +60,6 @@
     }
 
     /**
-     * Show the onboarding screen and call {@code onAccept} if the user agreed to proceed, shutdown
-     * otherwise.
-     */
-    public void showOnboarding(String experimentIds, Runnable onAccept) {
-        mBottomBarCoordinator.showOnboarding(experimentIds, accepted -> {
-            if (accepted) {
-                onAccept.run();
-            } else {
-                mDelegate.stop(DropOutReason.DECLINED);
-            }
-        });
-    }
-
-    /**
      * Get the model representing the current state of the UI.
      */
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
index 7b89cd5..064ee59a 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
@@ -25,7 +25,7 @@
             new WritableBooleanPropertyKey();
     static final WritableBooleanPropertyKey VISIBLE = new WritableBooleanPropertyKey();
 
-    private final AssistantOverlayModel mOverlayModel = new AssistantOverlayModel();
+    private final AssistantOverlayModel mOverlayModel;
     private final AssistantHeaderModel mHeaderModel = new AssistantHeaderModel();
     private final AssistantDetailsModel mDetailsModel = new AssistantDetailsModel();
     private final AssistantInfoBoxModel mInfoBoxModel = new AssistantInfoBoxModel();
@@ -36,7 +36,12 @@
     private final AssistantCarouselModel mActionsModel = new AssistantCarouselModel();
 
     AssistantModel() {
+        this(new AssistantOverlayModel());
+    }
+
+    AssistantModel(AssistantOverlayModel overlayModel) {
         super(ALLOW_SOFT_KEYBOARD, VISIBLE, ALLOW_TALKBACK_ON_WEBSITE);
+        mOverlayModel = overlayModel;
     }
 
     @CalledByNative
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java
index ddd3211..6ceb41d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java
@@ -9,12 +9,11 @@
 import android.text.method.LinkMovementMethod;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
+import android.widget.ScrollView;
 import android.widget.TextView;
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.metrics.OnBoarding;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
@@ -29,15 +28,14 @@
     private static final String SMALL_ONBOARDING_EXPERIMENT_ID = "4257013";
 
     /**
-     * Shows the onboarding screen and returns whether we should proceed.
+     * Set the content of {@code bottomSheetContent} to be the Autofill Assistant onboarding. {@code
+     * callback} will be called with true or false when the user accepts or cancels the onboarding
+     * (respectively).
      */
-    static View show(
-            String experimentIds, Context context, ViewGroup root, Callback<Boolean> callback) {
-        AutofillAssistantMetrics.recordOnBoarding(OnBoarding.OB_SHOWN);
-
-        View initView = LayoutInflater.from(context)
-                                .inflate(R.layout.autofill_assistant_onboarding, root)
-                                .findViewById(R.id.assistant_onboarding);
+    static void setOnboardingContent(String experimentIds, Context context,
+            AssistantBottomSheetContent bottomSheetContent, Callback<Boolean> callback) {
+        ScrollView initView = (ScrollView) LayoutInflater.from(context).inflate(
+                R.layout.autofill_assistant_onboarding, /* root= */ null);
 
         TextView termsTextView = initView.findViewById(R.id.google_terms_message);
         String termsString = context.getApplicationContext().getString(
@@ -57,9 +55,9 @@
         initView.setFocusable(true);
 
         initView.findViewById(R.id.button_init_ok)
-                .setOnClickListener(unusedView -> onClicked(true, root, initView, callback));
+                .setOnClickListener(unusedView -> onClicked(true, callback));
         initView.findViewById(R.id.button_init_not_ok)
-                .setOnClickListener(unusedView -> onClicked(false, root, initView, callback));
+                .setOnClickListener(unusedView -> onClicked(false, callback));
         initView.announceForAccessibility(
                 context.getString(R.string.autofill_assistant_first_run_accessibility));
 
@@ -70,22 +68,15 @@
             hide(initView, R.id.onboarding_separator);
         }
 
-        return initView;
+        bottomSheetContent.setContent(initView, initView);
     }
 
     private static void hide(View root, int resId) {
         root.findViewById(resId).setVisibility(View.GONE);
     }
 
-    private static void onClicked(
-            boolean accept, ViewGroup root, View initView, Callback<Boolean> callback) {
+    private static void onClicked(boolean accept, Callback<Boolean> callback) {
         AutofillAssistantPreferencesUtil.setInitialPreferences(accept);
-        root.removeView(initView);
-        if (accept) {
-            AutofillAssistantMetrics.recordOnBoarding(OnBoarding.OB_ACCEPTED);
-        } else {
-            AutofillAssistantMetrics.recordOnBoarding(OnBoarding.OB_CANCELLED);
-        }
         callback.onResult(accept);
     }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
index c3c20c3d..9fd64b7 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
@@ -13,6 +13,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.OAuth2TokenService;
 import org.chromium.content_public.browser.WebContents;
@@ -26,7 +27,7 @@
  * This mainly a bridge to autofill_assistant::ClientAndroid.
  */
 @JNINamespace("autofill_assistant")
-class AutofillAssistantClient implements AutofillAssistantModuleEntry {
+class AutofillAssistantClient {
     /** OAuth2 scope that RPCs require. */
     private static final String AUTH_TOKEN_TYPE =
             "oauth2:https://www.googleapis.com/auth/userinfo.profile";
@@ -66,25 +67,18 @@
         mNativeClientAndroid = nativeClientAndroid;
     }
 
-    @Override
-    public void showOnboarding(String experimentIds, Runnable onAccept) {
-        checkNativeClientIsAliveOrThrow();
-        nativeShowOnboarding(mNativeClientAndroid, experimentIds, onAccept);
-    }
-
     private void checkNativeClientIsAliveOrThrow() {
         if (mNativeClientAndroid == 0) {
             throw new IllegalStateException("Native instance is dead");
         }
     }
 
-    @Override
-    public void start(String initialUrl, Map<String, String> parameters, String experimentIds,
-            Bundle intentExtras) {
+    void start(String initialUrl, Map<String, String> parameters, String experimentIds,
+            Bundle intentExtras, @Nullable AssistantOverlayCoordinator overlayCoordinator) {
         checkNativeClientIsAliveOrThrow();
         nativeStart(mNativeClientAndroid, initialUrl, experimentIds,
                 parameters.keySet().toArray(new String[parameters.size()]),
-                parameters.values().toArray(new String[parameters.size()]));
+                parameters.values().toArray(new String[parameters.size()]), overlayCoordinator);
         chooseAccountAsync(parameters.get(PARAMETER_USER_EMAIL), intentExtras);
     }
 
@@ -246,10 +240,9 @@
     }
 
     private static native AutofillAssistantClient nativeFromWebContents(WebContents webContents);
-    private native void nativeShowOnboarding(
-            long nativeClientAndroid, String experimentIds, Object onAccept);
     private native void nativeStart(long nativeClientAndroid, String initialUrl,
-            String experimentIds, String[] parameterNames, String[] parameterValues);
+            String experimentIds, String[] parameterNames, String[] parameterValues,
+            @Nullable AssistantOverlayCoordinator overlayCoordinator);
     private native void nativeOnAccessToken(
             long nativeClientAndroid, boolean success, String accessToken);
     private native String nativeGetPrimaryAccountName(long nativeClientAndroid);
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryFactoryImpl.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryFactoryImpl.java
index a36a25f..2eba277e1 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryFactoryImpl.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryFactoryImpl.java
@@ -4,9 +4,22 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
 import org.chromium.base.annotations.UsedByReflection;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
+import org.chromium.chrome.browser.autofill_assistant.metrics.OnBoarding;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState;
+import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.content_public.browser.WebContents;
 
+import java.util.Map;
+
 /**
  * Factory implementation to create AutofillAssistantClient as
  * as AutofillAssistantModuleEntry to serve as interface between
@@ -16,7 +29,61 @@
 public class AutofillAssistantModuleEntryFactoryImpl
         implements AutofillAssistantModuleEntryFactory {
     @Override
-    public AutofillAssistantModuleEntry createEntry(WebContents webContents) {
-        return AutofillAssistantClient.fromWebContents(webContents);
+    public AutofillAssistantModuleEntry createEntry(
+            @NonNull ChromeActivity activity, @NonNull WebContents webContents) {
+        return new AutofillAssistantModuleEntryImpl(activity, webContents);
+    }
+
+    private static class AutofillAssistantModuleEntryImpl implements AutofillAssistantModuleEntry {
+        private final ChromeActivity mActivity;
+        private final WebContents mWebContents;
+
+        private AutofillAssistantModuleEntryImpl(ChromeActivity activity, WebContents webContents) {
+            mActivity = activity;
+            mWebContents = webContents;
+        }
+
+        @Override
+        public void start(boolean skipOnboarding, String initialUrl, Map<String, String> parameters,
+                String experimentIds, Bundle intentExtras) {
+            if (skipOnboarding) {
+                AutofillAssistantMetrics.recordOnBoarding(OnBoarding.OB_NOT_SHOWN);
+
+                start(initialUrl, parameters, experimentIds, intentExtras, null);
+            } else {
+                AutofillAssistantMetrics.recordOnBoarding(OnBoarding.OB_SHOWN);
+
+                BottomSheetController controller = mActivity.getBottomSheetController();
+                AssistantBottomSheetContent content = new AssistantBottomSheetContent(mActivity);
+                AssistantOverlayModel overlayModel = new AssistantOverlayModel();
+                AssistantOverlayCoordinator overlayCoordinator =
+                        new AssistantOverlayCoordinator(mActivity, overlayModel);
+                overlayModel.set(AssistantOverlayModel.STATE, AssistantOverlayState.FULL);
+                AssistantOnboardingCoordinator.setOnboardingContent(
+                        experimentIds, mActivity, content, accepted -> {
+                            if (accepted) {
+                                AutofillAssistantMetrics.recordOnBoarding(OnBoarding.OB_ACCEPTED);
+
+                                // We transfer the ownership of the overlay to #start() and the
+                                // bottom sheet content will be replaced.
+                                start(initialUrl, parameters, experimentIds, intentExtras,
+                                        overlayCoordinator);
+                            } else {
+                                AutofillAssistantMetrics.recordOnBoarding(OnBoarding.OB_CANCELLED);
+                                overlayCoordinator.destroy();
+                                controller.hideContent(content, /* animate= */ true);
+                                AutofillAssistantMetrics.recordDropOut(DropOutReason.DECLINED);
+                            }
+                        });
+
+                BottomSheetUtils.showContentAndExpand(controller, content);
+            }
+        }
+
+        private void start(String initialUrl, Map<String, String> parameters, String experimentIds,
+                Bundle intentExtras, @Nullable AssistantOverlayCoordinator overlayCoordinator) {
+            AutofillAssistantClient client = AutofillAssistantClient.fromWebContents(mWebContents);
+            client.start(initialUrl, parameters, experimentIds, intentExtras, overlayCoordinator);
+        }
     }
 }
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 416693d..86d8638 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -17,6 +17,7 @@
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip.Type;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
+import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
 import org.chromium.chrome.browser.tab.Tab;
@@ -38,7 +39,7 @@
 @JNINamespace("autofill_assistant")
 // TODO(crbug.com/806868): This class should be removed once all logic is in native side and the
 // model is directly modified by the native AssistantMediator.
-class AutofillAssistantUiController implements AssistantCoordinator.Delegate {
+class AutofillAssistantUiController {
     private static Set<ChromeActivity> sActiveChromeActivities;
     private long mNativeUiController;
 
@@ -81,8 +82,9 @@
     }
 
     @CalledByNative
-    private static AutofillAssistantUiController create(
-            ChromeActivity activity, boolean allowTabSwitching, long nativeUiController) {
+    private static AutofillAssistantUiController create(ChromeActivity activity,
+            boolean allowTabSwitching, long nativeUiController,
+            @Nullable AssistantOverlayCoordinator overlayCoordinator) {
         assert activity != null;
         assert activity.getBottomSheetController() != null;
 
@@ -92,14 +94,15 @@
         sActiveChromeActivities.add(activity);
 
         return new AutofillAssistantUiController(activity, activity.getBottomSheetController(),
-                allowTabSwitching, nativeUiController);
+                allowTabSwitching, nativeUiController, overlayCoordinator);
     }
 
     private AutofillAssistantUiController(ChromeActivity activity, BottomSheetController controller,
-            boolean allowTabSwitching, long nativeUiController) {
+            boolean allowTabSwitching, long nativeUiController,
+            @Nullable AssistantOverlayCoordinator overlayCoordinator) {
         mNativeUiController = nativeUiController;
         mActivity = activity;
-        mCoordinator = new AssistantCoordinator(activity, this, controller);
+        mCoordinator = new AssistantCoordinator(activity, controller, overlayCoordinator);
         mActivityTabObserver =
                 new ActivityTabProvider.ActivityTabTabObserver(activity.getActivityTabProvider()) {
                     @Override
@@ -150,14 +153,6 @@
                 };
     }
 
-    // Java => native methods.
-
-    /** Shut down the Autofill Assistant immediately, without showing a message. */
-    @Override
-    public void stop(@DropOutReason int reason) {
-        safeNativeStop(reason);
-    }
-
     // Native => Java methods.
 
     // TODO(crbug.com/806868): Some of these functions still have a little bit of logic (e.g. make
@@ -196,11 +191,6 @@
     }
 
     @CalledByNative
-    private void onShowOnboarding(String experimentIds, Runnable onAccept) {
-        mCoordinator.showOnboarding(experimentIds, onAccept);
-    }
-
-    @CalledByNative
     private void expandBottomSheet() {
         mCoordinator.getBottomBarCoordinator().showAndExpand();
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java
new file mode 100644
index 0000000..3c2d6dd
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java
@@ -0,0 +1,83 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import android.view.View;
+
+import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
+import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
+import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
+
+class BottomSheetUtils {
+    /** Request {@code controller} to show {@code content} and expand the sheet when it is shown. */
+    static void showContentAndExpand(
+            BottomSheetController controller, AssistantBottomSheetContent content) {
+        // Add an observer that makes sure the bottom sheet content is always shown, even in the
+        // peek state.
+        BottomSheet bottomSheet = controller.getBottomSheet();
+        View bottomSheetContainer = bottomSheet.findViewById(R.id.bottom_sheet_content);
+        bottomSheet.addObserver(new EmptyBottomSheetObserver() {
+            private boolean mEnabled;
+
+            @Override
+            public void onSheetContentChanged(BottomSheet.BottomSheetContent newContent) {
+                if (newContent == content) {
+                    mEnabled = true;
+                } else if (mEnabled) {
+                    // Content was shown then hidden: remove this observer.
+                    mEnabled = false;
+                    bottomSheet.removeObserver(this);
+                }
+            }
+
+            @Override
+            public void onSheetClosed(int reason) {
+                if (!mEnabled) return;
+
+                // When scrolling with y < peekHeight, the BottomSheet will make the content
+                // invisible. This is a workaround to prevent that as our toolbar is transparent and
+                // we want to sheet content to stay visible.
+                if ((bottomSheet.getSheetState() == BottomSheet.SheetState.SCROLLING
+                            || bottomSheet.getSheetState() == BottomSheet.SheetState.PEEK)
+                        && bottomSheet.getCurrentSheetContent() == content) {
+                    bottomSheetContainer.setVisibility(View.VISIBLE);
+                }
+            }
+
+            @Override
+            public void onSheetStateChanged(int newState) {
+                if (!mEnabled) return;
+
+                if (newState == BottomSheet.SheetState.PEEK
+                        && bottomSheet.getCurrentSheetContent() == content) {
+                    // When in the peek state, the BottomSheet hides the content view. We override
+                    // that because we artificially increase the height of the transparent toolbar
+                    // to show parts of the content view.
+                    bottomSheetContainer.setVisibility(View.VISIBLE);
+                }
+            }
+        });
+
+        // Show the content.
+        if (controller.requestShowContent(content, /* animate= */ true)) {
+            controller.expandSheet();
+        } else {
+            // If the content is not directly shown, add an observer that will expand the sheet when
+            // it is.
+            bottomSheet.addObserver(new EmptyBottomSheetObserver() {
+                @Override
+                public void onSheetContentChanged(BottomSheet.BottomSheetContent newContent) {
+                    if (newContent == content) {
+                        bottomSheet.removeObserver(this);
+                        controller.expandSheet();
+                    }
+                }
+            });
+        }
+    }
+
+    private BottomSheetUtils() {}
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java
index d019f87b..be9cbe54 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java
@@ -50,7 +50,6 @@
         AssistantHeaderViewBinder viewBinder = new AssistantHeaderViewBinder();
         PropertyModelChangeProcessor.create(model, viewHolder, viewBinder);
 
-        model.set(AssistantHeaderModel.VISIBLE, true);
         model.set(AssistantHeaderModel.PROGRESS_VISIBLE, true);
     }
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
index e5022a80..cd2870f 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
@@ -15,8 +15,6 @@
  */
 @JNINamespace("autofill_assistant")
 public class AssistantHeaderModel extends PropertyModel {
-    public static final WritableBooleanPropertyKey VISIBLE = new WritableBooleanPropertyKey();
-
     @VisibleForTesting
     public static final WritableObjectPropertyKey<String> STATUS_MESSAGE =
             new WritableObjectPropertyKey<>();
@@ -38,8 +36,8 @@
     public static final WritableBooleanPropertyKey CHIP_VISIBLE = new WritableBooleanPropertyKey();
 
     public AssistantHeaderModel() {
-        super(VISIBLE, STATUS_MESSAGE, PROGRESS, PROGRESS_VISIBLE, SPIN_POODLE,
-                FEEDBACK_BUTTON_CALLBACK, CHIP, CHIP_VISIBLE);
+        super(STATUS_MESSAGE, PROGRESS, PROGRESS_VISIBLE, SPIN_POODLE, FEEDBACK_BUTTON_CALLBACK,
+                CHIP, CHIP_VISIBLE);
     }
 
     @CalledByNative
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
index ddc9b96..4db357b 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
@@ -54,18 +54,18 @@
 
     @Override
     public void bind(AssistantHeaderModel model, ViewHolder view, PropertyKey propertyKey) {
-        if (AssistantHeaderModel.VISIBLE == propertyKey) {
-            view.mHeader.setVisibility(
-                    model.get(AssistantHeaderModel.VISIBLE) ? View.VISIBLE : View.GONE);
-            setProgressBarVisibility(view, model);
-        } else if (AssistantHeaderModel.STATUS_MESSAGE == propertyKey) {
+        if (AssistantHeaderModel.STATUS_MESSAGE == propertyKey) {
             String message = model.get(AssistantHeaderModel.STATUS_MESSAGE);
             view.mStatusMessage.setText(message);
             view.mStatusMessage.announceForAccessibility(message);
         } else if (AssistantHeaderModel.PROGRESS == propertyKey) {
             view.mProgressBar.setProgress(model.get(AssistantHeaderModel.PROGRESS));
         } else if (AssistantHeaderModel.PROGRESS_VISIBLE == propertyKey) {
-            setProgressBarVisibility(view, model);
+            if (model.get(AssistantHeaderModel.PROGRESS_VISIBLE)) {
+                view.mProgressBar.show();
+            } else {
+                view.mProgressBar.hide();
+            }
         } else if (AssistantHeaderModel.SPIN_POODLE == propertyKey) {
             view.mPoodle.setSpinEnabled(model.get(AssistantHeaderModel.SPIN_POODLE));
         } else if (AssistantHeaderModel.FEEDBACK_BUTTON_CALLBACK == propertyKey) {
@@ -118,15 +118,6 @@
         view.mChip.bind(chip);
     }
 
-    private void setProgressBarVisibility(ViewHolder view, AssistantHeaderModel model) {
-        if (model.get(AssistantHeaderModel.VISIBLE)
-                && model.get(AssistantHeaderModel.PROGRESS_VISIBLE)) {
-            view.mProgressBar.show();
-        } else {
-            view.mProgressBar.hide();
-        }
-    }
-
     private void setProfileMenuListener(ViewHolder view, @Nullable Runnable feedbackCallback) {
         view.mProfileIconMenu.setOnMenuItemClickListener(item -> {
             int itemId = item.getItemId();
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java
index 54ee845..a648f8d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java
@@ -19,6 +19,7 @@
  */
 public class AssistantOverlayCoordinator {
     private final ChromeActivity mActivity;
+    private final AssistantOverlayModel mModel;
     private final AssistantOverlayEventFilter mEventFilter;
     private final AssistantOverlayDrawable mDrawable;
     private final ScrimView mScrim;
@@ -26,6 +27,7 @@
 
     public AssistantOverlayCoordinator(ChromeActivity activity, AssistantOverlayModel model) {
         mActivity = activity;
+        mModel = model;
         mScrim = mActivity.getScrim();
         mEventFilter = new AssistantOverlayEventFilter(
                 mActivity, mActivity.getFullscreenManager(), mActivity.getCompositorViewHolder());
@@ -44,6 +46,10 @@
                 List<RectF> area = model.get(AssistantOverlayModel.TOUCHABLE_AREA);
                 mEventFilter.setTouchableArea(area);
                 mDrawable.setTransparentArea(area);
+            } else if (AssistantOverlayModel.RESTRICTED_AREA == propertyKey) {
+                List<RectF> area = model.get(AssistantOverlayModel.RESTRICTED_AREA);
+                mEventFilter.setRestrictedArea(area);
+                mDrawable.setRestrictedArea(area);
             } else if (AssistantOverlayModel.DELEGATE == propertyKey) {
                 AssistantOverlayDelegate delegate = model.get(AssistantOverlayModel.DELEGATE);
                 mEventFilter.setDelegate(delegate);
@@ -57,6 +63,11 @@
         });
     }
 
+    /** Return the model observed by this coordinator. */
+    public AssistantOverlayModel getModel() {
+        return mModel;
+    }
+
     /**
      * Destroy this coordinator.
      */
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDrawable.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDrawable.java
index 91ecf96..a2b3e3a1 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDrawable.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDrawable.java
@@ -18,6 +18,7 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
@@ -33,6 +34,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -85,6 +87,7 @@
     private final RectF mVisualViewport = new RectF();
 
     private final List<Box> mTransparentArea = new ArrayList<>();
+    private List<RectF> mRestrictedArea = Collections.emptyList();
 
     /** Padding added between the element area and the grayed-out area. */
     private final float mPaddingPx;
@@ -226,6 +229,12 @@
         invalidateSelf();
     }
 
+    /** Set or update the restricted area. */
+    void setRestrictedArea(List<RectF> restrictedArea) {
+        mRestrictedArea = restrictedArea;
+        invalidateSelf();
+    }
+
     @Override
     public void setAlpha(int alpha) {
         // Alpha is ignored.
@@ -260,6 +269,16 @@
         float cssPixelsToPhysical = ((float) width) / mVisualViewport.width();
 
         int yTop = mFullscreenManager.getContentOffset();
+
+        // Don't draw on top of the restricted area.
+        for (RectF rect : mRestrictedArea) {
+            mDrawRect.left = (rect.left - mVisualViewport.left) * cssPixelsToPhysical;
+            mDrawRect.top = yTop + (rect.top - mVisualViewport.top) * cssPixelsToPhysical;
+            mDrawRect.right = (rect.right - mVisualViewport.left) * cssPixelsToPhysical;
+            mDrawRect.bottom = yTop + (rect.bottom - mVisualViewport.top) * cssPixelsToPhysical;
+            canvas.clipRect(mDrawRect, Region.Op.DIFFERENCE);
+        }
+
         for (Box box : mTransparentArea) {
             RectF rect = box.getRectToDraw();
             if (rect.isEmpty() || (!mPartial && box.mAnimationType != AnimationType.FADE_IN)) {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayEventFilter.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayEventFilter.java
index aff95267..6cededa 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayEventFilter.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayEventFilter.java
@@ -71,6 +71,9 @@
     /** Touchable area, expressed in CSS pixels relative to the layout viewport. */
     private List<RectF> mTouchableArea = Collections.emptyList();
 
+    /** Restricted area, expressed in CSS pixels relative to the layout viewport. */
+    private List<RectF> mRestrictedArea = Collections.emptyList();
+
     /**
      * Detects taps: {@link GestureDetector#onTouchEvent} returns {@code true} after a tap event.
      */
@@ -151,6 +154,13 @@
         mTouchableArea = touchableArea;
     }
 
+    /**
+     * Set the restricted area. This only applies if current state is AssistantOverlayState.PARTIAL.
+     */
+    void setRestrictedArea(List<RectF> restrictedArea) {
+        mRestrictedArea = restrictedArea;
+    }
+
     /** Sets the visual viewport. */
     void setVisualViewport(RectF visualViewport) {
         mVisualViewport.set(visualViewport);
@@ -341,6 +351,11 @@
                 ((float) mVisualViewport.width()) / ((float) mCompositorView.getWidth());
         float absoluteXCss = (x * physicalPixelsToCss) + mVisualViewport.left;
         float absoluteYCss = (y * physicalPixelsToCss) + mVisualViewport.top;
+
+        for (RectF rect : mRestrictedArea) {
+            if (rect.contains(absoluteXCss, absoluteYCss)) return false;
+        }
+
         for (RectF rect : mTouchableArea) {
             if (rect.contains(absoluteXCss, absoluteYCss)) return true;
         }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java
index e2124893..492a744 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java
@@ -25,6 +25,9 @@
     public static final WritableObjectPropertyKey<List<RectF>> TOUCHABLE_AREA =
             new WritableObjectPropertyKey<>();
 
+    public static final WritableObjectPropertyKey<List<RectF>> RESTRICTED_AREA =
+            new WritableObjectPropertyKey<>();
+
     public static final WritableObjectPropertyKey<RectF> VISUAL_VIEWPORT =
             new WritableObjectPropertyKey<>();
 
@@ -38,7 +41,7 @@
             new WritableObjectPropertyKey<>();
 
     public AssistantOverlayModel() {
-        super(STATE, TOUCHABLE_AREA, VISUAL_VIEWPORT, DELEGATE, BACKGROUND_COLOR,
+        super(STATE, TOUCHABLE_AREA, RESTRICTED_AREA, VISUAL_VIEWPORT, DELEGATE, BACKGROUND_COLOR,
                 HIGHLIGHT_BORDER_COLOR);
     }
 
@@ -54,12 +57,21 @@
 
     @CalledByNative
     private void setTouchableArea(float[] coords) {
+        set(TOUCHABLE_AREA, toRectangles(coords));
+    }
+
+    private static List<RectF> toRectangles(float[] coords) {
         List<RectF> boxes = new ArrayList<>();
         for (int i = 0; i < coords.length; i += 4) {
             boxes.add(new RectF(/* left= */ coords[i], /* top= */ coords[i + 1],
                     /* right= */ coords[i + 2], /* bottom= */ coords[i + 3]));
         }
-        set(TOUCHABLE_AREA, boxes);
+        return boxes;
+    }
+
+    @CalledByNative
+    private void setRestrictedArea(float[] coords) {
+        set(RESTRICTED_AREA, toRectangles(coords));
     }
 
     @CalledByNative
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
index 8c3271d..7d1f674 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
@@ -5,8 +5,11 @@
 package org.chromium.chrome.browser.autofill_assistant;
 
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
 
+import android.content.Context;
 import android.content.Intent;
+import android.support.annotation.IdRes;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.v7.widget.RecyclerView;
@@ -69,9 +72,6 @@
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock
-    public AssistantCoordinator.Delegate mCoordinatorDelegateMock;
-
-    @Mock
     public Runnable mRunnableMock;
 
     @Rule
@@ -123,6 +123,37 @@
                 /* suppressSheetForContextualSearch= */ false);
     }
 
+    @Test
+    @MediumTest
+    public void testAcceptOnboarding() throws Exception {
+        testOnboarding(true, R.id.button_init_ok);
+    }
+
+    @Test
+    @MediumTest
+    public void testDeclineOnboarding() throws Exception {
+        testOnboarding(false, R.id.button_init_not_ok);
+    }
+
+    private void testOnboarding(boolean expectedAccepted, @IdRes int buttonToClick)
+            throws Exception {
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent());
+        Context context = getActivity();
+        AssistantBottomSheetContent bottomSheetContent = new AssistantBottomSheetContent(context);
+
+        AssistantOnboardingCoordinator.setOnboardingContent(
+                /* experimentIds= */ "", context, bottomSheetContent, accepted -> {
+                    Assert.assertEquals(expectedAccepted, accepted);
+                    mRunnableMock.run();
+                });
+
+        View button = bottomSheetContent.getContentView().findViewById(buttonToClick);
+        Assert.assertNotNull(button);
+
+        TestThreadUtils.runOnUiThreadBlocking(button::performClick);
+        TestThreadUtils.runOnUiThreadBlocking(() -> verify(mRunnableMock).run());
+    }
+
     // TODO(crbug.com/806868): Add more UI details test and check, like payment request UI,
     // highlight chips and so on.
     @Test
@@ -135,8 +166,8 @@
                 ThreadUtils.runOnUiThreadBlocking(this::initializeBottomSheet);
         AssistantCoordinator assistantCoordinator = ThreadUtils.runOnUiThreadBlocking(
                 ()
-                        -> new AssistantCoordinator(
-                                getActivity(), mCoordinatorDelegateMock, bottomSheetController));
+                        -> new AssistantCoordinator(getActivity(), bottomSheetController,
+                                /* overlayCoordinator= */ null));
 
         // Bottom sheet is shown in the BottomSheet when creating the AssistantCoordinator.
         ViewGroup bottomSheetContent =
@@ -146,16 +177,6 @@
         // Disable bottom sheet content animations. This is a workaround for http://crbug/943483.
         TestThreadUtils.runOnUiThreadBlocking(() -> bottomSheetContent.setLayoutTransition(null));
 
-        // Show onboarding.
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> assistantCoordinator.showOnboarding(/* experimentIds= */ "", mRunnableMock));
-        View onboardingView = bottomSheetContent.findViewById(R.id.assistant_onboarding);
-        Assert.assertNotNull(onboardingView);
-        View initOkButton = onboardingView.findViewById(R.id.button_init_ok);
-        Assert.assertNotNull(initOkButton);
-        ThreadUtils.runOnUiThreadBlocking(() -> { initOkButton.performClick(); });
-        ThreadUtils.runOnUiThreadBlocking(() -> inOrder.verify(mRunnableMock).run());
-
         // Show and check status message.
         String testStatusMessage = "test message";
         ThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
index 271f3b1..0816db2 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
@@ -11,7 +11,6 @@
 
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
-import org.chromium.chrome.browser.autofill_assistant.metrics.OnBoarding;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
 import org.chromium.chrome.browser.util.IntentUtils;
 
@@ -90,8 +89,8 @@
         }
 
         // Early exit if autofill assistant should not be triggered.
-        if (!canStart(activity.getInitialIntent())
-                && !AutofillAssistantPreferencesUtil.getShowOnboarding()) {
+        boolean canStartWithoutOnboarding = canStart(activity.getInitialIntent());
+        if (!canStartWithoutOnboarding && !AutofillAssistantPreferencesUtil.getShowOnboarding()) {
             return;
         }
 
@@ -99,22 +98,16 @@
         AutofillAssistantMetrics.recordDropOut(DropOutReason.AA_START);
         AutofillAssistantModuleEntryProvider.getModuleEntry(activity, (moduleEntry) -> {
             if (moduleEntry == null) {
-                AutofillAssistantMetrics.recordDropOut(DropOutReason.DFM_CANCELLED);
+                AutofillAssistantMetrics.recordDropOut(DropOutReason.DFM_INSTALL_FAILED);
                 return;
             }
-            // Starting autofill assistant without onboarding.
-            if (canStart(activity.getInitialIntent())) {
-                AutofillAssistantMetrics.recordOnBoarding(OnBoarding.OB_NOT_SHOWN);
-                startNow(activity, moduleEntry);
-                return;
-            }
-            // Starting autofill assistant with onboarding.
-            if (AutofillAssistantPreferencesUtil.getShowOnboarding()) {
-                moduleEntry.showOnboarding(
-                        getExperimentIds(activity.getInitialIntent().getExtras()),
-                        () -> startNow(activity, moduleEntry));
-                return;
-            }
+
+            Bundle bundleExtras = activity.getInitialIntent().getExtras();
+            Map<String, String> parameters = extractParameters(bundleExtras);
+            parameters.remove(PARAMETER_ENABLED);
+            String initialUrl = activity.getInitialIntent().getDataString();
+            moduleEntry.start(canStartWithoutOnboarding, initialUrl, parameters, experimentIds,
+                    activity.getInitialIntent().getExtras());
         });
     }
 
@@ -144,16 +137,6 @@
         return experiments.toString();
     }
 
-    private static void startNow(ChromeActivity activity, AutofillAssistantModuleEntry entry) {
-        Bundle bundleExtras = activity.getInitialIntent().getExtras();
-        Map<String, String> parameters = extractParameters(bundleExtras);
-        parameters.remove(PARAMETER_ENABLED);
-        String initialUrl = activity.getInitialIntent().getDataString();
-
-        entry.start(initialUrl, parameters, getExperimentIds(bundleExtras),
-                activity.getInitialIntent().getExtras());
-    }
-
     /** Return the value if the given boolean parameter from the extras. */
     private static boolean getBooleanParameter(@Nullable Bundle extras, String parameterName) {
         return extras != null
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntry.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntry.java
index 0aaf6df..44b802e2 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntry.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntry.java
@@ -14,13 +14,10 @@
  */
 interface AutofillAssistantModuleEntry {
     /**
-     * Show the onboarding screen and run {@code onAccept} if user agreed to proceed.
+     * Launches Autofill Assistant on the current web contents, expecting autostart. If {@code
+     * skipOnboarding} is false, the onboarding will first be shown and the Autofill Assistant will
+     * start only if the user accepts to proceed.
      */
-    void showOnboarding(String experimentIds, Runnable onAccept);
-
-    /**
-     * Launches Autofill Assistant on the current web contents, expecting autostart.
-     */
-    void start(String initialUrl, Map<String, String> parameters, String experimentIds,
-            Bundle intentExtras);
+    void start(boolean skipOnboarding, String initialUrl, Map<String, String> parameters,
+            String experimentIds, Bundle intentExtras);
 }
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryFactory.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryFactory.java
index 045c6b8..214ce76 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryFactory.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryFactory.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
+import android.support.annotation.NonNull;
+
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.components.module_installer.ModuleInterface;
 import org.chromium.content_public.browser.WebContents;
 
@@ -15,5 +18,6 @@
         impl = "org.chromium.chrome.browser.autofill_assistant."
                 + "AutofillAssistantModuleEntryFactoryImpl")
 interface AutofillAssistantModuleEntryFactory {
-    AutofillAssistantModuleEntry createEntry(WebContents webContents);
+    AutofillAssistantModuleEntry createEntry(
+            @NonNull ChromeActivity activity, @NonNull WebContents webContents);
 }
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryProvider.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryProvider.java
index ac99548..517615c9 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryProvider.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryProvider.java
@@ -6,6 +6,7 @@
 
 import org.chromium.base.BundleUtils;
 import org.chromium.base.Callback;
+import org.chromium.base.Log;
 import org.chromium.base.SysUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
@@ -19,6 +20,8 @@
  * AutofillAssistantModuleEntry.
  */
 public class AutofillAssistantModuleEntryProvider {
+    private static final String TAG = "AutofillAssistant";
+
     /**
      * Returns AutofillAssistantModuleEntry by using it as argument to the
      * passed in callback, or null if DFM loading fails.
@@ -50,16 +53,26 @@
      * </ul>
      */
     public static void maybeInstallDeferred() {
-        if (!BundleUtils.isBundle()) return;
-        if (AutofillAssistantModule.isInstalled()) return;
-        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) return;
-        if (!SysUtils.isHighEndDiskDevice()) return;
+        boolean isNotBundle = !BundleUtils.isBundle();
+        boolean isInstalled = AutofillAssistantModule.isInstalled();
+        boolean isVersionBeforeLollipop =
+                android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP;
+        boolean isNotHighEndDiskDevice = !SysUtils.isHighEndDiskDevice();
+        if (isNotBundle || isInstalled || isVersionBeforeLollipop || isNotHighEndDiskDevice) {
+            Log.v(TAG,
+                    "Deferred install not triggered: not_bundle=" + isNotBundle
+                            + ", already_installed=" + isInstalled
+                            + ", before_lollipop=" + isVersionBeforeLollipop
+                            + ", not_high_end_device=" + isNotHighEndDiskDevice);
+            return;
+        }
+        Log.v(TAG, "Deferred install triggered.");
         AutofillAssistantModule.installDeferred();
     }
 
     private static AutofillAssistantModuleEntry createEntry(Tab tab) {
         AutofillAssistantModuleEntryFactory factory = AutofillAssistantModule.getImpl();
-        return factory.createEntry(tab.getWebContents());
+        return factory.createEntry(tab.getActivity(), tab.getWebContents());
     }
 
     private static void loadDynamicModuleWithUi(
diff --git a/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background_grey.xml b/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background_grey.xml
new file mode 100644
index 0000000..0bd6ef9
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/drawable/tab_grid_card_background_grey.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/modern_grey_200"/>
+    <corners android:radius="@dimen/default_rounded_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
index 1c9aae6..6cb71de 100644
--- a/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item.xml
@@ -13,6 +13,7 @@
         android:layout_margin="3dp"
         android:visibility="visible"/>
     <RelativeLayout
+        android:id="@+id/content_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center"
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index fcd70d2..57d14d416 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -7,6 +7,7 @@
     <dimen name="tab_list_selected_inset">7dp</dimen>
     <dimen name="tab_list_selected_inset_kitkat">2dp</dimen>
     <dimen name="tab_list_card_padding">8dp</dimen>
+    <dimen name="tab_list_card_background_margin">3dp</dimen>
     <dimen name="tab_list_mini_card_radius">4dp</dimen>
     <dimen name="tab_list_mini_card_frame_size">1dp</dimen>
     <dimen name="tab_grid_close_button_size">18dp</dimen>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
index d538feae..f4ac5b5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridItemTouchHelperCallback.java
@@ -24,7 +24,6 @@
 /**
  * A {@link ItemTouchHelper.SimpleCallback} implementation to host the logic for swipe and drag
  * related actions in grid related layouts.
- * TODO(yuezhanggg): Get rid of using notifyDataSetChanged in adapter.
  */
 public class TabGridItemTouchHelperCallback extends ItemTouchHelper.SimpleCallback {
 
@@ -143,18 +142,22 @@
             }
             if (mHoveredTabIndex != TabModel.INVALID_TAB_INDEX && mActionsOnAllRelatedTabs) {
                 onTabMergeToGroup(mSelectedTabIndex, mHoveredTabIndex);
-                mRecyclerView.getAdapter().notifyDataSetChanged();
+                mRecyclerView.removeViewAt(mSelectedTabIndex);
                 RecordUserAction.record("GridTabSwitcher.Drag.AddToGroupOrCreateGroup");
             }
-            if (mHoveredTabIndex == TabModel.INVALID_TAB_INDEX) {
-                mModel.updateSelectedTabForMergeToGroup(mSelectedTabIndex, false);
+            mModel.updateSelectedTabForMergeToGroup(mSelectedTabIndex, false);
+            if (mHoveredTabIndex != TabModel.INVALID_TAB_INDEX) {
+                mModel.updateHoveredTabForMergeToGroup(mSelectedTabIndex > mHoveredTabIndex
+                                ? mHoveredTabIndex
+                                : mHoveredTabIndex - 1,
+                        false);
             }
             if (mUnGroupTabIndex != TabModel.INVALID_TAB_INDEX) {
                 TabGroupModelFilter filter =
                         (TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
                                 .getCurrentTabModelFilter();
                 filter.moveTabOutOfGroup(mModel.get(mUnGroupTabIndex).get(TabProperties.TAB_ID));
-                mRecyclerView.getAdapter().notifyDataSetChanged();
+                mRecyclerView.removeViewAt(mUnGroupTabIndex);
                 RecordUserAction.record("TabGridDialog.Drag.RemoveFromGroup");
             }
             mHoveredTabIndex = TabModel.INVALID_TAB_INDEX;
@@ -214,7 +217,6 @@
     }
 
     private void onTabMergeToGroup(int selectedCardIndex, int hoveredCardIndex) {
-        mModel.updateHoveredTabForMergeToGroup(hoveredCardIndex, false);
         TabGroupModelFilter filter =
                 (TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
                         .getCurrentTabModelFilter();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 57dfe68..5fbe1f7 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -718,7 +718,6 @@
                 mModel.get(modelIndex).set(TabProperties.FAVICON, drawable);
             }
         };
-
         mTabListFaviconProvider.getFaviconForUrlAsync(
                 tab.getUrl(), tab.isIncognito(), faviconCallback);
         if (mThumbnailProvider != null
@@ -814,7 +813,7 @@
                                 getCreateGroupButtonListener(tab, isSelected))
                         .with(TabProperties.ALPHA, 1f)
                         .with(TabProperties.CARD_ANIMATION_STATUS,
-                                TabListRecyclerView.ANIMATION_STATUS_RESTORE)
+                                TabListRecyclerView.AnimationStatus.CARD_RESTORE)
                         .with(TabProperties.SELECTABLE_TAB_CLICKED_LISTENER,
                                 mSelectableTabOnClickListener)
                         .with(TabProperties.TAB_SELECTION_DELEGATE, getTabSelectionDelegate())
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
index 17dde61..92f2041 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
@@ -78,8 +78,8 @@
      *         state. If not, restore it to original state.
      */
     void updateSelectedTabForMergeToGroup(int index, boolean isSelected) {
-        int status = isSelected ? TabListRecyclerView.ANIMATION_STATUS_ZOOM_IN
-                                : TabListRecyclerView.ANIMATION_STATUS_ZOOM_OUT;
+        int status = isSelected ? TabListRecyclerView.AnimationStatus.SELECTED_CARD_ZOOM_IN
+                                : TabListRecyclerView.AnimationStatus.SELECTED_CARD_ZOOM_OUT;
         if (index < 0 || index >= size()
                 || get(index).get(TabProperties.CARD_ANIMATION_STATUS) == status)
             return;
@@ -97,12 +97,11 @@
      *         If not, restore it to original state.
      */
     void updateHoveredTabForMergeToGroup(int index, boolean isHovered) {
-        int status = isHovered ? TabListRecyclerView.ANIMATION_STATUS_ZOOM_IN
-                               : TabListRecyclerView.ANIMATION_STATUS_ZOOM_OUT;
+        int status = isHovered ? TabListRecyclerView.AnimationStatus.HOVERED_CARD_ZOOM_IN
+                               : TabListRecyclerView.AnimationStatus.HOVERED_CARD_ZOOM_OUT;
         if (index < 0 || index >= size()
                 || get(index).get(TabProperties.CARD_ANIMATION_STATUS) == status)
             return;
-
         get(index).set(TabProperties.CARD_ANIMATION_STATUS, status);
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
index 9ec1f22d..73ff78e5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
@@ -13,7 +13,9 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.SystemClock;
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.v7.content.res.AppCompatResources;
 import android.support.v7.widget.RecyclerView;
@@ -24,12 +26,15 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.tab_ui.R;
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
 import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * A custom RecyclerView implementation for the tab grid, to handle show/hide logic in class.
  */
@@ -37,9 +42,17 @@
     public static final long BASE_ANIMATION_DURATION_MS = 218;
     public static final long FINAL_FADE_IN_DURATION_MS = 50;
     public static final long RESTORE_ANIMATION_DURATION_MS = 10;
-    public static final int ANIMATION_STATUS_RESTORE = 0;
-    public static final int ANIMATION_STATUS_ZOOM_OUT = 1;
-    public static final int ANIMATION_STATUS_ZOOM_IN = 2;
+    @IntDef({AnimationStatus.SELECTED_CARD_ZOOM_IN, AnimationStatus.SELECTED_CARD_ZOOM_OUT,
+            AnimationStatus.HOVERED_CARD_ZOOM_IN, AnimationStatus.HOVERED_CARD_ZOOM_OUT,
+            AnimationStatus.CARD_RESTORE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AnimationStatus {
+        int CARD_RESTORE = 0;
+        int SELECTED_CARD_ZOOM_OUT = 1;
+        int SELECTED_CARD_ZOOM_IN = 2;
+        int HOVERED_CARD_ZOOM_OUT = 3;
+        int HOVERED_CARD_ZOOM_IN = 4;
+    }
 
     /**
      * Field trial parameter for downsampling scaling factor.
@@ -180,12 +193,13 @@
         if (mShadowImageView == null) {
             Context context = getContext();
             mShadowImageView = new ImageView(context);
-            mShadowImageView.setImageDrawable(
-                    AppCompatResources.getDrawable(context, R.drawable.modern_toolbar_shadow));
+            mShadowImageView.setImageDrawable(AppCompatResources.getDrawable(
+                    context, org.chromium.chrome.R.drawable.modern_toolbar_shadow));
             Resources res = context.getResources();
-            FrameLayout.LayoutParams params =
-                    new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
-                            res.getDimensionPixelSize(R.dimen.toolbar_shadow_height), Gravity.TOP);
+            FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+                    LayoutParams.MATCH_PARENT,
+                    res.getDimensionPixelSize(org.chromium.chrome.R.dimen.toolbar_shadow_height),
+                    Gravity.TOP);
             params.topMargin = mShadowTopMargin;
             mShadowImageView.setScaleType(ImageView.ScaleType.FIT_XY);
             mShadowImageView.setLayoutParams(params);
@@ -364,15 +378,56 @@
         return rect;
     }
 
-    static void scaleTabGridCardView(View view, int status) {
+    /**
+     * Play the zoom-in and zoom-out animations for tab grid card.
+     * @param view   The view of the {@link TabGridViewHolder} that is playing animations.
+     * @param status The target animation status in {@link AnimationStatus}.
+     *
+     */
+    static void scaleTabGridCardView(View view, @AnimationStatus int status) {
+        Context context = view.getContext();
+        final View backgroundView = view.findViewById(R.id.background_view);
+        final View contentView = view.findViewById(R.id.content_view);
+        final int cardNormalMargin =
+                (int) context.getResources().getDimension(R.dimen.tab_list_card_padding);
+        final int cardBackgroundMargin =
+                (int) context.getResources().getDimension(R.dimen.tab_list_card_background_margin);
+        final Drawable greyBackground =
+                AppCompatResources.getDrawable(context, R.drawable.tab_grid_card_background_grey);
+        final Drawable normalBackground =
+                AppCompatResources.getDrawable(context, R.drawable.popup_bg);
+        boolean isZoomIn = status == AnimationStatus.SELECTED_CARD_ZOOM_IN
+                || status == AnimationStatus.HOVERED_CARD_ZOOM_IN;
+        boolean isHovered = status == AnimationStatus.HOVERED_CARD_ZOOM_IN
+                || status == AnimationStatus.HOVERED_CARD_ZOOM_OUT;
+        boolean isRestore = status == AnimationStatus.CARD_RESTORE;
+        long duration = isRestore ? RESTORE_ANIMATION_DURATION_MS : BASE_ANIMATION_DURATION_MS;
+        float scale = isZoomIn ? 0.8f : 1f;
+        MarginLayoutParams backgroundParams = (MarginLayoutParams) backgroundView.getLayoutParams();
+        View animateView = isHovered ? contentView : view;
+
+        if (status == AnimationStatus.HOVERED_CARD_ZOOM_IN) {
+            backgroundParams.setMargins(
+                    cardNormalMargin, cardNormalMargin, cardNormalMargin, cardNormalMargin);
+            backgroundView.setBackground(greyBackground);
+        }
+
         AnimatorSet scaleAnimator = new AnimatorSet();
-        float scale = status == ANIMATION_STATUS_ZOOM_IN ? 0.8f : 1f;
-        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", scale);
-        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", scale);
-        scaleX.setDuration(status == ANIMATION_STATUS_RESTORE ? RESTORE_ANIMATION_DURATION_MS
-                                                              : BASE_ANIMATION_DURATION_MS);
-        scaleY.setDuration(status == ANIMATION_STATUS_RESTORE ? RESTORE_ANIMATION_DURATION_MS
-                                                              : BASE_ANIMATION_DURATION_MS);
+        if (!isZoomIn) {
+            scaleAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    // Restore normal background status.
+                    backgroundParams.setMargins(cardBackgroundMargin, cardBackgroundMargin,
+                            cardBackgroundMargin, cardBackgroundMargin);
+                    backgroundView.setBackground(normalBackground);
+                }
+            });
+        }
+        ObjectAnimator scaleX = ObjectAnimator.ofFloat(animateView, "scaleX", scale);
+        ObjectAnimator scaleY = ObjectAnimator.ofFloat(animateView, "scaleY", scale);
+        scaleX.setDuration(duration);
+        scaleY.setDuration(duration);
         scaleAnimator.play(scaleX).with(scaleY);
         scaleAnimator.start();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 92932ff..99d8efc9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -255,6 +255,10 @@
      * @param isOffTheRecord  Whether to check downloads for the off the record profile.
      */
     public static void checkForExternallyRemovedDownloads(boolean isOffTheRecord) {
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.DOWNLOAD_OFFLINE_CONTENT_PROVIDER)) {
+            return;
+        }
+
         if (isOffTheRecord) {
             DownloadManagerService.getDownloadManagerService().checkForExternallyRemovedDownloads(
                     true);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java
index b178b9e74..bfff758e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java
@@ -65,7 +65,12 @@
     @Override
     public void onDownloadItemUpdated(DownloadItem item) {
         if (!canShowDownloadItem(item)) return;
-        mDelegate.onItemUpdated(DownloadItem.createOfflineItem(item), null);
+        OfflineItem offlineItem = DownloadItem.createOfflineItem(item);
+        mDelegate.onItemUpdated(offlineItem, null);
+
+        if (offlineItem.externallyRemoved) {
+            new Handler().post(() -> removeItem(offlineItem));
+        }
     }
 
     /** @see OfflineContentProvider.Observer#onItemRemoved(ContentId) */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
index d08b04a8f..446fd69 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
@@ -385,7 +385,7 @@
                             .getFieldTrialParamByFeature(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
                                     "tab_grid_layout_android_new_tab")
                             .equals("NewTabVariation")
-                || ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_DUET)) {
+                || FeatureUtilities.isBottomToolbarEnabled()) {
             return;
         }
         boolean hasIncognitoTabs =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 7e50fa7..737e9c76 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -937,9 +937,9 @@
         // toolbar is not visible (e.g. in tab switcher mode).
         if (isInTabSwitcherMode()) return;
 
+        int toolbarButtonVisibility = getToolbarButtonVisibility();
+        mToolbarButtonsContainer.setVisibility(toolbarButtonVisibility);
         if (!mIsBottomToolbarVisible && !getToolbarDataProvider().isInOverviewAndShowingOmnibox()) {
-            int toolbarButtonVisibility = getToolbarButtonVisibility();
-            mToolbarButtonsContainer.setVisibility(toolbarButtonVisibility);
             if (mHomeButton != null && mHomeButton.getVisibility() != GONE) {
                 mHomeButton.setVisibility(toolbarButtonVisibility);
             }
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 092fda70..d85d798f 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -3912,17 +3912,17 @@
       <message name="IDS_SEND_TAB_TO_SELF_INFOBAR_MESSAGE_URL" desc="Clickable text displayed as part of a URL in the inforbar displayed when a user receives a shared tab from another device.">
         Open
       </message>
-      <message name="IDS_SEND_TAB_TO_SELF_CONTENT_DESCRIPTION" desc="Content description for the target device picker.">
-        Device picker to share a tab with.
+      <message name="IDS_SEND_TAB_TO_SELF_CONTENT_DESCRIPTION" desc="Accessibility string read when the bottom sheet is opened. It describes the bottom sheet where a user can pick a device to share the tab with.">
+        List of devices to share a tab with.
       </message>
-      <message name="IDS_SEND_TAB_TO_SELF_SHEET_HALF_HEIGHT" desc="Accessibility string read when the target device picker sheet is opened at half height. The sheet will occupy the bottom half the screen.">
-        Device picker to share a tab with is opened at half height.
+      <message name="IDS_SEND_TAB_TO_SELF_SHEET_HALF_HEIGHT" desc="Accessibility string read when the bottom sheet showing a list of the user's devices is opened at half height. The sheet will occupy the bottom half the screen.">
+        List of devices to share a tab with opened at half height.
       </message>
-      <message name="IDS_SEND_TAB_TO_SELF_SHEET_FULL_HEIGHT" desc="Accessibility string read when the target device picker sheet is opened at full height. The sheet will occupy the entire screen.">
-        Device picker to share a tab with is opened at full height.
+      <message name="IDS_SEND_TAB_TO_SELF_SHEET_FULL_HEIGHT" desc="Accessibility string read when the bottom sheet showing a list of the user's devices is opened at full height. The sheet will occupy the entire screen.">
+        List of devices to share a tab with opened at full height.
       </message>
-      <message name="IDS_SEND_TAB_TO_SELF_SHEET_CLOSED" desc="Accessibility string read when the target device picker sheet is closed.">
-        Device picker to share a tab with is closed.
+      <message name="IDS_SEND_TAB_TO_SELF_SHEET_CLOSED" desc="Accessibility string read when the bottom sheet showing a list of the user's devices is closed.">
+        List of devices to share a tab with is closed.
       </message>
       <message name="IDS_SEND_TAB_TO_SELF_SHEET_TOOLBAR" desc="Header for device picker sheet where users can pick a device to send a tab to.">
         Send to
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
index 5bfe280..5f9a5e4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
@@ -781,6 +781,7 @@
     @Feature({"Omnibox"})
     @RetryOnFailure
     @MinAndroidSdkLevel(Build.VERSION_CODES.JELLY_BEAN_MR1)
+    @DisabledTest // https://crbug.com/950556
     public void testSuggestionDirectionSwitching() throws InterruptedException {
         final TextView urlBarView =
                 (TextView) mActivityTestRule.getActivity().findViewById(R.id.url_bar);
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index b79a4cd..d7ba61f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1424,12 +1424,12 @@
     {"shelf-dense-clamshell", flag_descriptions::kShelfDenseClamshellName,
      flag_descriptions::kShelfDenseClamshellDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(chromeos::switches::kShelfDenseClamshell)},
+    {"shelf-hotseat", flag_descriptions::kShelfHotseatName,
+     flag_descriptions::kShelfHotseatDescription, kOsCrOS,
+     SINGLE_VALUE_TYPE(chromeos::switches::kShelfHotseat)},
     {"shelf-hover-previews", flag_descriptions::kShelfHoverPreviewsName,
      flag_descriptions::kShelfHoverPreviewsDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(chromeos::switches::kShelfHoverPreviews)},
-    {"shelf-new-ui", flag_descriptions::kShelfNewUiName,
-     flag_descriptions::kShelfNewUiDescription, kOsCrOS,
-     SINGLE_VALUE_TYPE(chromeos::switches::kShelfNewUi)},
     {"shelf-scrollable", flag_descriptions::kShelfScrollableName,
      flag_descriptions::kShelfScrollableDescription, kOsCrOS,
      SINGLE_VALUE_TYPE(chromeos::switches::kShelfScrollable)},
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index ce8b01c..8e450ff 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -103,15 +103,6 @@
                                               java_object_);
 }
 
-void ClientAndroid::ShowOnboarding(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
-    const JavaParamRef<jstring>& jexperiment_ids,
-    const JavaParamRef<jobject>& on_accept) {
-  ShowUI();
-  ui_controller_android_->ShowOnboarding(env, jexperiment_ids, on_accept);
-}
-
 base::android::ScopedJavaLocalRef<jobject> ClientAndroid::GetJavaObject() {
   return base::android::ScopedJavaLocalRef<jobject>(java_object_);
 }
@@ -121,8 +112,15 @@
                           const JavaParamRef<jstring>& jinitial_url,
                           const JavaParamRef<jstring>& jexperiment_ids,
                           const JavaParamRef<jobjectArray>& parameterNames,
-                          const JavaParamRef<jobjectArray>& parameterValues) {
+                          const JavaParamRef<jobjectArray>& parameterValues,
+                          const JavaParamRef<jobject>& joverlay_coordinator) {
   CreateController();
+
+  // If an overlay is already shown, then show the rest of the UI.
+  if (joverlay_coordinator) {
+    CreateUI(joverlay_coordinator);
+  }
+
   GURL initial_url(base::android::ConvertJavaStringToUTF8(env, jinitial_url));
   std::map<std::string, std::string> parameters;
   FillParametersFromJava(env, parameterNames, parameterValues, &parameters);
@@ -191,13 +189,19 @@
     // onboarding.
   }
   if (!ui_controller_android_) {
-    std::unique_ptr<UiControllerAndroid> ui_ptr =
-        UiControllerAndroid::CreateFromWebContents(web_contents_);
-    if (ui_ptr)
-      SetUI(std::move(ui_ptr));
+    CreateUI(nullptr);
   }
 }
 
+void ClientAndroid::CreateUI(
+    const JavaParamRef<jobject>& joverlay_coordinator) {
+  std::unique_ptr<UiControllerAndroid> ui_ptr =
+      UiControllerAndroid::CreateFromWebContents(web_contents_,
+                                                 joverlay_coordinator);
+  if (ui_ptr)
+    SetUI(std::move(ui_ptr));
+}
+
 void ClientAndroid::DestroyUI() {
   ui_controller_android_.reset();
 }
diff --git a/chrome/browser/android/autofill_assistant/client_android.h b/chrome/browser/android/autofill_assistant/client_android.h
index fac273d..478fd24 100644
--- a/chrome/browser/android/autofill_assistant/client_android.h
+++ b/chrome/browser/android/autofill_assistant/client_android.h
@@ -38,19 +38,13 @@
   // Returns the corresponding Java AutofillAssistantClient.
   base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
 
-  // Called from the Java side:
-  void ShowOnboarding(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller,
-      const base::android::JavaParamRef<jstring>& jexperiment_ids,
-      const base::android::JavaParamRef<jobject>& on_accept);
-
   void Start(JNIEnv* env,
              const base::android::JavaParamRef<jobject>& jcaller,
              const base::android::JavaParamRef<jstring>& jinitial_url,
              const base::android::JavaParamRef<jstring>& jexperiment_ids,
              const base::android::JavaParamRef<jobjectArray>& parameterNames,
-             const base::android::JavaParamRef<jobjectArray>& parameterValues);
+             const base::android::JavaParamRef<jobjectArray>& parameterValues,
+             const base::android::JavaParamRef<jobject>& joverlay_coordinator);
   void DestroyUI(JNIEnv* env,
                  const base::android::JavaParamRef<jobject>& jcaller);
   void TransferUITo(
@@ -90,6 +84,8 @@
   explicit ClientAndroid(content::WebContents* web_contents);
   void CreateController();
   void DestroyController();
+  void CreateUI(
+      const base::android::JavaParamRef<jobject>& joverlay_coordinator);
   bool NeedsUI();
   void SetUI(std::unique_ptr<UiControllerAndroid> ui_controller_android);
 
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 94ca49e..d931a43 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -62,23 +62,37 @@
 static constexpr base::TimeDelta kGracefulShutdownDelay =
     base::TimeDelta::FromSeconds(5);
 
+std::vector<float> ToFloatVector(const std::vector<RectF>& areas) {
+  std::vector<float> flattened;
+  for (const auto& rect : areas) {
+    flattened.emplace_back(rect.left);
+    flattened.emplace_back(rect.top);
+    flattened.emplace_back(rect.right);
+    flattened.emplace_back(rect.bottom);
+  }
+  return flattened;
+}
+
 }  // namespace
 
 // static
 std::unique_ptr<UiControllerAndroid> UiControllerAndroid::CreateFromWebContents(
-    content::WebContents* web_contents) {
+    content::WebContents* web_contents,
+    const base::android::JavaParamRef<jobject>& joverlay_coordinator) {
   JNIEnv* env = AttachCurrentThread();
   auto jactivity = Java_AutofillAssistantUiController_findAppropriateActivity(
       env, web_contents->GetJavaWebContents());
   if (!jactivity) {
     return nullptr;
   }
-  return std::make_unique<UiControllerAndroid>(env, jactivity);
+  return std::make_unique<UiControllerAndroid>(env, jactivity,
+                                               joverlay_coordinator);
 }
 
 UiControllerAndroid::UiControllerAndroid(
     JNIEnv* env,
-    const base::android::JavaRef<jobject>& jactivity)
+    const base::android::JavaRef<jobject>& jactivity,
+    const base::android::JavaParamRef<jobject>& joverlay_coordinator)
     : overlay_delegate_(this),
       header_delegate_(this),
       payment_request_delegate_(this),
@@ -88,7 +102,7 @@
       env, jactivity,
       /* allowTabSwitching= */
       base::FeatureList::IsEnabled(features::kAutofillAssistantChromeEntry),
-      reinterpret_cast<intptr_t>(this));
+      reinterpret_cast<intptr_t>(this), joverlay_coordinator);
 
   // Register overlay_delegate_ as delegate for the overlay.
   Java_AssistantOverlayModel_setDelegate(env, GetOverlayModel(),
@@ -135,9 +149,11 @@
 
     std::vector<RectF> area;
     ui_delegate->GetTouchableArea(&area);
+    std::vector<RectF> restricted_area;
+    ui_delegate->GetRestrictedArea(&restricted_area);
     RectF visual_viewport;
     ui_delegate->GetVisualViewport(&visual_viewport);
-    OnTouchableAreaChanged(visual_viewport, area);
+    OnTouchableAreaChanged(visual_viewport, area, restricted_area);
     OnResizeViewportChanged(ui_delegate->GetResizeViewport());
     OnPeekModeChanged(ui_delegate->GetPeekMode());
     OnFormChanged(ui_delegate->GetForm());
@@ -530,21 +546,20 @@
 
 void UiControllerAndroid::OnTouchableAreaChanged(
     const RectF& visual_viewport,
-    const std::vector<RectF>& areas) {
+    const std::vector<RectF>& touchable_areas,
+    const std::vector<RectF>& restricted_areas) {
   JNIEnv* env = AttachCurrentThread();
   Java_AssistantOverlayModel_setVisualViewport(
       env, GetOverlayModel(), visual_viewport.left, visual_viewport.top,
       visual_viewport.right, visual_viewport.bottom);
 
-  std::vector<float> flattened;
-  for (const auto& rect : areas) {
-    flattened.emplace_back(rect.left);
-    flattened.emplace_back(rect.top);
-    flattened.emplace_back(rect.right);
-    flattened.emplace_back(rect.bottom);
-  }
   Java_AssistantOverlayModel_setTouchableArea(
-      env, GetOverlayModel(), base::android::ToJavaFloatArray(env, flattened));
+      env, GetOverlayModel(),
+      base::android::ToJavaFloatArray(env, ToFloatVector(touchable_areas)));
+
+  Java_AssistantOverlayModel_setRestrictedArea(
+      AttachCurrentThread(), GetOverlayModel(),
+      base::android::ToJavaFloatArray(env, ToFloatVector(restricted_areas)));
 }
 
 void UiControllerAndroid::OnUnexpectedTaps() {
@@ -566,14 +581,6 @@
 
 // Other methods.
 
-void UiControllerAndroid::ShowOnboarding(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jstring>& jexperiment_ids,
-    const base::android::JavaParamRef<jobject>& on_accept) {
-  Java_AutofillAssistantUiController_onShowOnboarding(
-      env, java_object_, jexperiment_ids, on_accept);
-}
-
 void UiControllerAndroid::WillShutdown(Metrics::DropOutReason reason) {
   if (reason == Metrics::CUSTOM_TAB_CLOSED) {
     Java_AutofillAssistantUiController_scheduleCloseCustomTab(
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index cabcf97..4c5ae6a9 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -31,15 +31,18 @@
 class UiControllerAndroid : public UiController {
  public:
   static std::unique_ptr<UiControllerAndroid> CreateFromWebContents(
-      content::WebContents* web_contents);
+      content::WebContents* web_contents,
+      const base::android::JavaParamRef<jobject>& joverlay_coordinator);
 
   // pointers to |web_contents|, |client| must remain valid for the lifetime of
   // this instance.
   //
   // Pointer to |ui_delegate| must remain valid for the lifetime of this
   // instance or until WillShutdown is called.
-  UiControllerAndroid(JNIEnv* env,
-                      const base::android::JavaRef<jobject>& jactivity);
+  UiControllerAndroid(
+      JNIEnv* env,
+      const base::android::JavaRef<jobject>& jactivity,
+      const base::android::JavaParamRef<jobject>& joverlay_coordinator);
   ~UiControllerAndroid() override;
 
   // Attaches the UI to the given client, its web contents and delegate.
@@ -53,12 +56,6 @@
               Client* client,
               UiDelegate* ui_delegate);
 
-  // Called by ClientAndroid.
-  void ShowOnboarding(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jstring>& jexperiment_ids,
-      const base::android::JavaParamRef<jobject>& on_accept);
-
   // Overrides UiController:
   void OnStateChanged(AutofillAssistantState new_state) override;
   void OnStatusMessageChanged(const std::string& message) override;
@@ -73,8 +70,10 @@
   void OnInfoBoxChanged(const InfoBox* info_box) override;
   void OnProgressChanged(int progress) override;
   void OnProgressVisibilityChanged(bool visible) override;
-  void OnTouchableAreaChanged(const RectF& visual_viewport,
-                              const std::vector<RectF>& areas) override;
+  void OnTouchableAreaChanged(
+      const RectF& visual_viewport,
+      const std::vector<RectF>& touchable_areas,
+      const std::vector<RectF>& restricted_areas) override;
   void OnResizeViewportChanged(bool resize_viewport) override;
   void OnPeekModeChanged(
       ConfigureBottomSheetProto::PeekMode peek_mode) override;
diff --git a/chrome/browser/apps/app_service/app_service_proxy_impl.cc b/chrome/browser/apps/app_service/app_service_proxy_impl.cc
index d786ea5..a600b9ea 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_impl.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_impl.cc
@@ -74,17 +74,29 @@
       AppServiceProxyFactory::GetForProfile(profile));
 }
 
+// static
+AppServiceProxyImpl* AppServiceProxyImpl::CreateForTesting(
+    Profile* profile,
+    service_manager::Connector* connector) {
+  return new AppServiceProxyImpl(profile, connector);
+}
+
 AppServiceProxyImpl::AppServiceProxyImpl(Profile* profile)
+    : AppServiceProxyImpl(profile, nullptr) {}
+
+AppServiceProxyImpl::AppServiceProxyImpl(Profile* profile,
+                                         service_manager::Connector* connector)
     : inner_icon_loader_(this),
       outer_icon_loader_(&inner_icon_loader_,
                          apps::IconCache::GarbageCollectionPolicy::kEager) {
   if (!profile) {
     return;
   }
-  service_manager::Connector* connector =
-      content::BrowserContext::GetConnectorFor(profile);
   if (!connector) {
-    return;
+    connector = content::BrowserContext::GetConnectorFor(profile);
+    if (!connector) {
+      return;
+    }
   }
   connector->BindInterface(apps::mojom::kServiceName,
                            mojo::MakeRequest(&app_service_));
diff --git a/chrome/browser/apps/app_service/app_service_proxy_impl.h b/chrome/browser/apps/app_service/app_service_proxy_impl.h
index 721bec5..b914842 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_impl.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_impl.h
@@ -21,6 +21,10 @@
 
 class Profile;
 
+namespace service_manager {
+class Connector;
+}
+
 namespace apps {
 
 // Singleton (per Profile) proxy and cache of an App Service's apps.
@@ -41,6 +45,10 @@
   // instead.
   static AppServiceProxyImpl* GetImplForTesting(Profile* profile);
 
+  static AppServiceProxyImpl* CreateForTesting(
+      Profile* profile,
+      service_manager::Connector* connector);
+
   explicit AppServiceProxyImpl(Profile* profile);
 
   ~AppServiceProxyImpl() override;
@@ -132,6 +140,8 @@
     apps::IconLoader* overriding_icon_loader_for_testing_;
   };
 
+  AppServiceProxyImpl(Profile* profile, service_manager::Connector* connector);
+
   // KeyedService overrides.
   void Shutdown() override;
 
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index 4fc0ce60..71c65ab 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -161,15 +161,25 @@
   return ArcAppsFactory::GetForProfile(profile);
 }
 
-ArcApps::ArcApps(Profile* profile)
+// static
+ArcApps* ArcApps::CreateForTesting(Profile* profile,
+                                   apps::AppServiceProxy* proxy) {
+  return new ArcApps(profile, proxy);
+}
+
+ArcApps::ArcApps(Profile* profile) : ArcApps(profile, nullptr) {}
+
+ArcApps::ArcApps(Profile* profile, apps::AppServiceProxy* proxy)
     : binding_(this), profile_(profile), prefs_(nullptr) {
   if (!arc::IsArcAllowedForProfile(profile_) ||
       (arc::ArcServiceManager::Get() == nullptr)) {
     return;
   }
 
-  apps::mojom::AppServicePtr& app_service =
-      apps::AppServiceProxyFactory::GetForProfile(profile)->AppService();
+  if (!proxy) {
+    proxy = apps::AppServiceProxyFactory::GetForProfile(profile);
+  }
+  apps::mojom::AppServicePtr& app_service = proxy->AppService();
   if (!app_service.is_bound()) {
     return;
   }
diff --git a/chrome/browser/apps/app_service/arc_apps.h b/chrome/browser/apps/app_service/arc_apps.h
index 43e75f3..28de1334 100644
--- a/chrome/browser/apps/app_service/arc_apps.h
+++ b/chrome/browser/apps/app_service/arc_apps.h
@@ -25,6 +25,8 @@
 
 namespace apps {
 
+class AppServiceProxy;
+
 // An app publisher (in the App Service sense) of ARC++ apps,
 //
 // See chrome/services/app_service/README.md.
@@ -38,11 +40,16 @@
 
   static ArcApps* Get(Profile* profile);
 
+  static ArcApps* CreateForTesting(Profile* profile,
+                                   apps::AppServiceProxy* proxy);
+
   explicit ArcApps(Profile* profile);
 
   ~ArcApps() override;
 
  private:
+  ArcApps(Profile* profile, apps::AppServiceProxy* proxy);
+
   // apps::mojom::Publisher overrides.
   void Connect(apps::mojom::SubscriberPtr subscriber,
                apps::mojom::ConnectOptionsPtr opts) override;
diff --git a/chrome/browser/browser_keyevents_browsertest.cc b/chrome/browser/browser_keyevents_browsertest.cc
index 7895c76..580b3b4 100644
--- a/chrome/browser/browser_keyevents_browsertest.cc
+++ b/chrome/browser/browser_keyevents_browsertest.cc
@@ -25,7 +25,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
 // TODO(kbr): remove: http://crbug.com/222296
@@ -528,14 +527,6 @@
 #define MAYBE_AccessKeys AccessKeys
 #endif
 IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, MAYBE_AccessKeys) {
-  // TODO(crbug.com/916379): this test fails in mash because of differences in
-  // timing. In particular, in classic mode an accelerator that moves focus is
-  // processed *after* text is inserted, where as now the accelerator runs
-  // first, resulting in text going to the wrong place. The right fix likely
-  // entails updating the test for mash.
-  if (features::IsSingleProcessMash())
-    return;
-
 #if defined(OS_MACOSX)
   // On Mac, access keys use ctrl+alt modifiers.
   static const KeyEventTestData kTestAccessA = {
diff --git a/chrome/browser/chrome_content_browser_client_browsertest_chromeos.cc b/chrome/browser/chrome_content_browser_client_browsertest_chromeos.cc
deleted file mode 100644
index 80aa8eb..0000000
--- a/chrome/browser/chrome_content_browser_client_browsertest_chromeos.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2017 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/chrome_content_browser_client.h"
-
-#include "base/base_switches.h"
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/run_loop.h"
-#include "base/task/post_task.h"
-#include "base/threading/thread_restrictions.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "content/public/browser/browser_child_process_host_iterator.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/child_process_data.h"
-#include "content/public/common/process_type.h"
-#include "ui/base/ui_base_features.h"
-
-using content::BrowserChildProcessHostIterator;
-using content::BrowserThread;
-
-namespace {
-
-void GetUtilityProcessPidsOnIOThread(std::vector<pid_t>* pids) {
-  for (BrowserChildProcessHostIterator it(content::PROCESS_TYPE_UTILITY);
-       !it.Done(); ++it) {
-    pid_t pid = it.GetData().GetProcess().Pid();
-    pids->push_back(pid);
-  }
-}
-
-std::string ReadCmdLine(pid_t pid) {
-  // Files in "/proc" are in-memory, so it's safe to do IO.
-  base::ScopedAllowBlockingForTesting allow_io;
-  base::FilePath cmdline_file = base::FilePath("/proc")
-                                    .Append(base::NumberToString(pid))
-                                    .Append("cmdline");
-  std::string cmdline;
-  base::ReadFileToString(cmdline_file, &cmdline);
-  return cmdline;
-}
-
-// We don't seem to have a string utility or STL utility for this.
-bool HasSubstring(base::StringPiece str, base::StringPiece sub) {
-  return str.find(sub) != base::StringPiece::npos;
-}
-
-}  // namespace
-
-using ChromeContentBrowserClientMashTest = InProcessBrowserTest;
-
-// Verifies that mash service child processes use in-process breakpad crash
-// dumping.
-IN_PROC_BROWSER_TEST_F(ChromeContentBrowserClientMashTest, CrashReporter) {
-  // Test only applies to out-of-process.
-  if (!features::IsMultiProcessMash())
-    return;
-
-  // Child process management lives on the IO thread.
-  std::vector<pid_t> pids;
-  base::RunLoop loop;
-  base::PostTaskWithTraitsAndReply(
-      FROM_HERE, {BrowserThread::IO},
-      base::Bind(&GetUtilityProcessPidsOnIOThread, &pids), loop.QuitClosure());
-  loop.Run();
-  ASSERT_FALSE(pids.empty());
-
-  // Iterate through all utility processes looking for mash services. We are
-  // guaranteed at least ash and mus have launched because we block during
-  // startup waiting for mus.
-  int mash_service_count = 0;
-  for (pid_t pid : pids) {
-    std::string cmdline = ReadCmdLine(pid);
-    ASSERT_FALSE(cmdline.empty());
-    // Subprocess command lines may have their null separators replaced with
-    // spaces, which makes them hard to tokenize. Just search the whole string.
-    if (HasSubstring(cmdline, switches::kMashServiceName)) {
-      ++mash_service_count;
-      // Mash services use in-process breakpad crash dumping.
-      EXPECT_TRUE(HasSubstring(cmdline, switches::kEnableCrashReporter));
-    }
-  }
-  // There's at least one mash service, for ash.
-  EXPECT_GT(mash_service_count, 0);
-}
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
index 237eeb3cb..977131e 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -90,7 +90,6 @@
 #include "ui/accessibility/ax_enum_util.h"
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 #include "url/gurl.h"
@@ -394,10 +393,6 @@
   if (!profile_)
     return;
 
-  // TODO(crbug.com/594887): Fix for mash by moving pref into ash.
-  if (features::IsMultiProcessMash())
-    return;
-
   // Update system tray menu visibility.
   ash::Shell::Get()
       ->accessibility_controller()
@@ -991,13 +986,9 @@
     input_method::InputMethodManager* manager,
     Profile* /* profile */,
     bool show_message) {
-  // Sticky keys is implemented only in ash.
-  // TODO(crbug.com/678820): Mash support.
-  if (!features::IsMultiProcessMash()) {
     ash::Shell::Get()->sticky_keys_controller()->SetModifiersEnabled(
         manager->IsISOLevel5ShiftUsedByCurrentInputMethod(),
         manager->IsAltGrUsedByCurrentInputMethod());
-  }
   const chromeos::input_method::InputMethodDescriptor descriptor =
       manager->GetActiveIMEState()->GetCurrentInputMethod();
   braille_ime_current_ =
@@ -1162,10 +1153,6 @@
     const AccessibilityStatusEventDetails& details) {
   callback_list_.Notify(details);
 
-  // TODO(crbug.com/594887): Fix for mash by moving pref into ash.
-  if (features::IsMultiProcessMash())
-    return;
-
   if (details.notification_type == ACCESSIBILITY_TOGGLE_DICTATION) {
     ash::Shell::Get()->accessibility_controller()->SetDictationActive(
         details.enabled);
diff --git a/chrome/browser/chromeos/accessibility/accessibility_panel.cc b/chrome/browser/chromeos/accessibility/accessibility_panel.cc
index fa174187..1241c80c 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_panel.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_panel.cc
@@ -14,7 +14,6 @@
 #include "content/public/common/service_manager_connection.h"
 #include "extensions/browser/view_type_utils.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/views/controls/webview/webview.h"
@@ -70,14 +69,6 @@
   params.name = widget_name;
   params.shadow_elevation = wm::kShadowElevationInactiveWindow;
   widget_->Init(params);
-
-  // WebContentsObserver::DidFirstVisuallyNonEmptyPaint is not called under
-  // mash. Work around this by showing the window immediately.
-  // TODO(jamescook|fsamuel): Fix this. It causes a white flash when opening the
-  // window. The underlying problem is FrameToken plumbing, see
-  // ui::ws::ServerWindow::OnFrameTokenChanged. https://crbug.com/771331
-  if (features::IsMultiProcessMash())
-    widget_->Show();
 }
 
 AccessibilityPanel::~AccessibilityPanel() = default;
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc
index 61803df3..d81cede1 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.cc
@@ -342,6 +342,7 @@
 }
 
 void ArcFileSystemBridge::GetFileSelectorElements(
+    mojom::GetFileSelectorElementsRequestPtr request,
     GetFileSelectorElementsCallback callback) {
   if (!IsTestImageBuild()) {
     LOG(ERROR)
@@ -349,7 +350,8 @@
     std::move(callback).Run(mojom::FileSelectorElements::New());
     return;
   }
-  select_files_handler_->GetFileSelectorElements(std::move(callback));
+  select_files_handler_->GetFileSelectorElements(std::move(request),
+                                                 std::move(callback));
 }
 
 void ArcFileSystemBridge::OpenFileToReadAfterGetFileSize(
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.h b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.h
index c746c1e..a128068 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.h
+++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_bridge.h
@@ -92,6 +92,7 @@
   void OnFileSelectorEvent(mojom::FileSelectorEventPtr event,
                            OnFileSelectorEventCallback callback) override;
   void GetFileSelectorElements(
+      mojom::GetFileSelectorElementsRequestPtr request,
       GetFileSelectorElementsCallback callback) override;
 
  private:
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler.cc b/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler.cc
index 2cc0da4e..474d632 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler.cc
@@ -186,7 +186,7 @@
       request->action_type == mojom::SelectFilesActionType::GET_CONTENT;
 
   dialog_holder_->SelectFile(dialog_type, default_path, &file_type_info,
-                             show_android_picker_apps);
+                             request->task_id, show_android_picker_apps);
 }
 
 void ArcSelectFilesHandler::FileSelected(const base::FilePath& path,
@@ -275,6 +275,7 @@
 }
 
 void ArcSelectFilesHandler::GetFileSelectorElements(
+    mojom::GetFileSelectorElementsRequestPtr request,
     mojom::FileSystemHost::GetFileSelectorElementsCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
@@ -304,6 +305,7 @@
     ui::SelectFileDialog::Type type,
     const base::FilePath& default_path,
     const ui::SelectFileDialog::FileTypeInfo* file_types,
+    int task_id,
     bool show_android_picker_apps) {
   select_file_dialog_->SelectFileWithFileManagerParams(
       type,
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler.h b/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler.h
index e215cc0..112df25 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler.h
+++ b/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler.h
@@ -46,6 +46,7 @@
       mojom::FileSystemHost::OnFileSelectorEventCallback callback);
 
   void GetFileSelectorElements(
+      mojom::GetFileSelectorElementsRequestPtr request,
       mojom::FileSystemHost::GetFileSelectorElementsCallback callback);
 
   // ui::SelectFileDialog::Listener overrides:
@@ -84,6 +85,7 @@
   virtual void SelectFile(ui::SelectFileDialog::Type type,
                           const base::FilePath& default_path,
                           const ui::SelectFileDialog::FileTypeInfo* file_types,
+                          int task_id,
                           bool show_android_picker_apps);
 
   virtual void ExecuteJavaScript(
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler_unittest.cc
index 7b90241..f15dcc49 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_select_files_handler_unittest.cc
@@ -79,10 +79,11 @@
   explicit MockSelectFileDialogHolder(ui::SelectFileDialog::Listener* listener)
       : SelectFileDialogHolder(listener) {}
   ~MockSelectFileDialogHolder() override = default;
-  MOCK_METHOD4(SelectFile,
+  MOCK_METHOD5(SelectFile,
                void(ui::SelectFileDialog::Type type,
                     const base::FilePath& default_path,
                     const ui::SelectFileDialog::FileTypeInfo* file_types,
+                    int task_id,
                     bool show_android_picker_apps));
   MOCK_METHOD2(ExecuteJavaScript,
                void(const std::string&, JavaScriptResultCallback));
@@ -128,7 +129,7 @@
     request->allow_multiple = request_allow_multiple;
 
     EXPECT_CALL(*mock_dialog_holder_,
-                SelectFile(expected_dialog_type, _, _,
+                SelectFile(expected_dialog_type, _, _, _,
                            expected_show_android_picker_apps))
         .Times(1);
 
@@ -185,6 +186,7 @@
   SelectFilesRequestPtr request = SelectFilesRequest::New();
   request->action_type = SelectFilesActionType::OPEN_DOCUMENT;
   request->mime_types.push_back("text/plain");
+  request->task_id = 1234;
 
   SelectFileDialog::FileTypeInfo expected_file_type_info;
   expected_file_type_info.allowed_paths =
@@ -198,7 +200,7 @@
       *mock_dialog_holder_,
       SelectFile(_, _,
                  testing::Pointee(FileTypeInfoMatcher(expected_file_type_info)),
-                 _))
+                 1234, _))
       .Times(1);
 
   base::MockCallback<SelectFilesCallback> callback;
@@ -217,7 +219,7 @@
       "/special/arc-documents-provider/testing.provider/doc:root");
 
   EXPECT_CALL(*mock_dialog_holder_,
-              SelectFile(_, FilePathMatcher(expected_file_path), _, _))
+              SelectFile(_, FilePathMatcher(expected_file_path), _, _, _))
       .Times(1);
 
   base::MockCallback<SelectFilesCallback> callback;
@@ -309,7 +311,8 @@
               Run(FileSelectorElementsMatcher(expectedElements.get())))
       .Times(1);
 
-  arc_select_files_handler_->GetFileSelectorElements(callback.Get());
+  arc_select_files_handler_->GetFileSelectorElements(
+      arc::mojom::GetFileSelectorElementsRequest::New(), callback.Get());
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 96644cfc..97b4d28a 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -288,9 +288,7 @@
 class DBusServices {
  public:
   explicit DBusServices(const content::MainFunctionParams& parameters) {
-    // In Mash, power policy is sent to powerd by ash.
-    if (!::features::IsMultiProcessMash())
-      PowerPolicyController::Initialize(PowerManagerClient::Get());
+    PowerPolicyController::Initialize(PowerManagerClient::Get());
 
     dbus::Bus* system_bus = DBusThreadManager::Get()->IsUsingFakes()
                                 ? nullptr
@@ -406,8 +404,7 @@
     drive_file_stream_service_.reset();
     ProcessDataCollector::Shutdown();
     PowerDataCollector::Shutdown();
-    if (!::features::IsMultiProcessMash())
-      PowerPolicyController::Shutdown();
+    PowerPolicyController::Shutdown();
     device::BluetoothAdapterFactory::Shutdown();
   }
 
@@ -754,12 +751,9 @@
 
   AccessibilityManager::Initialize();
 
-  if (!::features::IsMultiProcessMash()) {
-    // Initialize magnification manager before ash tray is created. And this
-    // must be placed after UserManager::SessionStarted();
-    // TODO(crbug.com/821551): Mash support.
-    MagnificationManager::Initialize();
-  }
+  // Initialize magnification manager before ash tray is created. And this
+  // must be placed after UserManager::SessionStarted();
+  MagnificationManager::Initialize();
 
   base::PostTaskWithTraitsAndReplyWithResult(
       FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
@@ -1122,8 +1116,7 @@
   if (LoginScreenExtensionUiHandler::Get(false /*can_create*/))
     LoginScreenExtensionUiHandler::Shutdown();
 
-  if (!::features::IsMultiProcessMash())
-    MagnificationManager::Shutdown();
+  MagnificationManager::Shutdown();
 
   audio::SoundsManager::Shutdown();
 
@@ -1189,9 +1182,7 @@
   // ChromeBrowserMainPartsLinux::PostMainMessageLoopRun().
   arc_service_launcher_.reset();
 
-  // TODO(crbug.com/594887): Mash support.
-  if (!::features::IsMultiProcessMash())
-    AccessibilityManager::Shutdown();
+  AccessibilityManager::Shutdown();
 
   input_method::Shutdown();
 
diff --git a/chrome/browser/chromeos/crostini/crostini_export_import_notification.h b/chrome/browser/chromeos/crostini/crostini_export_import_notification.h
index c4cdc5ff..436ccf3 100644
--- a/chrome/browser/chromeos/crostini/crostini_export_import_notification.h
+++ b/chrome/browser/chromeos/crostini/crostini_export_import_notification.h
@@ -62,7 +62,7 @@
   base::FilePath path_;
   Status status_ = Status::RUNNING;
   // Time when the operation started.  Used for estimating time remaining.
-  base::Time started_ = base::Time::Now();
+  base::TimeTicks started_ = base::TimeTicks::Now();
   base::string16 title_running_;
   base::string16 title_done_;
   base::string16 message_done_;
diff --git a/chrome/browser/chromeos/crostini/crostini_package_notification.cc b/chrome/browser/chromeos/crostini/crostini_package_notification.cc
index 0d12b0a..c52bf6b 100644
--- a/chrome/browser/chromeos/crostini/crostini_package_notification.cc
+++ b/chrome/browser/chromeos/crostini/crostini_package_notification.cc
@@ -45,7 +45,7 @@
       visible_(true),
       weak_ptr_factory_(this) {
   if (status == PackageOperationStatus::RUNNING) {
-    running_start_time_ = base::Time::Now();
+    running_start_time_ = base::TimeTicks::Now();
   }
   message_center::RichNotificationData rich_notification_data;
   rich_notification_data.vector_small_image = &ash::kNotificationLinuxIcon;
@@ -135,7 +135,7 @@
                                                  int progress_percent) {
   if (status == PackageOperationStatus::RUNNING &&
       current_status_ != PackageOperationStatus::RUNNING) {
-    running_start_time_ = base::Time::Now();
+    running_start_time_ = base::TimeTicks::Now();
   }
   current_status_ = status;
 
diff --git a/chrome/browser/chromeos/crostini/crostini_package_notification.h b/chrome/browser/chromeos/crostini/crostini_package_notification.h
index 44429d4..7675643 100644
--- a/chrome/browser/chromeos/crostini/crostini_package_notification.h
+++ b/chrome/browser/chromeos/crostini/crostini_package_notification.h
@@ -81,7 +81,7 @@
 
   // The most-recent time we entered the "RUNNING" state. Used for
   // guesstimating when we'll be done.
-  base::Time running_start_time_;
+  base::TimeTicks running_start_time_;
 
   // These notifications are owned by the package service.
   CrostiniPackageService* package_service_;
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index d634079..f8c675e 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -465,14 +465,14 @@
       vm_name, container_name);
 }
 
-base::string16 GetTimeRemainingMessage(base::Time start, int percent) {
+base::string16 GetTimeRemainingMessage(base::TimeTicks start, int percent) {
   // Only estimate once we've spent at least 3 seconds OR gotten 10% of the way
   // through.
   constexpr base::TimeDelta kMinTimeForEstimate =
       base::TimeDelta::FromSeconds(3);
   constexpr base::TimeDelta kTimeDeltaZero = base::TimeDelta::FromSeconds(0);
   constexpr int kMinPercentForEstimate = 10;
-  base::TimeDelta elapsed = base::Time::Now() - start;
+  base::TimeDelta elapsed = base::TimeTicks::Now() - start;
   if ((elapsed >= kMinTimeForEstimate && percent > 0) ||
       (percent >= kMinPercentForEstimate && elapsed > kTimeDeltaZero)) {
     base::TimeDelta total_time_expected = (elapsed * 100) / percent;
diff --git a/chrome/browser/chromeos/crostini/crostini_util.h b/chrome/browser/chromeos/crostini/crostini_util.h
index 45ef41c..fbfe5da 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.h
+++ b/chrome/browser/chromeos/crostini/crostini_util.h
@@ -171,7 +171,7 @@
 // Returns a string to be displayed in a notification with the estimated time
 // left for an operation to run which started and time |start| and is current
 // at |percent| way through.
-base::string16 GetTimeRemainingMessage(base::Time start, int percent);
+base::string16 GetTimeRemainingMessage(base::TimeTicks start, int percent);
 
 }  // namespace crostini
 
diff --git a/chrome/browser/chromeos/extensions/info_private_apitest.cc b/chrome/browser/chromeos/extensions/info_private_apitest.cc
index e24d0b24..8125bb0 100644
--- a/chrome/browser/chromeos/extensions/info_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/info_private_apitest.cc
@@ -21,7 +21,6 @@
 #include "components/arc/arc_util.h"
 #include "components/prefs/pref_service.h"
 #include "ui/aura/window.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/devices/device_data_manager_test_api.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/devices/touchscreen_device.h"
@@ -191,9 +190,7 @@
   test_api.SetTouchscreenDevices({touchscreen});
 
   ui::test::EventGenerator generator(
-      features::IsUsingWindowService()
-          ? nullptr
-          : browser()->window()->GetNativeWindow()->GetRootWindow());
+      browser()->window()->GetNativeWindow()->GetRootWindow());
   generator.EnterPenPointerMode();
   generator.PressTouch();
   generator.ReleaseTouch();
diff --git a/chrome/browser/chromeos/input_method/candidate_window_controller_impl.cc b/chrome/browser/chromeos/input_method/candidate_window_controller_impl.cc
index 416faa2f..b20689b 100644
--- a/chrome/browser/chromeos/input_method/candidate_window_controller_impl.cc
+++ b/chrome/browser/chromeos/input_method/candidate_window_controller_impl.cc
@@ -12,7 +12,6 @@
 #include "ash/wm/window_util.h"  // mash-ok
 #include "base/logging.h"
 #include "ui/base/ime/ime_bridge.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/chromeos/ime/infolist_window.h"
 #include "ui/views/widget/widget.h"
 
@@ -41,16 +40,13 @@
 
   gfx::NativeView parent = nullptr;
 
-  // NOTE: CandidateWindowView takes care of the mash (window-service) case.
+  aura::Window* active_window = ash::wm::GetActiveWindow();
   // Use VirtualKeyboardContainer so that it works even with a system modal
   // dialog.
-  if (!features::IsUsingWindowService()) {
-    aura::Window* active_window = ash::wm::GetActiveWindow();
-    parent = ash::Shell::GetContainer(
-        active_window ? active_window->GetRootWindow()
-                      : ash::Shell::GetRootWindowForNewWindows(),
-        ash::kShellWindowId_VirtualKeyboardContainer);
-  }
+  parent = ash::Shell::GetContainer(
+      active_window ? active_window->GetRootWindow()
+                    : ash::Shell::GetRootWindowForNewWindows(),
+      ash::kShellWindowId_VirtualKeyboardContainer);
   candidate_window_view_ = new ui::ime::CandidateWindowView(
       parent, ash::kShellWindowId_VirtualKeyboardContainer);
   candidate_window_view_->AddObserver(this);
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index dae91e670..5b92a0a 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -47,7 +47,6 @@
 #include "ui/base/ime/chromeos/ime_keyboard_impl.h"
 #include "ui/base/ime/chromeos/input_method_delegate.h"
 #include "ui/base/ime/ime_bridge.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/chromeos/ime/input_method_menu_item.h"
 #include "ui/chromeos/ime/input_method_menu_manager.h"
 
@@ -1372,9 +1371,6 @@
 
 ui::InputMethodKeyboardController*
 InputMethodManagerImpl::GetInputMethodKeyboardController() {
-  // TODO(stevenjb/shuchen): Fix this for Mash. https://crbug.com/756059
-  if (features::IsMultiProcessMash())
-    return nullptr;
   // Callers expect a nullptr when the keyboard is disabled. See
   // https://crbug.com/850020.
   if (!keyboard::KeyboardController::HasInstance() ||
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc b/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
index 322050b..9e6f762 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
+++ b/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
@@ -28,7 +28,6 @@
 #include "components/user_manager/user_names.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 
 namespace chromeos {
@@ -136,11 +135,6 @@
 
 // Test how locking the screen affects an active fullscreen window.
 IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestFullscreenExit) {
-  // WebUiScreenLockerTest fails with Mash because of unexpected window
-  // structure. Fortunately we will deprecate the WebUI-based screen locker
-  // soon, so it is okay to skip it.  See https://crbug.com/888779
-  if (features::IsUsingWindowService())
-    return;
   // 1) If the active browser window is in fullscreen and the fullscreen window
   // does not have all the pixels (e.g. the shelf is auto hidden instead of
   // hidden), locking the screen should not exit fullscreen. The shelf is
diff --git a/chrome/browser/chromeos/login/login_browsertest.cc b/chrome/browser/chromeos/login/login_browsertest.cc
index 03a387a3..fed8f91 100644
--- a/chrome/browser/chromeos/login/login_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_browsertest.cc
@@ -23,7 +23,6 @@
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/user_manager/user_names.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/gfx/geometry/test/rect_test_util.h"
 
 using ::gfx::test::RectContains;
@@ -97,11 +96,7 @@
   EXPECT_TRUE(tray->GetVisible());
 
   // This check flakes for LoginGuestTest: https://crbug.com/693106.
-  // This check is suppressed for Mash since the warning button of Mash changes
-  // the tray bounds which triggers the failure. See: https://crbug.com/892730
-  // TODO(jamescook): remove this when Mash is on by default or the button is
-  // removed.
-  if (!otr && !features::IsUsingWindowService())
+  if (!otr)
     EXPECT_TRUE(RectContains(primary_win->bounds(), tray->GetBoundsInScreen()));
 }
 
diff --git a/chrome/browser/chromeos/login/login_utils_browsertest.cc b/chrome/browser/chromeos/login/login_utils_browsertest.cc
index 322a7ff..f1bf38b 100644
--- a/chrome/browser/chromeos/login/login_utils_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_utils_browsertest.cc
@@ -34,7 +34,6 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "rlz/buildflags/buildflags.h"
-#include "ui/base/ui_base_features.h"
 
 #if BUILDFLAG(ENABLE_RLZ)
 #include "base/task/post_task.h"
@@ -83,24 +82,6 @@
   DISALLOW_COPY_AND_ASSIGN(LoginUtilsTest);
 };
 
-// Exercises login, like the desktopui_MashLogin Chrome OS autotest.
-// Test is flaky, see https://crbug.com/957584.
-#if defined(MEMORY_SANITIZER)
-#define MAYBE_MashLogin DISABLED_MashLogin
-#else
-#define MAYBE_MashLogin MashLogin
-#endif
-IN_PROC_BROWSER_TEST_F(LoginUtilsTest, MAYBE_MashLogin) {
-  // Test is relevant for both SingleProcessMash and MultiProcessMash, but
-  // not classic ash.
-  if (!features::IsUsingWindowService())
-    return;
-
-  WaitForSigninScreen();
-  Login("username");
-  // Login did not time out and did not crash.
-}
-
 #if BUILDFLAG(ENABLE_RLZ)
 IN_PROC_BROWSER_TEST_F(LoginUtilsTest, RlzInitialized) {
   WaitForSigninScreen();
diff --git a/chrome/browser/chromeos/login/ui/input_events_blocker.cc b/chrome/browser/chromeos/login/ui/input_events_blocker.cc
index cc2aad8..ba315ea8 100644
--- a/chrome/browser/chromeos/login/ui/input_events_blocker.cc
+++ b/chrome/browser/chromeos/login/ui/input_events_blocker.cc
@@ -6,31 +6,20 @@
 
 #include "ash/shell.h"
 #include "base/logging.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/event.h"
 
 namespace chromeos {
 
 InputEventsBlocker::InputEventsBlocker() {
-  // TODO(crbug.com/854323): Mash support. Needs mojo API, or to move to ash.
-  if (!features::IsMultiProcessMash()) {
-    ash::Shell::Get()->AddPreTargetHandler(this,
-                                           ui::EventTarget::Priority::kSystem);
-    VLOG(1) << "InputEventsBlocker " << this << " created.";
-  } else {
-    NOTIMPLEMENTED();
-  }
+  ash::Shell::Get()->AddPreTargetHandler(this,
+                                         ui::EventTarget::Priority::kSystem);
+  VLOG(1) << "InputEventsBlocker " << this << " created.";
 }
 
 InputEventsBlocker::~InputEventsBlocker() {
-  if (!features::IsMultiProcessMash()) {
-    if (ash::Shell::HasInstance())
-      ash::Shell::Get()->RemovePreTargetHandler(this);
-
-    VLOG(1) << "InputEventsBlocker " << this << " destroyed.";
-  } else {
-    NOTIMPLEMENTED();
-  }
+  if (ash::Shell::HasInstance())
+    ash::Shell::Get()->RemovePreTargetHandler(this);
+  VLOG(1) << "InputEventsBlocker " << this << " destroyed.";
 }
 
 void InputEventsBlocker::OnKeyEvent(ui::KeyEvent* event) {
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
index 11a8dcb..0768a71 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
@@ -90,7 +90,6 @@
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/chromeos/input_method_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/compositor_observer.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_observer.h"
@@ -595,15 +594,11 @@
   // We should emit this signal only at login screen (after reboot or sign out).
   login_view_->set_should_emit_login_prompt_visible(false);
 
-  if (!features::IsMultiProcessMash()) {
-    // Lock container can be transparent after lock screen animation.
-    aura::Window* lock_container = ash::Shell::GetContainer(
-        ash::Shell::GetPrimaryRootWindow(),
-        ash::kShellWindowId_LockScreenContainersContainer);
-    lock_container->layer()->SetOpacity(1.0);
-  } else {
-    NOTIMPLEMENTED();
-  }
+  // Lock container can be transparent after lock screen animation.
+  aura::Window* lock_container = ash::Shell::GetContainer(
+      ash::Shell::GetPrimaryRootWindow(),
+      ash::kShellWindowId_LockScreenContainersContainer);
+  lock_container->layer()->SetOpacity(1.0);
 
   CreateExistingUserController();
 
@@ -874,13 +869,6 @@
 // LoginDisplayHostWebUI, private
 
 void LoginDisplayHostWebUI::ScheduleWorkspaceAnimation() {
-  // TODO(mash): Support finalize animations without reaching directly into
-  // ash::Shell.
-  if (features::IsMultiProcessMash()) {
-    NOTIMPLEMENTED();
-    return;
-  }
-
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDisableLoginAnimations)) {
     ash::Shell::Get()->DoInitialWorkspaceAnimation();
@@ -963,9 +951,7 @@
   if (login_window_)
     return;
 
-  // TODO(crbug.com/881390): Mash support for keyboard driven OOBE.
-  if (!features::IsMultiProcessMash() &&
-      system::InputDeviceSettings::ForceKeyboardDrivenUINavigation()) {
+  if (system::InputDeviceSettings::ForceKeyboardDrivenUINavigation()) {
     views::FocusManager::set_arrow_key_traversal_enabled(true);
     focus_ring_controller_ = std::make_unique<ash::FocusRingController>();
     focus_ring_controller_->SetVisible(true);
@@ -1023,16 +1009,10 @@
   }
 
   if (login_window_) {
-    if (features::IsUsingWindowService()) {
-      // TODO(mash): CompositorObserver::OnCompositingDidCommit() doesn't fire
-      // for either SingleProcessMash or MultiProcessMash.
-      login_window_->Close();
-    } else {
-      login_window_->Hide();
-      // This CompositorObserver becomes "owned" by login_window_ after
-      // construction and will delete itself once login_window_ is destroyed.
-      new CloseAfterCommit(login_window_);
-    }
+    login_window_->Hide();
+    // This CompositorObserver becomes "owned" by login_window_ after
+    // construction and will delete itself once login_window_ is destroyed.
+    new CloseAfterCommit(login_window_);
     login_window_->RemoveRemovalsObserver(this);
     login_window_->RemoveObserver(this);
     login_window_ = nullptr;
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.cc b/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.cc
index 3bf6b81..2891acf 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.cc
+++ b/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.cc
@@ -37,7 +37,6 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/viz/public/interfaces/compositing/video_detector_observer.mojom.h"
 #include "ui/aura/env.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/base/user_activity/user_activity_detector.h"
 
 namespace chromeos {
@@ -160,13 +159,8 @@
 
 std::unique_ptr<AdaptiveScreenBrightnessManager>
 AdaptiveScreenBrightnessManager::CreateInstance() {
-  // TODO(jiameng): video detector below doesn't work with MASH. Temporary
-  // solution is to disable logging if we're under MASH env.
-  // https://crbug.com/871914
-  if (chromeos::GetDeviceType() != chromeos::DeviceType::kChromebook ||
-      features::IsMultiProcessMash()) {
+  if (chromeos::GetDeviceType() != chromeos::DeviceType::kChromebook)
     return nullptr;
-  }
 
   chromeos::PowerManagerClient* const power_manager_client =
       chromeos::PowerManagerClient::Get();
diff --git a/chrome/browser/chromeos/power/ml/user_activity_controller.cc b/chrome/browser/chromeos/power/ml/user_activity_controller.cc
index 8506ecc..32345e5 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_controller.cc
+++ b/chrome/browser/chromeos/power/ml/user_activity_controller.cc
@@ -11,7 +11,6 @@
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "services/viz/public/interfaces/compositing/video_detector_observer.mojom.h"
 #include "ui/aura/env.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/compositor.h"
 
 namespace chromeos {
@@ -19,13 +18,8 @@
 namespace ml {
 
 UserActivityController::UserActivityController() {
-  // TODO(jiameng): video detector below doesn't work with MASH. Temporary
-  // solution is to disable logging if we're under MASH env.
-  // https://crbug.com/871914
-  if (chromeos::GetDeviceType() != chromeos::DeviceType::kChromebook ||
-      features::IsMultiProcessMash()) {
+  if (chromeos::GetDeviceType() != chromeos::DeviceType::kChromebook)
     return;
-  }
 
   chromeos::PowerManagerClient* power_manager_client =
       chromeos::PowerManagerClient::Get();
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
index 8a73e49..8c7bc9b 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
@@ -48,11 +48,11 @@
 DeviceOAuth2TokenService::DeviceOAuth2TokenService(
     std::unique_ptr<DeviceOAuth2TokenServiceDelegate> delegate)
     : OAuth2TokenService(std::move(delegate)) {
-  GetDeviceDelegate()->SetValidationStatusDelegate(this);
+  GetDeviceDelegate()->InitializeWithValidationStatusDelegate(this);
 }
 
 DeviceOAuth2TokenService::~DeviceOAuth2TokenService() {
-  GetDeviceDelegate()->SetValidationStatusDelegate(nullptr);
+  GetDeviceDelegate()->ClearValidationStatusDelegate();
   FlushPendingRequests(false, GoogleServiceAuthError::REQUEST_CANCELED);
 }
 
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc
index cad84e0..997e68f8 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.cc
@@ -196,14 +196,12 @@
     LOG(ERROR) << "Failed to get system salt.";
     FlushTokenSaveCallbacks(false);
     state_ = STATE_NO_TOKEN;
-    FireRefreshTokensLoaded();
     return;
   }
 
   // If the token has been set meanwhile, write it to |local_state_|.
   if (!refresh_token_.empty()) {
     EncryptAndSaveToken();
-    FireRefreshTokensLoaded();
     return;
   }
 
@@ -216,7 +214,6 @@
     if (refresh_token_.empty()) {
       LOG(ERROR) << "Failed to decrypt refresh token.";
       state_ = STATE_NO_TOKEN;
-      FireRefreshTokensLoaded();
       return;
     }
   }
@@ -231,7 +228,6 @@
   if (!GetRobotAccountId().empty()) {
     FireRefreshTokenAvailable(GetRobotAccountId());
   }
-  FireRefreshTokensLoaded();
 }
 
 void DeviceOAuth2TokenServiceDelegate::CheckRobotAccountId(
@@ -324,9 +320,20 @@
   validation_requested_ = true;
 }
 
-void DeviceOAuth2TokenServiceDelegate::SetValidationStatusDelegate(
+void DeviceOAuth2TokenServiceDelegate::InitializeWithValidationStatusDelegate(
     ValidationStatusDelegate* delegate) {
   validation_status_delegate_ = delegate;
+
+  // Now that |delegate| (i.e., DeviceOAuth2TokenService) has been initialized
+  // and is listening to this object as an observer, fire the notification that
+  // refresh tokens were loaded; otherwise,
+  // OAuth2TokenService::{GetAccounts(), RefreshTokenIsAvailable()} will short-
+  // circuit out to match O2TS semantics.
+  FireRefreshTokensLoaded();
+}
+
+void DeviceOAuth2TokenServiceDelegate::ClearValidationStatusDelegate() {
+  validation_status_delegate_ = nullptr;
 }
 
 void DeviceOAuth2TokenServiceDelegate::ReportServiceError(
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h b/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h
index 0c3d58f..5c547151 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h
@@ -124,7 +124,9 @@
 
   void RequestValidation();
 
-  void SetValidationStatusDelegate(ValidationStatusDelegate* delegate);
+  void InitializeWithValidationStatusDelegate(
+      ValidationStatusDelegate* delegate);
+  void ClearValidationStatusDelegate();
 
   void ReportServiceError(GoogleServiceAuthError::State error);
 
diff --git a/chrome/browser/chromeos/system_logs/touch_log_source.cc b/chrome/browser/chromeos/system_logs/touch_log_source.cc
index f951e49..d0f2856 100644
--- a/chrome/browser/chromeos/system_logs/touch_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/touch_log_source.cc
@@ -18,7 +18,6 @@
 #include "base/process/launch.h"
 #include "base/task/post_task.h"
 #include "content/public/browser/browser_thread.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/ozone/public/input_controller.h"
 #include "ui/ozone/public/ozone_platform.h"
 
@@ -168,11 +167,6 @@
 
 // Collect touch HUD debug logs. This needs to be done on the UI thread.
 void CollectTouchHudDebugLog(system_logs::SystemLogsResponse* response) {
-  // TODO(crbug.com/807408): Collect this data from window server over mojo.
-  if (features::IsMultiProcessMash()) {
-    NOTIMPLEMENTED();
-    return;
-  }
   std::unique_ptr<base::DictionaryValue> dictionary =
       ash::TouchHudDebug::GetAllAsDictionary();
   if (!dictionary->empty()) {
diff --git a/chrome/browser/download/download_offline_content_provider.cc b/chrome/browser/download/download_offline_content_provider.cc
index 2b97166..e9d8312 100644
--- a/chrome/browser/download/download_offline_content_provider.cc
+++ b/chrome/browser/download/download_offline_content_provider.cc
@@ -41,6 +41,11 @@
 // Thumbnail size used for generating thumbnails for image files.
 const int kThumbnailSizeInDP = 64;
 
+// The delay to wait after loading history and before starting the check for
+// externally removed downloads.
+const base::TimeDelta kCheckExternallyRemovedDownloadsDelay =
+    base::TimeDelta::FromMilliseconds(100);
+
 bool ShouldShowDownloadItem(const DownloadItem* item) {
   return !item->IsTemporary() && !item->IsTransient() && !item->IsDangerous() &&
          !item->GetTargetFilePath().empty();
@@ -60,6 +65,61 @@
   return share_info;
 }
 
+// Observes the all downloads, primrarily responsible for cleaning up the
+// externally removed downloads, and notifying the provider about download
+// deletions.
+class AllDownloadObserver
+    : public download::AllDownloadEventNotifier::Observer {
+ public:
+  explicit AllDownloadObserver(DownloadOfflineContentProvider* provider);
+  ~AllDownloadObserver() override;
+
+  void OnDownloadUpdated(SimpleDownloadManagerCoordinator* manager,
+                         DownloadItem* item) override;
+  void OnDownloadRemoved(SimpleDownloadManagerCoordinator* manager,
+                         DownloadItem* item) override;
+
+ private:
+  void DeleteDownloadItem(SimpleDownloadManagerCoordinator* manager,
+                          const std::string& guid);
+
+  DownloadOfflineContentProvider* provider_;
+  base::WeakPtrFactory<AllDownloadObserver> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AllDownloadObserver);
+};
+
+AllDownloadObserver::AllDownloadObserver(
+    DownloadOfflineContentProvider* provider)
+    : provider_(provider), weak_ptr_factory_(this) {}
+
+AllDownloadObserver::~AllDownloadObserver() {}
+
+void AllDownloadObserver::OnDownloadUpdated(
+    SimpleDownloadManagerCoordinator* manager,
+    DownloadItem* item) {
+  if (item->GetFileExternallyRemoved()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(&AllDownloadObserver::DeleteDownloadItem,
+                                  weak_ptr_factory_.GetWeakPtr(), manager,
+                                  item->GetGuid()));
+  }
+}
+
+void AllDownloadObserver::OnDownloadRemoved(
+    SimpleDownloadManagerCoordinator* manager,
+    DownloadItem* item) {
+  provider_->OnDownloadRemoved(item);
+}
+
+void AllDownloadObserver::DeleteDownloadItem(
+    SimpleDownloadManagerCoordinator* manager,
+    const std::string& guid) {
+  DownloadItem* item = manager->GetDownloadByGuid(guid);
+  if (item)
+    item->Remove();
+}
+
 }  // namespace
 
 DownloadOfflineContentProvider::DownloadOfflineContentProvider(
@@ -70,12 +130,15 @@
       manager_(nullptr),
       weak_ptr_factory_(this) {
   aggregator_->RegisterProvider(name_space_, this);
+  all_download_observer_.reset(new AllDownloadObserver(this));
 }
 
 DownloadOfflineContentProvider::~DownloadOfflineContentProvider() {
   aggregator_->UnregisterProvider(name_space_);
-  if (manager_)
+  if (manager_) {
     manager_->RemoveObserver(this);
+    manager_->GetNotifier()->RemoveObserver(all_download_observer_.get());
+  }
 }
 
 void DownloadOfflineContentProvider::SetSimpleDownloadManagerCoordinator(
@@ -84,6 +147,7 @@
   DCHECK(!manager_);
   manager_ = manager;
   manager_->AddObserver(this);
+  manager_->GetNotifier()->AddObserver(all_download_observer_.get());
 }
 
 // TODO(shaktisahu) : Pass DownloadOpenSource.
@@ -96,8 +160,10 @@
 
 void DownloadOfflineContentProvider::RemoveItem(const ContentId& id) {
   DownloadItem* item = GetDownload(id.id);
-  if (item)
+  if (item) {
+    item->DeleteFile(base::DoNothing());
     item->Remove();
+  }
 }
 
 void DownloadOfflineContentProvider::CancelDownload(const ContentId& id) {
@@ -249,6 +315,19 @@
   observers_.RemoveObserver(observer);
 }
 
+void DownloadOfflineContentProvider::OnDownloadsInitialized(
+    bool active_downloads_only) {
+  if (active_downloads_only)
+    return;
+
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(
+          &DownloadOfflineContentProvider::CheckForExternallyRemovedDownloads,
+          weak_ptr_factory_.GetWeakPtr()),
+      kCheckExternallyRemovedDownloadsDelay);
+}
+
 void DownloadOfflineContentProvider::OnManagerGoingDown() {
   std::vector<DownloadItem*> all_items;
   GetAllDownloads(&all_items);
@@ -351,3 +430,11 @@
   for (auto& observer : observers_)
     observer.OnItemUpdated(item, update_delta);
 }
+
+void DownloadOfflineContentProvider::CheckForExternallyRemovedDownloads() {
+  if (checked_for_externally_removed_downloads_ || !manager_)
+    return;
+
+  checked_for_externally_removed_downloads_ = true;
+  manager_->CheckForExternallyRemovedDownloads();
+}
diff --git a/chrome/browser/download/download_offline_content_provider.h b/chrome/browser/download/download_offline_content_provider.h
index cc8af56..d54b253 100644
--- a/chrome/browser/download/download_offline_content_provider.h
+++ b/chrome/browser/download/download_offline_content_provider.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "components/download/public/common/all_download_event_notifier.h"
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/simple_download_manager_coordinator.h"
 #include "components/offline_items_collection/core/offline_content_aggregator.h"
@@ -72,13 +73,14 @@
   // observing the given download.
   void OnDownloadStarted(DownloadItem* download_item);
 
- private:
   // DownloadItem::Observer overrides
   void OnDownloadUpdated(DownloadItem* item) override;
   void OnDownloadRemoved(DownloadItem* item) override;
   void OnDownloadDestroyed(DownloadItem* download) override;
 
+ private:
   // SimpleDownloadManagerCoordinator::Observer overrides
+  void OnDownloadsInitialized(bool active_downloads_only) override;
   void OnManagerGoingDown() override;
 
   void GetAllDownloads(std::vector<DownloadItem*>* all_items);
@@ -95,6 +97,7 @@
                                     DownloadItem::DownloadRenameResult result);
   void UpdateObservers(const OfflineItem& item,
                        const base::Optional<UpdateDelta>& update_delta);
+  void CheckForExternallyRemovedDownloads();
 
   base::ObserverList<OfflineContentProvider::Observer>::Unchecked observers_;
   OfflineContentAggregator* aggregator_;
@@ -103,6 +106,9 @@
 
   // Tracks the completed downloads in the current session.
   std::set<std::string> completed_downloads_;
+  std::unique_ptr<download::AllDownloadEventNotifier::Observer>
+      all_download_observer_;
+  bool checked_for_externally_removed_downloads_ = false;
 
   base::WeakPtrFactory<DownloadOfflineContentProvider> weak_ptr_factory_;
 
diff --git a/chrome/browser/exo_parts.cc b/chrome/browser/exo_parts.cc
index 6029e489c..5bf4d0e 100644
--- a/chrome/browser/exo_parts.cc
+++ b/chrome/browser/exo_parts.cc
@@ -22,7 +22,6 @@
 #include "content/public/common/drop_data.h"
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/file_system_url.h"
-#include "ui/base/ui_base_features.h"
 
 namespace {
 
@@ -105,9 +104,6 @@
 
 // static
 std::unique_ptr<ExoParts> ExoParts::CreateIfNecessary() {
-  // For mash, exosphere will not run in the browser process.
-  if (features::IsMultiProcessMash())
-    return nullptr;
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           ash::switches::kAshEnableWaylandServer)) {
     return nullptr;
@@ -121,7 +117,6 @@
 }
 
 ExoParts::ExoParts() {
-  DCHECK(!features::IsMultiProcessMash());
   std::unique_ptr<ChromeFileHelper> file_helper =
       std::make_unique<ChromeFileHelper>();
   ash::Shell::Get()->InitWaylandServer(std::move(file_helper));
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index fec35ae..e389b4b5 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -4390,7 +4390,6 @@
   ASSERT_TRUE(result->GetAsInteger(&result_id));
   DownloadItem* item = GetCurrentManager()->GetDownload(result_id);
   ASSERT_TRUE(item);
-  ScopedCancellingItem canceller(item);
   ASSERT_EQ(download_url, item->GetOriginalUrl().spec());
 
   ASSERT_TRUE(WaitFor(downloads::OnCreated::kEventName,
@@ -4416,6 +4415,8 @@
                                          R"(    "previous": true,)"
                                          R"(    "current": false}}])",
                                          result_id)));
+  EXPECT_EQ(static_cast<DownloadItem*>(NULL),
+            GetCurrentManager()->GetDownload(result_id));
 }
 
 class DownloadsApiTest : public ExtensionApiTest {
diff --git a/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc b/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
index 37f434a3..b19df51e 100644
--- a/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
+++ b/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc
@@ -127,6 +127,7 @@
 
 std::unique_ptr<MessagePort>
 ChromeMessagingDelegate::CreateReceiverForNativeApp(
+    content::BrowserContext* browser_context,
     base::WeakPtr<MessagePort::ChannelDelegate> channel_delegate,
     content::RenderFrameHost* source,
     const std::string& extension_id,
@@ -136,8 +137,9 @@
     std::string* error_out) {
   DCHECK(error_out);
   gfx::NativeView native_view = source ? source->GetNativeView() : nullptr;
-  std::unique_ptr<NativeMessageHost> native_host = NativeMessageHost::Create(
-      native_view, extension_id, native_app_name, allow_user_level, error_out);
+  std::unique_ptr<NativeMessageHost> native_host =
+      NativeMessageHost::Create(browser_context, native_view, extension_id,
+                                native_app_name, allow_user_level, error_out);
   if (!native_host.get())
     return nullptr;
   return std::make_unique<NativeMessagePort>(channel_delegate, receiver_port_id,
diff --git a/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.h b/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.h
index 04734169..050786e 100644
--- a/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.h
+++ b/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.h
@@ -31,6 +31,7 @@
       content::WebContents* receiver_contents,
       int receiver_frame_id) override;
   std::unique_ptr<MessagePort> CreateReceiverForNativeApp(
+      content::BrowserContext* browser_context,
       base::WeakPtr<MessagePort::ChannelDelegate> channel_delegate,
       content::RenderFrameHost* source,
       const std::string& extension_id,
diff --git a/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc b/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
index e4116adad..1ba3053c 100644
--- a/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
@@ -146,6 +146,7 @@
 }  // namespace
 
 std::unique_ptr<NativeMessageHost> NativeMessageHost::Create(
+    content::BrowserContext* browser_context,
     gfx::NativeView native_view,
     const std::string& source_extension_id,
     const std::string& native_host_name,
diff --git a/chrome/browser/extensions/api/messaging/native_message_process_host.cc b/chrome/browser/extensions/api/messaging/native_message_process_host.cc
index 7b713364..94b83a3e 100644
--- a/chrome/browser/extensions/api/messaging/native_message_process_host.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_process_host.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 #include <stdint.h>
+
 #include <utility>
 
 #include "base/bind.h"
@@ -16,6 +17,8 @@
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/common/constants.h"
@@ -38,6 +41,14 @@
 // Size of the buffer to be allocated for each read.
 const size_t kReadBufferSize = 4096;
 
+base::FilePath GetProfilePathIfEnabled(Profile* profile,
+                                       const std::string& extension_id,
+                                       const std::string& host_id) {
+  // TODO(crbug.com/967262): Return an empty path if the extension would not
+  // accept an inbound native messaging connection.
+  return profile->GetPath();
+}
+
 }  // namespace
 
 namespace extensions {
@@ -82,15 +93,18 @@
 
 // static
 std::unique_ptr<NativeMessageHost> NativeMessageHost::Create(
+    content::BrowserContext* browser_context,
     gfx::NativeView native_view,
     const std::string& source_extension_id,
     const std::string& native_host_name,
     bool allow_user_level,
     std::string* error_message) {
   return NativeMessageProcessHost::CreateWithLauncher(
-      source_extension_id,
-      native_host_name,
-      NativeProcessLauncher::CreateDefault(allow_user_level, native_view));
+      source_extension_id, native_host_name,
+      NativeProcessLauncher::CreateDefault(
+          allow_user_level, native_view,
+          GetProfilePathIfEnabled(Profile::FromBrowserContext(browser_context),
+                                  source_extension_id, native_host_name)));
 }
 
 // static
diff --git a/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc b/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc
index 4e27254..702ac7c 100644
--- a/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc
@@ -10,23 +10,33 @@
 #include <memory>
 #include <utility>
 
+#include "base/base_paths.h"
+#include "base/base_switches.h"
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/json/json_reader.h"
+#include "base/path_service.h"
 #include "base/process/process_metrics.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/messaging/native_messaging_test_util.h"
 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/testing_profile.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -167,6 +177,7 @@
   std::unique_ptr<NativeMessageHost> native_message_host_;
   std::unique_ptr<base::RunLoop> run_loop_;
   content::TestBrowserThreadBundle thread_bundle_;
+  TestingProfile profile_;
 
   std::string last_message_;
   std::unique_ptr<base::DictionaryValue> last_message_parsed_;
@@ -267,11 +278,8 @@
   ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(false));
   std::string error_message;
   native_message_host_ = NativeMessageProcessHost::Create(
-      NULL,
-      ScopedTestNativeMessagingHost::kExtensionId,
-      ScopedTestNativeMessagingHost::kHostName,
-      false,
-      &error_message);
+      &profile_, NULL, ScopedTestNativeMessagingHost::kExtensionId,
+      ScopedTestNativeMessagingHost::kHostName, false, &error_message);
   native_message_host_->Start(this);
   ASSERT_TRUE(native_message_host_);
 
@@ -302,6 +310,91 @@
   EXPECT_EQ("bar", text);
   EXPECT_TRUE(last_message_parsed_->GetString("caller_url", &url));
   EXPECT_EQ(expected_url, url);
+
+  const base::Value* args = nullptr;
+  ASSERT_TRUE(last_message_parsed_->Get("args", &args));
+  EXPECT_TRUE(args->is_none());
+}
+
+// Test send message with a real client. The args passed when launching the
+// native messaging host should contain reconnect args.
+TEST_F(NativeMessagingTest, ReconnectArgs) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kOnConnectNative);
+  ScopedTestNativeMessagingHost test_host;
+  ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(false));
+  std::string error_message;
+  native_message_host_ = NativeMessageProcessHost::Create(
+      &profile_, NULL, ScopedTestNativeMessagingHost::kExtensionId,
+      ScopedTestNativeMessagingHost::
+          kSupportsNativeInitiatedConnectionsHostName,
+      false, &error_message);
+  native_message_host_->Start(this);
+  ASSERT_TRUE(native_message_host_);
+
+  native_message_host_->OnMessage("{\"text\": \"Hello.\"}");
+  run_loop_.reset(new base::RunLoop());
+  run_loop_->Run();
+  ASSERT_FALSE(last_message_.empty());
+  ASSERT_TRUE(last_message_parsed_);
+
+  const base::ListValue* args_value = nullptr;
+  ASSERT_TRUE(last_message_parsed_->GetList("args", &args_value));
+  std::vector<base::CommandLine::StringType> args;
+  args.reserve(args_value->GetSize());
+  for (auto& arg : args_value->GetList()) {
+    ASSERT_TRUE(arg.is_string());
+#if defined(OS_WIN)
+    args.push_back(base::UTF8ToUTF16(arg.GetString()));
+#else
+    args.push_back(arg.GetString());
+#endif
+  }
+  base::CommandLine cmd_line(args);
+  base::FilePath exe_path;
+  ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &exe_path));
+  EXPECT_EQ(exe_path, cmd_line.GetProgram());
+  EXPECT_TRUE(cmd_line.HasSwitch(switches::kNoStartupWindow));
+  EXPECT_EQ(
+      ScopedTestNativeMessagingHost::
+          kSupportsNativeInitiatedConnectionsHostName,
+      cmd_line.GetSwitchValueASCII(switches::kNativeMessagingConnectHost));
+  EXPECT_EQ(
+      ScopedTestNativeMessagingHost::kExtensionId,
+      cmd_line.GetSwitchValueASCII(switches::kNativeMessagingConnectExtension));
+  EXPECT_EQ(features::kOnConnectNative.name,
+            cmd_line.GetSwitchValueASCII(switches::kEnableFeatures));
+  EXPECT_EQ(profile_.GetPath().BaseName(),
+            cmd_line.GetSwitchValuePath(switches::kProfileDirectory));
+  EXPECT_EQ(profile_.GetPath().DirName(),
+            cmd_line.GetSwitchValuePath(switches::kUserDataDir));
+}
+
+// Test send message with a real client. The args passed when launching the
+// native messaging host should not contain reconnect args.
+TEST_F(NativeMessagingTest, ReconnectArgs_Disabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(features::kOnConnectNative);
+  ScopedTestNativeMessagingHost test_host;
+  ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(false));
+  std::string error_message;
+  native_message_host_ = NativeMessageProcessHost::Create(
+      &profile_, NULL, ScopedTestNativeMessagingHost::kExtensionId,
+      ScopedTestNativeMessagingHost::
+          kSupportsNativeInitiatedConnectionsHostName,
+      false, &error_message);
+  native_message_host_->Start(this);
+  ASSERT_TRUE(native_message_host_);
+
+  native_message_host_->OnMessage("{\"text\": \"Hello.\"}");
+  run_loop_.reset(new base::RunLoop());
+  run_loop_->Run();
+  ASSERT_FALSE(last_message_.empty());
+  ASSERT_TRUE(last_message_parsed_);
+
+  const base::Value* args = nullptr;
+  ASSERT_TRUE(last_message_parsed_->Get("args", &args));
+  EXPECT_TRUE(args->is_none());
 }
 
 TEST_F(NativeMessagingTest, UserLevel) {
@@ -310,11 +403,8 @@
 
   std::string error_message;
   native_message_host_ = NativeMessageProcessHost::Create(
-      NULL,
-      ScopedTestNativeMessagingHost::kExtensionId,
-      ScopedTestNativeMessagingHost::kHostName,
-      true,
-      &error_message);
+      &profile_, NULL, ScopedTestNativeMessagingHost::kExtensionId,
+      ScopedTestNativeMessagingHost::kHostName, true, &error_message);
   native_message_host_->Start(this);
   ASSERT_TRUE(native_message_host_);
 
@@ -331,11 +421,8 @@
 
   std::string error_message;
   native_message_host_ = NativeMessageProcessHost::Create(
-      NULL,
-      ScopedTestNativeMessagingHost::kExtensionId,
-      ScopedTestNativeMessagingHost::kHostName,
-      false,
-      &error_message);
+      &profile_, NULL, ScopedTestNativeMessagingHost::kExtensionId,
+      ScopedTestNativeMessagingHost::kHostName, false, &error_message);
   native_message_host_->Start(this);
   ASSERT_TRUE(native_message_host_);
   run_loop_.reset(new base::RunLoop());
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc b/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc
index 1acf4ae..34d944d 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc
+++ b/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc
@@ -29,12 +29,15 @@
 void WriteTestNativeHostManifest(const base::FilePath& target_dir,
                                  const std::string& host_name,
                                  const base::FilePath& host_path,
-                                 bool user_level) {
+                                 bool user_level,
+                                 bool supports_native_initiated_connections) {
   std::unique_ptr<base::DictionaryValue> manifest(new base::DictionaryValue());
   manifest->SetString("name", host_name);
   manifest->SetString("description", "Native Messaging Echo Test");
   manifest->SetString("type", "stdio");
   manifest->SetString("path", host_path.AsUTF8Unsafe());
+  manifest->SetBoolean("supports_native_initiated_connections",
+                       supports_native_initiated_connections);
 
   std::unique_ptr<base::ListValue> origins(new base::ListValue());
   origins->AppendString(base::StringPrintf(
@@ -63,6 +66,9 @@
     "com.google.chrome.test.echo";
 const char ScopedTestNativeMessagingHost::kBinaryMissingHostName[] =
     "com.google.chrome.test.host_binary_missing";
+const char ScopedTestNativeMessagingHost::
+    kSupportsNativeInitiatedConnectionsHostName[] =
+        "com.google.chrome.test.inbound_native_echo";
 const char ScopedTestNativeMessagingHost::kExtensionId[] =
     "knldjmfmopnpolahpmmgbagdohdnhkik";
 
@@ -95,11 +101,16 @@
   base::FilePath host_path = test_user_data_dir.AppendASCII("echo.bat");
 #endif
   ASSERT_NO_FATAL_FAILURE(WriteTestNativeHostManifest(
-      temp_dir_.GetPath(), kHostName, host_path, user_level));
+      temp_dir_.GetPath(), kHostName, host_path, user_level, false));
 
   ASSERT_NO_FATAL_FAILURE(WriteTestNativeHostManifest(
       temp_dir_.GetPath(), kBinaryMissingHostName,
-      test_user_data_dir.AppendASCII("missing_nm_binary.exe"), user_level));
+      test_user_data_dir.AppendASCII("missing_nm_binary.exe"), user_level,
+      false));
+
+  ASSERT_NO_FATAL_FAILURE(WriteTestNativeHostManifest(
+      temp_dir_.GetPath(), kSupportsNativeInitiatedConnectionsHostName,
+      host_path, user_level, true));
 }
 
 ScopedTestNativeMessagingHost::~ScopedTestNativeMessagingHost() {
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_test_util.h b/chrome/browser/extensions/api/messaging/native_messaging_test_util.h
index 0757bf7..b5ccbdc 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_test_util.h
+++ b/chrome/browser/extensions/api/messaging/native_messaging_test_util.h
@@ -29,6 +29,8 @@
  public:
   static const char kHostName[];
   static const char kBinaryMissingHostName[];
+  static const char kSupportsNativeInitiatedConnectionsHostName[];
+
   static const char kExtensionId[];
 
   ScopedTestNativeMessagingHost();
diff --git a/chrome/browser/extensions/api/messaging/native_process_launcher.cc b/chrome/browser/extensions/api/messaging/native_process_launcher.cc
index a281b85..03c3e3009 100644
--- a/chrome/browser/extensions/api/messaging/native_process_launcher.cc
+++ b/chrome/browser/extensions/api/messaging/native_process_launcher.cc
@@ -5,20 +5,30 @@
 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
 
 #include <inttypes.h>
+
 #include <utility>
 
+#include "base/base64.h"
+#include "base/base_paths.h"
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
+#include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/path_service.h"
+#include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
@@ -35,7 +45,8 @@
 class NativeProcessLauncherImpl : public NativeProcessLauncher {
  public:
   NativeProcessLauncherImpl(bool allow_user_level_hosts,
-                            intptr_t native_window);
+                            intptr_t native_window,
+                            const base::FilePath& profile_directory);
   ~NativeProcessLauncherImpl() override;
 
   void Launch(const GURL& origin,
@@ -45,7 +56,9 @@
  private:
   class Core : public base::RefCountedThreadSafe<Core> {
    public:
-    Core(bool allow_user_level_hosts, intptr_t native_window);
+    Core(bool allow_user_level_hosts,
+         intptr_t native_window,
+         const base::FilePath& profile_directory);
     void Launch(const GURL& origin,
                 const std::string& native_host_name,
                 const LaunchedCallback& callback);
@@ -73,6 +86,8 @@
 
     bool allow_user_level_hosts_;
 
+    base::FilePath profile_directory_;
+
 #if defined(OS_WIN)
     // Handle of the native window corresponding to the extension.
     intptr_t window_handle_;
@@ -87,11 +102,14 @@
 };
 
 NativeProcessLauncherImpl::Core::Core(bool allow_user_level_hosts,
-                                      intptr_t window_handle)
+                                      intptr_t window_handle,
+                                      const base::FilePath& profile_directory)
     : detached_(false),
-      allow_user_level_hosts_(allow_user_level_hosts)
+      allow_user_level_hosts_(allow_user_level_hosts),
+      profile_directory_(profile_directory)
 #if defined(OS_WIN)
-      , window_handle_(window_handle)
+      ,
+      window_handle_(window_handle)
 #endif // OS_WIN
 {}
 
@@ -196,6 +214,41 @@
       base::StringPrintf("--parent-window=%" PRIdPTR, window_handle_));
 #endif  // !defined(OS_WIN)
 
+  if (manifest->supports_native_initiated_connections() &&
+      !profile_directory_.empty()) {
+    base::FilePath exe_path;
+    base::PathService::Get(base::FILE_EXE, &exe_path);
+
+    base::CommandLine reconnect_command_line(exe_path);
+    reconnect_command_line.AppendSwitch(switches::kNoStartupWindow);
+    reconnect_command_line.AppendSwitchASCII(
+        switches::kNativeMessagingConnectHost, native_host_name);
+    reconnect_command_line.AppendSwitchASCII(
+        switches::kNativeMessagingConnectExtension, origin.host());
+    reconnect_command_line.AppendSwitchASCII(switches::kEnableFeatures,
+                                             features::kOnConnectNative.name);
+    reconnect_command_line.AppendSwitchPath(switches::kProfileDirectory,
+                                            profile_directory_.BaseName());
+    reconnect_command_line.AppendSwitchPath(switches::kUserDataDir,
+                                            profile_directory_.DirName());
+#if defined(OS_WIN)
+    reconnect_command_line.AppendArg(
+        switches::kPrefetchArgumentBrowserBackground);
+#endif
+    base::Value args(base::Value::Type::LIST);
+    args.GetList().reserve(reconnect_command_line.argv().size());
+    for (const auto& arg : reconnect_command_line.argv()) {
+      args.GetList().emplace_back(arg);
+    }
+    std::string encoded_reconnect_command;
+    bool success =
+        base::JSONWriter::Write(std::move(args), &encoded_reconnect_command);
+    DCHECK(success);
+    base::Base64Encode(encoded_reconnect_command, &encoded_reconnect_command);
+    command_line.AppendArg(
+        base::StrCat({"--reconnect-command=", encoded_reconnect_command}));
+  }
+
   base::Process process;
   base::File read_file;
   base::File write_file;
@@ -246,9 +299,10 @@
 
 NativeProcessLauncherImpl::NativeProcessLauncherImpl(
     bool allow_user_level_hosts,
-    intptr_t window_handle)
-    : core_(new Core(allow_user_level_hosts, window_handle)) {
-}
+    intptr_t window_handle,
+    const base::FilePath& profile_directory)
+    : core_(
+          new Core(allow_user_level_hosts, window_handle, profile_directory)) {}
 
 NativeProcessLauncherImpl::~NativeProcessLauncherImpl() {
   core_->Detach();
@@ -265,14 +319,15 @@
 // static
 std::unique_ptr<NativeProcessLauncher> NativeProcessLauncher::CreateDefault(
     bool allow_user_level_hosts,
-    gfx::NativeView native_view) {
+    gfx::NativeView native_view,
+    const base::FilePath& profile_directory) {
   intptr_t window_handle = 0;
 #if defined(OS_WIN)
   window_handle = reinterpret_cast<intptr_t>(
       views::HWNDForNativeView(native_view));
 #endif
-  return std::unique_ptr<NativeProcessLauncher>(
-      new NativeProcessLauncherImpl(allow_user_level_hosts, window_handle));
+  return std::make_unique<NativeProcessLauncherImpl>(
+      allow_user_level_hosts, window_handle, profile_directory);
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/messaging/native_process_launcher.h b/chrome/browser/extensions/api/messaging/native_process_launcher.h
index 8ebf09c..68032648 100644
--- a/chrome/browser/extensions/api/messaging/native_process_launcher.h
+++ b/chrome/browser/extensions/api/messaging/native_process_launcher.h
@@ -42,10 +42,13 @@
 
   // Creates default launcher for the current OS. |native_view| refers to the
   // window that contains calling page. Can be nullptr, e.g. for background
-  // pages.
+  // pages. If |profile_directory| is non-empty and the host supports
+  // native-initiated connections, additional reconnect args will be passed to
+  // the host.
   static std::unique_ptr<NativeProcessLauncher> CreateDefault(
       bool allow_user_level_hosts,
-      gfx::NativeView native_view);
+      gfx::NativeView native_view,
+      const base::FilePath& profile_directory);
 
   NativeProcessLauncher() {}
   virtual ~NativeProcessLauncher() {}
diff --git a/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc b/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc
index 5705f21..0a076f31 100644
--- a/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc
@@ -20,7 +20,6 @@
 #include "ui/aura/window.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/clipboard_types.h"
-#include "ui/base/ui_base_features.h"
 
 namespace extensions {
 namespace tabs_util {
@@ -47,10 +46,7 @@
   browser->command_controller()->LockedFullscreenStateChanged();
 
   // Disallow screenshots in locked fullscreen mode.
-  // TODO(isandrk, 816900): ChromeScreenshotGrabber isn't implemented in Mash
-  // yet, remove this conditional when it becomes available.
-  if (!features::IsMultiProcessMash())
-    ChromeScreenshotGrabber::Get()->set_screenshots_allowed(!locked);
+  ChromeScreenshotGrabber::Get()->set_screenshots_allowed(!locked);
 
   // Reset the clipboard and kill dev tools when entering or exiting locked
   // fullscreen (security concerns).
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 8416b4cc..ff0f502 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2702,16 +2702,16 @@
     "expiry_milestone": 82
   },
   {
+    "name": "shelf-hotseat",
+    "owners": [ "manucornet", "newcomer" ],
+    "expiry_milestone": 82
+  },
+  {
     "name": "shelf-hover-previews",
     "owners": [ "manucornet" ],
     "expiry_milestone": 77
   },
   {
-    "name": "shelf-new-ui",
-    "owners": [ "manucornet", "newcomer" ],
-    "expiry_milestone": 82
-  },
-  {
     "name": "shelf-scrollable",
     "owners": [ "andrewxu", "manucornet" ],
     "expiry_milestone": 82
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 5116734..258cea4 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1661,17 +1661,18 @@
 const char kShelfDenseClamshellDescription[] =
     "Reduces the size of the shelf and its apps when in laptop mode.";
 
+const char kShelfHotseatName[] = "Enable a modular design for the shelf.";
+const char kShelfHotseatDescription[] =
+    "Shows a modular design for the shelf where the apps are shown separately "
+    "in a 'hotseat' interface when in tablet mode, and where various pieces "
+    "are separate and behave independently.";
+
 const char kShelfHoverPreviewsName[] =
     "Show previews of running apps when hovering over the shelf.";
 const char kShelfHoverPreviewsDescription[] =
     "Shows previews of the open windows for a given running app when hovering "
     "over the shelf.";
 
-const char kShelfNewUiName[] = "Enable a new modular design for the shelf.";
-const char kShelfNewUiDescription[] =
-    "Shows a new modular design for the shelf where the various pieces are "
-    "separate and behave independently, especially in tablet mode.";
-
 const char kShelfScrollableName[] =
     "Enable a scrollable list of apps on the shelf";
 const char kShelfScrollableDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 81bb1b7..f250824 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -983,12 +983,12 @@
 extern const char kShelfDenseClamshellName[];
 extern const char kShelfDenseClamshellDescription[];
 
+extern const char kShelfHotseatName[];
+extern const char kShelfHotseatDescription[];
+
 extern const char kShelfHoverPreviewsName[];
 extern const char kShelfHoverPreviewsDescription[];
 
-extern const char kShelfNewUiName[];
-extern const char kShelfNewUiDescription[];
-
 extern const char kShelfScrollableName[];
 extern const char kShelfScrollableDescription[];
 
diff --git a/chrome/browser/media/cast_mirroring_service_host_browsertest.cc b/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
index 07453b96..ab6ddf2f 100644
--- a/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
+++ b/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
@@ -6,7 +6,6 @@
 
 #include "base/containers/flat_map.h"
 #include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/browser.h"
@@ -27,7 +26,6 @@
 #include "media/mojo/interfaces/audio_input_stream.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "ui/base/ui_base_features.h"
 
 using testing::_;
 using testing::InvokeWithoutArgs;
@@ -206,9 +204,6 @@
 
   // InProcessBrowserTest override.
   void SetUp() override {
-#if defined(OS_CHROMEOS)
-    scoped_feature_list_.InitWithFeatures({}, {features::kMash});
-#endif
     InProcessBrowserTest::SetUp();
   }
 
@@ -233,10 +228,6 @@
     OnAudioStreamCreated();
   }
 
-#if defined(OS_CHROMEOS)
-  base::test::ScopedFeatureList scoped_feature_list_;
-#endif
-
   mojo::Binding<mojom::SessionObserver> observer_binding_;
   mojo::Binding<mojom::CastMessageChannel> outbound_channel_binding_;
   mojo::Binding<mojom::AudioStreamCreatorClient> audio_client_binding_;
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
index 9fe0127..6b9128f 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
@@ -180,15 +180,6 @@
   blink::mojom::MediaStreamRequestResult result =
       blink::mojom::MediaStreamRequestResult::INVALID_STATE;
 
-#if defined(OS_CHROMEOS)
-  if (features::IsMultiProcessMash()) {
-    // TODO(crbug.com/806366): Screen capture support for mash.
-    NOTIMPLEMENTED() << "Screen capture not yet implemented in --mash";
-    screen_capture_enabled = false;
-    result = blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
-  }
-#endif  // defined(OS_CHROMEOS)
-
   // Approve request only when the following conditions are met:
   //  1. Screen capturing is enabled via command line switch or white-listed for
   //     the given origin.
diff --git a/chrome/browser/memory/oom_memory_details.cc b/chrome/browser/memory/oom_memory_details.cc
index 4a0f24a..5d7f6bfee 100644
--- a/chrome/browser/memory/oom_memory_details.cc
+++ b/chrome/browser/memory/oom_memory_details.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/memory/oom_memory_details.h"
 
 #include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/process/process_metrics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -42,7 +41,6 @@
     log_string += base::UTF16ToASCII(ui::FormatBytes(memory.gem_size));
   }
 #endif
-  UMA_HISTOGRAM_MEDIUM_TIMES("TabManager.Discarding.LogMemoryTime", delta);
   LOG(WARNING) << title_ << " (" << delta.InMilliseconds() << " ms):\n"
                << log_string;
   // Delete ourselves so we don't have to worry about OomPriorityManager
diff --git a/chrome/browser/platform_util_chromeos.cc b/chrome/browser/platform_util_chromeos.cc
index 11391d2..259c1ace 100644
--- a/chrome/browser/platform_util_chromeos.cc
+++ b/chrome/browser/platform_util_chromeos.cc
@@ -20,7 +20,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/chromeos/strings/grit/ui_chromeos_strings.h"
 #include "url/gurl.h"
 
@@ -129,8 +128,6 @@
   // |window| can be nullptr inside of unit tests.
   if (!window)
     return false;
-  if (features::IsUsingWindowService())
-    window = window->GetRootWindow();
   return window->GetProperty(ash::kWindowPinTypeKey) ==
          ash::WindowPinType::kTrustedPinned;
 }
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index f8e216d..d3ee033 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -16,6 +16,7 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browsing_data/browsing_data_helper.h"
@@ -54,6 +55,7 @@
 #include "content/public/browser/browsing_data_remover.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/browsing_data_remover_test_util.h"
@@ -1600,6 +1602,56 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+                       PushEventIgnoresScheduledNotificationsForEnforcement) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(features::kNotificationTriggers);
+
+  std::string script_result;
+
+  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+  PushMessagingAppIdentifier app_identifier =
+      GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+  LoadTestPage();  // Reload to become controlled.
+
+  RemoveAllNotifications();
+
+  // We'll need to specify the web_contents in which to eval script, since we're
+  // going to run script in a background tab.
+  content::WebContents* web_contents =
+      GetBrowser()->tab_strip_model()->GetActiveWebContents();
+
+  // Initialize site engagement score to have no budget for silent pushes.
+  SetSiteEngagementScore(web_contents->GetURL(), 0);
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      GetBrowser(), GURL("about:blank"),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
+
+  gcm::IncomingMessage message;
+  message.sender_id = GetTestApplicationServerKey();
+  message.raw_data = "shownotification-with-showtrigger";
+  message.decrypted = true;
+
+  // If the Service Worker push event handler only schedules a notification, we
+  // should show a forced one providing there is no foreground tab and the
+  // origin ran out of budget.
+  SendMessageAndWaitUntilHandled(app_identifier, message);
+  ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+  EXPECT_EQ("shownotification-with-showtrigger", script_result);
+
+  // Because scheduled notifications do not count as displayed notifications,
+  // this should have shown a default notification.
+  std::vector<message_center::Notification> notifications =
+      GetDisplayedNotifications();
+  ASSERT_EQ(notifications.size(), 1u);
+
+  EXPECT_TRUE(TagEquals(notifications[0], kPushMessagingForcedNotificationTag));
+  EXPECT_TRUE(notifications[0].silent());
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                        PushEventEnforcesUserVisibleNotificationAfterQueue) {
   std::string script_result;
 
diff --git a/chrome/browser/push_messaging/push_messaging_notification_manager.cc b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
index ed5026d..22260185 100644
--- a/chrome/browser/push_messaging/push_messaging_notification_manager.cc
+++ b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
@@ -11,6 +11,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
@@ -33,6 +34,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/url_constants.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "third_party/blink/public/common/notifications/notification_resources.h"
@@ -95,6 +97,19 @@
   return database_data;
 }
 
+int CountVisibleNotifications(
+    const std::vector<NotificationDatabaseData>& data) {
+  if (!base::FeatureList::IsEnabled(features::kNotificationTriggers))
+    return data.size();
+
+  return std::count_if(
+      data.begin(), data.end(),
+      [](const NotificationDatabaseData& notification) {
+        return notification.has_triggered ||
+               !notification.notification_data.show_trigger_timestamp;
+      });
+}
+
 }  // namespace
 
 PushMessagingNotificationManager::PushMessagingNotificationManager(
@@ -140,7 +155,10 @@
   // user-visible action done in response to a push message - but make sure that
   // sending two messages in rapid succession which show then hide a
   // notification doesn't count.
-  int notification_count = success ? data.size() : 0;
+  // TODO(knollr): Scheduling a notification should count as a user-visible
+  // action, if it is not immediately cancelled or the |origin| schedules too
+  // many notifications too far in the future.
+  int notification_count = success ? CountVisibleNotifications(data) : 0;
   bool notification_shown = notification_count > 0;
   bool notification_needed = true;
 
diff --git a/chrome/browser/resources/chromeos/login/sync_consent.css b/chrome/browser/resources/chromeos/login/sync_consent.css
index 5686783..08c1fe2 100644
--- a/chrome/browser/resources/chromeos/login/sync_consent.css
+++ b/chrome/browser/resources/chromeos/login/sync_consent.css
@@ -34,12 +34,10 @@
 }
 
 cr-checkbox {
-  --cr-checkbox-label-container: {
-    padding-inline-start: 12px;
-  };
   align-self: start; /* Prevent label from spanning the whole width. */
   margin-top: 28px; /* 40 to label base line => 40 - 16/2 - 13/3 = 28 */
   padding-inline-start: 8px;
+  --cr-checkbox-label-padding-start: 12px;
 }
 
 #syncConsentOverviewDialog [slot='bottom-buttons'] {
diff --git a/chrome/browser/resources/media_router/elements/route_controls/route_controls.css b/chrome/browser/resources/media_router/elements/route_controls/route_controls.css
index 811d8db7..371416a 100644
--- a/chrome/browser/resources/media_router/elements/route_controls/route_controls.css
+++ b/chrome/browser/resources/media_router/elements/route_controls/route_controls.css
@@ -88,10 +88,8 @@
 }
 
 #hangouts-local-present-checkbox {
-  --cr-checkbox-label-container: {
-    padding-inline-start: 14px;
-  }
   align-items: start;
+  --cr-checkbox-label-padding-start: 14px;
 }
 
 #hangouts-local-present-checkbox-subtitle {
diff --git a/chrome/browser/resources/print_preview/ui/print_preview_shared_css.html b/chrome/browser/resources/print_preview/ui/print_preview_shared_css.html
index 3cfa753..c2a865f 100644
--- a/chrome/browser/resources/print_preview/ui/print_preview_shared_css.html
+++ b/chrome/browser/resources/print_preview/ui/print_preview_shared_css.html
@@ -28,10 +28,11 @@
       .checkbox cr-checkbox {
         min-height: var(--print-preview-row-height);
         --cr-checkbox-ripple-size: var(--print-preview-row-height);
-        --cr-checkbox-label-container: {
-          overflow: hidden;
-          padding-inline-start: 16px;
-        };
+      }
+
+      .checkbox cr-checkbox::part(label-container) {
+        overflow: hidden;
+        padding-inline-start: 16px;
       }
 
       cr-input {
diff --git a/chrome/browser/resources/settings/appearance_page/appearance_page.html b/chrome/browser/resources/settings/appearance_page/appearance_page.html
index f06a9f69..fd6ed00a 100644
--- a/chrome/browser/resources/settings/appearance_page/appearance_page.html
+++ b/chrome/browser/resources/settings/appearance_page/appearance_page.html
@@ -23,11 +23,10 @@
     <style include="settings-shared md-select iron-flex">
       /* Lines up with cr-input. */
       #custom-input {
-        --cr-radio-button-disc: {
-          /* (cr-input line-height + cr-input top/bottom padding) / 2 -
-             cr-radio disc-wrapper height / 2 */
-          margin-top: calc((1.54em + 12px) / 2 - 8px);
-        }
+        /* (cr-input line-height + cr-input top/bottom padding) / 2 -
+           cr-radio disc-wrapper height / 2 */
+        --cr-radio-button-disc-margin-block-start: calc(
+            (1.54em + 12px) / 2 - 8px);
         align-items: start;
       }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_add_languages_dialog.html b/chrome/browser/resources/settings/chromeos/os_languages_page/os_add_languages_dialog.html
index 28ed776..e0a8a82 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_add_languages_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_add_languages_dialog.html
@@ -35,10 +35,8 @@
         padding-inline-start: 20px;
       }
 
-      cr-checkbox {
-        --cr-checkbox-label-container: {
-          white-space: nowrap;
-        };
+      cr-checkbox::part(label-container) {
+        white-space: nowrap;
       }
     </style>
     <cr-dialog id="dialog" close-text="$i18n{close}">
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.html b/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
index bdb4c7200..9611202 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.html
@@ -35,10 +35,8 @@
         padding-inline-start: 20px;
       }
 
-      cr-checkbox {
-        --cr-checkbox-label-container: {
-          white-space: nowrap;
-        };
+      cr-checkbox::part(label-container) {
+        white-space: nowrap;
       }
     </style>
     <cr-dialog id="dialog" close-text="$i18n{close}">
diff --git a/chrome/browser/resources/settings/languages_page/languages_page.html b/chrome/browser/resources/settings/languages_page/languages_page.html
index 73fe939..c220332e 100644
--- a/chrome/browser/resources/settings/languages_page/languages_page.html
+++ b/chrome/browser/resources/settings/languages_page/languages_page.html
@@ -147,10 +147,9 @@
 
       .spell-check-radio-button.enhanced {
         align-items: start;
-        --cr-radio-button-disc: {
-          /* Align with just the first line of text */
-          margin-top: calc((1.5em - var(--cr-radio-button-size)) / 2);
-        }
+        /* Align with just the first line of text */
+        --cr-radio-button-disc-margin-block-start: calc(
+            (1.5em - var(--cr-radio-button-size)) / 2);
       }
 
       .enhanced-spellcheck-description {
diff --git a/chrome/browser/resources/settings/people_page/lock_screen.html b/chrome/browser/resources/settings/people_page/lock_screen.html
index bdd84b5e..a2ce568 100644
--- a/chrome/browser/resources/settings/people_page/lock_screen.html
+++ b/chrome/browser/resources/settings/people_page/lock_screen.html
@@ -36,16 +36,15 @@
         display: block;
       }
 
-      cr-radio-button {
-        --cr-radio-button-slot: {
-          display: flex;
-          line-height: 154%;  /* Apply 20px line-height to paper radio button
-                                 text to match rest of settings line-heights. */
-          width: 100%;
-        };
+      #pinPasswordDiv {
+        display: flex;
       }
 
-      #pinPasswordDiv,
+      #pinPasswordLabel {
+        flex: 1;
+      }
+
+      #pinPasswordLabel,
       #pinPasswordSecondaryActionDiv {
         margin: auto;
       }
@@ -92,23 +91,25 @@
                 </div>
               </cr-radio-button>
               <cr-radio-button name="pin+password" class="list-item">
-                <div id="pinPasswordDiv" class="start">
-                  $i18n{lockScreenPinOrPassword}
-                </div>
-                <template is="dom-if"
-                    if="[[showConfigurePinButton_(selectedUnlockType)]]">
-                  <div class="separator"></div>
-                  <div id="pinPasswordSecondaryActionDiv"
-                      class="secondary-action">
-                    <!-- Use stop-keyboard-event-propagation to prevent
-                         triggering this when focused after closing the
-                         dialog. -->
-                    <cr-button id="setupPinButton" on-click="onConfigurePin_"
-                        stop-keyboard-event-propagation>
-                      [[getSetupPinText_(hasPin)]]
-                    </cr-button>
+                <div id="pinPasswordDiv">
+                  <div id="pinPasswordLabel">
+                    $i18n{lockScreenPinOrPassword}
                   </div>
-                </template>
+                  <template is="dom-if"
+                      if="[[showConfigurePinButton_(selectedUnlockType)]]">
+                    <div class="separator"></div>
+                    <div id="pinPasswordSecondaryActionDiv"
+                        class="secondary-action">
+                      <!-- Use stop-keyboard-event-propagation to prevent
+                           triggering this when focused after closing the
+                           dialog. -->
+                      <cr-button id="setupPinButton" on-click="onConfigurePin_"
+                          stop-keyboard-event-propagation>
+                        [[getSetupPinText_(hasPin)]]
+                      </cr-button>
+                    </div>
+                  </template>
+                </div>
               </cr-radio-button>
             </cr-radio-group>
           </div>
diff --git a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
index 4d6ed91..904043e 100644
--- a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
+++ b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
@@ -4,14 +4,18 @@
 
 #include "chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h"
 
+#include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/sync/sessions/sync_sessions_web_contents_router.h"
+#include "components/favicon/content/content_favicon_driver.h"
+#include "components/favicon/core/features.h"
 #include "components/language/core/common/language_experiments.h"
 #include "components/sync_sessions/synced_tab_delegate.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
+#include "ui/gfx/image/image_skia.h"
 
 namespace sync_sessions {
 
@@ -38,6 +42,11 @@
   // A translate client is not always attached to web contents (e.g. tests).
   if (chrome_translate_client_)
     chrome_translate_client_->translate_driver().AddObserver(this);
+
+  favicon_driver_ =
+      favicon::ContentFaviconDriver::FromWebContents(web_contents);
+  if (favicon_driver_)
+    favicon_driver_->AddObserver(this);
 }
 
 SyncSessionsRouterTabHelper::~SyncSessionsRouterTabHelper() {}
@@ -56,6 +65,8 @@
   NotifyRouter();
   if (chrome_translate_client_)
     chrome_translate_client_->translate_driver().RemoveObserver(this);
+  if (favicon_driver_)
+    favicon_driver_->RemoveObserver(this);
 }
 
 void SyncSessionsRouterTabHelper::DidFinishLoad(
@@ -102,6 +113,19 @@
     router_->NotifyTabModified(web_contents(), page_load_completed);
 }
 
+void SyncSessionsRouterTabHelper::OnFaviconUpdated(
+    favicon::FaviconDriver* favicon_driver,
+    FaviconDriverObserver::NotificationIconType notification_icon_type,
+    const GURL& icon_url,
+    bool icon_url_changed,
+    const gfx::Image& image) {
+  if (icon_url_changed &&
+      base::FeatureList::IsEnabled(
+          favicon::kNotifySessionsOfMostRecentIconUrlChange)) {
+    NotifyRouter();
+  }
+}
+
 WEB_CONTENTS_USER_DATA_KEY_IMPL(SyncSessionsRouterTabHelper)
 
 }  // namespace sync_sessions
diff --git a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h
index 93756a4..b76434b 100644
--- a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h
+++ b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h
@@ -6,11 +6,16 @@
 #define CHROME_BROWSER_SYNC_SESSIONS_SYNC_SESSIONS_ROUTER_TAB_HELPER_H_
 
 #include "chrome/browser/translate/chrome_translate_client.h"
+#include "components/favicon/core/favicon_driver_observer.h"
 #include "components/sessions/core/session_id.h"
 #include "components/translate/content/browser/content_translate_driver.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
+namespace favicon {
+class FaviconDriver;
+}
+
 namespace sync_sessions {
 
 class SyncSessionsWebContentsRouter;
@@ -25,7 +30,8 @@
 class SyncSessionsRouterTabHelper
     : public content::WebContentsUserData<SyncSessionsRouterTabHelper>,
       public content::WebContentsObserver,
-      public translate::ContentTranslateDriver::Observer {
+      public translate::ContentTranslateDriver::Observer,
+      public favicon::FaviconDriverObserver {
  public:
   ~SyncSessionsRouterTabHelper() override;
 
@@ -53,6 +59,14 @@
   void OnLanguageDetermined(
       const translate::LanguageDetectionDetails& details) override;
 
+  // favicon::FaviconDriverObserver implementation.
+  void OnFaviconUpdated(
+      favicon::FaviconDriver* favicon_driver,
+      FaviconDriverObserver::NotificationIconType notification_icon_type,
+      const GURL& icon_url,
+      bool icon_url_changed,
+      const gfx::Image& image) override;
+
   // Sets the source tab id for the given child WebContents to the id of the
   // WebContents that owns this helper.
   void SetSourceTabIdForChild(content::WebContents* child_contents);
@@ -85,6 +99,8 @@
 
   ChromeTranslateClient* chrome_translate_client_;
 
+  favicon::FaviconDriver* favicon_driver_;
+
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
   DISALLOW_COPY_AND_ASSIGN(SyncSessionsRouterTabHelper);
diff --git a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
index 550ed9e..ea55ece 100644
--- a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
@@ -21,11 +21,13 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/url_constants.h"
+#include "components/favicon/core/features.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/sessions/core/session_types.h"
 #include "components/signin/core/browser/account_info.h"
 #include "components/sync/base/time.h"
 #include "components/sync/protocol/proto_value_conversions.h"
+#include "components/sync/protocol/session_specifics.pb.h"
 #include "components/sync/test/fake_server/sessions_hierarchy.h"
 #include "components/sync_sessions/session_store.h"
 #include "components/sync_sessions/session_sync_service.h"
@@ -110,6 +112,49 @@
   fake_server::FakeServer* fake_server_;
 };
 
+class IsIconURLSyncedChecker : public SingleClientStatusChangeChecker {
+ public:
+  IsIconURLSyncedChecker(const std::string& page_url,
+                         const std::string& icon_url,
+                         fake_server::FakeServer* fake_server,
+                         syncer::ProfileSyncService* service)
+      : SingleClientStatusChangeChecker(service),
+        page_url_(page_url),
+        icon_url_(icon_url),
+        fake_server_(fake_server) {}
+
+  // StatusChangeChecker implementation.
+  bool IsExitConditionSatisfied() override {
+    std::vector<sync_pb::SyncEntity> sessions =
+        fake_server_->GetSyncEntitiesByModelType(syncer::SESSIONS);
+    for (const auto& entity : sessions) {
+      const sync_pb::SessionSpecifics& session_specifics =
+          entity.specifics().session();
+      if (!session_specifics.has_tab()) {
+        continue;
+      }
+      for (int i = 0; i < session_specifics.tab().navigation_size(); i++) {
+        const sync_pb::TabNavigation& nav =
+            session_specifics.tab().navigation(i);
+        if (nav.has_virtual_url() && nav.has_favicon_url() &&
+            nav.virtual_url() == page_url_ && nav.favicon_url() == icon_url_) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  std::string GetDebugMessage() const override {
+    return "Waiting for URLs to be commited to the server";
+  }
+
+ private:
+  const std::string page_url_;
+  const std::string icon_url_;
+  fake_server::FakeServer* fake_server_;
+};
+
 class SingleClientSessionsSyncTest : public SyncTest {
  public:
   SingleClientSessionsSyncTest() : SyncTest(SINGLE_CLIENT) {}
@@ -748,4 +793,29 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
+                       ShouldNotifyLoadedIconUrl) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      favicon::kNotifySessionsOfMostRecentIconUrlChange);
+
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+  ASSERT_TRUE(CheckInitialState(0));
+
+  // Url with endoded 1 pixel icon.
+  std::string icon_url =
+      "data:image/png;base64,"
+      "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
+  std::string page_url =
+      "data:text/html,<html><title>TestWithFavicon</title><link rel=icon "
+      "href=" +
+      icon_url + " /></html>";
+
+  ASSERT_TRUE(OpenTab(0, GURL(page_url)));
+
+  IsIconURLSyncedChecker checker(page_url, icon_url, GetFakeServer(),
+                                 GetSyncService(0));
+  EXPECT_TRUE(checker.Wait());
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/app_list/app_service_app_model_builder.cc b/chrome/browser/ui/app_list/app_service_app_model_builder.cc
index 5a0f4a4c..3eb20e5 100644
--- a/chrome/browser/ui/app_list/app_service_app_model_builder.cc
+++ b/chrome/browser/ui/app_list/app_service_app_model_builder.cc
@@ -5,12 +5,17 @@
 #include "chrome/browser/ui/app_list/app_service_app_model_builder.h"
 
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/ui/app_list/app_service_app_item.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/services/app_service/public/cpp/app_service_proxy.h"
 #include "ui/base/l10n/l10n_util.h"
 
+// static
+apps::AppServiceProxy*
+    AppServiceAppModelBuilder::app_service_proxy_for_testing_ = nullptr;
+
 // Folder items are created by the Ash process and their existence is
 // communicated to chrome via the AppListClient. Therefore, Crostini has an
 // observer that listens for the creation of its folder, and updates the
@@ -56,9 +61,19 @@
   }
 }
 
+// static
+apps::AppServiceProxy* AppServiceAppModelBuilder::SetAppServiceProxyForTesting(
+    apps::AppServiceProxy* proxy) {
+  apps::AppServiceProxy* old = app_service_proxy_for_testing_;
+  app_service_proxy_for_testing_ = proxy;
+  return old;
+}
+
 void AppServiceAppModelBuilder::BuildModel() {
   apps::AppServiceProxy* proxy =
-      apps::AppServiceProxyFactory::GetForProfile(profile());
+      app_service_proxy_for_testing_
+          ? app_service_proxy_for_testing_
+          : apps::AppServiceProxyFactory::GetForProfile(profile());
   if (proxy) {
     proxy->AppRegistryCache().ForEachApp(
         [this](const apps::AppUpdate& update) { OnAppUpdate(update); });
@@ -97,7 +112,13 @@
       }
 
     } else {
-      RemoveApp(update.AppId(), false /* unsynced_change */);
+      bool unsynced_change = false;
+      if (update.AppType() == apps::mojom::AppType::kArc) {
+        // Don't sync app removal in case it was caused by disabling Google
+        // Play Store.
+        unsynced_change = !arc::IsArcPlayStoreEnabledForProfile(profile());
+      }
+      RemoveApp(update.AppId(), unsynced_change);
     }
 
   } else if (show) {
diff --git a/chrome/browser/ui/app_list/app_service_app_model_builder.h b/chrome/browser/ui/app_list/app_service_app_model_builder.h
index c3a2a5b..beb6cb2 100644
--- a/chrome/browser/ui/app_list/app_service_app_model_builder.h
+++ b/chrome/browser/ui/app_list/app_service_app_model_builder.h
@@ -11,6 +11,10 @@
 
 class AppListControllerDelegate;
 
+namespace apps {
+class AppServiceProxy;
+}
+
 class AppServiceAppModelBuilder : public AppListModelBuilder,
                                   public apps::AppRegistryCache::Observer {
  public:
@@ -18,9 +22,14 @@
 
   ~AppServiceAppModelBuilder() override;
 
+  static apps::AppServiceProxy* SetAppServiceProxyForTesting(
+      apps::AppServiceProxy* proxy);
+
  private:
   class CrostiniFolderObserver;
 
+  static apps::AppServiceProxy* app_service_proxy_for_testing_;
+
   // AppListModelBuilder overrides:
   void BuildModel() override;
 
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.cc b/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.cc
index 8cb964e..0d5be3b9 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.cc
@@ -8,6 +8,7 @@
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/strings/string_util.h"
 #include "chrome/browser/ui/app_list/chrome_app_list_item.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
@@ -89,4 +90,35 @@
   return RankingItemType::kApp;
 }
 
+std::string SimplifyUrlId(const std::string& url_id) {
+  std::string result(url_id);
+
+  std::size_t query_index = result.find("?");
+  if (query_index != std::string::npos)
+    result.erase(query_index);
+
+  std::size_t fragment_index = result.find("#");
+  if (fragment_index != std::string::npos)
+    result.erase(fragment_index);
+
+  if (!result.empty() && result.back() == '/' &&
+      result.find("://") != result.size() - 3)
+    result.pop_back();
+
+  return result;
+}
+
+std::string SimplifyGoogleDocsUrlId(const std::string& url_id) {
+  std::string result = SimplifyUrlId(url_id);
+
+  // URLs that end with /view or /edit point to the same document, so should be
+  // the same for ranking purposes.
+  if (base::EndsWith(result, "/view", base::CompareCase::INSENSITIVE_ASCII) ||
+      base::EndsWith(result, "/edit", base::CompareCase::INSENSITIVE_ASCII)) {
+    result = result.substr(0, result.length() - 5);
+  }
+
+  return result;
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h b/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h
index f8d274a2..9f11f118 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_RANKING_ITEM_UTIL_H_
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_RANKING_ITEM_UTIL_H_
 
+#include <string>
+
 class ChromeAppListItem;
 class ChromeSearchResult;
 
@@ -37,6 +39,15 @@
 RankingItemType RankingItemTypeFromChromeAppListItem(
     const ChromeAppListItem& item);
 
+// Given a search result ID representing a URL, removes some components of the
+// URL such as the query and fragment. This is intended to normalize URLs that
+// should be considered the same for the purposes of ranking.
+std::string SimplifyUrlId(const std::string& url_id);
+
+// Given a search result ID representing a google docs file, remove parts of the
+// URL that can vary without affecting what doc the URL resolves to.
+std::string SimplifyGoogleDocsUrlId(const std::string& url_id);
+
 }  // namespace app_list
 
 #endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_SEARCH_RESULT_RANKER_RANKING_ITEM_UTIL_H_
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util_unittest.cc
index 4b4dcf6..0dd6086 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util_unittest.cc
@@ -66,4 +66,40 @@
   EXPECT_EQ(type, RankingItemType::kOmniboxHistory);
 }
 
+TEST_F(RankingItemUtilTest, SimplifyUrlId) {
+  // Test handling different kinds of scheme, domain, and path. These should all
+  // be no-ops.
+  EXPECT_EQ(SimplifyUrlId("scheme://domain.com/path"),
+            "scheme://domain.com/path");
+  EXPECT_EQ(SimplifyUrlId("://domain.com"), "://domain.com");
+  EXPECT_EQ(SimplifyUrlId("domain.com/path"), "domain.com/path");
+  EXPECT_EQ(SimplifyUrlId("domain.com:1123/path"), "domain.com:1123/path");
+  EXPECT_EQ(SimplifyUrlId("://"), "://");
+
+  // Test removing trailing slash.
+  EXPECT_EQ(SimplifyUrlId("scheme://domain.com/"), "scheme://domain.com");
+  EXPECT_EQ(SimplifyUrlId("scheme:///"), "scheme://");
+  EXPECT_EQ(SimplifyUrlId("scheme://"), "scheme://");
+
+  // Test removing queries and fragments.
+  EXPECT_EQ(SimplifyUrlId("domain.com/path?query=query"), "domain.com/path");
+  EXPECT_EQ(SimplifyUrlId("scheme://path?query=query#fragment"),
+            "scheme://path");
+  EXPECT_EQ(SimplifyUrlId("scheme://?query=query#fragment"), "scheme://");
+}
+
+TEST_F(RankingItemUtilTest, SimplifyGoogleDocsUrlId) {
+  EXPECT_EQ(SimplifyGoogleDocsUrlId("docs.google.com/hash/edit?"),
+            "docs.google.com/hash");
+  EXPECT_EQ(SimplifyGoogleDocsUrlId(
+                "http://docs.google.com/hash/view?query#fragment"),
+            "http://docs.google.com/hash");
+  EXPECT_EQ(SimplifyGoogleDocsUrlId("https://docs.google.com/d/document/hash/"),
+            "https://docs.google.com/d/document/hash");
+
+  // We only want to remove one /view or /edit from the end of the URL.
+  EXPECT_EQ(SimplifyGoogleDocsUrlId("docs.google.com/edit/hash/view/view"),
+            "docs.google.com/edit/hash/view");
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
index 901ac576..c040272 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/recurrence_ranker.h"
+#include "url/gurl.h"
 
 namespace app_list {
 namespace {
@@ -81,6 +82,27 @@
   }
 }
 
+// Performs any per-type normalization required on a search result ID. This is
+// meant to simplify the space of IDs in cases where they are too sparse.
+std::string NormalizeId(const std::string& id, RankingItemType type) {
+  // Put any further normalizations here.
+  switch (type) {
+    case RankingItemType::kOmniboxGeneric:
+    case RankingItemType::kOmniboxBookmark:
+    case RankingItemType::kOmniboxDocument:
+    case RankingItemType::kOmniboxHistory:
+    case RankingItemType::kOmniboxSearch:
+      // Heuristically check if the URL points to a Drive file. If so, strip
+      // some extra information from it.
+      if (GURL(id).host() == "docs.google.com")
+        return SimplifyGoogleDocsUrlId(id);
+      else
+        return SimplifyUrlId(id);
+    default:
+      return id;
+  }
+}
+
 }  // namespace
 
 SearchResultRanker::SearchResultRanker(Profile* profile)
@@ -202,8 +224,8 @@
               3.0);
         }
       } else if (query_based_mixed_types_ranker_) {
-        // TODO(931149): Add some normalization for URLs.
-        const auto& rank_it = query_mixed_ranks_.find(result.result->id());
+        const auto& rank_it =
+            query_mixed_ranks_.find(NormalizeId(result.result->id(), type));
         if (rank_it != query_mixed_ranks_.end()) {
           result.score = std::min(
               result.score + rank_it->second * results_list_boost_coefficient_,
@@ -216,12 +238,11 @@
 
 void SearchResultRanker::Train(const std::string& id, RankingItemType type) {
   if (ModelForType(type) == Model::MIXED_TYPES) {
-    // TODO(931149): Add some normalization for URLs.
     if (results_list_group_ranker_) {
       results_list_group_ranker_->Record(
           base::NumberToString(static_cast<int>(type)));
     } else if (query_based_mixed_types_ranker_) {
-      query_based_mixed_types_ranker_->Record(id);
+      query_based_mixed_types_ranker_->Record(NormalizeId(id, type));
     }
   }
 }
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc
index 41578438..c1ea9fd 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/search_result_ranker_unittest.cc
@@ -191,5 +191,34 @@
                                               HasId("A"), HasId("B"))));
 }
 
+// URL IDs should ignore the query and fragment, and URLs for google docs should
+// ignore a trailing /view or /edit.
+TEST_F(SearchResultRankerTest, ItemModelNormalizesUrlIds) {
+  // We want |url_1| and |_3| to be equivalent to |url_2| and |_4|. So, train on
+  // 1 and 3 but rank 2 and 4. Even with zero relevance, they should be at the
+  // top of the rankings.
+  const std::string& url_1 = "http://docs.google.com/mydoc/edit?query";
+  const std::string& url_2 = "http://docs.google.com/mydoc/view#fragment";
+  const std::string& url_3 = "some.domain.com?query#edit";
+  const std::string& url_4 = "some.domain.com";
+
+  auto ranker = MakeRanker(true, {{"boost_coefficient", "1.0"}});
+
+  for (int i = 0; i < 5; ++i) {
+    ranker->Train(url_1, RankingItemType::kOmniboxHistory);
+    ranker->Train(url_3, RankingItemType::kOmniboxHistory);
+  }
+  ranker->FetchRankings(base::string16());
+
+  auto results = MakeSearchResults(
+      {url_2, url_4, "untrained id"},
+      {ResultType::kOmnibox, ResultType::kOmnibox, ResultType::kOmnibox},
+      {0.0f, 0.0f, 0.1f});
+
+  ranker->Rank(&results);
+  EXPECT_THAT(results, WhenSorted(ElementsAre(HasId(url_4), HasId(url_2),
+                                              HasId("untrained id"))));
+}
+
 }  // namespace test
 }  // namespace app_list
diff --git a/chrome/browser/ui/ash/accelerator_commands_browsertest.cc b/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
index b9ed8080..99048491 100644
--- a/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
+++ b/chrome/browser/ui/ash/accelerator_commands_browsertest.cc
@@ -25,7 +25,6 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
@@ -48,9 +47,7 @@
 };
 
 bool IsInImmersive(aura::Window* window) {
-  aura::Window* toplevel =
-      features::IsUsingWindowService() ? window->GetRootWindow() : window;
-  return toplevel->GetProperty(ash::kImmersiveIsActive);
+  return window->GetProperty(ash::kImmersiveIsActive);
 }
 
 }  // namespace
@@ -107,10 +104,8 @@
 
   // 2) ash::ShellTestApi().ToggleFullscreen() should have no effect on windows
   // which cannot be maximized.
-  aura::Window* toplevel =
-      features::IsUsingWindowService() ? window->GetRootWindow() : window;
-  toplevel->SetProperty(aura::client::kResizeBehaviorKey,
-                        aura::client::kResizeBehaviorNone);
+  window->SetProperty(aura::client::kResizeBehaviorKey,
+                      aura::client::kResizeBehaviorNone);
   ash::ShellTestApi().ToggleFullscreen();
   EXPECT_TRUE(IsInitialShowState(widget));
 
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index d85fcac..ca32cb3 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -60,7 +60,6 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
-#include "ui/base/ui_base_features.h"
 
 #if BUILDFLAG(ENABLE_WAYLAND_SERVER)
 #include "chrome/browser/exo_parts.h"
@@ -197,12 +196,8 @@
             detector);
   }
 
-  // TODO(mash): Port TabScrubber. This depends on where gesture recognition
-  // happens because TabScrubber uses 3-finger scrolls. https://crbug.com/796366
-  if (!features::IsMultiProcessMash()) {
-    // Initialize TabScrubber after the Ash Shell has been initialized.
-    TabScrubber::GetInstance();
-  }
+  // Initialize TabScrubber after the Ash Shell has been initialized.
+  TabScrubber::GetInstance();
 
   if (base::FeatureList::IsEnabled(ash::features::kKioskNextShell)) {
     kiosk_next_shell_client_ = std::make_unique<KioskNextShellClient>();
diff --git a/chrome/browser/ui/ash/keyboard/DEPS b/chrome/browser/ui/ash/keyboard/DEPS
index 9de64e94..b24cb41 100644
--- a/chrome/browser/ui/ash/keyboard/DEPS
+++ b/chrome/browser/ui/ash/keyboard/DEPS
@@ -1,13 +1,10 @@
 specific_include_rules = {
-  # Only for !features::IsUsingWindowService().
   "chrome_keyboard_bounds_observer\.cc": [
     "+ash/root_window_controller.h",
   ],
-  # For classic Ash mode.
   "chrome_keyboard_controller_client\.cc": [
     "+ui/keyboard",
   ],
-  # This code is only used in classic Ash mode.
   "chrome_keyboard_ui.*": [
     "+ash/shell.h",
     "+ui/keyboard",
diff --git a/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc b/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc
index 3bf14a96..2ac63f5 100644
--- a/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc
+++ b/chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.cc
@@ -32,7 +32,6 @@
 #include "ui/base/ime/ime_bridge.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/text_input_client.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
@@ -116,8 +115,7 @@
   if (session_manager::SessionManager::Get())
     session_manager::SessionManager::Get()->RemoveObserver(this);
   pref_change_registrar_.RemoveAll();
-  if (!::features::IsUsingWindowService() &&
-      keyboard::KeyboardController::HasInstance()) {
+  if (keyboard::KeyboardController::HasInstance()) {
     // In classic Ash, keyboard::KeyboardController owns ChromeKeyboardUI which
     // accesses this class, so make sure that the UI has been destroyed.
     keyboard::KeyboardController::Get()->Shutdown();
@@ -246,11 +244,6 @@
 }
 
 aura::Window* ChromeKeyboardControllerClient::GetKeyboardWindow() const {
-  if (::features::IsUsingWindowService()) {
-    content::WebContents* contents =
-        keyboard_contents_ ? keyboard_contents_->web_contents() : nullptr;
-    return contents ? contents->GetNativeView() : nullptr;
-  }
   return keyboard::KeyboardController::Get()->GetKeyboardWindow();
 }
 
diff --git a/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.cc b/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.cc
index d5fc1eb..e20f0a3 100644
--- a/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.cc
+++ b/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui.cc
@@ -21,7 +21,6 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/ime/ime_bridge.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor_extra/shadow.h"
 #include "ui/gfx/geometry/rect.h"
@@ -35,7 +34,6 @@
 
 ChromeKeyboardUI::ChromeKeyboardUI(content::BrowserContext* context)
     : browser_context_(context) {
-  DCHECK(!::features::IsUsingWindowService());
 }
 
 ChromeKeyboardUI::~ChromeKeyboardUI() {
diff --git a/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui_unittest.cc b/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui_unittest.cc
index c92b633..cea1003c 100644
--- a/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui_unittest.cc
+++ b/chrome/browser/ui/ash/keyboard/chrome_keyboard_ui_unittest.cc
@@ -13,7 +13,6 @@
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_controller.h"
-#include "ui/base/ui_base_features.h"
 #include "url/gurl.h"
 
 // NOTE: ChromeKeyboardUITest is not used with the Window Service.
@@ -27,8 +26,6 @@
 
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
-    if (::features::IsUsingWindowService())
-      return;
     chrome_keyboard_controller_client_ =
         ChromeKeyboardControllerClient::CreateForTest();
     chrome_keyboard_ui_ = std::make_unique<ChromeKeyboardUI>(profile());
@@ -51,8 +48,6 @@
 // Ensure ChromeKeyboardContentsDelegate is successfully constructed and has
 // a valid aura::Window after calling LoadKeyboardWindow().
 TEST_F(ChromeKeyboardUITest, ChromeKeyboardContentsDelegate) {
-  if (::features::IsUsingWindowService())
-    return;
   aura::Window* window =
       chrome_keyboard_ui_->LoadKeyboardWindow(base::DoNothing());
   EXPECT_TRUE(window);
diff --git a/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc b/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
index fdb441f9..fd17cda6 100644
--- a/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/keyboard/keyboard_controller_browsertest.cc
@@ -25,7 +25,6 @@
 #include "ui/base/ime/dummy_text_input_client.h"
 #include "ui/base/ime/init/input_method_factory.h"
 #include "ui/base/ime/input_method.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/events/test/event_generator.h"
@@ -208,9 +207,6 @@
 // TODO(stevenjb/shend): Investigate/fix.
 IN_PROC_BROWSER_TEST_F(KeyboardControllerWebContentTest,
                        CanDragFloatingKeyboardWithMouse) {
-  if (::features::IsMultiProcessMash())
-    return;
-
   ChromeKeyboardControllerClient::Get()->SetContainerType(
       keyboard::ContainerType::kFloating, base::nullopt, base::DoNothing());
 
@@ -379,9 +375,6 @@
 // whether this needs to be tested in a keyboard::KeyboardController unit test.
 
 IN_PROC_BROWSER_TEST_F(KeyboardControllerStateTest, StateResolvesAfterPreload) {
-  if (::features::IsMultiProcessMash())
-    return;
-
   auto* controller = keyboard::KeyboardController::Get();
   EXPECT_EQ(controller->GetStateForTest(), keyboard::KeyboardUIState::kLoading);
   KeyboardLoadedWaiter().Wait();
@@ -390,9 +383,6 @@
 
 IN_PROC_BROWSER_TEST_F(KeyboardControllerStateTest,
                        OpenAndCloseAndOpenInternal) {
-  if (::features::IsMultiProcessMash())
-    return;
-
   auto* controller = keyboard::KeyboardController::Get();
   controller->ShowKeyboard(false);
   // Need to wait the extension to be loaded. Hence LOADING_EXTENSION.
@@ -410,9 +400,6 @@
 // See crbug.com/755354.
 IN_PROC_BROWSER_TEST_F(KeyboardControllerStateTest,
                        DisablingKeyboardGoesToInitialState) {
-  if (::features::IsMultiProcessMash())
-    return;
-
   auto* controller = keyboard::KeyboardController::Get();
 
   EXPECT_EQ(controller->GetStateForTest(), keyboard::KeyboardUIState::kLoading);
diff --git a/chrome/browser/ui/ash/kiosk_next_shell_client_browsertest.cc b/chrome/browser/ui/ash/kiosk_next_shell_client_browsertest.cc
index 4b91f7b..5f61dd9 100644
--- a/chrome/browser/ui/ash/kiosk_next_shell_client_browsertest.cc
+++ b/chrome/browser/ui/ash/kiosk_next_shell_client_browsertest.cc
@@ -77,26 +77,6 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(KioskNextShellClientTest, PRE_KioskNextShellLaunch) {
-  LoginAndEnableKioskNextShellPref();
-}
-
-// Checks that the Kiosk Next Home window is launched on sign-in when the
-// feature is enabled and its pref allows it.
-IN_PROC_BROWSER_TEST_F(KioskNextShellClientTest, KioskNextShellLaunch) {
-  // Enable all component extensions.
-  extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
-
-  Login("username");
-
-  // Wait for the app to launch.
-  apps::AppWindowWaiter waiter(
-      extensions::AppWindowRegistry::Get(ProfileHelper::Get()->GetProfileByUser(
-          user_manager::UserManager::Get()->GetActiveUser())),
-      extension_misc::kKioskNextHomeAppId);
-  EXPECT_TRUE(waiter.WaitForShownWithTimeout(TestTimeouts::action_timeout()));
-}
-
 IN_PROC_BROWSER_TEST_F(KioskNextShellClientTest, PRE_BrowserNotLaunched) {
   LoginAndEnableKioskNextShellPref();
 }
@@ -134,30 +114,13 @@
   EXPECT_FALSE(waiter.WaitForShownWithTimeout(TestTimeouts::action_timeout()));
 }
 
-// Variant of KioskNextShellClientTest that disables Mash in order to test the
-// Ash container of the Chrome app window.
-// TODO(crbug.com/945704): Once we can identify the app window's container with
-// SingleProcessMash enabled, remove this test class and add the container check
-// to the KioskNextShellLaunch test.
-class KioskNextShellClientMashDisabledTest : public KioskNextShellClientTest {
- public:
-  KioskNextShellClientMashDisabledTest() : KioskNextShellClientTest() {
-    feature_list_.InitAndDisableFeature(features::kSingleProcessMash);
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(KioskNextShellClientMashDisabledTest,
-                       PRE_KioskNextShellLaunch) {
+IN_PROC_BROWSER_TEST_F(KioskNextShellClientTest, PRE_KioskNextShellLaunch) {
   LoginAndEnableKioskNextShellPref();
 }
 
 // Checks that the Kiosk Next Home window is launched on sign-in when the
 // feature is enabled and its pref allows it.
-IN_PROC_BROWSER_TEST_F(KioskNextShellClientMashDisabledTest,
-                       KioskNextShellLaunch) {
+IN_PROC_BROWSER_TEST_F(KioskNextShellClientTest, KioskNextShellLaunch) {
   // Enable all component extensions.
   extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
 
diff --git a/chrome/browser/ui/ash/launcher/DEPS b/chrome/browser/ui/ash/launcher/DEPS
index 865f493..232c1c9 100644
--- a/chrome/browser/ui/ash/launcher/DEPS
+++ b/chrome/browser/ui/ash/launcher/DEPS
@@ -3,9 +3,11 @@
   "app_window_launcher_controller\.cc": [
     "+ash/shell.h",
   ],
-  # https://crbug.com/875111
   "chrome_launcher_controller_unittest\.cc": [
+    # https://crbug.com/875111
     "+ash/multi_user/multi_user_window_manager_impl.h",
+    # https://crbug.com/826982
+    "+chrome/services/app_service/app_service.h",
   ],
   "browser_shortcut_launcher_item_controller\.cc": [
     "+ash/wm/desks/desks_util.h",
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index e2644f1d..57bba6d 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -27,6 +27,7 @@
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/json/json_string_value_serializer.h"
 #include "base/location.h"
@@ -37,6 +38,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_impl.h"
+#include "chrome/browser/apps/app_service/arc_apps.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
@@ -48,6 +51,7 @@
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/ui/app_icon_loader.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
+#include "chrome/browser/ui/app_list/app_service_app_model_builder.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
@@ -77,9 +81,12 @@
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/services/app_service/app_service.h"
+#include "chrome/services/app_service/public/mojom/constants.mojom.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/test_browser_window_aura.h"
 #include "chrome/test/base/testing_profile.h"
@@ -113,6 +120,7 @@
 #include "extensions/common/manifest_constants.h"
 #include "extensions/grit/extensions_browser_resources.h"
 #include "services/network/test/test_network_connection_tracker.h"
+#include "services/service_manager/public/cpp/test/test_connector_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/window.h"
@@ -305,6 +313,15 @@
         base::CommandLine::ForCurrentProcess(), base::FilePath(), false);
     extension_service_->Init();
 
+    if (app_service_proxy_connector_) {
+      DCHECK(profile());
+      app_service_proxy_impl_.reset(apps::AppServiceProxyImpl::CreateForTesting(
+          profile(), app_service_proxy_connector_));
+      old_app_service_proxy_for_testing_ =
+          AppServiceAppModelBuilder::SetAppServiceProxyForTesting(
+              app_service_proxy_impl_.get());
+    }
+
     if (auto_start_arc_test_)
       arc_test_.SetUp(profile());
 
@@ -437,6 +454,11 @@
 
   void TearDown() override {
     arc_test_.TearDown();
+    if (app_service_proxy_impl_) {
+      AppServiceAppModelBuilder::SetAppServiceProxyForTesting(
+          old_app_service_proxy_for_testing_);
+      app_service_proxy_impl_.reset(nullptr);
+    }
     launcher_controller_ = nullptr;
     BrowserWithTestWindowTest::TearDown();
   }
@@ -905,6 +927,10 @@
 
   app_list::AppListSyncableService* app_list_syncable_service_ = nullptr;
 
+  service_manager::Connector* app_service_proxy_connector_ = nullptr;
+  std::unique_ptr<apps::AppServiceProxyImpl> app_service_proxy_impl_;
+  apps::AppServiceProxy* old_app_service_proxy_for_testing_ = nullptr;
+
  private:
   TestBrowserWindow* CreateTestBrowserWindowAura() {
     std::unique_ptr<aura::Window> window(
@@ -923,7 +949,23 @@
 class ChromeLauncherControllerWithArcTest
     : public ChromeLauncherControllerTest {
  protected:
-  ChromeLauncherControllerWithArcTest() { auto_start_arc_test_ = true; }
+  ChromeLauncherControllerWithArcTest() {
+    auto_start_arc_test_ = true;
+    if (!base::FeatureList::IsEnabled(features::kAppServiceAsh)) {
+      return;
+    }
+    // We set some state in the ChromeLauncherControllerTest superclass to
+    // affect what ChromeLauncherControllerTest::SetUp does, making it call
+    // AppServiceAppModelBuilder::SetAppServiceProxyForTesting (ASAMB::SASPFT).
+    // We can only call SASPFT after the TestingProfile is created (which
+    // happens in the superclass' SetUp) but before the ASAMB is constructed
+    // (which also happens in the superclass' SetUp).
+    app_service_ = std::make_unique<apps::AppService>(
+        test_connector_factory_.RegisterInstance(apps::mojom::kServiceName));
+    app_service_proxy_connector_ =
+        test_connector_factory_.GetDefaultConnector();
+  }
+
   ~ChromeLauncherControllerWithArcTest() override {}
 
   void SetUp() override {
@@ -931,9 +973,24 @@
     ArcAppIcon::DisableSafeDecodingForTesting();
 
     ChromeLauncherControllerTest::SetUp();
+    if (app_service_proxy_connector_) {
+      arc_apps_.reset(apps::ArcApps::CreateForTesting(
+          profile(), app_service_proxy_impl_.get()));
+    }
+  }
+
+  void TearDown() override {
+    if (app_service_proxy_connector_) {
+      arc_apps_.reset(nullptr);
+    }
+    ChromeLauncherControllerTest::TearDown();
   }
 
  private:
+  service_manager::TestConnectorFactory test_connector_factory_;
+  std::unique_ptr<service_manager::Service> app_service_;
+  std::unique_ptr<apps::ArcApps> arc_apps_;
+
   DISALLOW_COPY_AND_ASSIGN(ChromeLauncherControllerWithArcTest);
 };
 
@@ -2188,6 +2245,8 @@
   SendListOfArcApps();
   extension_service_->AddExtension(extension1_.get());
   extension_service_->AddExtension(extension2_.get());
+  // Allow async callbacks to run.
+  base::RunLoop().RunUntilIdle();
 
   EXPECT_FALSE(launcher_controller_->IsAppPinned(extension1_->id()));
   EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id));
@@ -2204,9 +2263,16 @@
   EXPECT_EQ("Chrome, App1, Fake App 0, App2", GetPinnedAppStatus());
 
   UninstallArcApps();
+  // Allow async callbacks to run.
+  base::RunLoop().RunUntilIdle();
+
   EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id));
   EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
+
   SendListOfArcApps();
+  // Allow async callbacks to run.
+  base::RunLoop().RunUntilIdle();
+
   EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id));
   EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
 
@@ -2217,7 +2283,11 @@
   EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
   EnablePlayStore(true);
   EXPECT_EQ("Chrome, App1, App2", GetPinnedAppStatus());
+
   SendListOfArcApps();
+  // Allow async callbacks to run.
+  base::RunLoop().RunUntilIdle();
+
   EXPECT_EQ("Chrome, App1, App2, Fake App 0", GetPinnedAppStatus());
 }
 
diff --git a/chrome/browser/ui/ash/multi_user/multi_profile_support_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_profile_support_unittest.cc
index 3590419..d14fcba7 100644
--- a/chrome/browser/ui/ash/multi_user/multi_profile_support_unittest.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_profile_support_unittest.cc
@@ -63,7 +63,6 @@
 #include "ui/aura/test/env_test_helper.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/test/display_manager_test_api.h"
@@ -1443,12 +1442,7 @@
 
   SwitchActiveUser(user1);
 
-  // This ternary doesn't make a lot of sense because the windows in this
-  // AshTest aren't created via the window service, but it's necessary to mirror
-  // the code in MultiProfileSupport, where the content window's
-  // root window is the Ash host window.
-  aura::Window* property_window =
-      features::IsUsingWindowService() ? window(0)->GetRootWindow() : window(0);
+  aura::Window* property_window = window(0);
 
   // Window #0 has no kAvatarIconKey property before teleporting.
   EXPECT_FALSE(property_window->GetProperty(aura::client::kAvatarIconKey));
diff --git a/chrome/browser/ui/ash/overview_window_drag_interactive_uitest.cc b/chrome/browser/ui/ash/overview_window_drag_interactive_uitest.cc
index 77293b0..454d7432 100644
--- a/chrome/browser/ui/ash/overview_window_drag_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/overview_window_drag_interactive_uitest.cc
@@ -19,7 +19,6 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/test/ui_controls.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/animation/tween.h"
@@ -197,13 +196,8 @@
   generator.Wait();
 
   Browser* active = chrome::FindLastActive();
-  LeftSnapWaiter waiter(
-      features::IsUsingWindowService()
-          ? active->window()->GetNativeWindow()->GetRootWindow()
-          : active->window()->GetNativeWindow());
-
   // Wait for the window to be snapped.
-  waiter.Wait();
+  LeftSnapWaiter(active->window()->GetNativeWindow()).Wait();
 }
 
 INSTANTIATE_TEST_SUITE_P(,
diff --git a/chrome/browser/ui/ash/tab_scrubber_browsertest.cc b/chrome/browser/ui/ash/tab_scrubber_browsertest.cc
index f5c0d64e..b88b99d 100644
--- a/chrome/browser/ui/ash/tab_scrubber_browsertest.cc
+++ b/chrome/browser/ui/ash/tab_scrubber_browsertest.cc
@@ -26,7 +26,6 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/test/test_utils.h"
 #include "ui/aura/window.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 
@@ -287,8 +286,7 @@
       Browser* browser) {
     aura::Window* window = browser->window()->GetNativeWindow();
     aura::Window* root = window->GetRootWindow();
-    return std::make_unique<ui::test::EventGenerator>(
-        features::IsUsingWindowService() ? nullptr : root, window);
+    return std::make_unique<ui::test::EventGenerator>(root, window);
   }
 
 
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
index fff0134..7663453 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
@@ -30,7 +30,6 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
 #include "ash/wm/window_util.h"
-#include "ui/base/ui_base_features.h"
 #endif
 
 #if DCHECK_IS_ON()
@@ -61,14 +60,11 @@
   cache_->SetDelegate(this);
 
 #if defined(OS_CHROMEOS)
-  // TODO(crbug.com/756054): Support MultiProcessMash.
-  if (!features::IsMultiProcessMash()) {
-    aura::Window* active_window = ash::wm::GetActiveWindow();
-    if (active_window) {
-      views::AXAuraObjWrapper* focus = cache_->GetOrCreate(active_window);
-      if (focus)
-        SendEvent(focus, ax::mojom::Event::kChildrenChanged);
-    }
+  aura::Window* active_window = ash::wm::GetActiveWindow();
+  if (active_window) {
+    views::AXAuraObjWrapper* focus = cache_->GetOrCreate(active_window);
+    if (focus)
+      SendEvent(focus, ax::mojom::Event::kChildrenChanged);
   }
 #endif
 }
diff --git a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
index 4b0a321..a83a05c 100644
--- a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
@@ -28,7 +28,6 @@
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/aura/window.h"
-#include "ui/base/ui_base_features.h"
 
 namespace {
 
@@ -72,8 +71,6 @@
                        NavigationBlockedInLockedFullscreen) {
   // Set locked fullscreen state.
   aura::Window* window = browser()->window()->GetNativeWindow();
-  if (features::IsUsingWindowService())
-    window = window->GetRootWindow();
   window->SetProperty(ash::kWindowPinTypeKey,
                       ash::WindowPinType::kTrustedPinned);
 
diff --git a/chrome/browser/ui/settings_window_manager_chromeos.cc b/chrome/browser/ui/settings_window_manager_chromeos.cc
index 6924138..fcfeca8 100644
--- a/chrome/browser/ui/settings_window_manager_chromeos.cc
+++ b/chrome/browser/ui/settings_window_manager_chromeos.cc
@@ -22,7 +22,6 @@
 #include "chromeos/constants/chromeos_features.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/aura/client/aura_constants.h"
-#include "ui/base/ui_base_features.h"
 #include "url/gurl.h"
 
 namespace chrome {
@@ -101,11 +100,8 @@
 
   auto* window = browser->window()->GetNativeWindow();
   window->SetProperty(kOverrideWindowIconResourceIdKey, IDR_SETTINGS_LOGO_192);
-  // For Mash, this is set by BrowserFrameMash.
-  if (!features::IsUsingWindowService()) {
-    window->SetProperty(aura::client::kAppType,
-                        static_cast<int>(ash::AppType::CHROME_APP));
-  }
+  window->SetProperty(aura::client::kAppType,
+                      static_cast<int>(ash::AppType::CHROME_APP));
 
   for (SettingsWindowManagerObserver& observer : observers_)
     observer.OnNewSettingsWindow(browser);
diff --git a/chrome/browser/ui/test/test_browser_dialog.cc b/chrome/browser/ui/test/test_browser_dialog.cc
index 3bff53d3..6a6cb50 100644
--- a/chrome/browser/ui/test/test_browser_dialog.cc
+++ b/chrome/browser/ui/test/test_browser_dialog.cc
@@ -14,7 +14,6 @@
 
 #if defined(OS_CHROMEOS)
 #include "ash/shell.h"  // mash-ok
-#include "ui/base/ui_base_features.h"
 #endif
 
 #if defined(OS_MACOSX)
@@ -146,15 +145,8 @@
 void TestBrowserDialog::UpdateWidgets() {
   widgets_.clear();
 #if defined(OS_CHROMEOS)
-  // Under mash, GetAllWidgets() uses MusClient to get the list of root windows.
-  // Otherwise, GetAllWidgets() relies on AuraTestHelper to get the root window,
-  // but that is not available in browser_tests, so use ash::Shell directly.
-  if (features::IsUsingWindowService()) {
-    widgets_ = views::test::WidgetTest::GetAllWidgets();
-  } else {
-    for (aura::Window* root_window : ash::Shell::GetAllRootWindows())
-      views::Widget::GetAllChildWidgets(root_window, &widgets_);
-  }
+  for (aura::Window* root_window : ash::Shell::GetAllRootWindows())
+    views::Widget::GetAllChildWidgets(root_window, &widgets_);
 #elif defined(TOOLKIT_VIEWS)
   widgets_ = views::test::WidgetTest::GetAllWidgets();
 #else
diff --git a/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc b/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc
index f9ffa17..5e70b80 100644
--- a/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate_chromeos.cc
@@ -9,7 +9,6 @@
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 
@@ -28,10 +27,6 @@
     const ui::Accelerator& accelerator) {
   DCHECK(base::MessageLoopCurrentForUI::IsSet());
 
-  // Early return because mash chrome does not have access to ash::Shell
-  if (features::IsMultiProcessMash())
-    return views::ViewsDelegate::ProcessMenuAcceleratorResult::LEAVE_MENU_OPEN;
-
   if (ash::AcceleratorController::Get()->OnMenuAccelerator(accelerator)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(ProcessAcceleratorNow, accelerator));
diff --git a/chrome/browser/ui/views/content_test_utils.cc b/chrome/browser/ui/views/content_test_utils.cc
index ac5d7b9..ca0c829 100644
--- a/chrome/browser/ui/views/content_test_utils.cc
+++ b/chrome/browser/ui/views/content_test_utils.cc
@@ -8,7 +8,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/public/test/browser_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/test/event_generator.h"
 
 #if defined(USE_AURA)
@@ -42,9 +41,6 @@
 #if defined(USE_AURA)
   event_window = event_window->GetRootWindow();
 #endif
-  if (features::IsUsingWindowService())
-    event_window = nullptr;
-
   ui::test::EventGenerator generator(event_window);
   generator.PressKey(ui::VKEY_A, ui::EF_NONE);
 
diff --git a/chrome/browser/ui/views/media_router/presentation_receiver_window_view.cc b/chrome/browser/ui/views/media_router/presentation_receiver_window_view.cc
index 33aaea7..2a617c6 100644
--- a/chrome/browser/ui/views/media_router/presentation_receiver_window_view.cc
+++ b/chrome/browser/ui/views/media_router/presentation_receiver_window_view.cc
@@ -48,7 +48,6 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/gfx/native_widget_types.h"
 #endif
 
@@ -63,8 +62,6 @@
   FullscreenWindowObserver(aura::Window* observed_window,
                            base::RepeatingClosure on_fullscreen_change)
       : on_fullscreen_change_(on_fullscreen_change) {
-    if (features::IsUsingWindowService())
-      observed_window = observed_window->GetRootWindow();
     observed_window_.Add(observed_window);
   }
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
index c5b06085..7f631ea 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc
@@ -26,7 +26,6 @@
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/omnibox_popup_model.h"
 #include "content/public/test/test_utils.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/accessibility/ax_event_manager.h"
@@ -290,10 +289,6 @@
 #if defined(USE_AURA)
   event_window = event_window->GetRootWindow();
 #endif
-#if defined(OS_CHROMEOS)
-  if (features::IsUsingWindowService())
-    event_window = nullptr;
-#endif
   ui::test::EventGenerator generator(event_window);
 
   OmniboxResultView* result = GetResultViewAt(0);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
index 033b5ed..b528600 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
@@ -34,7 +34,6 @@
 #include "ui/base/ime/text_edit_commands.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/base/test/ui_controls.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/events/event_processor.h"
 #include "ui/events/event_utils.h"
@@ -97,10 +96,6 @@
   void Tap(const gfx::Point& press_location,
            const gfx::Point& release_location) {
     gfx::NativeWindow window = GetRootWindow();
-#if defined(OS_CHROMEOS)
-    if (features::IsUsingWindowService())
-      window = nullptr;
-#endif
     ui::test::EventGenerator generator(window);
     if (press_location == release_location) {
       generator.GestureTapAt(press_location);
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
index 1f3f0b9a..a9b788a 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -34,7 +34,6 @@
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/text_edit_commands.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/gfx/geometry/rect.h"
@@ -698,13 +697,6 @@
   const GURL kFullUrl = GURL("https://www.example.com/");
 
   void SetUp() override {
-#if defined(OS_CHROMEOS)
-    // This is necessary in Mash because the touch handles which get created
-    // during GestureTaps require a MusClient.
-    if (features::IsUsingWindowService())
-      set_native_widget_type(NativeWidgetType::kDesktop);
-#endif
-
     OmniboxViewViewsTest::SetUp();
 
     // Advance 5 seconds from epoch so the time is not considered null.
diff --git a/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc b/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
index 5fecfa9..d351da7c 100644
--- a/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
+++ b/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
@@ -17,7 +17,6 @@
 #if defined(USE_AURA)
 #include "ui/aura/window.h"
 #include "ui/aura/window_targeter.h"
-#include "ui/base/ui_base_features.h"
 #endif
 
 #if defined(OS_WIN)
@@ -234,13 +233,7 @@
   // portion of the Widget to pass through to the omnibox beneath it.
   auto results_targeter = std::make_unique<aura::WindowTargeter>();
   results_targeter->SetInsets(GetInsets() + GetContentInsets());
-  aura::Window* window = GetWidget()->GetNativeWindow();
-  if (features::IsUsingWindowService()) {
-    // The WindowService ends up creating an additional window (by way of
-    // DesktopNativeWidgetAura). The targeter needs to be installed on it.
-    window = window->GetRootWindow();
-  }
-  window->SetEventTargeter(std::move(results_targeter));
+  GetWidget()->GetNativeWindow()->SetEventTargeter(std::move(results_targeter));
 #endif  // USE_AURA
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 69f270b..82a6874 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -51,7 +51,6 @@
 #include "ash/public/cpp/window_properties.h"               // nogncheck
 #include "ash/public/cpp/window_state_type.h"               // nogncheck
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #endif
 
@@ -88,15 +87,8 @@
 #if defined(OS_CHROMEOS)
 
 // Returns the aura::Window which stores the window properties for tab-dragging.
-// It should return the root window when WindowService is used, since it is
-// corresponded with a widget in Ash.
 aura::Window* GetWindowForTabDraggingProperties(const TabDragContext* context) {
-  if (!context)
-    return nullptr;
-  aura::Window* window = context->AsView()->GetWidget()->GetNativeWindow();
-  if (features::IsUsingWindowService())
-    return window->GetRootWindow();
-  return window;
+  return context ? context->AsView()->GetWidget()->GetNativeWindow() : nullptr;
 }
 
 // Returns true if |context| browser window is snapped.
@@ -849,13 +841,6 @@
   // Only Aura windows are gesture consumers.
   gfx::NativeView attached_native_view =
       GetAttachedBrowserWidget()->GetNativeView();
-#if defined(OS_CHROMEOS)
-  // When using WindowService, the touch events for the window move have
-  // happened on the root window, so the transfer should happen from the root of
-  // the currently attached window to the target.
-  if (features::IsUsingWindowService())
-    attached_native_view = attached_native_view->GetRootWindow();
-#endif
   GetAttachedBrowserWidget()->GetGestureRecognizer()->TransferEventsTo(
       attached_native_view,
       target_context->AsView()->GetWidget()->GetNativeView(),
@@ -1383,16 +1368,6 @@
     attached_context_->AsView()->GetWidget()->ReleaseCapture();
     attached_context_->OwnDragController(this);
   }
-#if defined(OS_CHROMEOS)
-  // When the window service is used, there's some chance of having gesture
-  // events in the attached (moving) widget during the window move loop. Without
-  // setting the mouse handler, such gestures might arrive to a wrong view. See
-  // https://crbug.com/943316.
-  if (features::IsUsingWindowService()) {
-    attached_context_->AsView()->GetWidget()->GetRootView()->SetMouseHandler(
-        attached_context_->AsView());
-  }
-#endif
   const views::Widget::MoveLoopSource move_loop_source =
       event_source_ == EVENT_SOURCE_MOUSE ?
       views::Widget::MOVE_LOOP_SOURCE_MOUSE :
@@ -2024,13 +1999,8 @@
   if (exclude_dragged_view) {
     gfx::NativeWindow dragged_window =
         attached_context_->AsView()->GetWidget()->GetNativeWindow();
-    if (dragged_window) {
-#if defined(OS_CHROMEOS)
-      if (features::IsUsingWindowService())
-        dragged_window = dragged_window->GetRootWindow();
-#endif
+    if (dragged_window)
       exclude.insert(dragged_window);
-    }
   }
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   // Exclude windows which are pending deletion via Browser::TabStripEmpty().
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index f62b556..f05ba587 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -83,7 +83,6 @@
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/window_event_dispatcher.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/gesture_detection/gesture_configuration.h"
@@ -116,18 +115,8 @@
 };
 
 #if defined(OS_CHROMEOS)
-// Returns the window which stores window properties for this test. In mash, the
-// properties are stored in the root window of the browser window.
-aura::Window* GetWindowForProperties(aura::Window* window) {
-  if (features::IsUsingWindowService())
-    return window->GetRootWindow();
-  return window;
-}
-
 aura::Window* GetWindowForTabStrip(TabStrip* tab_strip) {
-  return tab_strip
-             ? GetWindowForProperties(tab_strip->GetWidget()->GetNativeWindow())
-             : nullptr;
+  return tab_strip ? tab_strip->GetWidget()->GetNativeWindow() : nullptr;
 }
 #endif
 
@@ -281,11 +270,8 @@
 
 bool GetIsDragged(Browser* browser) {
 #if defined(OS_CHROMEOS)
-  if (!features::IsUsingWindowService()) {
-    return ash::wm::GetWindowState(browser->window()->GetNativeWindow())
-        ->is_dragged();
-  }
-  // TODO(mukai): support for Mash.
+  return ash::wm::GetWindowState(browser->window()->GetNativeWindow())
+      ->is_dragged();
 #endif
   return false;
 }
@@ -830,14 +816,10 @@
 
 #if defined(OS_CHROMEOS)
 bool IsWindowPositionManaged(aura::Window* window) {
-  return test::GetWindowForProperties(window)->GetProperty(
-      ash::kWindowPositionManagedTypeKey);
+  return window->GetProperty(ash::kWindowPositionManagedTypeKey);
 }
 bool HasUserChangedWindowPositionOrSize(aura::Window* window) {
-  if (!features::IsUsingWindowService())
-    return ash::wm::GetWindowState(window)->bounds_changed_by_user();
-  // TODO(mukai): support Mash.
-  return false;
+  return ash::wm::GetWindowState(window)->bounds_changed_by_user();
 }
 #else
 bool IsWindowPositionManaged(gfx::NativeWindow window) {
@@ -2007,8 +1989,8 @@
   EXPECT_EQ(3u, test->browser_list->size());
   // Get this new created window and set it to non-attachable.
   Browser* new_browser = test->browser_list->get(2);
-  test::GetWindowForProperties(new_browser->window()->GetNativeWindow())
-      ->SetProperty(ash::kCanAttachToAnotherWindowKey, false);
+  new_browser->window()->GetNativeWindow()->SetProperty(
+      ash::kCanAttachToAnotherWindowKey, false);
 
   // Now drag to target_tab_strip.
   ASSERT_TRUE(
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index 1cb4180b..e4983b92 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -56,7 +56,6 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/accessibility/accessibility_switches.h"
 #include "ui/aura/window_tree_host.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/display/screen.h"
 #include "ui/events/event_sink.h"
 #include "ui/gfx/geometry/size.h"
@@ -482,18 +481,11 @@
       "enableExperimentalA11yFeatures",
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           ::switches::kEnableExperimentalAccessibilityFeatures));
-  if (!features::IsMultiProcessMash()) {
-    DCHECK(MagnificationManager::Get());
-    a11y_info.SetBoolean("screenMagnifierEnabled",
-                         MagnificationManager::Get()->IsMagnifierEnabled());
-    a11y_info.SetBoolean(
-        "dockedMagnifierEnabled",
-        MagnificationManager::Get()->IsDockedMagnifierEnabled());
-  } else {
-    // TODO: get MagnificationManager working with mash.
-    // https://crbug.com/817157
-    NOTIMPLEMENTED_LOG_ONCE();
-  }
+  DCHECK(MagnificationManager::Get());
+  a11y_info.SetBoolean("screenMagnifierEnabled",
+                       MagnificationManager::Get()->IsMagnifierEnabled());
+  a11y_info.SetBoolean("dockedMagnifierEnabled",
+                       MagnificationManager::Get()->IsDockedMagnifierEnabled());
   a11y_info.SetBoolean("virtualKeyboardEnabled",
                        AccessibilityManager::Get()->IsVirtualKeyboardEnabled());
   CallJS("cr.ui.Oobe.refreshA11yInfo", a11y_info);
@@ -555,13 +547,9 @@
 }
 
 void CoreOobeHandler::UpdateKeyboardState() {
-  // TODO(crbug.com/646565): Support virtual keyboard under MASH. There is no
-  // KeyboardController in the browser process under MASH.
-  if (!features::IsUsingWindowService()) {
-    const bool is_keyboard_shown =
-        ChromeKeyboardControllerClient::Get()->is_keyboard_visible();
-    SetVirtualKeyboardShown(is_keyboard_shown);
-  }
+  const bool is_keyboard_shown =
+      ChromeKeyboardControllerClient::Get()->is_keyboard_visible();
+  SetVirtualKeyboardShown(is_keyboard_shown);
 }
 
 void CoreOobeHandler::OnTabletModeToggled(bool enabled) {
diff --git a/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc b/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
index f5205f4..b3af756 100644
--- a/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
+++ b/chrome/browser/ui/webui/chromeos/system_web_dialog_browsertest.cc
@@ -23,7 +23,6 @@
 #include "content/public/common/web_preferences.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/aura/client/aura_constants.h"
-#include "ui/base/ui_base_features.h"
 #include "url/gurl.h"
 
 namespace chromeos {
@@ -82,9 +81,6 @@
   dialog->ShowSystemDialog();
   EXPECT_FALSE(ash::ShellTestApi().IsSystemModalWindowOpen());
   aura::Window* window_to_test = dialog->dialog_window();
-  // In Mash, the AlwaysOnTop property will be set on the parent.
-  if (::features::IsUsingWindowService())
-    window_to_test = window_to_test->parent();
   EXPECT_TRUE(window_to_test->GetProperty(aura::client::kAlwaysOnTopKey));
 }
 
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 8e5a417..fb274159b 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -508,14 +508,9 @@
                           arc::IsArcAllowedForProfile(profile));
   html_source->AddBoolean("havePlayStoreApp", arc::IsPlayStoreAvailable());
 
-  // TODO(mash): Support Chrome power settings in Mash. https://crbug.com/644348
-  bool enable_power_settings = !::features::IsMultiProcessMash();
-  html_source->AddBoolean("enablePowerSettings", enable_power_settings);
-  if (enable_power_settings) {
-    web_ui->AddMessageHandler(
-        std::make_unique<chromeos::settings::PowerHandler>(
-            profile->GetPrefs()));
-  }
+  html_source->AddBoolean("enablePowerSettings", true);
+  web_ui->AddMessageHandler(
+      std::make_unique<chromeos::settings::PowerHandler>(profile->GetPrefs()));
 
   html_source->AddBoolean(
       "showApps", base::FeatureList::IsEnabled(features::kAppManagement));
diff --git a/chrome/browser/ui/webui/version_handler_chromeos.cc b/chrome/browser/ui/webui/version_handler_chromeos.cc
index 501e27d..0791701 100644
--- a/chrome/browser/ui/webui/version_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/version_handler_chromeos.cc
@@ -9,7 +9,6 @@
 #include "chrome/common/channel_info.h"
 #include "components/version_info/channel.h"
 #include "content/public/browser/web_ui.h"
-#include "ui/base/ui_base_features.h"
 
 VersionHandlerChromeOS::VersionHandlerChromeOS() : weak_factory_(this) {}
 
@@ -40,15 +39,7 @@
 }
 
 void VersionHandlerChromeOS::OnVersion(const std::string& version) {
-  std::string os_version = version;
-  // Put a string in about:version so it's easy to copy/paste into bug reports,
-  // similar to the OS label on the login/lock screen in canary and dev.
-  // String does not need to be localized since it is a feature name.
-  if (chrome::GetChannel() <= version_info::Channel::DEV &&
-      ::features::IsSingleProcessMash()) {
-    os_version += " SingleProcessMash";
-  }
-  base::Value arg(os_version);
+  base::Value arg(version);
   web_ui()->CallJavascriptFunctionUnsafe("returnOsVersion", arg);
 }
 
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index a926a5c..ed23bc4 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -402,6 +402,17 @@
 // messages. Useful when running against a non-prod management server.
 const char kMonitoringDestinationID[]       = "monitoring-destination-id";
 
+// Requests a native messaging connection be established between the native
+// messaging host named by this switch and the extension with ID specified by
+// kNativeMessagingConnectExtension.
+const char kNativeMessagingConnectHost[] = "native-messaging-connect-host";
+
+// Requests a native messaging connection be established between the extension
+// with ID specified by this switch and the native messaging host named by the
+// kNativeMessagingConnectHost switch.
+const char kNativeMessagingConnectExtension[] =
+    "native-messaging-connect-extension";
+
 // Disables the default browser check. Useful for UI/browser tests where we
 // want to avoid having the default browser info-bar displayed.
 const char kNoDefaultBrowserCheck[]         = "no-default-browser-check";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 99b23b7..a4a52df 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -125,6 +125,8 @@
 extern const char kMakeDefaultBrowser[];
 extern const char kMediaCacheSize[];
 extern const char kMonitoringDestinationID[];
+extern const char kNativeMessagingConnectHost[];
+extern const char kNativeMessagingConnectExtension[];
 extern const char kNewNetErrorPageUI[];
 extern const char kNoDefaultBrowserCheck[];
 extern const char kNoExperiments[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index d4ccef0..4477ee4 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -780,7 +780,6 @@
       "../browser/browsing_data/counters/sync_aware_counter_browsertest.cc",
       "../browser/browsing_data/navigation_entry_remover_browsertest.cc",
       "../browser/chrome_content_browser_client_browsertest.cc",
-      "../browser/chrome_content_browser_client_browsertest_chromeos.cc",
       "../browser/chrome_do_not_track_browsertest.cc",
       "../browser/chrome_find_request_manager_browsertest.cc",
       "../browser/chrome_main_browsertest.cc",
@@ -2557,13 +2556,6 @@
     "//build/android/pylib/",
     "//tools/swarming_client/",
   ]
-
-  if (enable_mus) {
-    deps += [ "//chrome:chrome_test" ]
-    data_deps = [
-      "//chrome:chrome_test",
-    ]
-  }
 }
 
 group("telemetry_perf_tests") {
@@ -6120,13 +6112,6 @@
       "//testing/xvfb.py",
       "//testing/scripts/run_telemetry_as_googletest.py",
     ]
-
-    if (enable_mus) {
-      deps += [ "//chrome:chrome_test" ]
-      data_deps = [
-        "//chrome:chrome_test",
-      ]
-    }
   }
 
   group("telemetry_gpu_unittests_run") {
diff --git a/chrome/test/data/native_messaging/native_hosts/echo.py b/chrome/test/data/native_messaging/native_hosts/echo.py
index a611051..5e36ea6 100755
--- a/chrome/test/data/native_messaging/native_hosts/echo.py
+++ b/chrome/test/data/native_messaging/native_hosts/echo.py
@@ -6,6 +6,9 @@
 # A simple native client in python.
 # All this client does is echo the text it receives back at the extension.
 
+import argparse
+import base64
+import json
 import os
 import platform
 import sys
@@ -20,22 +23,24 @@
   except IOError:
     return False
 
+
+def ParseArgs():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--parent-window', type=int)
+  parser.add_argument('--reconnect-command')
+  parser.add_argument('origin')
+  return parser.parse_args()
+
 def Main():
   message_number = 0
 
-  parent_window = None
+  args = ParseArgs()
+  caller_url = args.origin
 
-  if len(sys.argv) < 2:
-    sys.stderr.write("URL of the calling application is not specified.\n")
+  if sys.argv[1] != args.origin:
+    sys.stderr.write(
+        "URL of the calling application is not specified as the first arg.\n")
     return 1
-  caller_url = sys.argv[1]
-
-  # TODO(sergeyu): Use argparse module to parse the arguments (not available in
-  # Python 2.6).
-  for arg in sys.argv[2:]:
-    if arg.startswith('--'):
-      if arg.startswith('--parent-window='):
-        parent_window = long(arg[len('--parent-window='):])
 
   # Verify that the process was started in the correct directory.
   cwd = os.getcwd()
@@ -45,12 +50,15 @@
     return 1
 
   # Verify that --parent-window parameter is correct.
-  if platform.system() == 'Windows' and parent_window:
+  if platform.system() == 'Windows' and args.parent_window:
     import win32gui
-    if not win32gui.IsWindow(parent_window):
+    if not win32gui.IsWindow(args.parent_window):
       sys.stderr.write('Invalid --parent-window.\n')
       return 1
 
+  reconnect_args = json.loads(base64.b64decode(
+      args.reconnect_command)) if args.reconnect_command else None
+
   while 1:
     # Read the message type (first 4 bytes).
     text_length_bytes = sys.stdin.read(4)
@@ -62,13 +70,13 @@
     text_length = struct.unpack('i', text_length_bytes)[0]
 
     # Read the text (JSON object) of the message.
-    text = sys.stdin.read(text_length).decode('utf-8')
+    text = json.loads(sys.stdin.read(text_length).decode('utf-8'))
 
     # bigMessage() test sends a special message that is sent to verify that
     # chrome rejects messages that are too big. Try sending a message bigger
     # than 1MB after receiving a message that contains 'bigMessageTest'.
     if 'bigMessageTest' in text:
-      text = '{"key": "' + ("x" * 1024 * 1024) + '"}'
+      text = {"key": "x" * 1024 * 1024}
 
     # "stopHostTest" verifies that Chrome properly handles the case when the
     # host quits before port is closed. When the test receives response it
@@ -84,8 +92,10 @@
 
     message_number += 1
 
-    message = '{{"id": {0}, "echo": {1}, "caller_url": "{2}"}}'.format(
-          message_number, text, caller_url).encode('utf-8')
+    message = json.dumps({
+        'id': message_number, 'echo': text, 'caller_url': caller_url,
+        'args': reconnect_args
+    }).encode('utf-8')
     if not WriteMessage(message):
       break
 
diff --git a/chrome/test/data/push_messaging/service_worker.js b/chrome/test/data/push_messaging/service_worker.js
index 19eadffa..2afead3 100644
--- a/chrome/test/data/push_messaging/service_worker.js
+++ b/chrome/test/data/push_messaging/service_worker.js
@@ -9,13 +9,16 @@
 // Don't wait for clients of old SW to close before activating.
 self.addEventListener('install', () => skipWaiting());
 
-// The "onpush" event currently understands two values as message payload
-// data coming from the test. Any other input is passed through to the
+// The "onpush" event currently understands the following values as message
+// payload data coming from the test. Any other input is passed through to the
 // document unchanged.
 //
-// "shownotification" - Display a Web Notification with event.waitUntil().
+// "shownotification"
+//     - Display a Web Notification with event.waitUntil().
 // "shownotification-without-waituntil"
 //     - Display a Web Notification without using event.waitUntil().
+// "shownotification-with-showtrigger"
+//     - Display a Web Notification with a showTrigger.
 this.onpush = function(event) {
   if (event.data === null) {
     sendMessageToClients('push', '[NULL]');
@@ -28,12 +31,19 @@
     return;
   }
 
-  var result = registration.showNotification('Push test title', {
+  var notificationOptions = {
     body: 'Push test body',
     tag: 'push_test_tag'
-  });
+  };
 
-  if (data == 'shownotification-without-waituntil') {
+  if (data === 'shownotification-with-showtrigger') {
+    notificationOptions.showTrigger = new TimestampTrigger(Date.now() + 60000);
+  }
+
+  var result =
+      registration.showNotification('Push test title', notificationOptions);
+
+  if (data === 'shownotification-without-waituntil') {
     sendMessageToClients('push', 'immediate:' + data);
     return;
   }
@@ -49,14 +59,15 @@
   let pushSubscriptionOptions = {
       userVisibleOnly: true
   };
-  if (event.data.command == 'workerSubscribe') {
+  if (event.data.command === 'workerSubscribe') {
     pushSubscriptionOptions.applicationServerKey = kApplicationServerKey.buffer;
-  } else if (event.data.command == 'workerSubscribeWithNumericKey') {
+  } else if (event.data.command === 'workerSubscribeWithNumericKey') {
     pushSubscriptionOptions.applicationServerKey =
         new TextEncoder().encode(event.data.key);
-  } else if (event.data.command == 'workerSubscribePushWithBase64URLEncodedString') {
+  } else if (
+      event.data.command === 'workerSubscribePushWithBase64URLEncodedString') {
     pushSubscriptionOptions.applicationServerKey = kBase64URLEncodedKey;
-  } else if (event.data.command == 'workerSubscribeNoKey') {
+  } else if (event.data.command === 'workerSubscribeNoKey') {
     // Nothing to set up
   } else {
     sendMessageToClients('message', 'error - unknown message request');
diff --git a/chromeos/audio/audio_device.cc b/chromeos/audio/audio_device.cc
index 416d7b3e..d17d9aff 100644
--- a/chromeos/audio/audio_device.cc
+++ b/chromeos/audio/audio_device.cc
@@ -190,11 +190,21 @@
     return false;
 
   if (is_input) {
-    return (type != AUDIO_TYPE_INTERNAL_MIC && type != AUDIO_TYPE_FRONT_MIC &&
-            type != AUDIO_TYPE_REAR_MIC);
+    return !IsInternalMic();
   } else {
     return (type != AUDIO_TYPE_INTERNAL_SPEAKER);
   }
 }
 
+bool AudioDevice::IsInternalMic() const {
+  switch (type) {
+    case AUDIO_TYPE_INTERNAL_MIC:
+    case AUDIO_TYPE_FRONT_MIC:
+    case AUDIO_TYPE_REAR_MIC:
+      return true;
+    default:
+      return false;
+  }
+}
+
 }  // namespace chromeos
diff --git a/chromeos/audio/audio_device.h b/chromeos/audio/audio_device.h
index 647ff06..7b12088 100644
--- a/chromeos/audio/audio_device.h
+++ b/chromeos/audio/audio_device.h
@@ -65,6 +65,8 @@
 
   bool IsExternalDevice() const;
 
+  bool IsInternalMic() const;
+
   bool is_input = false;
 
   // Id of this audio device. The legacy |id| is assigned to be unique everytime
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index fd7bb9b4..e22a651 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -474,12 +474,12 @@
 // Smaller, denser shelf in clamshell mode.
 const char kShelfDenseClamshell[] = "shelf-dense-clamshell";
 
+// New modular design for the shelf with apps separated into a hotseat UI.
+const char kShelfHotseat[] = "shelf-hotseat";
+
 // App window previews when hovering over the shelf.
 const char kShelfHoverPreviews[] = "shelf-hover-previews";
 
-// New modular UI design for the shelf.
-const char kShelfNewUi[] = "shelf-new-ui";
-
 // Scrollable list of apps on the shelf.
 const char kShelfScrollable[] = "shelf-scrollable";
 
@@ -615,12 +615,12 @@
       kShelfDenseClamshell);
 }
 
-bool ShouldShowShelfHoverPreviews() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(kShelfHoverPreviews);
+bool ShouldShowShelfHotseat() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(kShelfHotseat);
 }
 
-bool ShouldShowShelfNewUi() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(kShelfNewUi);
+bool ShouldShowShelfHoverPreviews() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(kShelfHoverPreviews);
 }
 
 bool ShouldShowScrollableShelf() {
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index eb496ee..731a3ba 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -178,7 +178,7 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kSamlPasswordChangeUrl[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShelfDenseClamshell[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShelfHoverPreviews[];
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShelfNewUi[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShelfHotseat[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShelfScrollable[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kShowAndroidFilesInFilesApp[];
@@ -267,13 +267,13 @@
 // Returns true if we should show a smaller, denser shelf in clamshell mode.
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldShowShelfDenseClamshell();
 
+// Returns true if we should show the modular shelf with the hotseat UI.
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldShowShelfHotseat();
+
 // Returns true if we should show window previews when hovering over an app
 // on the shelf.
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldShowShelfHoverPreviews();
 
-// Returns true if we should show the new modular shelf UI.
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldShowShelfNewUi();
-
 // Returns true if we should show a scrollable list of apps in the main shelf.
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool ShouldShowScrollableShelf();
 
diff --git a/components/arc/common/file_system.mojom b/components/arc/common/file_system.mojom
index 50d7fd4..8b237704 100644
--- a/components/arc/common/file_system.mojom
+++ b/components/arc/common/file_system.mojom
@@ -164,6 +164,9 @@
   // Filled only when DocumentsContract.EXTRA_INITIAL_URI points to a document
   // served by a DocumentsProvider.
   DocumentPath? initial_document_path;
+
+  // Android task ID of the request sender.
+  int32 task_id;
 };
 
 // Represents a path to a document served by a DocumentsProvider.
@@ -195,6 +198,10 @@
 
   // Specifies the target directory/file for CLICK_DIRECTORY/CLICK_FILE.
   FileSelectorElement click_target;
+
+  // Android task ID of the activity that created the file selector by sending
+  // a SelectFilesRequest.
+  int32 creator_task_id;
 };
 
 // Types of UI events for FileSelectorEvent.
@@ -206,6 +213,13 @@
   CLICK_CANCEL,     // Clicks Cancel button.
 };
 
+// Request for GetFileSelectorElements.
+struct GetFileSelectorElementsRequest {
+  // Android task ID of the activity that created the file selector by sending
+  // a SelectFilesRequest.
+  int32 creator_task_id;
+};
+
 // Represents a clickable UI element shown on ChromeOS file selector.
 struct FileSelectorElement {
   // User-visible label of the element (e.g. button text).
@@ -259,7 +273,8 @@
   // Returns UI elements shown on the ChromeOS file selector previously opened
   // by SelectFiles@5. This exists for running Android UI tests (CTS) on
   // ChromeOS file selector, and thus it is only allowed under test conditions.
-  [MinVersion=11] GetFileSelectorElements@8() =>
+  [MinVersion=11] GetFileSelectorElements@8(
+      GetFileSelectorElementsRequest request) =>
       (FileSelectorElements elements);
 };
 
diff --git a/components/arc/ime/arc_ime_service.cc b/components/arc/ime/arc_ime_service.cc
index bdafd0b0..0f86e8c 100644
--- a/components/arc/ime/arc_ime_service.cc
+++ b/components/arc/ime/arc_ime_service.cc
@@ -20,7 +20,6 @@
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/text_input_flags.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
 #include "ui/events/keycodes/keyboard_codes.h"
@@ -203,10 +202,7 @@
 // Overridden from aura::EnvObserver:
 
 void ArcImeService::OnWindowInitialized(aura::Window* new_window) {
-  // TODO(mash): Support virtual keyboard under MASH. There is no
-  // KeyboardController in the browser process under MASH.
-  if (!features::IsMultiProcessMash() &&
-      keyboard::KeyboardController::HasInstance()) {
+  if (keyboard::KeyboardController::HasInstance()) {
     auto* keyboard_controller = keyboard::KeyboardController::Get();
     if (keyboard_controller->IsEnabled() &&
         !keyboard_controller->HasObserver(this)) {
@@ -358,10 +354,7 @@
   if (!focused_arc_window_)
     return;
 
-  // TODO(mash): Support virtual keyboard under MASH. There is no
-  // KeyboardController in the browser process under MASH.
-  if (!features::IsMultiProcessMash() &&
-      keyboard::KeyboardController::HasInstance()) {
+  if (keyboard::KeyboardController::HasInstance()) {
     auto* keyboard_controller = keyboard::KeyboardController::Get();
     if (keyboard_controller->IsEnabled())
       keyboard_controller->HideKeyboardImplicitlyBySystem();
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 343ac46..4d24004 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -945,7 +945,12 @@
 
 void Controller::GetTouchableArea(std::vector<RectF>* area) const {
   if (touchable_element_area_)
-    touchable_element_area_->GetRectangles(area);
+    touchable_element_area_->GetTouchableRectangles(area);
+}
+
+void Controller::GetRestrictedArea(std::vector<RectF>* area) const {
+  if (touchable_element_area_)
+    touchable_element_area_->GetRestrictedRectangles(area);
 }
 
 void Controller::GetVisualViewport(RectF* visual_viewport) const {
@@ -1126,9 +1131,12 @@
          iter->second == "1";
 }
 
-void Controller::OnTouchableAreaChanged(const RectF& visual_viewport,
-                                        const std::vector<RectF>& areas) {
-  GetUiController()->OnTouchableAreaChanged(visual_viewport, areas);
+void Controller::OnTouchableAreaChanged(
+    const RectF& visual_viewport,
+    const std::vector<RectF>& touchable_areas,
+    const std::vector<RectF>& restricted_areas) {
+  GetUiController()->OnTouchableAreaChanged(visual_viewport, touchable_areas,
+                                            restricted_areas);
 }
 
 void Controller::SetPaymentRequestOptions(
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 2455ae14a..11445752 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -127,6 +127,7 @@
   void SetTermsAndConditions(
       TermsAndConditionsState terms_and_conditions) override;
   void GetTouchableArea(std::vector<RectF>* area) const override;
+  void GetRestrictedArea(std::vector<RectF>* area) const override;
   void GetVisualViewport(RectF* visual_viewport) const override;
   void OnFatalError(const std::string& error_message,
                     Metrics::DropOutReason reason) override;
@@ -208,7 +209,8 @@
       content::RenderWidgetHost* render_widget_host) override;
 
   void OnTouchableAreaChanged(const RectF& visual_viewport,
-                              const std::vector<RectF>& areas);
+                              const std::vector<RectF>& touchable_areas,
+                              const std::vector<RectF>& restricted_areas);
 
   void SelectChip(std::vector<Chip>* chips, int chip_index);
   void SetOverlayColors(std::unique_ptr<OverlayColors> colors);
diff --git a/components/autofill_assistant/browser/element_area.cc b/components/autofill_assistant/browser/element_area.cc
index 8a31f6b..14f7819 100644
--- a/components/autofill_assistant/browser/element_area.cc
+++ b/components/autofill_assistant/browser/element_area.cc
@@ -29,19 +29,8 @@
 
 void ElementArea::SetFromProto(const ElementAreaProto& proto) {
   rectangles_.clear();
-  for (const auto& rectangle_proto : proto.rectangles()) {
-    rectangles_.emplace_back();
-    Rectangle& rectangle = rectangles_.back();
-    rectangle.full_width = rectangle_proto.full_width();
-    DVLOG(3) << "Touchable Rectangle"
-             << (rectangle.full_width ? " (full_width)" : "") << ":";
-    for (const auto& element_proto : rectangle_proto.elements()) {
-      rectangle.positions.emplace_back();
-      ElementPosition& position = rectangle.positions.back();
-      position.selector = Selector(element_proto).MustBeVisible();
-      DVLOG(3) << "  " << position.selector;
-    }
-  }
+  AddRectangles(proto.touchable(), /* restricted= */ false);
+  AddRectangles(proto.restricted(), /* restricted= */ true);
 
   if (rectangles_.empty()) {
     timer_.Stop();
@@ -60,6 +49,27 @@
   }
 }
 
+void ElementArea::AddRectangles(
+    const ::google::protobuf::RepeatedPtrField<ElementAreaProto::Rectangle>&
+        rectangles_proto,
+    bool restricted) {
+  for (const auto& rectangle_proto : rectangles_proto) {
+    rectangles_.emplace_back();
+    Rectangle& rectangle = rectangles_.back();
+    rectangle.full_width = rectangle_proto.full_width();
+    rectangle.restricted = restricted;
+    DVLOG(3) << "Rectangle (full_width="
+             << (rectangle.full_width ? "true" : "false")
+             << ", restricted=" << (restricted ? "true" : "false") << "):";
+    for (const auto& element_proto : rectangle_proto.elements()) {
+      rectangle.positions.emplace_back();
+      ElementPosition& position = rectangle.positions.back();
+      position.selector = Selector(element_proto).MustBeVisible();
+      DVLOG(3) << "  " << position.selector;
+    }
+  }
+}
+
 void ElementArea::Update() {
   if (rectangles_.empty())
     return;
@@ -102,10 +112,21 @@
   }
 }
 
-void ElementArea::GetRectangles(std::vector<RectF>* area) {
+void ElementArea::GetTouchableRectangles(std::vector<RectF>* area) {
   for (auto& rectangle : rectangles_) {
-    area->emplace_back();
-    rectangle.FillRect(&area->back(), visual_viewport_);
+    if (!rectangle.restricted) {
+      area->emplace_back();
+      rectangle.FillRect(&area->back(), visual_viewport_);
+    }
+  }
+}
+
+void ElementArea::GetRestrictedRectangles(std::vector<RectF>* area) {
+  for (auto& rectangle : rectangles_) {
+    if (rectangle.restricted) {
+      area->emplace_back();
+      rectangle.FillRect(&area->back(), visual_viewport_);
+    }
   }
 }
 
@@ -192,7 +213,7 @@
   if (rectangles_.empty()) {
     // Reporting of visual viewport is best effort when reporting empty
     // rectangles. It might also be empty.
-    on_update_.Run(visual_viewport_, {});
+    on_update_.Run(visual_viewport_, {}, {});
     return;
   }
 
@@ -209,10 +230,12 @@
     }
   }
 
-  std::vector<RectF> area;
-  GetRectangles(&area);
+  std::vector<RectF> touchable_area;
+  std::vector<RectF> restricted_area;
+  GetTouchableRectangles(&touchable_area);
+  GetRestrictedRectangles(&restricted_area);
 
-  on_update_.Run(visual_viewport_, area);
+  on_update_.Run(visual_viewport_, touchable_area, restricted_area);
 }
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/element_area.h b/components/autofill_assistant/browser/element_area.h
index cf13880..fe9e2f20 100644
--- a/components/autofill_assistant/browser/element_area.h
+++ b/components/autofill_assistant/browser/element_area.h
@@ -53,7 +53,9 @@
   // elements, which might be empty.
   void SetOnUpdate(
       base::RepeatingCallback<void(const RectF& visual_viewport,
-                                   const std::vector<RectF>& rectangles)> cb) {
+                                   const std::vector<RectF>& touchable_area,
+                                   const std::vector<RectF>& restricted_area)>
+          cb) {
     on_update_ = cb;
   }
 
@@ -64,7 +66,8 @@
   // not be empty.
   //
   // Note that the vector is not cleared before rectangles are added.
-  void GetRectangles(std::vector<RectF>* area);
+  void GetTouchableRectangles(std::vector<RectF>* area);
+  void GetRestrictedRectangles(std::vector<RectF>* area);
 
   // Gets the coordinates of the visual viewport, in CSS pixels relative to the
   // layout viewport. Empty if the size of the visual viewport is not known.
@@ -96,6 +99,7 @@
   struct Rectangle {
     std::vector<ElementPosition> positions;
     bool full_width = false;
+    bool restricted = false;
 
     Rectangle();
     Rectangle(const Rectangle& orig);
@@ -108,6 +112,9 @@
     void FillRect(RectF* rect, const RectF& visual_viewport) const;
   };
 
+  void AddRectangles(const ::google::protobuf::RepeatedPtrField<
+                         ElementAreaProto::Rectangle>& rectangles_proto,
+                     bool restricted);
   void OnGetElementPosition(const Selector& selector,
                             bool found,
                             const RectF& rect);
@@ -128,7 +135,8 @@
   base::RepeatingTimer timer_;
 
   base::RepeatingCallback<void(const RectF& visual_viewport,
-                               const std::vector<RectF>& rectangles)>
+                               const std::vector<RectF>& touchable_area,
+                               const std::vector<RectF>& restricted_area)>
       on_update_;
 
   base::WeakPtrFactory<ElementArea> weak_ptr_factory_;
diff --git a/components/autofill_assistant/browser/element_area_unittest.cc b/components/autofill_assistant/browser/element_area_unittest.cc
index d6686ac..1a8fc9b 100644
--- a/components/autofill_assistant/browser/element_area_unittest.cc
+++ b/components/autofill_assistant/browser/element_area_unittest.cc
@@ -85,15 +85,23 @@
   }
 
   void SetElement(const std::string& selector) {
+    SetElement(selector, /* restricted= */ false);
+  }
+
+  void SetElement(const std::string& selector, bool restricted) {
     ElementAreaProto area;
-    area.add_rectangles()->add_elements()->add_selectors(selector);
+    auto* rectangle = restricted ? area.add_restricted() : area.add_touchable();
+    rectangle->add_elements()->add_selectors(selector);
     element_area_.SetFromProto(area);
   }
 
-  void OnUpdate(const RectF& visual_viewport, const std::vector<RectF>& area) {
+  void OnUpdate(const RectF& visual_viewport,
+                const std::vector<RectF>& touchable_area,
+                const std::vector<RectF>& restricted_area) {
     on_update_call_count_++;
     reported_visual_viewport_ = visual_viewport;
-    reported_area_ = area;
+    reported_area_ = touchable_area;
+    reported_restricted_area_ = restricted_area;
   }
 
   // scoped_task_environment_ must be first to guarantee other field
@@ -106,13 +114,14 @@
   int on_update_call_count_ = 0;
   RectF reported_visual_viewport_;
   std::vector<RectF> reported_area_;
+  std::vector<RectF> reported_restricted_area_;
 };
 
 TEST_F(ElementAreaTest, Empty) {
   EXPECT_THAT(reported_area_, IsEmpty());
 
   std::vector<RectF> rectangles;
-  element_area_.GetRectangles(&rectangles);
+  element_area_.GetTouchableRectangles(&rectangles);
   EXPECT_THAT(rectangles, IsEmpty());
 
   RectF viewport;
@@ -125,7 +134,7 @@
   EXPECT_THAT(reported_area_, ElementsAre(EmptyRectF()));
 
   std::vector<RectF> rectangles;
-  element_area_.GetRectangles(&rectangles);
+  element_area_.GetTouchableRectangles(&rectangles);
   EXPECT_THAT(rectangles, ElementsAre(EmptyRectF()));
 }
 
@@ -143,7 +152,7 @@
 
   SetElement("#found");
   std::vector<RectF> rectangles;
-  element_area_.GetRectangles(&rectangles);
+  element_area_.GetTouchableRectangles(&rectangles);
   EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(25, 25, 75, 75)));
 }
 
@@ -197,12 +206,12 @@
       .WillOnce(RunOnceCallback<1>(true, RectF(25, 25, 100, 100)));
 
   ElementAreaProto area_proto;
-  area_proto.add_rectangles()->add_elements()->add_selectors("#top_left");
-  area_proto.add_rectangles()->add_elements()->add_selectors("#bottom_right");
+  area_proto.add_touchable()->add_elements()->add_selectors("#top_left");
+  area_proto.add_touchable()->add_elements()->add_selectors("#bottom_right");
   element_area_.SetFromProto(area_proto);
 
   std::vector<RectF> rectangles;
-  element_area_.GetRectangles(&rectangles);
+  element_area_.GetTouchableRectangles(&rectangles);
   EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0, 0, 25, 25),
                                       MatchingRectF(25, 25, 100, 100)));
 }
@@ -218,13 +227,13 @@
       .WillOnce(RunOnceCallback<1>(true, RectF(5, 2, 6, 5)));
 
   ElementAreaProto area_proto;
-  auto* rectangle_proto = area_proto.add_rectangles();
+  auto* rectangle_proto = area_proto.add_touchable();
   rectangle_proto->add_elements()->add_selectors("#element1");
   rectangle_proto->add_elements()->add_selectors("#element2");
   element_area_.SetFromProto(area_proto);
 
   std::vector<RectF> rectangles;
-  element_area_.GetRectangles(&rectangles);
+  element_area_.GetTouchableRectangles(&rectangles);
   EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(1, 2, 6, 5)));
 }
 
@@ -242,7 +251,7 @@
       .WillOnce(DoNothing());  // overrides default action
 
   ElementAreaProto area_proto;
-  auto* rectangle_proto = area_proto.add_rectangles();
+  auto* rectangle_proto = area_proto.add_touchable();
   rectangle_proto->add_elements()->add_selectors("#element1");
   rectangle_proto->add_elements()->add_selectors("#element2");
   element_area_.SetFromProto(area_proto);
@@ -250,7 +259,7 @@
   EXPECT_THAT(reported_area_, IsEmpty());
 
   std::vector<RectF> rectangles;
-  element_area_.GetRectangles(&rectangles);
+  element_area_.GetTouchableRectangles(&rectangles);
   EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(1, 3, 2, 4)));
 }
 
@@ -273,7 +282,7 @@
       .WillOnce(RunOnceCallback<1>(true, RectF(9, 0, 100, 1)));
 
   ElementAreaProto area_proto;
-  auto* rectangle_proto = area_proto.add_rectangles();
+  auto* rectangle_proto = area_proto.add_touchable();
   rectangle_proto->add_elements()->add_selectors("#element1");
   rectangle_proto->add_elements()->add_selectors("#element2");
   rectangle_proto->add_elements()->add_selectors("#element3");
@@ -281,7 +290,7 @@
   element_area_.SetFromProto(area_proto);
 
   std::vector<RectF> rectangles;
-  element_area_.GetRectangles(&rectangles);
+  element_area_.GetTouchableRectangles(&rectangles);
   EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0, 0, 100, 100)));
 }
 
@@ -296,13 +305,13 @@
       .WillOnce(RunOnceCallback<1>(false, RectF()));
 
   ElementAreaProto area_proto;
-  auto* rectangle_proto = area_proto.add_rectangles();
+  auto* rectangle_proto = area_proto.add_touchable();
   rectangle_proto->add_elements()->add_selectors("#element1");
   rectangle_proto->add_elements()->add_selectors("#element2");
   element_area_.SetFromProto(area_proto);
 
   std::vector<RectF> rectangles;
-  element_area_.GetRectangles(&rectangles);
+  element_area_.GetTouchableRectangles(&rectangles);
   EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(1, 1, 2, 2)));
 
   EXPECT_THAT(reported_area_, ElementsAre(MatchingRectF(1, 1, 2, 2)));
@@ -321,14 +330,14 @@
       .WillRepeatedly(RunOnceCallback<0>(true, RectF(100, 0, 200, 400)));
 
   ElementAreaProto area_proto;
-  auto* rectangle_proto = area_proto.add_rectangles();
+  auto* rectangle_proto = area_proto.add_touchable();
   rectangle_proto->add_elements()->add_selectors("#element1");
   rectangle_proto->add_elements()->add_selectors("#element2");
   rectangle_proto->set_full_width(true);
   element_area_.SetFromProto(area_proto);
 
   std::vector<RectF> rectangles;
-  element_area_.GetRectangles(&rectangles);
+  element_area_.GetTouchableRectangles(&rectangles);
 
   // left and right of the box come from the visual viewport, top from the 1st
   // element, bottom from the 2nd.
@@ -346,7 +355,7 @@
   SetElement("#element");
 
   std::vector<RectF> original;
-  element_area_.GetRectangles(&original);
+  element_area_.GetTouchableRectangles(&original);
   EXPECT_THAT(original, ElementsAre(MatchingRectF(0, 25, 100, 50)));
   EXPECT_THAT(reported_area_, ElementsAre(MatchingRectF(0, 25, 100, 50)));
 
@@ -354,7 +363,7 @@
 
   // Updated area is available
   std::vector<RectF> updated;
-  element_area_.GetRectangles(&updated);
+  element_area_.GetTouchableRectangles(&updated);
   EXPECT_THAT(updated, ElementsAre(MatchingRectF(0, 50, 100, 75)));
 
   // Updated area is reported
@@ -378,11 +387,34 @@
 
   // Updated area is available
   std::vector<RectF> rectangles;
-  element_area_.GetRectangles(&rectangles);
+  element_area_.GetTouchableRectangles(&rectangles);
   EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0, 50, 100, 75)));
 
   // Updated area is reported
   EXPECT_THAT(reported_area_, ElementsAre(MatchingRectF(0, 50, 100, 75)));
 }
+
+TEST_F(ElementAreaTest, RestrictedElement) {
+  EXPECT_CALL(mock_web_controller_,
+              OnGetElementPosition(
+                  Eq(Selector({"#restricted_element"}).MustBeVisible()), _))
+      .WillOnce(RunOnceCallback<1>(true, RectF(25, 25, 75, 75)));
+
+  SetElement("#restricted_element", /* restricted= */ true);
+
+  EXPECT_EQ(on_update_call_count_, 1);
+  EXPECT_THAT(reported_area_, IsEmpty());
+  EXPECT_THAT(reported_restricted_area_,
+              ElementsAre(MatchingRectF(25, 25, 75, 75)));
+
+  std::vector<RectF> touchable_rectangles;
+  std::vector<RectF> restricted_rectangles;
+  element_area_.GetTouchableRectangles(&touchable_rectangles);
+  element_area_.GetRestrictedRectangles(&restricted_rectangles);
+
+  EXPECT_THAT(touchable_rectangles, IsEmpty());
+  EXPECT_THAT(restricted_rectangles,
+              ElementsAre(MatchingRectF(25, 25, 75, 75)));
+}
 }  // namespace
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/metrics.h b/components/autofill_assistant/browser/metrics.h
index f10c736..fa5608a 100644
--- a/components/autofill_assistant/browser/metrics.h
+++ b/components/autofill_assistant/browser/metrics.h
@@ -43,7 +43,7 @@
     GET_SCRIPTS_FAILED = 17,
     GET_SCRIPTS_UNPARSABLE = 18,
     NO_INITIAL_SCRIPTS = 19,
-    DFM_CANCELLED = 19,
+    DFM_INSTALL_FAILED = 20,
 
     NUM_ENTRIES = 21,
   };
@@ -145,6 +145,12 @@
 
       case NO_INITIAL_SCRIPTS:
         out << "NO_INITIAL_SCRIPTS";
+        break;
+
+      case DFM_INSTALL_FAILED:
+        out << "DFM_INSTALL_FAILED";
+        break;
+
         // Intentionally no default case to make compilation fail if a new value
         // was added to the enum but not to this list.
     }
diff --git a/components/autofill_assistant/browser/mock_ui_controller.h b/components/autofill_assistant/browser/mock_ui_controller.h
index 71f9762..b312c3c 100644
--- a/components/autofill_assistant/browser/mock_ui_controller.h
+++ b/components/autofill_assistant/browser/mock_ui_controller.h
@@ -34,8 +34,10 @@
   MOCK_METHOD1(OnInfoBoxChanged, void(const InfoBox* info_box));
   MOCK_METHOD1(OnProgressChanged, void(int progress));
   MOCK_METHOD1(OnProgressVisibilityChanged, void(bool visible));
-  MOCK_METHOD2(OnTouchableAreaChanged,
-               void(const RectF&, const std::vector<RectF>& areas));
+  MOCK_METHOD3(OnTouchableAreaChanged,
+               void(const RectF&,
+                    const std::vector<RectF>& touchable_areas,
+                    const std::vector<RectF>& restricted_areas));
   MOCK_CONST_METHOD0(Terminate, bool());
   MOCK_CONST_METHOD0(GetDropOutReason, Metrics::DropOutReason());
   MOCK_METHOD1(OnResizeViewportChanged, void(bool resize_viewport));
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index eeb5422..c24aa88 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -753,7 +753,13 @@
     // the screen.
     optional bool full_width = 2;
   }
-  repeated Rectangle rectangles = 1;
+
+  // The rectangles that will be highlighted and touchable.
+  repeated Rectangle touchable = 1;
+
+  // The rectangles that should be neither highlighted nor touchable. Those
+  // rectangles have precedence over the |touchable| rectangles.
+  repeated Rectangle restricted = 2;
 }
 
 // Fill a form with an address if there is, otherwise fail this action.
diff --git a/components/autofill_assistant/browser/ui_controller.cc b/components/autofill_assistant/browser/ui_controller.cc
index a2819e78..3c77a5ca 100644
--- a/components/autofill_assistant/browser/ui_controller.cc
+++ b/components/autofill_assistant/browser/ui_controller.cc
@@ -25,8 +25,10 @@
 void UiController::OnInfoBoxChanged(const InfoBox* info_box) {}
 void UiController::OnProgressChanged(int progress) {}
 void UiController::OnProgressVisibilityChanged(bool visible) {}
-void UiController::OnTouchableAreaChanged(const RectF& visual_viewport,
-                                          const std::vector<RectF>& areas) {}
+void UiController::OnTouchableAreaChanged(
+    const RectF& visual_viewport,
+    const std::vector<RectF>& touchable_areas,
+    const std::vector<RectF>& restricted_areas) {}
 void UiController::OnResizeViewportChanged(bool resize_viewport) {}
 void UiController::OnPeekModeChanged(
     ConfigureBottomSheetProto::PeekMode peek_mode) {}
diff --git a/components/autofill_assistant/browser/ui_controller.h b/components/autofill_assistant/browser/ui_controller.h
index adb0342..808ec13 100644
--- a/components/autofill_assistant/browser/ui_controller.h
+++ b/components/autofill_assistant/browser/ui_controller.h
@@ -80,12 +80,18 @@
   // the layout viewport. It might be empty if not known or the touchable area
   // is empty.
   //
-  // |rectangles| contains one element per configured rectangles, though these
-  // can correspond to empty rectangles.
+  // |touchable_areas| contains one element per configured rectangle that should
+  // be visible/touchable, though these can correspond to empty rectangles.
+  //
+  // |restricted_areas| contains one element per configured rectangle that
+  // shouldn't be visible nor touchable. Those rectangles have precedence over
+  // |touchable_areas|.
   //
   // All rectangles are expressed in absolute CSS coordinates.
-  virtual void OnTouchableAreaChanged(const RectF& visual_viewport,
-                                      const std::vector<RectF>& rectangles);
+  virtual void OnTouchableAreaChanged(
+      const RectF& visual_viewport,
+      const std::vector<RectF>& touchable_areas,
+      const std::vector<RectF>& restricted_areas);
 
   // Called when the viewport resize flag has changed.
   virtual void OnResizeViewportChanged(bool resize_viewport);
diff --git a/components/autofill_assistant/browser/ui_delegate.h b/components/autofill_assistant/browser/ui_delegate.h
index 288dd0fc..1931600 100644
--- a/components/autofill_assistant/browser/ui_delegate.h
+++ b/components/autofill_assistant/browser/ui_delegate.h
@@ -113,6 +113,7 @@
   //
   // Note that the vector is not cleared before rectangles are added.
   virtual void GetTouchableArea(std::vector<RectF>* rectangles) const = 0;
+  virtual void GetRestrictedArea(std::vector<RectF>* rectangles) const = 0;
 
   // Returns the current size of the visual viewport. May be empty if unknown.
   //
diff --git a/components/download/internal/common/simple_download_manager_coordinator.cc b/components/download/internal/common/simple_download_manager_coordinator.cc
index bf1a4f4..4bfd24b 100644
--- a/components/download/internal/common/simple_download_manager_coordinator.cc
+++ b/components/download/internal/common/simple_download_manager_coordinator.cc
@@ -106,4 +106,8 @@
   return notifier_.get();
 }
 
+void SimpleDownloadManagerCoordinator::CheckForExternallyRemovedDownloads() {
+  simple_download_manager_->CheckForHistoryFilesRemoval();
+}
+
 }  // namespace download
diff --git a/components/download/public/common/simple_download_manager.h b/components/download/public/common/simple_download_manager.h
index 8a3bd82..ef9f5907 100644
--- a/components/download/public/common/simple_download_manager.h
+++ b/components/download/public/common/simple_download_manager.h
@@ -56,6 +56,11 @@
   // Get the download item for |guid|.
   virtual DownloadItem* GetDownloadByGuid(const std::string& guid) = 0;
 
+  // Checks whether downloaded files still exist. Updates state of downloads
+  // that refer to removed files. The check runs in the background and may
+  // finish asynchronously after this method returns.
+  virtual void CheckForHistoryFilesRemoval() {}
+
  protected:
   // Called when the manager is initailized.
   void OnInitialized();
diff --git a/components/download/public/common/simple_download_manager_coordinator.h b/components/download/public/common/simple_download_manager_coordinator.h
index c610486e..6c82535 100644
--- a/components/download/public/common/simple_download_manager_coordinator.h
+++ b/components/download/public/common/simple_download_manager_coordinator.h
@@ -75,6 +75,11 @@
 
   bool has_all_history_downloads() const { return has_all_history_downloads_; }
 
+  // Checks whether downloaded files still exist. Updates state of downloads
+  // that refer to removed files. The check runs in the background and may
+  // finish asynchronously after this method returns.
+  void CheckForExternallyRemovedDownloads();
+
  private:
   // SimpleDownloadManager::Observer implementation.
   void OnDownloadsInitialized() override;
diff --git a/components/exo/wayland/fuzzer/BUILD.gn b/components/exo/wayland/fuzzer/BUILD.gn
index 0f0a6433..262c116 100644
--- a/components/exo/wayland/fuzzer/BUILD.gn
+++ b/components/exo/wayland/fuzzer/BUILD.gn
@@ -49,6 +49,16 @@
   protocols = kDefaultWaylandProtocols
 }
 
+# We make the seed corpus by enumerating call sequences to all requests
+# using the wayland_sequencer script.
+wayland_templater("corpus") {
+  sources = [
+    "corpus.tmpl",
+  ]
+  protocols = kDefaultWaylandProtocols
+  script_override = "wayland_sequencer.py"
+}
+
 if (use_libfuzzer) {
   fuzzer_test("wayland_fuzzer") {
     sources = [
@@ -64,6 +74,10 @@
     ]
 
     libfuzzer_options = [ "len_control=0" ]
+
+    corpus_target_outputs = get_target_outputs(":corpus")
+    seed_corpus = corpus_target_outputs[0]
+    seed_corpus_deps = [ ":corpus" ]
   }
 }
 
diff --git a/components/exo/wayland/fuzzer/corpus.tmpl b/components/exo/wayland/fuzzer/corpus.tmpl
new file mode 100644
index 0000000..82e207b
--- /dev/null
+++ b/components/exo/wayland/fuzzer/corpus.tmpl
@@ -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.
+
+{% for interface, method, args in sequence %}
+  acts{
+    {% if interface %}
+      act_{{ interface }}_{{ method }} {
+        {% for name, literal in args %}
+          {{ name }} : {{ literal }}
+        {% endfor %}
+      }
+    {% endif %}
+  }
+{% endfor %}
diff --git a/components/exo/wayland/fuzzer/harness.cc.tmpl b/components/exo/wayland/fuzzer/harness.cc.tmpl
index e1151aec..fa394b85 100644
--- a/components/exo/wayland/fuzzer/harness.cc.tmpl
+++ b/components/exo/wayland/fuzzer/harness.cc.tmpl
@@ -71,13 +71,22 @@
       ::{{interface.name}}* receiver = harness->get_{{interface.name}}(action.receiver());
       if (!receiver)
         return;
+      {% for arg in request.args %}
+        {% if arg.type == 'object' %}
+          {{arg.cpp_type}} {{arg.name}} = harness->get_{{arg.interface}}(action.{{arg.name}}());
+          {% if not arg.nullable %}
+            if (!{{arg.name}})
+	      return;
+          {% endif %}
+        {% endif %}
+      {% endfor %}
       {% if request.is_constructor %}
         ::{{request.constructed}}* new_object =
       {% endif %}
       {{interface.name}}_{{request.name}}(receiver
         {% for arg in request.args %}
           {% if arg.type == 'object' %}
-            , harness->get_{{arg.interface}}(action.{{arg.name}}())
+            , {{arg.name}}
           {% elif arg.type != 'new_id' %}
             , action.{{arg.name}}(){% if arg.type == 'string' %}.c_str(){% endif %}
           {% endif %}
diff --git a/components/exo/wayland/fuzzer/wayland_sequencer.py b/components/exo/wayland/fuzzer/wayland_sequencer.py
new file mode 100644
index 0000000..47cde1da
--- /dev/null
+++ b/components/exo/wayland/fuzzer/wayland_sequencer.py
@@ -0,0 +1,177 @@
+# Copyright (c) 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Generate sequences of valid wayland instructions.
+
+Determines sequences of calls necessary to instantiate and invoke a given
+message of one of the wayland interfaces. The procedure is as follows:
+  - Get a dependency graph between messages, and the interfaces you need in
+    order to invoke them.
+  - The list of messages you need to invoke is the sum of transitive
+    dependencies for the target message.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+
+import os
+import sys
+
+import wayland_templater
+import wayland_utils as wlu
+
+# To prevent the fuzzer from exploring too-much space, we limit it to binding at
+# most a small number of each interface. So the index has to be in this map.
+small_value = {0: 'ZERO', 1: 'ONE', 2: 'TWO', 3: 'THREE'}
+
+# The default (empty) message is a wl_display_roundtrip.
+round_trip = ('', '', [])
+
+
+def ToNode(interface, message):
+  return (interface.attrib['name'], message.attrib['name'])
+
+
+def GetDependencyGraph(protocols):
+  """Determine the dependencies between interfaces and their messages.
+
+  Args:
+    protocols: the list of wayland xml protocols you want the dependencies of.
+
+  Returns:
+    A bipartite graph where messages (i, m) depend on interfaces (i) and
+    vice-versa. An edge from (i,m) to (i') indicates an i' instance is needed to
+    invoke m, whereas (i) to (i',m') indicates m' is a constructor for i
+    instances.
+  """
+  dep_graph = {}
+  constructed_interfaces = set()
+  for _, i, m in wlu.AllMessages(protocols):
+    dep_graph[ToNode(i, m)] = [('receiver', i.attrib['name'])] + [
+        (a.attrib['name'], a.attrib.get('interface', '?'))
+        for a in m.findall('arg')
+        if a.attrib['type'] == 'object'
+    ]
+    constructed = wlu.GetConstructedInterface(m)
+    if constructed:
+      constructed_interfaces.add(constructed)
+      dep_graph[constructed] = ToNode(i, m)
+  for _, i in wlu.AllInterfaces(protocols):
+    if i.attrib['name'] not in constructed_interfaces:
+      dep_graph[i.attrib['name']] = ('wl_registry', 'bind')
+  return dep_graph
+
+
+class SequenceContext(object):
+  """Store for data used when building one sequence.
+
+  This class is used to store the data which will help build the sequences but
+  which does not actually appear in the sequences.  You should make a new
+  SequenceContext for every sequence you want to generate.
+  """
+
+  def __init__(self, dep_graph):
+    self.counts = {}
+    self.prevented = set()
+    self.dep_graph = dep_graph
+    # To simulate what the harness itself does, we make a singleton wl_display.
+    self.RecordInterfaceCreated('wl_display')
+    self.Prevent('wl_display')
+
+  def IsPrevented(self, interface_name):
+    return interface_name in self.prevented
+
+  def Prevent(self, interface_name):
+    self.prevented.add(interface_name)
+
+  def RecordInterfaceCreated(self, interface_name):
+    self.counts[interface_name] = self.counts.get(interface_name, -1) + 1
+
+  def GetLastInterfaceCreated(self, interface_name):
+    """Return the small_value index for the currently available interface.
+
+    Args:
+      interface_name: the name of the interface you want an index of.
+
+    Returns:
+      A small_value index for the topmost-version of the interface.
+    """
+    return small_value[self.counts[interface_name]]
+
+
+def GetSequenceForInterface(i_name, context):
+  if context.IsPrevented(i_name):
+    return []
+  if i_name == 'wl_registry' or context.dep_graph[i_name] == ('wl_registry',
+                                                              'bind'):
+    context.Prevent(i_name)
+  (cons_i, cons_m) = context.dep_graph[i_name]
+  seq = GetSequenceForMessage(cons_i, cons_m, context, i_name)
+  context.RecordInterfaceCreated(i_name)
+  return seq
+
+
+def GetSequenceForMessage(i_name, m_name, context, target_i):
+  """Return the message sequence up to and including the supplied message.
+
+  Args:
+    i_name: the name of the interface that defines the message you want to
+      send.
+    m_name: the name of the message you want to send.
+    context: the global context state,
+    target_i: the interface you expect to be created by this message (if there
+      is one, otherwise use '').
+
+  Returns:
+    A list of (i, m, [args...]) tuples, each having an interface name, message
+    name, and arguments for invoking that message. The ('', '', []) tuple
+    indicates the default message (which sends wl_display_roundtrip()).
+  """
+  seq = []
+  args = []
+  # get the message sequence needed to create each argument for the target
+  # message.
+  for arg_n, arg_t in context.dep_graph[(i_name, m_name)]:
+    seq += GetSequenceForInterface(arg_t, context)
+    args.append((arg_n, context.GetLastInterfaceCreated(arg_t)))
+  if i_name == 'wl_registry' and m_name == 'bind' and target_i:
+    args.append(('global', target_i))
+  seq.append((i_name, m_name, args))
+  # We need to do a round-trip after binding the registry so that we have the
+  # globals available for binding.
+  if i_name == 'wl_display' and m_name == 'get_registry' and target_i:
+    seq.append(round_trip)
+  return seq
+
+
+def Main(argv):
+  """Instantiate the group of message-sequences used to seed the fuzzer.
+
+  Args:
+    argv: command-line arguments used to run the sequencer.
+  """
+  parsed = wlu.ParseOpts(argv)
+  out_dir = parsed.output
+  if not os.path.exists(out_dir):
+    os.mkdir(out_dir)
+
+  protocols = wlu.ReadProtocols(parsed.spec)
+  dep_graph = GetDependencyGraph(protocols)
+  for _, interface in wlu.AllInterfaces(protocols):
+    for req in interface.findall('request'):
+      interface_name = interface.attrib['name']
+      message_name = req.attrib['name']
+      sequence = GetSequenceForMessage(interface_name, message_name,
+                                       SequenceContext(dep_graph), '')
+      # Add a round-trip to the sequence in case the server wants to do
+      # something funky.
+      sequence += [round_trip]
+
+      out_path = os.path.join(out_dir,
+                              '%s_%s.asciipb' % (interface_name, message_name))
+      wayland_templater.InstantiateTemplate(
+          parsed.input, {'sequence': sequence}, out_path, parsed.directory)
+
+
+if __name__ == '__main__':
+  Main(sys.argv)
diff --git a/components/exo/wayland/fuzzer/wayland_templater.gni b/components/exo/wayland/fuzzer/wayland_templater.gni
index 0c59cc0..9a4c9de 100644
--- a/components/exo/wayland/fuzzer/wayland_templater.gni
+++ b/components/exo/wayland/fuzzer/wayland_templater.gni
@@ -21,6 +21,9 @@
 #     ":foo_tmpl",
 #   ]
 # }
+#
+# If needed, the user can set 'script_override=foo.py' to use a different
+# templating script to the default (wayland_templater.py).
 template("wayland_templater") {
   assert(defined(invoker.sources), "Need sources for wayland_templater")
   assert(defined(invoker.protocols), "Need protocols for wayland_templater")
@@ -34,6 +37,11 @@
     template_outputs += [ "${target_gen_dir}/${dir}/${name}" ]
   }
 
+  templater_script_name = "wayland_templater.py"
+  if (defined(invoker.script_override)) {
+    templater_script_name = invoker.script_override
+  }
+
   # Jinja2 doesnt like having ".." in the target path, so we give it the
   # source-tree's path rather than the build-directory's relative path (which
   # usually contains "..").
@@ -46,13 +54,13 @@
     script = "//third_party/blink/renderer/build/scripts/run_with_pythonpath.py"
     sources = [
                 script,
-                "wayland_templater.py",
+                templater_script_name,
               ] + invoker.sources + invoker.protocols
     outputs = template_outputs
     args = [
              "-I",
              rebase_path("//third_party", root_build_dir),
-             rebase_path("wayland_templater.py", root_build_dir),
+             rebase_path(templater_script_name, root_build_dir),
              "--directory",
              build_to_src_path,
              "--input",
diff --git a/components/exo/wayland/fuzzer/wayland_templater.py b/components/exo/wayland/fuzzer/wayland_templater.py
index c87df3a..71ab881 100644
--- a/components/exo/wayland/fuzzer/wayland_templater.py
+++ b/components/exo/wayland/fuzzer/wayland_templater.py
@@ -11,11 +11,10 @@
 from __future__ import absolute_import
 from __future__ import print_function
 
-import argparse
 import sys
-from xml.etree import ElementTree
 
 import jinja2
+import wayland_utils as wlu
 
 proto_type_conversions = {
     'object': 'small_value',
@@ -35,84 +34,6 @@
 }
 
 
-def AllInterfaces(protocols):
-  """Get the interfaces in these protocols.
-
-  Args:
-    protocols: the list of protocols you want the interfaces of.
-
-  Yields:
-    Tuples (p, i) of (p)rotocol (i)nterface.
-  """
-  for p in protocols:
-    for i in p.findall('interface'):
-      yield (p, i)
-
-
-def AllMessages(protocols):
-  """Get the messages in these protocols.
-
-  Args:
-    protocols: the list of protocols you want the messages of.
-
-  Yields:
-    Tuples (p, i, m) of (p)rotocol, (i)nterface, and (m)essage.
-  """
-  for (p, i) in AllInterfaces(protocols):
-    for r in i.findall('request'):
-      yield (p, i, r)
-    for e in i.findall('event'):
-      yield (p, i, e)
-
-
-def GetConstructorArg(message):
-  """Get the argument that this message constructs, or None.
-
-  Args:
-    message: the message which you want to find the constructor arg of.
-
-  Returns:
-    The argument (as an ElementTree node) that constructs a new interface, or
-    None.
-  """
-  return message.find('arg[@type="new_id"]')
-
-
-def IsConstructor(message):
-  """Check if a message is a constructor.
-
-  Args:
-    message: the message which you want to check.
-
-  Returns:
-    True if the message constructs an object (via new_id), False otherwise.
-  """
-  return GetConstructorArg(message) is not None
-
-
-def GetConstructedInterface(message):
-  """Gets the interface constructed by a message.
-
-  Note that even if IsConstructor(message) returns true, get_constructed can
-  still return None when the message constructs an unknown interface (e.g.
-  wl_registry.bind()).
-
-  Args:
-    message: the event/request which may be a constructor.
-
-  Returns:
-    The name of the constructed interface (if there is one), or None.
-  """
-  cons_arg = GetConstructorArg(message)
-  if cons_arg is None:
-    return None
-  return cons_arg.attrib.get('interface')
-
-
-def NeedsListener(interface):
-  return interface.find('event') is not None
-
-
 def GetCppType(arg):
   ty = arg.attrib['type']
   if ty in ['object', 'new_id']:
@@ -131,12 +52,12 @@
 
   def __init__(self, protocols):
     self.non_global_names = {
-        GetConstructedInterface(m) for _, _, m in AllMessages(protocols)
+        wlu.GetConstructedInterface(m) for _, _, m in wlu.AllMessages(protocols)
     } - {None}
     self.interfaces_with_listeners = {
         i.attrib['name']
-        for p, i in AllInterfaces(protocols)
-        if NeedsListener(i)
+        for p, i in wlu.AllInterfaces(protocols)
+        if wlu.NeedsListener(i)
     }
     self.counts = {}
 
@@ -159,6 +80,7 @@
   return {
       'name': arg.attrib['name'],
       'type': ty,
+      'nullable': arg.attrib.get('allow-null', 'false') == 'true',
       'proto_type': proto_type_conversions.get(ty),
       'cpp_type': GetCppType(arg),
       'interface': arg.attrib.get('interface'),
@@ -167,7 +89,7 @@
 
 def GetMessage(message, context):
   name = message.attrib['name']
-  constructed = GetConstructedInterface(message)
+  constructed = wlu.GetConstructedInterface(message)
   return {
       'name':
           name,
@@ -175,7 +97,7 @@
           context.GetAndIncrementCount('message_index'),
       'args': [GetArg(a) for a in message.findall('arg')],
       'is_constructor':
-          IsConstructor(message),
+          wlu.IsConstructor(message),
       'constructed':
           constructed,
       'constructed_has_listener':
@@ -197,12 +119,12 @@
           GetMessage(m, context) for m in interface.findall('request')
       ],
       'has_listener':
-          NeedsListener(interface)
+          wlu.NeedsListener(interface)
   }
 
 
 def GetTemplateData(protocol_paths):
-  protocols = [ElementTree.parse(path).getroot() for path in protocol_paths]
+  protocols = wlu.ReadProtocols(protocol_paths)
   context = TemplaterContext(protocols)
   interfaces = []
   for p in protocols:
@@ -214,44 +136,26 @@
   }
 
 
+def InstantiateTemplate(in_tmpl, in_ctx, output, in_directory):
+  env = jinja2.Environment(
+      loader=jinja2.FileSystemLoader(in_directory),
+      keep_trailing_newline=True,  # newline-terminate generated files
+      lstrip_blocks=True,
+      trim_blocks=True)  # so don't need {%- -%} everywhere
+  template = env.get_template(in_tmpl)
+  with open(output, 'w') as out_fi:
+    out_fi.write(template.render(in_ctx))
+
+
 def main(argv):
   """Execute the templater, based on the user provided args.
 
   Args:
     argv: the command line arguments (including the script name)
   """
-  parser = argparse.ArgumentParser(description=__doc__)
-  parser.add_argument(
-      '-d',
-      '--directory',
-      help='treat input paths as relative to this directory',
-      default='.')
-  parser.add_argument(
-      '-i',
-      '--input',
-      help='path to the input template file (relative to -d)',
-      required=True)
-  parser.add_argument(
-      '-o',
-      '--output',
-      help='path to write the generated file to',
-      required=True)
-  parser.add_argument(
-      '-s',
-      '--spec',
-      help='path(s) to the wayland specification(s)',
-      nargs='+',
-      required=True)
-  parsed_args = parser.parse_args(argv[1:])
-
-  env = jinja2.Environment(
-      loader=jinja2.FileSystemLoader(parsed_args.directory),
-      keep_trailing_newline=True,  # newline-terminate generated files
-      lstrip_blocks=True,
-      trim_blocks=True)  # so don't need {%- -%} everywhere
-  template = env.get_template(parsed_args.input)
-  with open(parsed_args.output, 'w') as out_fi:
-    out_fi.write(template.render(GetTemplateData(parsed_args.spec)))
+  parsed_args = wlu.ParseOpts(argv)
+  InstantiateTemplate(parsed_args.input, GetTemplateData(parsed_args.spec),
+                      parsed_args.output, parsed_args.directory)
 
 
 if __name__ == '__main__':
diff --git a/components/exo/wayland/fuzzer/wayland_utils.py b/components/exo/wayland/fuzzer/wayland_utils.py
new file mode 100644
index 0000000..fe20b78d
--- /dev/null
+++ b/components/exo/wayland/fuzzer/wayland_utils.py
@@ -0,0 +1,130 @@
+# Copyright (c) 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Utilities for working with wayland protocols.
+
+A collection of all the generically useful wayland utilities.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+
+import argparse
+from xml.etree import ElementTree
+
+
+def ReadProtocols(protocol_paths):
+  return [ElementTree.parse(path).getroot() for path in protocol_paths]
+
+
+def AllInterfaces(protocols):
+  """Get the interfaces in these protocols.
+
+  Args:
+    protocols: the list of protocols you want the interfaces of.
+
+  Yields:
+    Tuples (p, i) of (p)rotocol (i)nterface.
+  """
+  for p in protocols:
+    for i in p.findall('interface'):
+      yield (p, i)
+
+
+def AllMessages(protocols):
+  """Get the messages in these protocols.
+
+  Args:
+    protocols: the list of protocols you want the messages of.
+
+  Yields:
+    Tuples (p, i, m) of (p)rotocol, (i)nterface, and (m)essage.
+  """
+  for (p, i) in AllInterfaces(protocols):
+    for r in i.findall('request'):
+      yield (p, i, r)
+    for e in i.findall('event'):
+      yield (p, i, e)
+
+
+def GetConstructorArg(message):
+  """Get the argument that this message constructs, or None.
+
+  Args:
+    message: the message which you want to find the constructor arg of.
+
+  Returns:
+    The argument (as an ElementTree node) that constructs a new interface, or
+    None.
+  """
+  return message.find('arg[@type="new_id"]')
+
+
+def IsConstructor(message):
+  """Check if a message is a constructor.
+
+  Args:
+    message: the message which you want to check.
+
+  Returns:
+    True if the message constructs an object (via new_id), False otherwise.
+  """
+  return GetConstructorArg(message) is not None
+
+
+def GetConstructedInterface(message):
+  """Gets the interface constructed by a message.
+
+  Note that even if IsConstructor(message) returns true, get_constructed can
+  still return None when the message constructs an unknown interface (e.g.
+  wl_registry.bind()).
+
+  Args:
+    message: the event/request which may be a constructor.
+
+  Returns:
+    The name of the constructed interface (if there is one), or None.
+  """
+  cons_arg = GetConstructorArg(message)
+  if cons_arg is None:
+    return None
+  return cons_arg.attrib.get('interface')
+
+
+def NeedsListener(interface):
+  return interface.find('event') is not None
+
+
+def ParseOpts(argv):
+  """Parses the given command line arguments for the templater.
+
+  Args:
+    argv: the arguments to be parsed.
+
+  Returns:
+    An argparse.ArgumentParser which provides the user's chosen configuration
+    for this templater run.
+  """
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '-d',
+      '--directory',
+      help='treat input paths as relative to this directory',
+      default='.')
+  parser.add_argument(
+      '-i',
+      '--input',
+      help='path to the input template file (relative to -d)',
+      required=True)
+  parser.add_argument(
+      '-o',
+      '--output',
+      help='path to write the generated file to',
+      required=True)
+  parser.add_argument(
+      '-s',
+      '--spec',
+      help='path(s) to the wayland specification(s)',
+      nargs='+',
+      required=True)
+  return parser.parse_args(argv[1:])
diff --git a/components/favicon/core/features.cc b/components/favicon/core/features.cc
index 7532efa5..d018a51 100644
--- a/components/favicon/core/features.cc
+++ b/components/favicon/core/features.cc
@@ -12,5 +12,8 @@
     "kAllowPropagationOfFaviconCacheHits", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableHistoryFaviconsGoogleServerQuery{
     "EnableHistoryFaviconsGoogleServerQuery", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kNotifySessionsOfMostRecentIconUrlChange{
+    "NotifySessionsOfMostRecentIconUrlChange",
+    base::FEATURE_DISABLED_BY_DEFAULT};
 
 }  // namespace favicon
diff --git a/components/favicon/core/features.h b/components/favicon/core/features.h
index db1fa24..88a5c71 100644
--- a/components/favicon/core/features.h
+++ b/components/favicon/core/features.h
@@ -13,6 +13,7 @@
 
 extern const base::Feature kAllowPropagationOfFaviconCacheHits;
 extern const base::Feature kEnableHistoryFaviconsGoogleServerQuery;
+extern const base::Feature kNotifySessionsOfMostRecentIconUrlChange;
 
 }  // namespace favicon
 
diff --git a/components/gwp_asan/client/sampling_malloc_shims_unittest.cc b/components/gwp_asan/client/sampling_malloc_shims_unittest.cc
index 7dd943e..915ecf0 100644
--- a/components/gwp_asan/client/sampling_malloc_shims_unittest.cc
+++ b/components/gwp_asan/client/sampling_malloc_shims_unittest.cc
@@ -243,6 +243,8 @@
 }
 #endif  // !defined(COMPONENT_BUILD)
 
+// malloc_usable_size() is not currently used/shimmed on Android.
+#if !defined(OS_ANDROID)
 MULTIPROCESS_TEST_MAIN_WITH_SETUP(
     GetSizeEstimate,
     SamplingMallocShimsTest::multiprocessTestSetup) {
@@ -264,6 +266,7 @@
 TEST_F(SamplingMallocShimsTest, GetSizeEstimate) {
   runTest("GetSizeEstimate");
 }
+#endif
 
 #if defined(OS_WIN)
 MULTIPROCESS_TEST_MAIN_WITH_SETUP(
diff --git a/components/gwp_asan/client/sampling_state.h b/components/gwp_asan/client/sampling_state.h
index 933ed3f..1728a7a 100644
--- a/components/gwp_asan/client/sampling_state.h
+++ b/components/gwp_asan/client/sampling_state.h
@@ -11,7 +11,8 @@
 #include "base/rand_util.h"
 #include "build/build_config.h"
 
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_ANDROID)
+#define USE_PTHREAD_TLS
 #include <pthread.h>
 #endif
 
@@ -37,7 +38,7 @@
     DCHECK_GT(sampling_frequency, 0U);
     sampling_frequency_ = sampling_frequency;
 
-#if defined(OS_MACOSX)
+#if defined(USE_PTHREAD_TLS)
     pthread_key_create(&tls_key_, nullptr);
 #endif
   }
@@ -71,15 +72,15 @@
     return next_sample;
   }
 
-#if !defined(OS_MACOSX)
+#if !defined(USE_PTHREAD_TLS)
   ALWAYS_INLINE size_t GetCounter() { return tls_counter_; }
   ALWAYS_INLINE void SetCounter(size_t value) { tls_counter_ = value; }
 
   static thread_local size_t tls_counter_;
 #else
-  // On macOS, the first use of a thread_local variable on a new thread will
-  // cause a malloc(), causing infinite recursion. Instead, use pthread TLS to
-  // store the counter.
+  // On macOS and Android (before Q), the first use of a thread_local variable
+  // on a new thread will cause an allocation, leading to infinite recursion.
+  // Instead, use pthread TLS to store the counter.
   //
   // TODO: This is not necessary for PartitionAlloc and likely slower, refactor
   // SamplingState to be able to use pthread TLS for malloc() and thread_local
@@ -102,7 +103,7 @@
   size_t increment_ = 0;
 };
 
-#if !defined(OS_MACOSX)
+#if !defined(USE_PTHREAD_TLS)
 template <ParentAllocator PA>
 thread_local size_t SamplingState<PA>::tls_counter_ = 0;
 #endif
diff --git a/components/signin/core/browser/fake_profile_oauth2_token_service.cc b/components/signin/core/browser/fake_profile_oauth2_token_service.cc
index a5a0ce1..e1898681 100644
--- a/components/signin/core/browser/fake_profile_oauth2_token_service.cc
+++ b/components/signin/core/browser/fake_profile_oauth2_token_service.cc
@@ -128,7 +128,7 @@
     bool scope_matches = all_scopes || it->scopes == scope;
     bool account_matches = account_id.empty() || account_id == it->account_id;
     if (account_matches && scope_matches) {
-      for (auto& diagnostic_observer : GetDiagnicsObservers()) {
+      for (auto& diagnostic_observer : GetAccessTokenDiagnosticsObservers()) {
         diagnostic_observer.OnFetchAccessTokenComplete(
             account_id, it->request->GetConsumerId(), scope, error,
             base::Time());
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.cc b/components/signin/core/browser/gaia_cookie_manager_service.cc
index f684359..fe84d14 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -23,6 +23,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/oauth_multilogin_helper.h"
 #include "components/signin/core/browser/set_accounts_in_cookie_result.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #include "components/signin/core/browser/ubertoken_fetcher_impl.h"
@@ -61,6 +62,9 @@
 
 namespace {
 
+// The maximum number of retries for a fetcher used in this class.
+constexpr int kMaxFetcherRetries = 8;
+
 // In case of an error while fetching using the GaiaAuthFetcher or
 // SimpleURLLoader, retry with exponential backoff. Try up to 7 times within 15
 // minutes.
@@ -797,8 +801,7 @@
   const std::string account_id = requests_.front().GetAccountID();
   VLOG(1) << "Failed MergeSession"
           << " account=" << account_id << " error=" << error.ToString();
-  if (++fetcher_retries_ < signin::kMaxFetcherRetries &&
-      error.IsTransientError()) {
+  if (++fetcher_retries_ < kMaxFetcherRetries && error.IsTransientError()) {
     fetcher_backoff_.InformOfRequest(false);
     UMA_HISTOGRAM_ENUMERATION("OAuth2Login.MergeSessionRetry", error.state(),
                               GoogleServiceAuthError::NUM_STATES);
@@ -866,8 +869,7 @@
          GaiaCookieRequestType::LIST_ACCOUNTS);
   RecordListAccountsRetryResult(error, fetcher_retries_);
 
-  if (++fetcher_retries_ < signin::kMaxFetcherRetries &&
-      error.IsTransientError()) {
+  if (++fetcher_retries_ < kMaxFetcherRetries && error.IsTransientError()) {
     fetcher_backoff_.InformOfRequest(false);
     UMA_HISTOGRAM_ENUMERATION("Signin.ListAccountsRetry", error.state(),
                               GoogleServiceAuthError::NUM_STATES);
@@ -906,7 +908,7 @@
   VLOG(1) << "GaiaCookieManagerService::OnLogOutFailure";
   RecordLogoutRequestState(LogoutRequestState::kFailed);
 
-  if (++fetcher_retries_ < signin::kMaxFetcherRetries) {
+  if (++fetcher_retries_ < kMaxFetcherRetries) {
     fetcher_backoff_.InformOfRequest(false);
     fetcher_timer_.Start(
         FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(),
@@ -991,13 +993,8 @@
     return;
   }
 
-  // TODO(triploblastic): remove this block in the second part of the fix.
-  std::vector<std::string> account_ids;
-  for (const auto& id : requests_.front().accounts())
-    account_ids.push_back(id.first.id);
-
   oauth_multilogin_helper_ = std::make_unique<signin::OAuthMultiloginHelper>(
-      signin_client_, token_service_, account_ids,
+      signin_client_, token_service_, requests_.front().accounts(),
       external_cc_result_fetcher_.GetExternalCcResult(),
       base::BindOnce(&GaiaCookieManagerService::OnSetAccountsFinished,
                      weak_ptr_factory_.GetWeakPtr()));
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.h b/components/signin/core/browser/gaia_cookie_manager_service.h
index 4dbf82e..2cadfb1 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -18,7 +18,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
-#include "components/signin/core/browser/oauth_multilogin_helper.h"
 #include "components/signin/core/browser/signin_client.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "google_apis/gaia/gaia_auth_fetcher.h"
@@ -39,12 +38,10 @@
 
 namespace signin {
 
+class OAuthMultiloginHelper;
 class UbertokenFetcherImpl;
 enum class SetAccountsInCookieResult;
 
-// The maximum number of retries for a fetcher used in this class.
-constexpr int kMaxFetcherRetries = 8;
-
 struct MultiloginParameters {
   MultiloginParameters(gaia::MultiloginMode mode,
                        const std::vector<std::string>& accounts_to_send);
diff --git a/components/signin/core/browser/oauth_multilogin_helper.cc b/components/signin/core/browser/oauth_multilogin_helper.cc
index ab38dc09..a779cf4 100644
--- a/components/signin/core/browser/oauth_multilogin_helper.cc
+++ b/components/signin/core/browser/oauth_multilogin_helper.cc
@@ -25,15 +25,26 @@
 constexpr int kMaxFetcherRetries = 3;
 
 std::string FindTokenForAccount(
-    const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& token_id_pairs,
-    const std::string& account_id) {
-  for (auto it = token_id_pairs.cbegin(); it != token_id_pairs.cend(); ++it) {
-    if (account_id == it->gaia_id_)
-      return it->token_;
+    const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&
+        gaia_id_token_pairs,
+    const std::string& gaia_id) {
+  for (const auto& gaia_id_token : gaia_id_token_pairs) {
+    if (gaia_id == gaia_id_token.gaia_id_)
+      return gaia_id_token.token_;
   }
   return std::string();
 }
 
+CoreAccountId FindAccountIdForGaiaId(
+    const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>& accounts,
+    const std::string& gaia_id) {
+  for (const auto& account : accounts) {
+    if (gaia_id == account.second)
+      return account.first;
+  }
+  return CoreAccountId();
+}
+
 }  // namespace
 
 namespace signin {
@@ -41,25 +52,25 @@
 OAuthMultiloginHelper::OAuthMultiloginHelper(
     SigninClient* signin_client,
     OAuth2TokenService* token_service,
-    const std::vector<std::string>& account_ids,
+    const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>& accounts,
     const std::string& external_cc_result,
     base::OnceCallback<void(signin::SetAccountsInCookieResult)> callback)
     : signin_client_(signin_client),
       token_service_(token_service),
-      account_ids_(account_ids),
+      accounts_(accounts),
       external_cc_result_(external_cc_result),
       callback_(std::move(callback)),
       weak_ptr_factory_(this) {
   DCHECK(signin_client_);
   DCHECK(token_service_);
-  DCHECK(!account_ids_.empty());
+  DCHECK(!accounts_.empty());
   DCHECK(callback_);
 
 #ifndef NDEBUG
   // Check that there is no duplicate accounts.
-  std::set<std::string> accounts_no_duplicates(account_ids_.begin(),
-                                               account_ids_.end());
-  DCHECK_EQ(account_ids_.size(), accounts_no_duplicates.size());
+  std::set<GaiaCookieManagerService::AccountIdGaiaIdPair>
+      accounts_no_duplicates(accounts_.begin(), accounts_.end());
+  DCHECK_EQ(accounts_.size(), accounts_no_duplicates.size());
 #endif
 
   StartFetchingTokens();
@@ -69,9 +80,13 @@
 
 void OAuthMultiloginHelper::StartFetchingTokens() {
   DCHECK(!token_fetcher_);
-  DCHECK(token_id_pairs_.empty());
+  DCHECK(gaia_id_token_pairs_.empty());
+  std::vector<CoreAccountId> account_ids;
+  for (const auto& account : accounts_)
+    account_ids.push_back(account.first);
+
   token_fetcher_ = std::make_unique<signin::OAuthMultiloginTokenFetcher>(
-      signin_client_, token_service_, account_ids_,
+      signin_client_, token_service_, account_ids,
       base::BindOnce(&OAuthMultiloginHelper::OnAccessTokensSuccess,
                      base::Unretained(this)),
       base::BindOnce(&OAuthMultiloginHelper::OnAccessTokensFailure,
@@ -79,10 +94,17 @@
 }
 
 void OAuthMultiloginHelper::OnAccessTokensSuccess(
-    const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>& token_id_pairs) {
-  DCHECK(token_id_pairs_.empty());
-  token_id_pairs_ = token_id_pairs;
-  DCHECK_EQ(token_id_pairs_.size(), account_ids_.size());
+    const std::vector<OAuthMultiloginTokenFetcher::AccountIdTokenPair>&
+        account_token_pairs) {
+  DCHECK(gaia_id_token_pairs_.empty());
+  for (size_t index = 0; index < accounts_.size(); index++) {
+    // OAuthMultiloginTokenFetcher should return the tokens in the same order
+    // as the account_ids that was passed to it.
+    DCHECK_EQ(accounts_[index].first, account_token_pairs[index].account_id);
+    gaia_id_token_pairs_.emplace_back(accounts_[index].second,
+                                      account_token_pairs[index].token);
+  }
+  DCHECK_EQ(gaia_id_token_pairs_.size(), accounts_.size());
   token_fetcher_.reset();
 
   signin_client_->DelayNetworkCall(
@@ -101,35 +123,38 @@
 }
 
 void OAuthMultiloginHelper::StartFetchingMultiLogin() {
-  DCHECK_EQ(token_id_pairs_.size(), account_ids_.size());
+  DCHECK_EQ(gaia_id_token_pairs_.size(), accounts_.size());
   gaia_auth_fetcher_ =
       signin_client_->CreateGaiaAuthFetcher(this, gaia::GaiaSource::kChrome);
-  gaia_auth_fetcher_->StartOAuthMultilogin(token_id_pairs_,
+  gaia_auth_fetcher_->StartOAuthMultilogin(gaia_id_token_pairs_,
                                            external_cc_result_);
 }
 
 void OAuthMultiloginHelper::OnOAuthMultiloginFinished(
     const OAuthMultiloginResult& result) {
   if (result.status() == OAuthMultiloginResponseStatus::kOk) {
+    std::vector<std::string> account_ids;
+    for (const auto& account : accounts_)
+      account_ids.push_back(account.first.id);
     VLOG(1) << "Multilogin successful accounts="
-            << base::JoinString(account_ids_, " ");
+            << base::JoinString(account_ids, " ");
     StartSettingCookies(result);
     return;
   }
 
   // If Gaia responded with kInvalidTokens, we have to mark tokens as invalid.
   if (result.status() == OAuthMultiloginResponseStatus::kInvalidTokens) {
-    for (const std::string& failed_account_id : result.failed_accounts()) {
+    for (const std::string& failed_gaia_id : result.failed_gaia_ids()) {
       std::string failed_token =
-          FindTokenForAccount(token_id_pairs_, failed_account_id);
+          FindTokenForAccount(gaia_id_token_pairs_, failed_gaia_id);
       if (failed_token.empty()) {
         LOG(ERROR)
             << "Unexpected failed token for account not present in request: "
-            << failed_account_id;
+            << failed_gaia_id;
         continue;
       }
-      token_service_->InvalidateTokenForMultilogin(failed_account_id,
-                                                   failed_token);
+      token_service_->InvalidateTokenForMultilogin(
+          FindAccountIdForGaiaId(accounts_, failed_gaia_id), failed_token);
     }
   }
 
@@ -138,7 +163,7 @@
       result.status() == OAuthMultiloginResponseStatus::kRetry;
 
   if (is_transient_error && ++fetcher_retries_ < kMaxFetcherRetries) {
-    token_id_pairs_.clear();
+    gaia_id_token_pairs_.clear();
     StartFetchingTokens();
     return;
   }
diff --git a/components/signin/core/browser/oauth_multilogin_helper.h b/components/signin/core/browser/oauth_multilogin_helper.h
index 69064a9..176fb0e 100644
--- a/components/signin/core/browser/oauth_multilogin_helper.h
+++ b/components/signin/core/browser/oauth_multilogin_helper.h
@@ -13,6 +13,9 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "components/signin/core/browser/gaia_cookie_manager_service.h"
+#include "components/signin/core/browser/oauth_multilogin_token_fetcher.h"
+#include "google_apis/gaia/core_account_id.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "google_apis/gaia/gaia_auth_fetcher.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
@@ -24,7 +27,6 @@
 
 namespace signin {
 
-class OAuthMultiloginTokenFetcher;
 enum class SetAccountsInCookieResult;
 
 // This is a helper class that drives the OAuth multilogin process.
@@ -38,7 +40,8 @@
   OAuthMultiloginHelper(
       SigninClient* signin_client,
       OAuth2TokenService* token_service,
-      const std::vector<std::string>& account_ids,
+      const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>&
+          accounts,
       const std::string& external_cc_result,
       base::OnceCallback<void(signin::SetAccountsInCookieResult)> callback);
 
@@ -50,8 +53,8 @@
 
   // Callbacks for OAuthMultiloginTokenFetcher.
   void OnAccessTokensSuccess(
-      const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&
-          token_id_pairs);
+      const std::vector<OAuthMultiloginTokenFetcher::AccountIdTokenPair>&
+          account_token_pairs);
   void OnAccessTokensFailure(const GoogleServiceAuthError& error);
 
   // Actual call to the multilogin endpoint.
@@ -74,11 +77,11 @@
   int fetcher_retries_ = 0;
 
   // Account ids to set in the cookie.
-  const std::vector<std::string> account_ids_;
+  const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair> accounts_;
   // See GaiaCookieManagerService::ExternalCcResultFetcher for details.
   const std::string external_cc_result_;
   // Access tokens, in the same order as the account ids.
-  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs_;
+  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> gaia_id_token_pairs_;
 
   base::OnceCallback<void(signin::SetAccountsInCookieResult)> callback_;
   std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_;
diff --git a/components/signin/core/browser/oauth_multilogin_helper_unittest.cc b/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
index e76e385..7e7642e 100644
--- a/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
+++ b/components/signin/core/browser/oauth_multilogin_helper_unittest.cc
@@ -23,6 +23,8 @@
 
 const CoreAccountId kAccountId("account_id_1");
 const CoreAccountId kAccountId2("account_id_2");
+const char kGaiaId[] = "gaia_id_1";
+const char kGaiaId2[] = "gaia_id_2";
 const char kAccessToken[] = "access_token_1";
 const char kAccessToken2[] = "access_token_2";
 
@@ -119,8 +121,8 @@
        {
          "status": "INVALID_TOKENS",
          "failed_accounts": [
-           { "obfuscated_id": "account_id_1", "status": "RECOVERABLE" },
-           { "obfuscated_id": "account_id_2", "status": "OK" }
+           { "obfuscated_id": "gaia_id_1", "status": "RECOVERABLE" },
+           { "obfuscated_id": "gaia_id_2", "status": "OK" }
          ]
        }
       )";
@@ -169,17 +171,19 @@
   ~OAuthMultiloginHelperTest() override = default;
 
   std::unique_ptr<OAuthMultiloginHelper> CreateHelper(
-      const std::vector<std::string> account_ids) {
+      const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>
+          accounts) {
     return std::make_unique<OAuthMultiloginHelper>(
-        &test_signin_client_, token_service(), account_ids, std::string(),
+        &test_signin_client_, token_service(), accounts, std::string(),
         base::BindOnce(&OAuthMultiloginHelperTest::OnOAuthMultiloginFinished,
                        base::Unretained(this)));
   }
 
   std::unique_ptr<OAuthMultiloginHelper> CreateHelperWithExternalCcResult(
-      const std::vector<std::string> account_ids) {
+      const std::vector<GaiaCookieManagerService::AccountIdGaiaIdPair>
+          accounts) {
     return std::make_unique<OAuthMultiloginHelper>(
-        &test_signin_client_, token_service(), account_ids, kExternalCcResult,
+        &test_signin_client_, token_service(), accounts, kExternalCcResult,
         base::BindOnce(&OAuthMultiloginHelperTest::OnOAuthMultiloginFinished,
                        base::Unretained(this)));
   }
@@ -221,7 +225,8 @@
 // Everything succeeds.
 TEST_F(OAuthMultiloginHelperTest, Success) {
   token_service()->AddAccount(kAccountId);
-  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+  std::unique_ptr<OAuthMultiloginHelper> helper =
+      CreateHelper({{kAccountId, kGaiaId}});
 
   // Configure mock cookie manager:
   // - check that the cookie is the expected one
@@ -249,7 +254,8 @@
 // Multiple cookies in the multilogin response.
 TEST_F(OAuthMultiloginHelperTest, MultipleCookies) {
   token_service()->AddAccount(kAccountId);
-  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+  std::unique_ptr<OAuthMultiloginHelper> helper =
+      CreateHelper({{kAccountId, kGaiaId}});
 
   // Configure mock cookie manager:
   // - check that the cookie is the expected one
@@ -284,7 +290,7 @@
 TEST_F(OAuthMultiloginHelperTest, SuccessWithExternalCcResult) {
   token_service()->AddAccount(kAccountId);
   std::unique_ptr<OAuthMultiloginHelper> helper =
-      CreateHelperWithExternalCcResult({kAccountId});
+      CreateHelperWithExternalCcResult({{kAccountId, kGaiaId}});
 
   // Configure mock cookie manager:
   // - check that the cookie is the expected one
@@ -320,7 +326,8 @@
 // Failure to get the access token.
 TEST_F(OAuthMultiloginHelperTest, OneAccountAccessTokenFailure) {
   token_service()->AddAccount(kAccountId);
-  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+  std::unique_ptr<OAuthMultiloginHelper> helper =
+      CreateHelper({{kAccountId, kGaiaId}});
 
   token_service()->IssueErrorForAllPendingRequestsForAccount(
       kAccountId,
@@ -332,7 +339,8 @@
 // Retry on transient errors in the multilogin call.
 TEST_F(OAuthMultiloginHelperTest, OneAccountTransientMultiloginError) {
   token_service()->AddAccount(kAccountId);
-  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+  std::unique_ptr<OAuthMultiloginHelper> helper =
+      CreateHelper({{kAccountId, kGaiaId}});
 
   // Configure mock cookie manager:
   // - check that the cookie is the expected one
@@ -368,7 +376,8 @@
 TEST_F(OAuthMultiloginHelperTest,
        OneAccountTransientMultiloginErrorMaxRetries) {
   token_service()->AddAccount(kAccountId);
-  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+  std::unique_ptr<OAuthMultiloginHelper> helper =
+      CreateHelper({{kAccountId, kGaiaId}});
 
   // Issue access token.
   OAuth2AccessTokenConsumer::TokenResponse success_response;
@@ -391,7 +400,8 @@
 // Persistent error in the multilogin call.
 TEST_F(OAuthMultiloginHelperTest, OneAccountPersistentMultiloginError) {
   token_service()->AddAccount(kAccountId);
-  std::unique_ptr<OAuthMultiloginHelper> helper = CreateHelper({kAccountId});
+  std::unique_ptr<OAuthMultiloginHelper> helper =
+      CreateHelper({{kAccountId, kGaiaId}});
 
   // Issue access token.
   OAuth2AccessTokenConsumer::TokenResponse success_response;
@@ -412,7 +422,7 @@
   token_service()->AddAccount(kAccountId);
   token_service()->AddAccount(kAccountId2);
   std::unique_ptr<OAuthMultiloginHelper> helper =
-      CreateHelper({kAccountId, kAccountId2});
+      CreateHelper({{kAccountId, kGaiaId}, {kAccountId2, kGaiaId2}});
 
   // Configure mock cookie manager:
   // - check that the cookie is the expected one
@@ -460,7 +470,7 @@
   token_service()->AddAccount(kAccountId);
   token_service()->AddAccount(kAccountId2);
   std::unique_ptr<OAuthMultiloginHelper> helper =
-      CreateHelper({kAccountId, kAccountId2});
+      CreateHelper({{kAccountId, kGaiaId}, {kAccountId2, kGaiaId2}});
 
   // The failed access token should be invalidated.
   EXPECT_CALL(*token_service(),
diff --git a/components/signin/core/browser/oauth_multilogin_token_fetcher.cc b/components/signin/core/browser/oauth_multilogin_token_fetcher.cc
index d01a50aa..15782fd9 100644
--- a/components/signin/core/browser/oauth_multilogin_token_fetcher.cc
+++ b/components/signin/core/browser/oauth_multilogin_token_fetcher.cc
@@ -28,7 +28,7 @@
 OAuthMultiloginTokenFetcher::OAuthMultiloginTokenFetcher(
     SigninClient* signin_client,
     OAuth2TokenService* token_service,
-    const std::vector<std::string>& account_ids,
+    const std::vector<CoreAccountId>& account_ids,
     SuccessCallback success_callback,
     FailureCallback failure_callback)
     : OAuth2TokenService::Consumer("oauth_multilogin_token_fetcher"),
@@ -46,19 +46,19 @@
 
 #ifndef NDEBUG
   // Check that there is no duplicate accounts.
-  std::set<std::string> accounts_no_duplicates(account_ids_.begin(),
-                                               account_ids_.end());
+  std::set<CoreAccountId> accounts_no_duplicates(account_ids_.begin(),
+                                                 account_ids_.end());
   DCHECK_EQ(account_ids_.size(), accounts_no_duplicates.size());
 #endif
 
-  for (const std::string& account_id : account_ids_)
+  for (const CoreAccountId& account_id : account_ids_)
     StartFetchingToken(account_id);
 }
 
 OAuthMultiloginTokenFetcher::~OAuthMultiloginTokenFetcher() = default;
 
 void OAuthMultiloginTokenFetcher::StartFetchingToken(
-    const std::string& account_id) {
+    const CoreAccountId& account_id) {
   DCHECK(!account_id.empty());
   token_requests_.push_back(
       token_service_->StartRequestForMultilogin(account_id, this));
@@ -67,7 +67,7 @@
 void OAuthMultiloginTokenFetcher::OnGetTokenSuccess(
     const OAuth2TokenService::Request* request,
     const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
-  std::string account_id = request->GetAccountId();
+  CoreAccountId account_id = request->GetAccountId();
   DCHECK(account_ids_.cend() !=
          std::find(account_ids_.cbegin(), account_ids_.cend(), account_id));
 
@@ -82,16 +82,14 @@
   DCHECK(inserted.second);  // If this fires, we have a duplicate account.
 
   if (access_tokens_.size() == account_ids_.size()) {
-    std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs;
+    std::vector<AccountIdTokenPair> account_token_pairs;
     for (const auto& id : account_ids_) {
       const auto& it = access_tokens_.find(id);
       DCHECK(!it->second.empty());
-      // TODO(https://crbug.com/956503): Don't assume that the account ID is the
-      // Gaia ID.
-      token_id_pairs.emplace_back(id, it->second);
+      account_token_pairs.emplace_back(id, it->second);
     }
     RecordGetAccessTokenFinished(GoogleServiceAuthError::AuthErrorNone());
-    std::move(success_callback_).Run(token_id_pairs);
+    std::move(success_callback_).Run(account_token_pairs);
     // Do not add anything below this line, as this may be deleted.
   }
 }
@@ -99,7 +97,7 @@
 void OAuthMultiloginTokenFetcher::OnGetTokenFailure(
     const OAuth2TokenService::Request* request,
     const GoogleServiceAuthError& error) {
-  std::string account_id = request->GetAccountId();
+  CoreAccountId account_id = request->GetAccountId();
   VLOG(1) << "Failed to retrieve accesstoken account=" << account_id
           << " error=" << error.ToString();
   if (error.IsTransientError() &&
diff --git a/components/signin/core/browser/oauth_multilogin_token_fetcher.h b/components/signin/core/browser/oauth_multilogin_token_fetcher.h
index 168ba88..5831c00 100644
--- a/components/signin/core/browser/oauth_multilogin_token_fetcher.h
+++ b/components/signin/core/browser/oauth_multilogin_token_fetcher.h
@@ -27,21 +27,29 @@
 // It is safe to delete this object from within the callbacks.
 class OAuthMultiloginTokenFetcher : public OAuth2TokenService::Consumer {
  public:
-  using SuccessCallback = base::OnceCallback<void(
-      const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&)>;
+  struct AccountIdTokenPair {
+    CoreAccountId account_id;
+    std::string token;
+
+    AccountIdTokenPair(const CoreAccountId& account_id,
+                       const std::string& token)
+        : account_id(account_id), token(token) {}
+  };
+  using SuccessCallback =
+      base::OnceCallback<void(const std::vector<AccountIdTokenPair>&)>;
   using FailureCallback =
       base::OnceCallback<void(const GoogleServiceAuthError&)>;
 
   OAuthMultiloginTokenFetcher(SigninClient* signin_client,
                               OAuth2TokenService* token_service,
-                              const std::vector<std::string>& account_ids,
+                              const std::vector<CoreAccountId>& account_ids,
                               SuccessCallback success_callback,
                               FailureCallback failure_callback);
 
   ~OAuthMultiloginTokenFetcher() override;
 
  private:
-  void StartFetchingToken(const std::string& account_id);
+  void StartFetchingToken(const CoreAccountId& account_id);
 
   // Overridden from OAuth2TokenService::Consumer.
   void OnGetTokenSuccess(
@@ -55,14 +63,14 @@
 
   SigninClient* signin_client_;
   OAuth2TokenService* token_service_;
-  const std::vector<std::string> account_ids_;
+  const std::vector<CoreAccountId> account_ids_;
 
   SuccessCallback success_callback_;
   FailureCallback failure_callback_;
 
   std::vector<std::unique_ptr<OAuth2TokenService::Request>> token_requests_;
-  std::map<std::string, std::string> access_tokens_;
-  std::set<std::string> retried_requests_;  // Requests are retried once.
+  std::map<CoreAccountId, std::string> access_tokens_;
+  std::set<CoreAccountId> retried_requests_;  // Requests are retried once.
 
   base::WeakPtrFactory<OAuthMultiloginTokenFetcher> weak_ptr_factory_;
 
diff --git a/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc b/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc
index a40df52..d3bd9a3 100644
--- a/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc
+++ b/components/signin/core/browser/oauth_multilogin_token_fetcher_unittest.cc
@@ -21,7 +21,7 @@
 
 namespace {
 
-const char kAccountId[] = "account_id";
+const CoreAccountId kAccountId("account_id");
 const char kAccessToken[] = "access_token";
 
 // Status of the token fetch.
@@ -36,7 +36,7 @@
   ~OAuthMultiloginTokenFetcherTest() override = default;
 
   std::unique_ptr<OAuthMultiloginTokenFetcher> CreateFetcher(
-      const std::vector<std::string> account_ids) {
+      const std::vector<CoreAccountId> account_ids) {
     return std::make_unique<OAuthMultiloginTokenFetcher>(
         &test_signin_client_, &token_service_, account_ids,
         base::BindOnce(&OAuthMultiloginTokenFetcherTest::OnSuccess,
@@ -55,12 +55,13 @@
 
  protected:
   // Success callback for OAuthMultiloginTokenFetcher.
-  void OnSuccess(const std::vector<GaiaAuthFetcher::MultiloginTokenIDPair>&
-                     token_id_pairs) {
+  void OnSuccess(
+      const std::vector<OAuthMultiloginTokenFetcher::AccountIdTokenPair>&
+          account_id_token_pairs) {
     DCHECK(!success_callback_called_);
-    DCHECK(token_id_pairs_.empty());
+    DCHECK(account_id_token_pairs_.empty());
     success_callback_called_ = true;
-    token_id_pairs_ = token_id_pairs;
+    account_id_token_pairs_ = account_id_token_pairs;
   }
 
   // Failure callback for OAuthMultiloginTokenFetcher.
@@ -75,7 +76,8 @@
   bool success_callback_called_ = false;
   bool failure_callback_called_ = false;
   GoogleServiceAuthError error_;
-  std::vector<GaiaAuthFetcher::MultiloginTokenIDPair> token_id_pairs_;
+  std::vector<OAuthMultiloginTokenFetcher::AccountIdTokenPair>
+      account_id_token_pairs_;
 
   TestSigninClient test_signin_client_;
   FakeOAuth2TokenService token_service_;
@@ -91,9 +93,9 @@
   token_service_.IssueAllTokensForAccount(kAccountId, success_response);
   EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
   // Check result.
-  EXPECT_EQ(1u, token_id_pairs_.size());
-  EXPECT_EQ(kAccountId, token_id_pairs_[0].gaia_id_);
-  EXPECT_EQ(kAccessToken, token_id_pairs_[0].token_);
+  EXPECT_EQ(1u, account_id_token_pairs_.size());
+  EXPECT_EQ(kAccountId, account_id_token_pairs_[0].account_id);
+  EXPECT_EQ(kAccessToken, account_id_token_pairs_[0].token);
 }
 
 TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountPersistentError) {
@@ -123,9 +125,9 @@
   token_service_.IssueAllTokensForAccount(kAccountId, success_response);
   EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
   // Check result.
-  EXPECT_EQ(1u, token_id_pairs_.size());
-  EXPECT_EQ(kAccountId, token_id_pairs_[0].gaia_id_);
-  EXPECT_EQ(kAccessToken, token_id_pairs_[0].token_);
+  EXPECT_EQ(1u, account_id_token_pairs_.size());
+  EXPECT_EQ(kAccountId, account_id_token_pairs_[0].account_id);
+  EXPECT_EQ(kAccessToken, account_id_token_pairs_[0].token);
 }
 
 TEST_F(OAuthMultiloginTokenFetcherTest, OneAccountTransientErrorMaxRetries) {
@@ -147,76 +149,85 @@
 
 // The flow succeeds even if requests are received out of order.
 TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsSuccess) {
-  token_service_.AddAccount("account_1");
-  token_service_.AddAccount("account_2");
-  token_service_.AddAccount("account_3");
+  const CoreAccountId account_1("account_1");
+  const CoreAccountId account_2("account_2");
+  const CoreAccountId account_3("account_3");
+  token_service_.AddAccount(account_1);
+  token_service_.AddAccount(account_2);
+  token_service_.AddAccount(account_3);
   std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
-      CreateFetcher({"account_1", "account_2", "account_3"});
+      CreateFetcher({account_1, account_2, account_3});
   OAuth2AccessTokenConsumer::TokenResponse success_response;
   success_response.access_token = "token_3";
-  token_service_.IssueAllTokensForAccount("account_3", success_response);
+  token_service_.IssueAllTokensForAccount(account_3, success_response);
   success_response.access_token = "token_1";
-  token_service_.IssueAllTokensForAccount("account_1", success_response);
+  token_service_.IssueAllTokensForAccount(account_1, success_response);
   EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
   success_response.access_token = "token_2";
-  token_service_.IssueAllTokensForAccount("account_2", success_response);
+  token_service_.IssueAllTokensForAccount(account_2, success_response);
   EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
   // Check result.
-  EXPECT_EQ(3u, token_id_pairs_.size());
-  EXPECT_EQ("account_1", token_id_pairs_[0].gaia_id_);
-  EXPECT_EQ("account_2", token_id_pairs_[1].gaia_id_);
-  EXPECT_EQ("account_3", token_id_pairs_[2].gaia_id_);
-  EXPECT_EQ("token_1", token_id_pairs_[0].token_);
-  EXPECT_EQ("token_2", token_id_pairs_[1].token_);
-  EXPECT_EQ("token_3", token_id_pairs_[2].token_);
+  EXPECT_EQ(3u, account_id_token_pairs_.size());
+  EXPECT_EQ(account_1, account_id_token_pairs_[0].account_id);
+  EXPECT_EQ(account_2, account_id_token_pairs_[1].account_id);
+  EXPECT_EQ(account_3, account_id_token_pairs_[2].account_id);
+  EXPECT_EQ("token_1", account_id_token_pairs_[0].token);
+  EXPECT_EQ("token_2", account_id_token_pairs_[1].token);
+  EXPECT_EQ("token_3", account_id_token_pairs_[2].token);
 }
 
 TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsTransientError) {
-  token_service_.AddAccount("account_1");
-  token_service_.AddAccount("account_2");
-  token_service_.AddAccount("account_3");
+  const CoreAccountId account_1("account_1");
+  const CoreAccountId account_2("account_2");
+  const CoreAccountId account_3("account_3");
+  token_service_.AddAccount(account_1);
+  token_service_.AddAccount(account_2);
+  token_service_.AddAccount(account_3);
   std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
-      CreateFetcher({"account_1", "account_2", "account_3"});
+      CreateFetcher({account_1, account_2, account_3});
   // Connection failures will be retried.
   token_service_.IssueErrorForAllPendingRequestsForAccount(
-      "account_1",
+      account_1,
       GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
   token_service_.IssueErrorForAllPendingRequestsForAccount(
-      "account_2",
+      account_2,
       GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
   token_service_.IssueErrorForAllPendingRequestsForAccount(
-      "account_3",
+      account_3,
       GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
   // Success on retry.
   OAuth2AccessTokenConsumer::TokenResponse success_response;
   success_response.access_token = kAccessToken;
   success_response.access_token = "token_1";
-  token_service_.IssueAllTokensForAccount("account_1", success_response);
+  token_service_.IssueAllTokensForAccount(account_1, success_response);
   success_response.access_token = "token_2";
-  token_service_.IssueAllTokensForAccount("account_2", success_response);
+  token_service_.IssueAllTokensForAccount(account_2, success_response);
   EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
   success_response.access_token = "token_3";
-  token_service_.IssueAllTokensForAccount("account_3", success_response);
+  token_service_.IssueAllTokensForAccount(account_3, success_response);
   EXPECT_EQ(FetchStatus::kSuccess, GetFetchStatus());
   // Check result.
-  EXPECT_EQ(3u, token_id_pairs_.size());
-  EXPECT_EQ("account_1", token_id_pairs_[0].gaia_id_);
-  EXPECT_EQ("account_2", token_id_pairs_[1].gaia_id_);
-  EXPECT_EQ("account_3", token_id_pairs_[2].gaia_id_);
-  EXPECT_EQ("token_1", token_id_pairs_[0].token_);
-  EXPECT_EQ("token_2", token_id_pairs_[1].token_);
-  EXPECT_EQ("token_3", token_id_pairs_[2].token_);
+  EXPECT_EQ(3u, account_id_token_pairs_.size());
+  EXPECT_EQ(account_1, account_id_token_pairs_[0].account_id);
+  EXPECT_EQ(account_2, account_id_token_pairs_[1].account_id);
+  EXPECT_EQ(account_3, account_id_token_pairs_[2].account_id);
+  EXPECT_EQ("token_1", account_id_token_pairs_[0].token);
+  EXPECT_EQ("token_2", account_id_token_pairs_[1].token);
+  EXPECT_EQ("token_3", account_id_token_pairs_[2].token);
 }
 
 TEST_F(OAuthMultiloginTokenFetcherTest, MultipleAccountsPersistentError) {
-  token_service_.AddAccount("account_1");
-  token_service_.AddAccount("account_2");
-  token_service_.AddAccount("account_3");
+  const CoreAccountId account_1("account_1");
+  const CoreAccountId account_2("account_2");
+  const CoreAccountId account_3("account_3");
+  token_service_.AddAccount(account_1);
+  token_service_.AddAccount(account_2);
+  token_service_.AddAccount(account_3);
   std::unique_ptr<OAuthMultiloginTokenFetcher> fetcher =
-      CreateFetcher({"account_1", "account_2", "account_3"});
+      CreateFetcher({account_1, account_2, account_3});
   EXPECT_EQ(FetchStatus::kPending, GetFetchStatus());
   token_service_.IssueErrorForAllPendingRequestsForAccount(
-      "account_2",
+      account_2,
       GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
   // Fail as soon as one of the accounts is in error.
   EXPECT_EQ(FetchStatus::kFailure, GetFetchStatus());
diff --git a/components/signin/core/browser/profile_oauth2_token_service.cc b/components/signin/core/browser/profile_oauth2_token_service.cc
index 07c6b1a9..a425e1f0 100644
--- a/components/signin/core/browser/profile_oauth2_token_service.cc
+++ b/components/signin/core/browser/profile_oauth2_token_service.cc
@@ -156,7 +156,7 @@
       is_valid, update_refresh_token_source_);
 
   std::string source_string = SourceToString(update_refresh_token_source_);
-  for (auto& diagnostic_observer : GetDiagnicsObservers()) {
+  for (auto& diagnostic_observer : GetDiagnosticsObservers()) {
     diagnostic_observer.OnRefreshTokenAvailableFromSource(account_id, is_valid,
                                                           source_string);
   }
@@ -175,7 +175,7 @@
   signin_metrics::RecordRefreshTokenRevokedFromSource(
       update_refresh_token_source_);
   std::string source_string = SourceToString(update_refresh_token_source_);
-  for (auto& diagnostic_observer : GetDiagnicsObservers()) {
+  for (auto& diagnostic_observer : GetDiagnosticsObservers()) {
     diagnostic_observer.OnRefreshTokenRevokedFromSource(account_id,
                                                         source_string);
   }
diff --git a/components/sync/syncable/directory_unittest.h b/components/sync/syncable/directory_unittest.h
index 4f1faeeba..4227950 100644
--- a/components/sync/syncable/directory_unittest.h
+++ b/components/sync/syncable/directory_unittest.h
@@ -10,7 +10,7 @@
 #include <memory>
 #include <string>
 
-#include "base/message_loop/message_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/sync/base/fake_encryptor.h"
 #include "components/sync/base/test_unrecoverable_error_handler.h"
 #include "components/sync/syncable/in_memory_directory_backing_store.h"
@@ -94,7 +94,7 @@
                      int64_t server_version,
                      bool is_del);
 
-  base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<Directory> dir_;
   NullDirectoryChangeDelegate delegate_;
   FakeEncryptor encryptor_;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 9aa7ce7..8fa7fdc 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -511,9 +511,13 @@
     "background_sync/background_sync_network_observer.h",
     "background_sync/background_sync_registration.cc",
     "background_sync/background_sync_registration.h",
-    "background_sync/background_sync_service_impl.cc",
-    "background_sync/background_sync_service_impl.h",
+    "background_sync/background_sync_registration_helper.cc",
+    "background_sync/background_sync_registration_helper.h",
     "background_sync/background_sync_status.h",
+    "background_sync/one_shot_background_sync_service_impl.cc",
+    "background_sync/one_shot_background_sync_service_impl.h",
+    "background_sync/periodic_background_sync_service_impl.cc",
+    "background_sync/periodic_background_sync_service_impl.h",
     "bad_message.cc",
     "bad_message.h",
     "blob_storage/blob_dispatcher_host.cc",
diff --git a/content/browser/accessibility/accessibility_auralinux_browsertest.cc b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
index f5389ef..0b2d905d 100644
--- a/content/browser/accessibility/accessibility_auralinux_browsertest.cc
+++ b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
@@ -772,6 +772,64 @@
   g_object_unref(list_item_1);
 }
 
+IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
+                       TestTextSelectionChangedDuplicateSignals) {
+  LoadInitialAccessibilityTreeFromHtml(
+      R"HTML(<!DOCTYPE html>
+      <html>
+      <body>
+      <div>
+        Sufficiently long div content
+      </div>
+      </body>
+      </html>)HTML");
+
+  // Retrieve the AtkObject interface for the document node.
+  AtkObject* document = GetRendererAccessible();
+  ASSERT_TRUE(ATK_IS_COMPONENT(document));
+
+  AtkObject* div = atk_object_ref_accessible_child(document, 0);
+  EXPECT_NE(div, nullptr);
+
+  int selection_changed_signals = 0;
+  g_signal_connect(div, "text-selection-changed",
+                   G_CALLBACK(+[](AtkText*, int* count) { *count += 1; }),
+                   &selection_changed_signals);
+
+  int caret_moved_signals = 0;
+  g_signal_connect(div, "text-caret-moved",
+                   G_CALLBACK(+[](AtkText*, gint, int* count) { *count += 1; }),
+                   &caret_moved_signals);
+
+  auto waiter = std::make_unique<AccessibilityNotificationWaiter>(
+      shell()->web_contents(), ui::kAXModeComplete,
+      ax::mojom::Event::kTextSelectionChanged);
+  atk_text_set_caret_offset(ATK_TEXT(div), 0);
+  waiter->WaitForNotification();
+  ASSERT_EQ(selection_changed_signals, 0);
+  ASSERT_EQ(caret_moved_signals, 1);
+
+  caret_moved_signals = selection_changed_signals = 0;
+  atk_text_set_selection(ATK_TEXT(div), 0, 0, 3);
+  waiter->WaitForNotification();
+  ASSERT_EQ(selection_changed_signals, 1);
+  ASSERT_EQ(caret_moved_signals, 1);
+
+  caret_moved_signals = selection_changed_signals = 0;
+  atk_text_set_caret_offset(ATK_TEXT(div), 3);
+  waiter->WaitForNotification();
+  ASSERT_EQ(selection_changed_signals, 1);
+  ASSERT_EQ(caret_moved_signals, 0);
+
+  caret_moved_signals = selection_changed_signals = 0;
+  atk_text_set_caret_offset(ATK_TEXT(div), 2);
+  waiter->WaitForNotification();
+  ASSERT_EQ(selection_changed_signals, 0);
+  ASSERT_EQ(caret_moved_signals, 1);
+
+  g_object_unref(div);
+}
+
 IN_PROC_BROWSER_TEST_F(
     AccessibilityAuraLinuxBrowserTest,
     MAYBE_TestSetCaretSetsSequentialFocusNavigationStartingPoint) {
diff --git a/content/browser/appcache/appcache_quota_client.cc b/content/browser/appcache/appcache_quota_client.cc
index c9266e4..2742e29 100644
--- a/content/browser/appcache/appcache_quota_client.cc
+++ b/content/browser/appcache/appcache_quota_client.cc
@@ -87,13 +87,13 @@
     return;
   }
 
-  const AppCacheStorage::UsageMap* map = GetUsageMap();
-  auto found = map->find(origin);
-  if (found == map->end()) {
+  const std::map<url::Origin, int64_t>& map = GetUsageMap();
+  auto it = map.find(origin);
+  if (it == map.end()) {
     std::move(callback).Run(0);
     return;
   }
-  std::move(callback).Run(found->second);
+  std::move(callback).Run(it->second);
 }
 
 void AppCacheQuotaClient::GetOriginsForType(StorageType type,
@@ -182,7 +182,7 @@
   }
 
   std::set<url::Origin> origins;
-  for (const auto& pair : *GetUsageMap()) {
+  for (const auto& pair : GetUsageMap()) {
     if (opt_host.empty() || pair.first.host() == opt_host)
       origins.insert(pair.first);
   }
@@ -203,7 +203,7 @@
   pending_serial_requests_.clear();
 }
 
-const AppCacheStorage::UsageMap* AppCacheQuotaClient::GetUsageMap() {
+const std::map<url::Origin, int64_t>& AppCacheQuotaClient::GetUsageMap() const {
   DCHECK(service_);
   return service_->storage()->usage_map();
 }
diff --git a/content/browser/appcache/appcache_quota_client.h b/content/browser/appcache/appcache_quota_client.h
index a5dc1e6..39790d44 100644
--- a/content/browser/appcache/appcache_quota_client.h
+++ b/content/browser/appcache/appcache_quota_client.h
@@ -66,7 +66,7 @@
                         GetOriginsCallback callback);
   void ProcessPendingRequests();
   void DeletePendingRequests();
-  const AppCacheStorage::UsageMap* GetUsageMap();
+  const std::map<url::Origin, int64_t>& GetUsageMap() const;
   net::CancelableCompletionRepeatingCallback* GetServiceDeleteCallback();
 
   // For use by appcache internals during initialization and shutdown.
diff --git a/content/browser/appcache/appcache_request_handler.cc b/content/browser/appcache/appcache_request_handler.cc
index c508103..f97bbe40 100644
--- a/content/browser/appcache/appcache_request_handler.cc
+++ b/content/browser/appcache/appcache_request_handler.cc
@@ -347,7 +347,7 @@
   DCHECK(host_);
 
   if (storage()->IsInitialized() &&
-      !base::Contains(*service_->storage()->usage_map(),
+      !base::Contains(service_->storage()->usage_map(),
                       url::Origin::Create(request_->GetURL()))) {
     return nullptr;
   }
diff --git a/content/browser/appcache/appcache_storage.cc b/content/browser/appcache/appcache_storage.cc
index 016a146..d0cf584 100644
--- a/content/browser/appcache/appcache_storage.cc
+++ b/content/browser/appcache/appcache_storage.cc
@@ -35,7 +35,7 @@
     Delegate* delegate, AppCacheStorage* storage)
     : delegate(delegate), storage(storage) {
   storage->delegate_references_.insert(
-      DelegateReferenceMap::value_type(delegate, this));
+      std::map<Delegate*, DelegateReference*>::value_type(delegate, this));
 }
 
 AppCacheStorage::DelegateReference::~DelegateReference() {
@@ -54,8 +54,7 @@
   storage_->pending_info_loads_[response_id] = base::WrapUnique(this);
 }
 
-AppCacheStorage::ResponseInfoLoadTask::~ResponseInfoLoadTask() {
-}
+AppCacheStorage::ResponseInfoLoadTask::~ResponseInfoLoadTask() = default;
 
 void AppCacheStorage::ResponseInfoLoadTask::StartIfNeeded() {
   if (reader_)
@@ -77,7 +76,10 @@
         storage_->GetWeakPtr(), manifest_url_, response_id_,
         std::move(info_buffer_->http_info), info_buffer_->response_data_size);
   }
-  FOR_EACH_DELEGATE(delegates_, OnResponseInfoLoaded(info.get(), response_id_));
+  AppCacheStorage::ForEachDelegate(
+      delegates_, [&](AppCacheStorage::Delegate* delegate) {
+        delegate->OnResponseInfoLoaded(info.get(), response_id_);
+      });
 
   // returning deletes this
 }
diff --git a/content/browser/appcache/appcache_storage.h b/content/browser/appcache/appcache_storage.h
index 296aa93..73f8fc4 100644
--- a/content/browser/appcache/appcache_storage.h
+++ b/content/browser/appcache/appcache_storage.h
@@ -44,8 +44,6 @@
 
 class CONTENT_EXPORT AppCacheStorage {
  public:
-  using UsageMap = std::map<url::Origin, int64_t>;
-
   class CONTENT_EXPORT Delegate {
    public:
     Delegate(const Delegate&) = delete;
@@ -217,7 +215,7 @@
   AppCacheWorkingSet* working_set() { return &working_set_; }
 
   // A map of origins to usage.
-  const UsageMap* usage_map() { return &usage_map_; }
+  const std::map<url::Origin, int64_t>& usage_map() const { return usage_map_; }
 
   // Simple ptr back to the service object that owns us.
   AppCacheServiceImpl* service() { return service_; }
@@ -230,15 +228,6 @@
   friend class content::AppCacheResponseTest;
   friend class content::appcache_storage_unittest::AppCacheStorageTest;
 
-  // Helper to call a collection of delegates.
-#define FOR_EACH_DELEGATE(delegates, func_and_args)                 \
-  do {                                                              \
-    for (const scoped_refptr<DelegateReference>& ref : delegates) { \
-      if (ref.get()->delegate)                                      \
-        ref.get()->delegate->func_and_args;                         \
-    }                                                               \
-  } while (0)
-
   // Helper used to manage multiple references to a 'delegate' and to
   // allow all pending callbacks to that delegate to be easily cancelled.
   struct CONTENT_EXPORT DelegateReference :
@@ -250,17 +239,28 @@
 
     void CancelReference() {
       storage->delegate_references_.erase(delegate);
-      storage = NULL;
-      delegate = NULL;
+      storage = nullptr;
+      delegate = nullptr;
     }
 
    private:
     friend class base::RefCounted<DelegateReference>;
-
-    virtual ~DelegateReference();
+    ~DelegateReference();
   };
-  using DelegateReferenceMap = std::map<Delegate*, DelegateReference*>;
-  using DelegateReferenceVector = std::vector<scoped_refptr<DelegateReference>>;
+
+  // Helper for calling a function on a collection of delegates.
+  //
+  // ForEachCallable: (AppCacheStorage::Delegate*) -> void
+  template <typename ForEachCallable>
+  static void ForEachDelegate(
+      const std::vector<scoped_refptr<DelegateReference>>& delegates,
+      const ForEachCallable& callable) {
+    for (const scoped_refptr<DelegateReference>& delegate_ref : delegates) {
+      Delegate* delegate = delegate_ref->delegate;
+      if (delegate != nullptr)
+        callable(delegate);
+    }
+  }
 
   // Helper used to manage an async LoadResponseInfo calls on behalf of
   // multiple callers.
@@ -287,12 +287,12 @@
     GURL manifest_url_;
     int64_t response_id_;
     std::unique_ptr<AppCacheResponseReader> reader_;
-    DelegateReferenceVector delegates_;
+    std::vector<scoped_refptr<DelegateReference>> delegates_;
     scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_;
   };
 
   DelegateReference* GetDelegateReference(Delegate* delegate) {
-    DelegateReferenceMap::iterator iter =
+    std::map<Delegate*, DelegateReference*>::iterator iter =
         delegate_references_.find(delegate);
     if (iter != delegate_references_.end())
       return iter->second;
@@ -329,10 +329,10 @@
   int64_t last_response_id_;
 
   // Maps origin to usage (includes padding, unless padding feature is disabled)
-  UsageMap usage_map_;
+  std::map<url::Origin, int64_t> usage_map_;
   AppCacheWorkingSet working_set_;
   AppCacheServiceImpl* service_;
-  DelegateReferenceMap delegate_references_;
+  std::map<Delegate*, DelegateReference*> delegate_references_;
 
   // Note that the ResponseInfoLoadTask items add themselves to this map.
   std::map<int64_t, std::unique_ptr<ResponseInfoLoadTask>> pending_info_loads_;
diff --git a/content/browser/appcache/appcache_storage_impl.cc b/content/browser/appcache/appcache_storage_impl.cc
index cb02db3..4543bf3 100644
--- a/content/browser/appcache/appcache_storage_impl.cc
+++ b/content/browser/appcache/appcache_storage_impl.cc
@@ -175,7 +175,7 @@
 
   AppCacheStorageImpl* storage_;
   AppCacheDatabase* const database_;
-  DelegateReferenceVector delegates_;
+  std::vector<scoped_refptr<DelegateReference>> delegates_;
 
  private:
   void CallRun();
@@ -371,7 +371,10 @@
 
 void AppCacheStorageImpl::GetAllInfoTask::RunCompleted() {
   DCHECK_EQ(1U, delegates_.size());
-  FOR_EACH_DELEGATE(delegates_, OnAllInfo(info_collection_.get()));
+  AppCacheStorage::ForEachDelegate(
+      delegates_, [&](AppCacheStorage::Delegate* delegate) {
+        delegate->OnAllInfo(info_collection_.get());
+      });
 }
 
 // StoreOrLoadTask -------
@@ -517,7 +520,10 @@
     DCHECK(cache_record_.cache_id == cache_id_);
     CreateCacheAndGroupFromRecords(&cache, &group);
   }
-  FOR_EACH_DELEGATE(delegates_, OnCacheLoaded(cache.get(), cache_id_));
+  AppCacheStorage::ForEachDelegate(
+      delegates_, [&](AppCacheStorage::Delegate* delegate) {
+        delegate->OnCacheLoaded(cache.get(), cache_id_);
+      });
 }
 
 // GroupLoadTask -------
@@ -569,7 +575,10 @@
       }
     }
   }
-  FOR_EACH_DELEGATE(delegates_, OnGroupLoaded(group.get(), manifest_url_));
+  AppCacheStorage::ForEachDelegate(
+      delegates_, [&](AppCacheStorage::Delegate* delegate) {
+        delegate->OnGroupLoaded(group.get(), manifest_url_);
+      });
 }
 
 // StoreGroupAndCacheTask -------
@@ -782,10 +791,11 @@
       group_->set_creation_time(group_record_.creation_time);
     group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_);
   }
-  FOR_EACH_DELEGATE(
-      delegates_,
-      OnGroupAndNewestCacheStored(
-          group_.get(), cache_.get(), success_, would_exceed_quota_));
+  AppCacheStorage::ForEachDelegate(
+      delegates_, [&](AppCacheStorage::Delegate* delegate) {
+        delegate->OnGroupAndNewestCacheStored(group_.get(), cache_.get(),
+                                              success_, would_exceed_quota_);
+      });
   group_ = nullptr;
   cache_ = nullptr;
 
@@ -1195,8 +1205,11 @@
       storage_->working_set()->RemoveGroup(group_.get());
     }
   }
-  FOR_EACH_DELEGATE(
-      delegates_, OnGroupMadeObsolete(group_.get(), success_, response_code_));
+
+  AppCacheStorage::ForEachDelegate(
+      delegates_, [&](AppCacheStorage::Delegate* delegate) {
+        delegate->OnGroupMadeObsolete(group_.get(), success_, response_code_);
+      });
   group_ = nullptr;
 }
 
@@ -1587,7 +1600,7 @@
     scoped_refptr<AppCache> cache,
     scoped_refptr<DelegateReference> delegate_ref) {
   if (delegate_ref->delegate) {
-    DelegateReferenceVector delegates(1, delegate_ref);
+    std::vector<scoped_refptr<DelegateReference>> delegates(1, delegate_ref);
     CallOnMainResponseFound(
         &delegates, url, found_entry, GURL(), AppCacheEntry(),
         cache.get() ? cache->cache_id() : blink::mojom::kAppCacheNoCacheId,
@@ -1597,7 +1610,7 @@
 }
 
 void AppCacheStorageImpl::CallOnMainResponseFound(
-    DelegateReferenceVector* delegates,
+    std::vector<scoped_refptr<DelegateReference>>* delegates,
     const GURL& url,
     const AppCacheEntry& entry,
     const GURL& namespace_entry_url,
@@ -1605,11 +1618,12 @@
     int64_t cache_id,
     int64_t group_id,
     const GURL& manifest_url) {
-  FOR_EACH_DELEGATE(
-      (*delegates),
-      OnMainResponseFound(url, entry,
-                          namespace_entry_url, fallback_entry,
-                          cache_id, group_id, manifest_url));
+  AppCacheStorage::ForEachDelegate(
+      *delegates, [&](AppCacheStorage::Delegate* delegate) {
+        delegate->OnMainResponseFound(url, entry, namespace_entry_url,
+                                      fallback_entry, cache_id, group_id,
+                                      manifest_url);
+      });
 }
 
 void AppCacheStorageImpl::FindResponseForSubRequest(
diff --git a/content/browser/appcache/appcache_storage_impl.h b/content/browser/appcache/appcache_storage_impl.h
index de16f25..4e9f73d 100644
--- a/content/browser/appcache/appcache_storage_impl.h
+++ b/content/browser/appcache/appcache_storage_impl.h
@@ -147,14 +147,15 @@
       scoped_refptr<AppCache> newest_cache,
       scoped_refptr<DelegateReference> delegate_ref);
 
-  void CallOnMainResponseFound(DelegateReferenceVector* delegates,
-                               const GURL& url,
-                               const AppCacheEntry& entry,
-                               const GURL& namespace_entry_url,
-                               const AppCacheEntry& fallback_entry,
-                               int64_t cache_id,
-                               int64_t group_id,
-                               const GURL& manifest_url);
+  void CallOnMainResponseFound(
+      std::vector<scoped_refptr<DelegateReference>>* delegates,
+      const GURL& url,
+      const AppCacheEntry& entry,
+      const GURL& namespace_entry_url,
+      const AppCacheEntry& fallback_entry,
+      int64_t cache_id,
+      int64_t group_id,
+      const GURL& manifest_url);
 
   // Don't call this when |is_disabled_| is true.
   CONTENT_EXPORT AppCacheDiskCache* disk_cache();
diff --git a/content/browser/appcache/appcache_update_job.cc b/content/browser/appcache/appcache_update_job.cc
index dcc1c518..49a6342 100644
--- a/content/browser/appcache/appcache_update_job.cc
+++ b/content/browser/appcache/appcache_update_job.cc
@@ -264,14 +264,14 @@
 
 void AppCacheUpdateJob::StartUpdate(AppCacheHost* host,
                                     const GURL& new_master_resource) {
-  DCHECK(group_->update_job() == this);
+  DCHECK_EQ(group_->update_job(), this);
   DCHECK(!group_->is_obsolete());
 
   bool is_new_pending_master_entry = false;
   if (!new_master_resource.is_empty()) {
-    DCHECK(new_master_resource == host->pending_master_entry_url());
+    DCHECK_EQ(new_master_resource, host->pending_master_entry_url());
     DCHECK(!new_master_resource.has_ref());
-    DCHECK(new_master_resource.GetOrigin() == manifest_url_.GetOrigin());
+    DCHECK_EQ(new_master_resource.GetOrigin(), manifest_url_.GetOrigin());
 
     if (base::Contains(failed_master_entries_, new_master_resource))
       return;
@@ -336,7 +336,7 @@
   BrowserThread::PostBestEffortTask(
       FROM_HERE, base::ThreadTaskRunnerHandle::Get(),
       base::BindOnce(&AppCacheUpdateJob::FetchManifest,
-                     weak_factory_.GetWeakPtr(), true));
+                     weak_factory_.GetWeakPtr()));
 }
 
 std::unique_ptr<AppCacheResponseWriter>
@@ -392,31 +392,34 @@
   DeleteSoon();  // To unwind the stack prior to deletion.
 }
 
-void AppCacheUpdateJob::FetchManifest(bool is_first_fetch) {
+void AppCacheUpdateJob::FetchManifest() {
   DCHECK(!manifest_fetcher_);
   manifest_fetcher_ = std::make_unique<URLFetcher>(
-      manifest_url_,
-      is_first_fetch ? URLFetcher::FetchType::kManifest
-                     : URLFetcher::FetchType::kManifestRefetch,
-      this, kAppCacheFetchBufferSize);
+      manifest_url_, URLFetcher::FetchType::kManifest, this,
+      kAppCacheFetchBufferSize);
 
-  if (is_first_fetch) {
-    // Maybe load the cached headers to make a condiditional request.
-    AppCacheEntry* entry =
-        (update_type_ == UPGRADE_ATTEMPT)
-            ? group_->newest_complete_cache()->GetEntry(manifest_url_)
-            : nullptr;
-    if (entry && !doing_full_update_check_) {
-      // Asynchronously load response info for manifest from newest cache.
-      storage_->LoadResponseInfo(manifest_url_, entry->response_id(), this);
-      return;
-    }
-    manifest_fetcher_->Start();
+  // Maybe load the cached headers to make a conditional request.
+  AppCacheEntry* entry =
+      (update_type_ == UPGRADE_ATTEMPT)
+          ? group_->newest_complete_cache()->GetEntry(manifest_url_)
+          : nullptr;
+  if (entry && !doing_full_update_check_) {
+    // Asynchronously load response info for manifest from newest cache.
+    storage_->LoadResponseInfo(manifest_url_, entry->response_id(), this);
     return;
   }
+  manifest_fetcher_->Start();
+  return;
+}
 
-  DCHECK(internal_state_ == REFETCH_MANIFEST);
+void AppCacheUpdateJob::RefetchManifest() {
+  DCHECK(!manifest_fetcher_);
+  DCHECK_EQ(internal_state_, REFETCH_MANIFEST);
   DCHECK(manifest_response_info_.get());
+
+  manifest_fetcher_ = std::make_unique<URLFetcher>(
+      manifest_url_, URLFetcher::FetchType::kManifestRefetch, this,
+      kAppCacheFetchBufferSize);
   manifest_fetcher_->set_existing_response_headers(
       manifest_response_info_->headers.get());
   manifest_fetcher_->Start();
@@ -492,10 +495,10 @@
 }
 
 void AppCacheUpdateJob::ContinueHandleManifestFetchCompleted(bool changed) {
-  DCHECK(internal_state_ == FETCH_MANIFEST);
+  DCHECK_EQ(internal_state_, FETCH_MANIFEST);
 
   if (!changed) {
-    DCHECK(update_type_ == UPGRADE_ATTEMPT);
+    DCHECK_EQ(update_type_, UPGRADE_ATTEMPT);
     internal_state_ = NO_UPDATE;
 
     // Wait for pending master entries to download.
@@ -564,7 +567,7 @@
 
 void AppCacheUpdateJob::HandleResourceFetchCompleted(URLFetcher* url_fetcher,
                                                      int net_error) {
-  DCHECK(internal_state_ == DOWNLOADING);
+  DCHECK_EQ(internal_state_, DOWNLOADING);
 
   UpdateURLLoaderRequest* request = url_fetcher->request();
   const GURL& url = request->GetURL();
@@ -724,7 +727,7 @@
     // In no-update case, associate host with the newest cache.
     if (!inprogress_cache_.get()) {
       // TODO(michaeln): defer until the updated cache has been stored
-      DCHECK(cache == group_->newest_complete_cache());
+      DCHECK_EQ(cache, group_->newest_complete_cache());
       for (AppCacheHost* host : hosts)
         host->AssociateCompleteCache(cache);
     }
@@ -877,7 +880,7 @@
 }
 
 void AppCacheUpdateJob::StoreGroupAndCache() {
-  DCHECK(stored_state_ == UNSTORED);
+  DCHECK_EQ(stored_state_, UNSTORED);
   stored_state_ = STORING;
 
   scoped_refptr<AppCache> newest_cache;
@@ -898,7 +901,7 @@
                                                     AppCache* newest_cache,
                                                     bool success,
                                                     bool would_exceed_quota) {
-  DCHECK(stored_state_ == STORING);
+  DCHECK_EQ(stored_state_, STORING);
   if (success) {
     stored_state_ = STORED;
     MaybeCompleteUpdate();  // will definitely complete
@@ -947,7 +950,7 @@
 }
 
 void AppCacheUpdateJob::NotifyAllFinalProgress() {
-  DCHECK(url_file_list_.size() == url_fetches_completed_);
+  DCHECK_EQ(url_file_list_.size(), url_fetches_completed_);
   NotifyAllProgress(GURL());
 }
 
@@ -1001,7 +1004,7 @@
 }
 
 void AppCacheUpdateJob::CheckIfManifestChanged() {
-  DCHECK(update_type_ == UPGRADE_ATTEMPT);
+  DCHECK_EQ(update_type_, UPGRADE_ATTEMPT);
   AppCacheEntry* entry = nullptr;
   if (group_->newest_complete_cache())
     entry = group_->newest_complete_cache()->GetEntry(manifest_url_);
@@ -1082,7 +1085,7 @@
 }
 
 void AppCacheUpdateJob::FetchUrls() {
-  DCHECK(internal_state_ == DOWNLOADING);
+  DCHECK_EQ(internal_state_, DOWNLOADING);
 
   // Fetch each URL in the list according to section 6.9.4 step 17.1-17.3.
   // Fetch up to the concurrent limit. Other fetches will be triggered as each
@@ -1113,8 +1116,8 @@
         AppCacheEntry* existing_entry =
             group_->newest_complete_cache()->GetEntry(url_to_fetch.url);
         DCHECK(existing_entry);
-        DCHECK(existing_entry->response_id() ==
-               url_to_fetch.existing_response_info->response_id());
+        DCHECK_EQ(existing_entry->response_id(),
+                  url_to_fetch.existing_response_info->response_id());
         fetcher->set_existing_response_headers(
             url_to_fetch.existing_response_info->http_response_info()
                 .headers.get());
@@ -1384,10 +1387,10 @@
       break;
     case DOWNLOADING:
       internal_state_ = REFETCH_MANIFEST;
-      FetchManifest(false);
+      RefetchManifest();
       break;
     case REFETCH_MANIFEST:
-      DCHECK(stored_state_ == STORED);
+      DCHECK_EQ(stored_state_, STORED);
       NotifyAllFinalProgress();
       group_->SetUpdateAppCacheStatus(AppCacheGroup::IDLE);
       if (update_type_ == CACHE_ATTEMPT)
diff --git a/content/browser/appcache/appcache_update_job.h b/content/browser/appcache/appcache_update_job.h
index 555af60..18a1a37 100644
--- a/content/browser/appcache/appcache_update_job.h
+++ b/content/browser/appcache/appcache_update_job.h
@@ -140,7 +140,12 @@
                           ResultType result,
                           const GURL& failed_resource_url);
 
-  void FetchManifest(bool is_first_fetch);
+  // Retrieves the cache's manifest.
+  //
+  // Called when a page referencing this job's manifest is loaded. This can
+  // result in a new cache getting created, or in an existing cache receiving a
+  // new master entry.
+  void FetchManifest();
   void HandleManifestFetchCompleted(URLFetcher* url_fetcher, int net_error);
   void ContinueHandleManifestFetchCompleted(bool changed);
 
@@ -148,7 +153,9 @@
   void HandleNewMasterEntryFetchCompleted(URLFetcher* url_fetcher,
                                           int net_error);
 
+  void RefetchManifest();
   void HandleManifestRefetchCompleted(URLFetcher* url_fetcher, int net_error);
+
   void OnManifestInfoWriteComplete(int result);
   void OnManifestDataWriteComplete(int result);
 
diff --git a/content/browser/appcache/appcache_update_job_unittest.cc b/content/browser/appcache/appcache_update_job_unittest.cc
index c741f1d8..8ddb1f1 100644
--- a/content/browser/appcache/appcache_update_job_unittest.cc
+++ b/content/browser/appcache/appcache_update_job_unittest.cc
@@ -3126,7 +3126,7 @@
     group_->update_status_ = AppCacheGroup::DOWNLOADING;
     update->manifest_response_info_ = std::move(response_info);
     update->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
-    update->FetchManifest(false);  // not first request
+    update->RefetchManifest();
 
     // We need to wait for the URL load requests to make it to the
     // MockURLLoaderFactory.
@@ -3165,7 +3165,7 @@
     group_->update_status_ = AppCacheGroup::DOWNLOADING;
     update->manifest_response_info_ = std::move(response_info);
     update->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
-    update->FetchManifest(false);  // not first request
+    update->RefetchManifest();
 
     // We need to wait for the URL load requests to make it to the
     // MockURLLoaderFactory.
@@ -3336,7 +3336,7 @@
     group_->update_status_ = AppCacheGroup::DOWNLOADING;
     update->manifest_response_info_ = std::move(response_info);
     update->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
-    update->FetchManifest(false);  // not first request
+    update->RefetchManifest();
 
     // We need to wait for the URL load requests to make it to the
     // MockURLLoaderFactory.
@@ -3377,7 +3377,7 @@
     group_->update_status_ = AppCacheGroup::DOWNLOADING;
     update->manifest_response_info_ = std::move(response_info);
     update->internal_state_ = AppCacheUpdateJob::REFETCH_MANIFEST;
-    update->FetchManifest(false);  // not first request
+    update->RefetchManifest();
 
     // We need to wait for the URL load requests to make it to the
     // MockURLLoaderFactory.
diff --git a/content/browser/appcache/appcache_working_set.cc b/content/browser/appcache/appcache_working_set.cc
index 144ffdc1..5182100 100644
--- a/content/browser/appcache/appcache_working_set.cc
+++ b/content/browser/appcache/appcache_working_set.cc
@@ -36,7 +36,7 @@
   DCHECK(cache->cache_id() != blink::mojom::kAppCacheNoCacheId);
   int64_t cache_id = cache->cache_id();
   DCHECK(caches_.find(cache_id) == caches_.end());
-  caches_.insert(CacheMap::value_type(cache_id, cache));
+  caches_.emplace(cache_id, cache);
 }
 
 void AppCacheWorkingSet::RemoveCache(AppCache* cache) {
@@ -48,9 +48,8 @@
     return;
   const GURL& url = group->manifest_url();
   DCHECK(groups_.find(url) == groups_.end());
-  groups_.insert(GroupMap::value_type(url, group));
-  groups_by_origin_[url::Origin::Create(url)].insert(
-      GroupMap::value_type(url, group));
+  groups_.emplace(url, group);
+  groups_by_origin_[url::Origin::Create(url)].emplace(url, group);
 }
 
 void AppCacheWorkingSet::RemoveGroup(AppCacheGroup* group) {
@@ -72,7 +71,7 @@
   DCHECK(info->response_id() != blink::mojom::kAppCacheNoResponseId);
   int64_t response_id = info->response_id();
   DCHECK(response_infos_.find(response_id) == response_infos_.end());
-  response_infos_.insert(ResponseInfoMap::value_type(response_id, info));
+  response_infos_.emplace(response_id, info);
 }
 
 void AppCacheWorkingSet::RemoveResponseInfo(AppCacheResponseInfo* info) {
diff --git a/content/browser/appcache/appcache_working_set.h b/content/browser/appcache/appcache_working_set.h
index caede7c7..751ec57 100644
--- a/content/browser/appcache/appcache_working_set.h
+++ b/content/browser/appcache/appcache_working_set.h
@@ -35,15 +35,15 @@
   void AddCache(AppCache* cache);
   void RemoveCache(AppCache* cache);
   AppCache* GetCache(int64_t id) {
-    CacheMap::iterator it = caches_.find(id);
-    return (it != caches_.end()) ? it->second : NULL;
+    auto it = caches_.find(id);
+    return (it != caches_.end()) ? it->second : nullptr;
   }
 
   void AddGroup(AppCacheGroup* group);
   void RemoveGroup(AppCacheGroup* group);
   AppCacheGroup* GetGroup(const GURL& manifest_url) {
-    GroupMap::iterator it = groups_.find(manifest_url);
-    return (it != groups_.end()) ? it->second : NULL;
+    auto it = groups_.find(manifest_url);
+    return (it != groups_.end()) ? it->second : nullptr;
   }
 
   const GroupMap* GetGroupsInOrigin(const url::Origin& origin) {
@@ -53,24 +53,21 @@
   void AddResponseInfo(AppCacheResponseInfo* response_info);
   void RemoveResponseInfo(AppCacheResponseInfo* response_info);
   AppCacheResponseInfo* GetResponseInfo(int64_t id) {
-    ResponseInfoMap::iterator it = response_infos_.find(id);
-    return (it != response_infos_.end()) ? it->second : NULL;
+    auto it = response_infos_.find(id);
+    return (it != response_infos_.end()) ? it->second : nullptr;
   }
 
  private:
-  using CacheMap = std::unordered_map<int64_t, AppCache*>;
-  using GroupsByOriginMap = std::map<url::Origin, GroupMap>;
-  using ResponseInfoMap = std::unordered_map<int64_t, AppCacheResponseInfo*>;
-
   GroupMap* GetMutableGroupsInOrigin(const url::Origin& origin) {
-    GroupsByOriginMap::iterator it = groups_by_origin_.find(origin);
-    return (it != groups_by_origin_.end()) ? &it->second : NULL;
+    auto it = groups_by_origin_.find(origin);
+    return (it != groups_by_origin_.end()) ? &it->second : nullptr;
   }
 
-  CacheMap caches_;
+  std::unordered_map<int64_t, AppCache*> caches_;
   GroupMap groups_;
-  GroupsByOriginMap groups_by_origin_;  // origin -> (manifest -> group)
-  ResponseInfoMap response_infos_;
+  // origin -> (manifest -> group)
+  std::map<url::Origin, GroupMap> groups_by_origin_;
+  std::unordered_map<int64_t, AppCacheResponseInfo*> response_infos_;
   bool is_disabled_;
 };
 
diff --git a/content/browser/background_sync/background_sync_context_impl.cc b/content/browser/background_sync/background_sync_context_impl.cc
index 66a9ed4..33b8b07 100644
--- a/content/browser/background_sync/background_sync_context_impl.cc
+++ b/content/browser/background_sync/background_sync_context_impl.cc
@@ -12,7 +12,8 @@
 #include "base/task/task_traits.h"
 #include "content/browser/background_sync/background_sync_launcher.h"
 #include "content/browser/background_sync/background_sync_manager.h"
-#include "content/browser/background_sync/background_sync_service_impl.h"
+#include "content/browser/background_sync/one_shot_background_sync_service_impl.h"
+#include "content/browser/background_sync/periodic_background_sync_service_impl.h"
 #include "content/browser/devtools/devtools_background_services_context_impl.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -31,7 +32,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   DCHECK(!background_sync_manager_);
-  DCHECK(services_.empty());
+  DCHECK(one_shot_sync_services_.empty());
+  DCHECK(periodic_sync_services_.empty());
 }
 
 // static
@@ -75,21 +77,44 @@
       base::BindOnce(&BackgroundSyncContextImpl::ShutdownOnIO, this));
 }
 
-void BackgroundSyncContextImpl::CreateService(
-    blink::mojom::BackgroundSyncServiceRequest request) {
+void BackgroundSyncContextImpl::CreateOneShotSyncService(
+    blink::mojom::OneShotBackgroundSyncServiceRequest request) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&BackgroundSyncContextImpl::CreateServiceOnIOThread, this,
-                     std::move(request)));
+      base::BindOnce(
+          &BackgroundSyncContextImpl::CreateOneShotSyncServiceOnIOThread, this,
+          std::move(request)));
 }
 
-void BackgroundSyncContextImpl::ServiceHadConnectionError(
-    BackgroundSyncServiceImpl* service) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(base::Contains(services_, service));
+void BackgroundSyncContextImpl::CreatePeriodicSyncService(
+    blink::mojom::PeriodicBackgroundSyncServiceRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(
+          &BackgroundSyncContextImpl::CreatePeriodicSyncServiceOnIOThread, this,
+          std::move(request)));
+}
 
-  services_.erase(service);
+void BackgroundSyncContextImpl::OneShotSyncServiceHadConnectionError(
+    OneShotBackgroundSyncServiceImpl* service) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(service);
+
+  auto iter = one_shot_sync_services_.find(service);
+  DCHECK(iter != one_shot_sync_services_.end());
+  one_shot_sync_services_.erase(iter);
+}
+
+void BackgroundSyncContextImpl::PeriodicSyncServiceHadConnectionError(
+    PeriodicBackgroundSyncServiceImpl* service) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(service);
+
+  auto iter = periodic_sync_services_.find(service);
+  DCHECK(iter != periodic_sync_services_.end());
+  periodic_sync_services_.erase(iter);
 }
 
 BackgroundSyncManager* BackgroundSyncContextImpl::background_sync_manager()
@@ -186,19 +211,31 @@
       std::move(service_worker_context), std::move(devtools_context));
 }
 
-void BackgroundSyncContextImpl::CreateServiceOnIOThread(
-    mojo::InterfaceRequest<blink::mojom::BackgroundSyncService> request) {
+void BackgroundSyncContextImpl::CreateOneShotSyncServiceOnIOThread(
+    mojo::InterfaceRequest<blink::mojom::OneShotBackgroundSyncService>
+        request) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(background_sync_manager_);
-  auto service =
-      std::make_unique<BackgroundSyncServiceImpl>(this, std::move(request));
-  services_[service.get()] = std::move(service);
+  one_shot_sync_services_.insert(
+      std::make_unique<OneShotBackgroundSyncServiceImpl>(this,
+                                                         std::move(request)));
+}
+
+void BackgroundSyncContextImpl::CreatePeriodicSyncServiceOnIOThread(
+    mojo::InterfaceRequest<blink::mojom::PeriodicBackgroundSyncService>
+        request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(background_sync_manager_);
+  periodic_sync_services_.insert(
+      std::make_unique<PeriodicBackgroundSyncServiceImpl>(this,
+                                                          std::move(request)));
 }
 
 void BackgroundSyncContextImpl::ShutdownOnIO() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  services_.clear();
+  one_shot_sync_services_.clear();
+  periodic_sync_services_.clear();
   background_sync_manager_.reset();
 }
 
diff --git a/content/browser/background_sync/background_sync_context_impl.h b/content/browser/background_sync/background_sync_context_impl.h
index 47fb476..b256e5a 100644
--- a/content/browser/background_sync/background_sync_context_impl.h
+++ b/content/browser/background_sync/background_sync_context_impl.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <memory>
 
+#include "base/containers/unique_ptr_adapters.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted_delete_on_sequence.h"
 #include "base/memory/scoped_refptr.h"
@@ -20,8 +21,9 @@
 namespace content {
 
 class BackgroundSyncManager;
-class BackgroundSyncServiceImpl;
 class DevToolsBackgroundServicesContextImpl;
+class OneShotBackgroundSyncServiceImpl;
+class PeriodicBackgroundSyncServiceImpl;
 class ServiceWorkerContextWrapper;
 
 // One instance of this exists per StoragePartition, and services multiple child
@@ -45,13 +47,22 @@
   // Shutdown must be called before deleting this. Call on the UI thread.
   void Shutdown();
 
-  // Create a BackgroundSyncServiceImpl that is owned by this. Call on the UI
-  // thread.
-  void CreateService(blink::mojom::BackgroundSyncServiceRequest request);
+  // Create a OneShotBackgroundSyncServiceImpl that is owned by this. Call on
+  // the UI thread.
+  void CreateOneShotSyncService(
+      blink::mojom::OneShotBackgroundSyncServiceRequest request);
 
-  // Called by BackgroundSyncServiceImpl objects so that they can
+  // Create a PeriodicBackgroundSyncServiceImpl that is owned by this. Call on
+  // the UI thread.
+  void CreatePeriodicSyncService(
+      blink::mojom::PeriodicBackgroundSyncServiceRequest request);
+
+  // Called by *BackgroundSyncServiceImpl objects so that they can
   // be deleted. Call on the IO thread.
-  void ServiceHadConnectionError(BackgroundSyncServiceImpl* service);
+  void OneShotSyncServiceHadConnectionError(
+      OneShotBackgroundSyncServiceImpl* service);
+  void PeriodicSyncServiceHadConnectionError(
+      PeriodicBackgroundSyncServiceImpl* service);
 
   // Call on the IO thread.
   BackgroundSyncManager* background_sync_manager() const;
@@ -71,7 +82,8 @@
   void set_wakeup_delta_for_testing(base::TimeDelta wakeup_delta);
 
  private:
-  friend class BackgroundSyncServiceImplTest;
+  friend class OneShotBackgroundSyncServiceImplTest;
+  friend class PeriodicBackgroundSyncServiceImplTest;
   friend class BackgroundSyncLauncherTest;
 
   void FireBackgroundSyncEventsOnIOThread(base::OnceClosure done_closure);
@@ -80,8 +92,12 @@
       scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
       scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context);
 
-  void CreateServiceOnIOThread(
-      mojo::InterfaceRequest<blink::mojom::BackgroundSyncService> request);
+  void CreateOneShotSyncServiceOnIOThread(
+      mojo::InterfaceRequest<blink::mojom::OneShotBackgroundSyncService>
+          request);
+  void CreatePeriodicSyncServiceOnIOThread(
+      mojo::InterfaceRequest<blink::mojom::PeriodicBackgroundSyncService>
+          request);
 
   void ShutdownOnIO();
 
@@ -90,15 +106,19 @@
       base::OnceCallback<void(base::TimeDelta)> callback,
       base::TimeDelta soonest_wakeup_delta);
 
+  // The services are owned by this. They're either deleted
+  // during ShutdownOnIO or when the channel is closed via
+  // *ServiceHadConnectionError. Only accessed on the IO thread.
+  std::set<std::unique_ptr<OneShotBackgroundSyncServiceImpl>,
+           base::UniquePtrComparator>
+      one_shot_sync_services_;
+  std::set<std::unique_ptr<PeriodicBackgroundSyncServiceImpl>,
+           base::UniquePtrComparator>
+      periodic_sync_services_;
+
   // Only accessed on the IO thread.
   std::unique_ptr<BackgroundSyncManager> background_sync_manager_;
 
-  // The services are owned by this. They're either deleted
-  // during ShutdownOnIO or when the channel is closed via
-  // ServiceHadConnectionError. Only accessed on the IO thread.
-  std::map<BackgroundSyncServiceImpl*,
-           std::unique_ptr<BackgroundSyncServiceImpl>>
-      services_;
   base::TimeDelta test_wakeup_delta_ = base::TimeDelta::Max();
 
   DISALLOW_COPY_AND_ASSIGN(BackgroundSyncContextImpl);
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index 3b4c6fed..9c697555 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -46,6 +46,28 @@
 
 namespace content {
 
+// TODO(crbug.com/932591): Use blink::mojom::BackgroundSyncError
+// directly and eliminate these checks.
+#define COMPILE_ASSERT_MATCHING_ENUM(mojo_name, manager_name) \
+  static_assert(static_cast<int>(blink::mojo_name) ==         \
+                    static_cast<int>(content::manager_name),  \
+                "mojo and manager enums must match")
+
+COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NONE,
+                             BACKGROUND_SYNC_STATUS_OK);
+COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::STORAGE,
+                             BACKGROUND_SYNC_STATUS_STORAGE_ERROR);
+COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NOT_FOUND,
+                             BACKGROUND_SYNC_STATUS_NOT_FOUND);
+COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NO_SERVICE_WORKER,
+                             BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER);
+COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NOT_ALLOWED,
+                             BACKGROUND_SYNC_STATUS_NOT_ALLOWED);
+COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::PERMISSION_DENIED,
+                             BACKGROUND_SYNC_STATUS_PERMISSION_DENIED);
+COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::MAX,
+                             BACKGROUND_SYNC_STATUS_PERMISSION_DENIED);
+
 namespace {
 
 // The only allowed value of min_interval for one shot Background Sync
diff --git a/content/browser/background_sync/background_sync_registration_helper.cc b/content/browser/background_sync/background_sync_registration_helper.cc
new file mode 100644
index 0000000..825bfe7d
--- /dev/null
+++ b/content/browser/background_sync/background_sync_registration_helper.cc
@@ -0,0 +1,94 @@
+// 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 "content/browser/background_sync/background_sync_registration_helper.h"
+
+#include "base/memory/weak_ptr.h"
+#include "content/browser/background_sync/background_sync_context_impl.h"
+#include "content/browser/background_sync/background_sync_manager.h"
+#include "content/browser/background_sync/background_sync_status.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+BackgroundSyncRegistrationHelper::BackgroundSyncRegistrationHelper(
+    BackgroundSyncContextImpl* background_sync_context)
+    : background_sync_context_(background_sync_context),
+      weak_ptr_factory_(this) {
+  DCHECK(background_sync_context_);
+}
+
+BackgroundSyncRegistrationHelper::~BackgroundSyncRegistrationHelper() = default;
+
+void BackgroundSyncRegistrationHelper::Register(
+    blink::mojom::SyncRegistrationOptionsPtr options,
+    int64_t sw_registration_id,
+    RegisterCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  BackgroundSyncManager* background_sync_manager =
+      background_sync_context_->background_sync_manager();
+  DCHECK(background_sync_manager);
+
+  background_sync_manager->Register(
+      sw_registration_id, *options,
+      base::BindOnce(&BackgroundSyncRegistrationHelper::OnRegisterResult,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void BackgroundSyncRegistrationHelper::DidResolveRegistration(
+    blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  BackgroundSyncManager* background_sync_manager =
+      background_sync_context_->background_sync_manager();
+  DCHECK(background_sync_manager);
+
+  background_sync_manager->DidResolveRegistration(std::move(registration_info));
+}
+
+void BackgroundSyncRegistrationHelper::OnRegisterResult(
+    RegisterCallback callback,
+    BackgroundSyncStatus status,
+    std::unique_ptr<BackgroundSyncRegistration> result) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // TODO(crbug.com/932591): Use blink::mojom::BackgroundSyncError
+  // directly.
+  if (status != BACKGROUND_SYNC_STATUS_OK) {
+    std::move(callback).Run(
+        static_cast<blink::mojom::BackgroundSyncError>(status),
+        /* options= */ nullptr);
+    return;
+  }
+
+  DCHECK(result);
+  std::move(callback).Run(
+      static_cast<blink::mojom::BackgroundSyncError>(status),
+      result->options()->Clone());
+}
+
+void BackgroundSyncRegistrationHelper::OnGetRegistrationsResult(
+    GetRegistrationsCallback callback,
+    BackgroundSyncStatus status,
+    std::vector<std::unique_ptr<BackgroundSyncRegistration>>
+        result_registrations) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  std::vector<blink::mojom::SyncRegistrationOptionsPtr> mojo_registrations;
+  mojo_registrations.reserve(result_registrations.size());
+  for (const auto& registration : result_registrations)
+    mojo_registrations.push_back(registration->options()->Clone());
+
+  std::move(callback).Run(
+      static_cast<blink::mojom::BackgroundSyncError>(status),
+      std::move(mojo_registrations));
+}
+
+base::WeakPtr<BackgroundSyncRegistrationHelper>
+BackgroundSyncRegistrationHelper::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+}  // namespace content
diff --git a/content/browser/background_sync/background_sync_registration_helper.h b/content/browser/background_sync/background_sync_registration_helper.h
new file mode 100644
index 0000000..04edbfe
--- /dev/null
+++ b/content/browser/background_sync/background_sync_registration_helper.h
@@ -0,0 +1,62 @@
+// 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 CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_REGISTRATION_HELPER_H_
+#define CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_REGISTRATION_HELPER_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/background_sync/background_sync_registration.h"
+#include "content/browser/background_sync/background_sync_status.h"
+#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h"
+
+namespace content {
+
+class BackgroundSyncContextImpl;
+
+// Used by OneShotBackgroundSyncService and PeriodicBackgroundSyncService to
+// create and get BackgroundSync registrations.
+class BackgroundSyncRegistrationHelper {
+ public:
+  using RegisterCallback =
+      base::OnceCallback<void(blink::mojom::BackgroundSyncError status,
+                              blink::mojom::SyncRegistrationOptionsPtr result)>;
+  using GetRegistrationsCallback = base::OnceCallback<void(
+      blink::mojom::BackgroundSyncError status,
+      std::vector<blink::mojom::SyncRegistrationOptionsPtr> results)>;
+
+  explicit BackgroundSyncRegistrationHelper(
+      BackgroundSyncContextImpl* background_sync_context);
+  ~BackgroundSyncRegistrationHelper();
+
+  void Register(blink::mojom::SyncRegistrationOptionsPtr options,
+                int64_t sw_registration_id,
+                RegisterCallback callback);
+  void DidResolveRegistration(
+      blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info);
+  void OnRegisterResult(RegisterCallback callback,
+                        BackgroundSyncStatus status,
+                        std::unique_ptr<BackgroundSyncRegistration> result);
+  void OnGetRegistrationsResult(
+      GetRegistrationsCallback callback,
+      BackgroundSyncStatus status,
+      std::vector<std::unique_ptr<BackgroundSyncRegistration>>
+          result_registrations);
+
+  base::WeakPtr<BackgroundSyncRegistrationHelper> GetWeakPtr();
+
+ private:
+  // |background_sync_context_| (indirectly) owns |this|.
+  BackgroundSyncContextImpl* const background_sync_context_;
+  base::WeakPtrFactory<BackgroundSyncRegistrationHelper> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundSyncRegistrationHelper);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_REGISTRATION_HELPER_H_
diff --git a/content/browser/background_sync/background_sync_service_impl.cc b/content/browser/background_sync/background_sync_service_impl.cc
deleted file mode 100644
index 0808e03..0000000
--- a/content/browser/background_sync/background_sync_service_impl.cc
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/background_sync/background_sync_service_impl.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/memory/weak_ptr.h"
-#include "base/stl_util.h"
-#include "content/browser/background_sync/background_sync_context_impl.h"
-#include "content/public/browser/browser_thread.h"
-
-namespace content {
-
-#define COMPILE_ASSERT_MATCHING_ENUM(mojo_name, manager_name) \
-  static_assert(static_cast<int>(blink::mojo_name) ==         \
-                    static_cast<int>(content::manager_name),  \
-                "mojo and manager enums must match")
-
-// TODO(iclelland): Move these tests somewhere else
-COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NONE,
-                             BACKGROUND_SYNC_STATUS_OK);
-COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::STORAGE,
-                             BACKGROUND_SYNC_STATUS_STORAGE_ERROR);
-COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NOT_FOUND,
-                             BACKGROUND_SYNC_STATUS_NOT_FOUND);
-COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NO_SERVICE_WORKER,
-                             BACKGROUND_SYNC_STATUS_NO_SERVICE_WORKER);
-COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::NOT_ALLOWED,
-                             BACKGROUND_SYNC_STATUS_NOT_ALLOWED);
-COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::PERMISSION_DENIED,
-                             BACKGROUND_SYNC_STATUS_PERMISSION_DENIED);
-COMPILE_ASSERT_MATCHING_ENUM(mojom::BackgroundSyncError::MAX,
-                             BACKGROUND_SYNC_STATUS_PERMISSION_DENIED);
-
-BackgroundSyncServiceImpl::~BackgroundSyncServiceImpl() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(background_sync_context_->background_sync_manager());
-}
-
-BackgroundSyncServiceImpl::BackgroundSyncServiceImpl(
-    BackgroundSyncContextImpl* background_sync_context,
-    mojo::InterfaceRequest<blink::mojom::BackgroundSyncService> request)
-    : background_sync_context_(background_sync_context),
-      binding_(this, std::move(request)),
-      weak_ptr_factory_(this) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(background_sync_context);
-
-  binding_.set_connection_error_handler(base::BindOnce(
-      &BackgroundSyncServiceImpl::OnConnectionError,
-      base::Unretained(this) /* the channel is owned by this */));
-}
-
-void BackgroundSyncServiceImpl::OnConnectionError() {
-  background_sync_context_->ServiceHadConnectionError(this);
-  // |this| is now deleted.
-}
-
-void BackgroundSyncServiceImpl::Register(
-    blink::mojom::SyncRegistrationOptionsPtr options,
-    int64_t sw_registration_id,
-    RegisterCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  BackgroundSyncManager* background_sync_manager =
-      background_sync_context_->background_sync_manager();
-  DCHECK(background_sync_manager);
-  background_sync_manager->Register(
-      sw_registration_id, *options,
-      base::BindOnce(&BackgroundSyncServiceImpl::OnRegisterResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void BackgroundSyncServiceImpl::DidResolveRegistration(
-    blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  BackgroundSyncManager* background_sync_manager =
-      background_sync_context_->background_sync_manager();
-  DCHECK(background_sync_manager);
-  background_sync_manager->DidResolveRegistration(std::move(registration_info));
-}
-
-void BackgroundSyncServiceImpl::GetOneShotSyncRegistrations(
-    int64_t sw_registration_id,
-    GetOneShotSyncRegistrationsCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  BackgroundSyncManager* background_sync_manager =
-      background_sync_context_->background_sync_manager();
-  DCHECK(background_sync_manager);
-  background_sync_manager->GetOneShotSyncRegistrations(
-      sw_registration_id,
-      base::BindOnce(&BackgroundSyncServiceImpl::OnGetRegistrationsResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void BackgroundSyncServiceImpl::OnRegisterResult(
-    RegisterCallback callback,
-    BackgroundSyncStatus status,
-    std::unique_ptr<BackgroundSyncRegistration> result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  if (status != BACKGROUND_SYNC_STATUS_OK) {
-    std::move(callback).Run(
-        static_cast<blink::mojom::BackgroundSyncError>(status),
-        blink::mojom::SyncRegistrationOptions::New());
-    return;
-  }
-
-  DCHECK(result);
-  blink::mojom::SyncRegistrationOptionsPtr mojo_options =
-      blink::mojom::SyncRegistrationOptions::New(*result->options());
-  std::move(callback).Run(
-      static_cast<blink::mojom::BackgroundSyncError>(status),
-      std::move(mojo_options));
-}
-
-void BackgroundSyncServiceImpl::OnGetRegistrationsResult(
-    GetOneShotSyncRegistrationsCallback callback,
-    BackgroundSyncStatus status,
-    std::vector<std::unique_ptr<BackgroundSyncRegistration>>
-        result_registrations) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  std::vector<blink::mojom::SyncRegistrationOptionsPtr> mojo_registrations;
-  for (const auto& registration : result_registrations) {
-    mojo_registrations.push_back(
-        blink::mojom::SyncRegistrationOptions::New(*registration->options()));
-  }
-
-  std::move(callback).Run(
-      static_cast<blink::mojom::BackgroundSyncError>(status),
-      std::move(mojo_registrations));
-}
-
-}  // namespace content
diff --git a/content/browser/background_sync/background_sync_service_impl.h b/content/browser/background_sync/background_sync_service_impl.h
deleted file mode 100644
index 91cfefe..0000000
--- a/content/browser/background_sync/background_sync_service_impl.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_SERVICE_IMPL_H_
-#define CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_SERVICE_IMPL_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/containers/id_map.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "content/browser/background_sync/background_sync_manager.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h"
-
-namespace content {
-
-class BackgroundSyncContextImpl;
-
-class CONTENT_EXPORT BackgroundSyncServiceImpl
-    : public blink::mojom::BackgroundSyncService {
- public:
-  BackgroundSyncServiceImpl(
-      BackgroundSyncContextImpl* background_sync_context,
-      mojo::InterfaceRequest<blink::mojom::BackgroundSyncService> request);
-
-  ~BackgroundSyncServiceImpl() override;
-
- private:
-  friend class BackgroundSyncServiceImplTest;
-
-  // blink::mojom::BackgroundSyncService methods:
-  void Register(blink::mojom::SyncRegistrationOptionsPtr options,
-                int64_t sw_registration_id,
-                RegisterCallback callback) override;
-  void DidResolveRegistration(blink::mojom::BackgroundSyncRegistrationInfoPtr
-                                  registration_info) override;
-  void GetOneShotSyncRegistrations(
-      int64_t sw_registration_id,
-      GetOneShotSyncRegistrationsCallback callback) override;
-
-  void OnRegisterResult(RegisterCallback callback,
-                        BackgroundSyncStatus status,
-                        std::unique_ptr<BackgroundSyncRegistration> result);
-  void OnGetRegistrationsResult(
-      GetOneShotSyncRegistrationsCallback callback,
-      BackgroundSyncStatus status,
-      std::vector<std::unique_ptr<BackgroundSyncRegistration>> result);
-
-  // Called when an error is detected on binding_.
-  void OnConnectionError();
-
-  // |background_sync_context_| owns |this|.
-  BackgroundSyncContextImpl* background_sync_context_;
-
-  mojo::Binding<blink::mojom::BackgroundSyncService> binding_;
-
-  base::WeakPtrFactory<BackgroundSyncServiceImpl> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(BackgroundSyncServiceImpl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_SERVICE_IMPL_H_
diff --git a/content/browser/background_sync/background_sync_service_impl_test_harness.cc b/content/browser/background_sync/background_sync_service_impl_test_harness.cc
new file mode 100644
index 0000000..2de0ca0
--- /dev/null
+++ b/content/browser/background_sync/background_sync_service_impl_test_harness.cc
@@ -0,0 +1,179 @@
+// 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 "content/browser/background_sync/background_sync_service_impl_test_harness.h"
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/run_loop.h"
+#include "content/browser/background_sync/background_sync_manager.h"
+#include "content/browser/background_sync/background_sync_network_observer.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/permission_type.h"
+#include "content/public/test/background_sync_test_util.h"
+#include "content/public/test/mock_permission_manager.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/test/test_background_sync_context.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+
+namespace content {
+
+using ::testing::_;
+
+const char kServiceWorkerScope[] = "https://example.com/a";
+const char kServiceWorkerScript[] = "https://example.com/a/script.js";
+
+void BackgroundSyncServiceImplTestHarness::RegisterServiceWorkerCallback(
+    bool* called,
+    int64_t* out_registration_id,
+    blink::ServiceWorkerStatusCode status,
+    const std::string& status_message,
+    int64_t registration_id) {
+  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
+      << blink::ServiceWorkerStatusToString(status);
+  *called = true;
+  *out_registration_id = registration_id;
+}
+
+void BackgroundSyncServiceImplTestHarness::
+    FindServiceWorkerRegistrationCallback(
+        scoped_refptr<ServiceWorkerRegistration>* out_registration,
+        blink::ServiceWorkerStatusCode status,
+        scoped_refptr<ServiceWorkerRegistration> registration) {
+  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
+      << blink::ServiceWorkerStatusToString(status);
+  *out_registration = std::move(registration);
+}
+
+void BackgroundSyncServiceImplTestHarness::ErrorAndRegistrationCallback(
+    bool* called,
+    blink::mojom::BackgroundSyncError* out_error,
+    blink::mojom::SyncRegistrationOptionsPtr* out_registration,
+    blink::mojom::BackgroundSyncError error,
+    blink::mojom::SyncRegistrationOptionsPtr registration) {
+  *called = true;
+  *out_error = error;
+  *out_registration = registration.Clone();
+}
+
+void BackgroundSyncServiceImplTestHarness::ErrorAndRegistrationListCallback(
+    bool* called,
+    blink::mojom::BackgroundSyncError* out_error,
+    unsigned long* out_array_size,
+    blink::mojom::BackgroundSyncError error,
+    std::vector<blink::mojom::SyncRegistrationOptionsPtr> registrations) {
+  *called = true;
+  *out_error = error;
+  if (error == blink::mojom::BackgroundSyncError::NONE)
+    *out_array_size = registrations.size();
+}
+
+void BackgroundSyncServiceImplTestHarness::ErrorCallback(
+    bool* called,
+    blink::mojom::BackgroundSyncError* out_error,
+    blink::mojom::BackgroundSyncError error) {
+  *called = true;
+  *out_error = error;
+}
+
+BackgroundSyncServiceImplTestHarness::BackgroundSyncServiceImplTestHarness()
+    : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {
+  default_sync_registration_ = blink::mojom::SyncRegistrationOptions::New();
+}
+
+BackgroundSyncServiceImplTestHarness::~BackgroundSyncServiceImplTestHarness() =
+    default;
+
+void BackgroundSyncServiceImplTestHarness::SetUp() {
+  // Don't let the tests be confused by the real-world device connectivity
+  background_sync_test_util::SetIgnoreNetworkChanges(true);
+
+  CreateTestHelper();
+  CreateStoragePartition();
+  CreateBackgroundSyncContext();
+  ASSERT_NO_FATAL_FAILURE(CreateServiceWorkerRegistration());
+}
+
+void BackgroundSyncServiceImplTestHarness::TearDown() {
+  // This must be explicitly destroyed here to ensure that destruction
+  // of both the BackgroundSyncContextImpl and the BackgroundSyncManager
+  // occurs on the correct thread.
+  background_sync_context_->Shutdown();
+  base::RunLoop().RunUntilIdle();
+  background_sync_context_ = nullptr;
+
+  // Restore the network observer functionality for subsequent tests
+  background_sync_test_util::SetIgnoreNetworkChanges(false);
+}
+
+// SetUp helper methods
+void BackgroundSyncServiceImplTestHarness::CreateTestHelper() {
+  embedded_worker_helper_ =
+      std::make_unique<EmbeddedWorkerTestHelper>((base::FilePath()));
+  std::unique_ptr<MockPermissionManager> mock_permission_manager =
+      std::make_unique<testing::NiceMock<MockPermissionManager>>();
+  ON_CALL(*mock_permission_manager,
+          GetPermissionStatus(PermissionType::BACKGROUND_SYNC, _, _))
+      .WillByDefault(testing::Return(blink::mojom::PermissionStatus::GRANTED));
+  embedded_worker_helper_->browser_context()->SetPermissionControllerDelegate(
+      std::move(mock_permission_manager));
+}
+
+void BackgroundSyncServiceImplTestHarness::CreateStoragePartition() {
+  // Creates a StoragePartition so that the BackgroundSyncManager can
+  // use it to access the BrowserContext.
+  storage_partition_impl_ = StoragePartitionImpl::Create(
+      embedded_worker_helper_->browser_context(), /* in_memory= */ true,
+      base::FilePath(), /* partition_domain= */ "");
+  embedded_worker_helper_->context_wrapper()->set_storage_partition(
+      storage_partition_impl_.get());
+}
+
+void BackgroundSyncServiceImplTestHarness::CreateBackgroundSyncContext() {
+  // Registering for background sync includes a check for having a same-origin
+  // main frame. Use a test context that allows control over that check.
+  background_sync_context_ = base::MakeRefCounted<TestBackgroundSyncContext>();
+  background_sync_context_->Init(
+      embedded_worker_helper_->context_wrapper(),
+      storage_partition_impl_->GetDevToolsBackgroundServicesContext());
+
+  // Tests do not expect the sync event to fire immediately after
+  // register (and cleanup up the sync registrations).  Prevent the sync
+  // event from firing by setting the network state to have no connection.
+  // NOTE: The setup of the network connection must happen after the
+  //       BackgroundSyncManager has been setup, including any asynchronous
+  //       initialization.
+  base::RunLoop().RunUntilIdle();
+  BackgroundSyncNetworkObserver* network_observer =
+      background_sync_context_->background_sync_manager()
+          ->GetNetworkObserverForTesting();
+  network_observer->NotifyManagerIfConnectionChangedForTesting(
+      network::mojom::ConnectionType::CONNECTION_NONE);
+  base::RunLoop().RunUntilIdle();
+}
+
+void BackgroundSyncServiceImplTestHarness::CreateServiceWorkerRegistration() {
+  bool called = false;
+  blink::mojom::ServiceWorkerRegistrationOptions options;
+  options.scope = GURL(kServiceWorkerScope);
+  embedded_worker_helper_->context()->RegisterServiceWorker(
+      GURL(kServiceWorkerScript), options,
+      base::BindOnce(&RegisterServiceWorkerCallback, &called,
+                     &sw_registration_id_));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(called);
+
+  embedded_worker_helper_->context_wrapper()->FindReadyRegistrationForId(
+      sw_registration_id_, GURL(kServiceWorkerScope).GetOrigin(),
+      base::BindOnce(FindServiceWorkerRegistrationCallback, &sw_registration_));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(sw_registration_);
+}
+
+}  // namespace content
diff --git a/content/browser/background_sync/background_sync_service_impl_test_harness.h b/content/browser/background_sync/background_sync_service_impl_test_harness.h
new file mode 100644
index 0000000..c678da75
--- /dev/null
+++ b/content/browser/background_sync/background_sync_service_impl_test_harness.h
@@ -0,0 +1,86 @@
+// 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 CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_SERVICE_IMPL_TEST_HARNESS_H_
+#define CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_SERVICE_IMPL_TEST_HARNESS_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "content/browser/background_sync/background_sync_context_impl.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
+#include "content/browser/storage_partition_impl.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
+
+namespace content {
+
+class BackgroundSyncServiceImplTestHarness : public testing::Test {
+ public:
+  static void RegisterServiceWorkerCallback(
+      bool* called,
+      int64_t* store_registration_id,
+      blink::ServiceWorkerStatusCode status,
+      const std::string& status_message,
+      int64_t registration_id);
+  static void FindServiceWorkerRegistrationCallback(
+      scoped_refptr<ServiceWorkerRegistration>* out_registration,
+      blink::ServiceWorkerStatusCode status,
+      scoped_refptr<ServiceWorkerRegistration> registration);
+  static void ErrorAndRegistrationCallback(
+      bool* called,
+      blink::mojom::BackgroundSyncError* out_error,
+      blink::mojom::SyncRegistrationOptionsPtr* out_registration,
+      blink::mojom::BackgroundSyncError error,
+      blink::mojom::SyncRegistrationOptionsPtr registration);
+  static void ErrorAndRegistrationListCallback(
+      bool* called,
+      blink::mojom::BackgroundSyncError* out_error,
+      unsigned long* out_array_size,
+      blink::mojom::BackgroundSyncError error,
+      std::vector<blink::mojom::SyncRegistrationOptionsPtr> registrations);
+  static void ErrorCallback(bool* called,
+                            blink::mojom::BackgroundSyncError* out_error,
+                            blink::mojom::BackgroundSyncError error);
+
+  BackgroundSyncServiceImplTestHarness();
+  ~BackgroundSyncServiceImplTestHarness() override;
+
+  void SetUp() override;
+
+  void TearDown() override;
+
+ protected:
+  scoped_refptr<BackgroundSyncContextImpl> background_sync_context_;
+  blink::mojom::SyncRegistrationOptionsPtr default_sync_registration_;
+  int64_t sw_registration_id_;
+
+ private:
+  // SetUp helper methods
+  void CreateTestHelper();
+  void CreateStoragePartition();
+  void CreateBackgroundSyncContext();
+  void CreateServiceWorkerRegistration();
+
+  // Helpers for testing *BackgroundSyncServiceImpl methods
+  void RegisterOneShotSync(
+      blink::mojom::SyncRegistrationOptionsPtr sync,
+      blink::mojom::OneShotBackgroundSyncService::RegisterCallback callback);
+  void GetOneShotSyncRegistrations(
+      blink::mojom::OneShotBackgroundSyncService::GetRegistrationsCallback
+          callback);
+
+  TestBrowserThreadBundle thread_bundle_;
+  std::unique_ptr<EmbeddedWorkerTestHelper> embedded_worker_helper_;
+  std::unique_ptr<StoragePartitionImpl> storage_partition_impl_;
+  scoped_refptr<ServiceWorkerRegistration> sw_registration_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundSyncServiceImplTestHarness);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_BACKGROUND_SYNC_BACKGROUND_SYNC_SERVICE_IMPL_TEST_HARNESS_H_
diff --git a/content/browser/background_sync/background_sync_service_impl_unittest.cc b/content/browser/background_sync/background_sync_service_impl_unittest.cc
deleted file mode 100644
index 45f6a28..0000000
--- a/content/browser/background_sync/background_sync_service_impl_unittest.cc
+++ /dev/null
@@ -1,272 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/background_sync/background_sync_service_impl.h"
-
-#include <stdint.h>
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/run_loop.h"
-#include "content/browser/background_sync/background_sync_context_impl.h"
-#include "content/browser/background_sync/background_sync_network_observer.h"
-#include "content/browser/service_worker/embedded_worker_test_helper.h"
-#include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/browser/storage_partition_impl.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/permission_type.h"
-#include "content/public/test/background_sync_test_util.h"
-#include "content/public/test/mock_permission_manager.h"
-#include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "content/test/test_background_sync_context.h"
-#include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
-
-namespace content {
-
-namespace {
-
-using ::testing::_;
-
-const char kServiceWorkerScope[] = "https://example.com/a";
-const char kServiceWorkerScript[] = "https://example.com/a/script.js";
-
-// Callbacks from SetUp methods
-void RegisterServiceWorkerCallback(bool* called,
-                                   int64_t* store_registration_id,
-                                   blink::ServiceWorkerStatusCode status,
-                                   const std::string& status_message,
-                                   int64_t registration_id) {
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
-      << blink::ServiceWorkerStatusToString(status);
-  *called = true;
-  *store_registration_id = registration_id;
-}
-
-void FindServiceWorkerRegistrationCallback(
-    scoped_refptr<ServiceWorkerRegistration>* out_registration,
-    blink::ServiceWorkerStatusCode status,
-    scoped_refptr<ServiceWorkerRegistration> registration) {
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status)
-      << blink::ServiceWorkerStatusToString(status);
-  *out_registration = std::move(registration);
-}
-
-// Callbacks from BackgroundSyncServiceImpl methods
-
-void ErrorAndRegistrationCallback(
-    bool* called,
-    blink::mojom::BackgroundSyncError* out_error,
-    blink::mojom::SyncRegistrationOptionsPtr* out_registration,
-    blink::mojom::BackgroundSyncError error,
-    blink::mojom::SyncRegistrationOptionsPtr registration) {
-  *called = true;
-  *out_error = error;
-  *out_registration = registration.Clone();
-}
-
-void ErrorAndRegistrationListCallback(
-    bool* called,
-    blink::mojom::BackgroundSyncError* out_error,
-    unsigned long* out_array_size,
-    blink::mojom::BackgroundSyncError error,
-    std::vector<blink::mojom::SyncRegistrationOptionsPtr> registrations) {
-  *called = true;
-  *out_error = error;
-  if (error == blink::mojom::BackgroundSyncError::NONE)
-    *out_array_size = registrations.size();
-}
-
-}  // namespace
-
-class BackgroundSyncServiceImplTest : public testing::Test {
- public:
-  BackgroundSyncServiceImplTest()
-      : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {
-    default_sync_registration_ = blink::mojom::SyncRegistrationOptions::New();
-  }
-
-  void SetUp() override {
-    // Don't let the tests be confused by the real-world device connectivity
-    background_sync_test_util::SetIgnoreNetworkChanges(true);
-
-    CreateTestHelper();
-    CreateStoragePartition();
-    CreateBackgroundSyncContext();
-    CreateServiceWorkerRegistration();
-    CreateBackgroundSyncServiceImpl();
-  }
-
-  void TearDown() override {
-    // This must be explicitly destroyed here to ensure that destruction
-    // of both the BackgroundSyncContextImpl and the BackgroundSyncManager
-    // occurs on the correct thread.
-    background_sync_context_->Shutdown();
-    base::RunLoop().RunUntilIdle();
-    background_sync_context_ = nullptr;
-
-    // Restore the network observer functionality for subsequent tests
-    background_sync_test_util::SetIgnoreNetworkChanges(false);
-  }
-
-  // SetUp helper methods
-  void CreateTestHelper() {
-    embedded_worker_helper_ =
-        std::make_unique<EmbeddedWorkerTestHelper>((base::FilePath()));
-    std::unique_ptr<MockPermissionManager> mock_permission_manager =
-        std::make_unique<testing::NiceMock<MockPermissionManager>>();
-    ON_CALL(*mock_permission_manager,
-            GetPermissionStatus(PermissionType::BACKGROUND_SYNC, _, _))
-        .WillByDefault(
-            testing::Return(blink::mojom::PermissionStatus::GRANTED));
-    embedded_worker_helper_->browser_context()->SetPermissionControllerDelegate(
-        std::move(mock_permission_manager));
-  }
-
-  void CreateStoragePartition() {
-    // Creates a StoragePartition so that the BackgroundSyncManager can
-    // use it to access the BrowserContext.
-    storage_partition_impl_ = StoragePartitionImpl::Create(
-        embedded_worker_helper_->browser_context(), /* in_memory= */ true,
-        base::FilePath(), /* partition_domain= */ "");
-    embedded_worker_helper_->context_wrapper()->set_storage_partition(
-        storage_partition_impl_.get());
-  }
-
-  void CreateBackgroundSyncContext() {
-    // Registering for background sync includes a check for having a same-origin
-    // main frame. Use a test context that allows control over that check.
-    background_sync_context_ =
-        base::MakeRefCounted<TestBackgroundSyncContext>();
-    background_sync_context_->Init(
-        embedded_worker_helper_->context_wrapper(),
-        storage_partition_impl_->GetDevToolsBackgroundServicesContext());
-
-    // Tests do not expect the sync event to fire immediately after
-    // register (and cleanup up the sync registrations).  Prevent the sync
-    // event from firing by setting the network state to have no connection.
-    // NOTE: The setup of the network connection must happen after the
-    //       BackgroundSyncManager has been setup, including any asynchronous
-    //       initialization.
-    base::RunLoop().RunUntilIdle();
-    BackgroundSyncNetworkObserver* network_observer =
-        background_sync_context_->background_sync_manager()
-            ->GetNetworkObserverForTesting();
-    network_observer->NotifyManagerIfConnectionChangedForTesting(
-        network::mojom::ConnectionType::CONNECTION_NONE);
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void CreateServiceWorkerRegistration() {
-    bool called = false;
-    blink::mojom::ServiceWorkerRegistrationOptions options;
-    options.scope = GURL(kServiceWorkerScope);
-    embedded_worker_helper_->context()->RegisterServiceWorker(
-        GURL(kServiceWorkerScript), options,
-        base::BindOnce(&RegisterServiceWorkerCallback, &called,
-                       &sw_registration_id_));
-    base::RunLoop().RunUntilIdle();
-    EXPECT_TRUE(called);
-
-    embedded_worker_helper_->context_wrapper()->FindReadyRegistrationForId(
-        sw_registration_id_, GURL(kServiceWorkerScope).GetOrigin(),
-        base::BindOnce(FindServiceWorkerRegistrationCallback,
-                       &sw_registration_));
-    base::RunLoop().RunUntilIdle();
-    EXPECT_TRUE(sw_registration_);
-  }
-
-  void CreateBackgroundSyncServiceImpl() {
-    // Create a dummy mojo channel so that the BackgroundSyncServiceImpl can be
-    // instantiated.
-    mojo::InterfaceRequest<blink::mojom::BackgroundSyncService>
-        service_request = mojo::MakeRequest(&service_ptr_);
-    // Create a new BackgroundSyncServiceImpl bound to the dummy channel.
-    background_sync_context_->CreateService(std::move(service_request));
-    base::RunLoop().RunUntilIdle();
-
-    service_impl_ = background_sync_context_->services_.begin()->first;
-    ASSERT_TRUE(service_impl_);
-  }
-
-  // Helpers for testing BackgroundSyncServiceImpl methods
-  void Register(
-      blink::mojom::SyncRegistrationOptionsPtr sync,
-      blink::mojom::BackgroundSyncService::RegisterCallback callback) {
-    service_impl_->Register(std::move(sync), sw_registration_id_,
-                            std::move(callback));
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void GetRegistrations(
-      blink::mojom::BackgroundSyncService::GetOneShotSyncRegistrationsCallback
-          callback) {
-    service_impl_->GetOneShotSyncRegistrations(sw_registration_id_,
-                                               std::move(callback));
-    base::RunLoop().RunUntilIdle();
-  }
-
-  TestBrowserThreadBundle thread_bundle_;
-  std::unique_ptr<EmbeddedWorkerTestHelper> embedded_worker_helper_;
-  std::unique_ptr<StoragePartitionImpl> storage_partition_impl_;
-  scoped_refptr<BackgroundSyncContextImpl> background_sync_context_;
-  int64_t sw_registration_id_;
-  scoped_refptr<ServiceWorkerRegistration> sw_registration_;
-  blink::mojom::BackgroundSyncServicePtr service_ptr_;
-  BackgroundSyncServiceImpl*
-      service_impl_;  // Owned by background_sync_context_
-  blink::mojom::SyncRegistrationOptionsPtr default_sync_registration_;
-};
-
-// Tests
-
-TEST_F(BackgroundSyncServiceImplTest, Register) {
-  bool called = false;
-  blink::mojom::BackgroundSyncError error;
-  blink::mojom::SyncRegistrationOptionsPtr reg;
-  Register(
-      default_sync_registration_.Clone(),
-      base::BindOnce(&ErrorAndRegistrationCallback, &called, &error, &reg));
-  EXPECT_TRUE(called);
-  EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
-  EXPECT_EQ("", reg->tag);
-}
-
-TEST_F(BackgroundSyncServiceImplTest, GetRegistrations) {
-  bool called = false;
-  blink::mojom::BackgroundSyncError error;
-  unsigned long array_size = 0UL;
-  GetRegistrations(base::BindOnce(&ErrorAndRegistrationListCallback, &called,
-                                  &error, &array_size));
-  EXPECT_TRUE(called);
-  EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
-  EXPECT_EQ(0UL, array_size);
-}
-
-TEST_F(BackgroundSyncServiceImplTest, GetRegistrationsWithRegisteredSync) {
-  bool register_called = false;
-  bool get_registrations_called = false;
-  blink::mojom::BackgroundSyncError register_error;
-  blink::mojom::BackgroundSyncError getregistrations_error;
-  blink::mojom::SyncRegistrationOptionsPtr register_reg;
-  unsigned long array_size = 0UL;
-  Register(default_sync_registration_.Clone(),
-           base::BindOnce(&ErrorAndRegistrationCallback, &register_called,
-                          &register_error, &register_reg));
-  EXPECT_TRUE(register_called);
-  EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, register_error);
-  GetRegistrations(base::BindOnce(&ErrorAndRegistrationListCallback,
-                                  &get_registrations_called,
-                                  &getregistrations_error, &array_size));
-  EXPECT_TRUE(get_registrations_called);
-  EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, getregistrations_error);
-  EXPECT_EQ(1UL, array_size);
-}
-
-}  // namespace content
diff --git a/content/browser/background_sync/one_shot_background_sync_service_impl.cc b/content/browser/background_sync/one_shot_background_sync_service_impl.cc
new file mode 100644
index 0000000..3db6e37
--- /dev/null
+++ b/content/browser/background_sync/one_shot_background_sync_service_impl.cc
@@ -0,0 +1,76 @@
+// 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 "content/browser/background_sync/one_shot_background_sync_service_impl.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "base/stl_util.h"
+#include "content/browser/background_sync/background_sync_context_impl.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+OneShotBackgroundSyncServiceImpl::OneShotBackgroundSyncServiceImpl(
+    BackgroundSyncContextImpl* background_sync_context,
+    mojo::InterfaceRequest<blink::mojom::OneShotBackgroundSyncService> request)
+    : background_sync_context_(background_sync_context),
+      binding_(this, std::move(request)),
+      weak_ptr_factory_(this) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(background_sync_context_);
+
+  registration_helper_ = std::make_unique<BackgroundSyncRegistrationHelper>(
+      background_sync_context_);
+
+  binding_.set_connection_error_handler(base::BindOnce(
+      &OneShotBackgroundSyncServiceImpl::OnConnectionError,
+      base::Unretained(this) /* the channel is owned by |this| */));
+}
+
+OneShotBackgroundSyncServiceImpl::~OneShotBackgroundSyncServiceImpl() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+}
+
+void OneShotBackgroundSyncServiceImpl::OnConnectionError() {
+  background_sync_context_->OneShotSyncServiceHadConnectionError(this);
+  // |this| is now deleted.
+}
+
+void OneShotBackgroundSyncServiceImpl::Register(
+    blink::mojom::SyncRegistrationOptionsPtr options,
+    int64_t sw_registration_id,
+    RegisterCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  registration_helper_->Register(std::move(options), sw_registration_id,
+                                 std::move(callback));
+}
+
+void OneShotBackgroundSyncServiceImpl::DidResolveRegistration(
+    blink::mojom::BackgroundSyncRegistrationInfoPtr registration_info) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  registration_helper_->DidResolveRegistration(std::move(registration_info));
+}
+
+void OneShotBackgroundSyncServiceImpl::GetRegistrations(
+    int64_t sw_registration_id,
+    GetRegistrationsCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  BackgroundSyncManager* background_sync_manager =
+      background_sync_context_->background_sync_manager();
+  DCHECK(background_sync_manager);
+
+  background_sync_manager->GetOneShotSyncRegistrations(
+      sw_registration_id,
+      base::BindOnce(
+          &BackgroundSyncRegistrationHelper::OnGetRegistrationsResult,
+          registration_helper_->GetWeakPtr(), std::move(callback)));
+}
+
+}  // namespace content
diff --git a/content/browser/background_sync/one_shot_background_sync_service_impl.h b/content/browser/background_sync/one_shot_background_sync_service_impl.h
new file mode 100644
index 0000000..67ef7d1
--- /dev/null
+++ b/content/browser/background_sync/one_shot_background_sync_service_impl.h
@@ -0,0 +1,64 @@
+// 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 CONTENT_BROWSER_BACKGROUND_SYNC_ONE_SHOT_BACKGROUND_SYNC_SERVICE_IMPL_H_
+#define CONTENT_BROWSER_BACKGROUND_SYNC_ONE_SHOT_BACKGROUND_SYNC_SERVICE_IMPL_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/background_sync/background_sync_manager.h"
+#include "content/browser/background_sync/background_sync_registration_helper.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h"
+
+namespace content {
+
+class BackgroundSyncContextImpl;
+
+class CONTENT_EXPORT OneShotBackgroundSyncServiceImpl
+    : public blink::mojom::OneShotBackgroundSyncService {
+ public:
+  OneShotBackgroundSyncServiceImpl(
+      BackgroundSyncContextImpl* background_sync_context,
+      mojo::InterfaceRequest<blink::mojom::OneShotBackgroundSyncService>
+          request);
+
+  ~OneShotBackgroundSyncServiceImpl() override;
+
+ private:
+  friend class OneShotBackgroundSyncServiceImplTest;
+
+  // blink::mojom::OneShotBackgroundSyncService methods:
+  void Register(blink::mojom::SyncRegistrationOptionsPtr options,
+                int64_t sw_registration_id,
+                RegisterCallback callback) override;
+  void DidResolveRegistration(blink::mojom::BackgroundSyncRegistrationInfoPtr
+                                  registration_info) override;
+  void GetRegistrations(int64_t sw_registration_id,
+                        GetRegistrationsCallback callback) override;
+
+  // Called when an error is detected on |binding_|.
+  void OnConnectionError();
+
+  // |background_sync_context_| owns |this|.
+  BackgroundSyncContextImpl* const background_sync_context_;
+
+  std::unique_ptr<BackgroundSyncRegistrationHelper> registration_helper_;
+  mojo::Binding<blink::mojom::OneShotBackgroundSyncService> binding_;
+
+  base::WeakPtrFactory<blink::mojom::OneShotBackgroundSyncService>
+      weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OneShotBackgroundSyncServiceImpl);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_BACKGROUND_SYNC_ONE_SHOT_BACKGROUND_SYNC_SERVICE_IMPL_H_
diff --git a/content/browser/background_sync/one_shot_background_sync_service_impl_unittest.cc b/content/browser/background_sync/one_shot_background_sync_service_impl_unittest.cc
new file mode 100644
index 0000000..aff4b12
--- /dev/null
+++ b/content/browser/background_sync/one_shot_background_sync_service_impl_unittest.cc
@@ -0,0 +1,111 @@
+// 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 "content/browser/background_sync/one_shot_background_sync_service_impl.h"
+
+#include "content/browser/background_sync/background_sync_service_impl_test_harness.h"
+
+namespace content {
+
+class OneShotBackgroundSyncServiceImplTest
+    : public BackgroundSyncServiceImplTestHarness {
+ public:
+  OneShotBackgroundSyncServiceImplTest() = default;
+  ~OneShotBackgroundSyncServiceImplTest() override = default;
+
+  void SetUp() override {
+    BackgroundSyncServiceImplTestHarness::SetUp();
+    CreateOneShotBackgroundSyncServiceImpl();
+  }
+
+ protected:
+  void CreateOneShotBackgroundSyncServiceImpl() {
+    // Create a dummy mojo channel so that the OneShotBackgroundSyncServiceImpl
+    // can be instantiated.
+    mojo::InterfaceRequest<blink::mojom::OneShotBackgroundSyncService>
+        service_request = mojo::MakeRequest(&one_shot_sync_service_ptr_);
+    // Create a new OneShotBackgroundSyncServiceImpl bound to the dummy channel.
+    background_sync_context_->CreateOneShotSyncService(
+        std::move(service_request));
+    base::RunLoop().RunUntilIdle();
+
+    // Since |background_sync_context_| is deleted after
+    // OneShotBackgroundSyncServiceImplTest is, this is safe.
+    one_shot_sync_service_impl_ =
+        background_sync_context_->one_shot_sync_services_.begin()->get();
+    ASSERT_TRUE(one_shot_sync_service_impl_);
+  }
+
+  // Helpers for testing *BackgroundSyncServiceImpl methods
+  void RegisterOneShotSync(
+      blink::mojom::SyncRegistrationOptionsPtr sync,
+      blink::mojom::OneShotBackgroundSyncService::RegisterCallback callback) {
+    one_shot_sync_service_impl_->Register(std::move(sync), sw_registration_id_,
+                                          std::move(callback));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void GetOneShotSyncRegistrations(
+      blink::mojom::OneShotBackgroundSyncService::GetRegistrationsCallback
+          callback) {
+    one_shot_sync_service_impl_->GetRegistrations(sw_registration_id_,
+                                                  std::move(callback));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  blink::mojom::OneShotBackgroundSyncServicePtr one_shot_sync_service_ptr_;
+
+  // Owned by |background_sync_context_|
+  OneShotBackgroundSyncServiceImpl* one_shot_sync_service_impl_;
+};
+
+// Tests
+
+TEST_F(OneShotBackgroundSyncServiceImplTest, RegisterOneShotSync) {
+  bool called = false;
+  blink::mojom::BackgroundSyncError error;
+  blink::mojom::SyncRegistrationOptionsPtr reg;
+  RegisterOneShotSync(
+      default_sync_registration_.Clone(),
+      base::BindOnce(&ErrorAndRegistrationCallback, &called, &error, &reg));
+  ASSERT_TRUE(called);
+  EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
+  EXPECT_EQ("", reg->tag);
+}
+
+TEST_F(OneShotBackgroundSyncServiceImplTest,
+       GetOneShotSyncRegistrationsNoSyncRegistered) {
+  bool called = false;
+  blink::mojom::BackgroundSyncError error;
+  unsigned long array_size = 0UL;
+  GetOneShotSyncRegistrations(base::BindOnce(&ErrorAndRegistrationListCallback,
+                                             &called, &error, &array_size));
+  ASSERT_TRUE(called);
+  EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
+  EXPECT_EQ(0UL, array_size);
+}
+
+TEST_F(OneShotBackgroundSyncServiceImplTest,
+       GetOneShotSyncRegistrationsWithRegisteredSync) {
+  bool register_called = false;
+  bool get_registrations_called = false;
+  blink::mojom::BackgroundSyncError register_error;
+  blink::mojom::BackgroundSyncError get_registrations_error;
+  blink::mojom::SyncRegistrationOptionsPtr registered_reg;
+  unsigned long array_size = 0UL;
+  RegisterOneShotSync(
+      default_sync_registration_.Clone(),
+      base::BindOnce(&ErrorAndRegistrationCallback, &register_called,
+                     &register_error, &registered_reg));
+  ASSERT_TRUE(register_called);
+  EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, register_error);
+  GetOneShotSyncRegistrations(base::BindOnce(
+      &ErrorAndRegistrationListCallback, &get_registrations_called,
+      &get_registrations_error, &array_size));
+  ASSERT_TRUE(get_registrations_called);
+  EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, get_registrations_error);
+  EXPECT_EQ(1UL, array_size);
+}
+
+}  // namespace content
diff --git a/content/browser/background_sync/periodic_background_sync_service_impl.cc b/content/browser/background_sync/periodic_background_sync_service_impl.cc
new file mode 100644
index 0000000..a1436ed
--- /dev/null
+++ b/content/browser/background_sync/periodic_background_sync_service_impl.cc
@@ -0,0 +1,95 @@
+// 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 "content/browser/background_sync/periodic_background_sync_service_impl.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "content/browser/background_sync/background_sync_context_impl.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+PeriodicBackgroundSyncServiceImpl::PeriodicBackgroundSyncServiceImpl(
+    BackgroundSyncContextImpl* background_sync_context,
+    mojo::InterfaceRequest<blink::mojom::PeriodicBackgroundSyncService> request)
+    : background_sync_context_(background_sync_context),
+      binding_(this, std::move(request)),
+      weak_ptr_factory_(this) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(background_sync_context_);
+
+  registration_helper_ = std::make_unique<BackgroundSyncRegistrationHelper>(
+      background_sync_context_);
+  DCHECK(registration_helper_);
+
+  binding_.set_connection_error_handler(base::BindOnce(
+      &PeriodicBackgroundSyncServiceImpl::OnConnectionError,
+      base::Unretained(this) /* the channel is owned by this */));
+}
+
+PeriodicBackgroundSyncServiceImpl::~PeriodicBackgroundSyncServiceImpl() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+}
+
+void PeriodicBackgroundSyncServiceImpl::OnConnectionError() {
+  background_sync_context_->PeriodicSyncServiceHadConnectionError(this);
+  // |this| is now deleted.
+}
+
+void PeriodicBackgroundSyncServiceImpl::Register(
+    blink::mojom::SyncRegistrationOptionsPtr options,
+    int64_t sw_registration_id,
+    RegisterCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  registration_helper_->Register(std::move(options), sw_registration_id,
+                                 std::move(callback));
+}
+
+void PeriodicBackgroundSyncServiceImpl::Unregister(
+    int64_t sw_registration_id,
+    const std::string& tag,
+    UnregisterCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  BackgroundSyncManager* background_sync_manager =
+      background_sync_context_->background_sync_manager();
+  DCHECK(background_sync_manager);
+  background_sync_manager->UnregisterPeriodicSync(
+      sw_registration_id, tag,
+      base::BindOnce(&PeriodicBackgroundSyncServiceImpl::OnUnregisterResult,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void PeriodicBackgroundSyncServiceImpl::GetRegistrations(
+    int64_t sw_registration_id,
+    GetRegistrationsCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  BackgroundSyncManager* background_sync_manager =
+      background_sync_context_->background_sync_manager();
+  DCHECK(background_sync_manager);
+
+  // BackgroundSyncContextImpl owns both PeriodicBackgroundSyncServiceImpl and
+  // BackgroundSyncManager. The manager will be destroyed after the service,
+  // thus passing the |registration_helper_| pointer is safe here.
+  background_sync_manager->GetPeriodicSyncRegistrations(
+      sw_registration_id,
+      base::BindOnce(
+          &BackgroundSyncRegistrationHelper::OnGetRegistrationsResult,
+          registration_helper_->GetWeakPtr(), std::move(callback)));
+}
+
+void PeriodicBackgroundSyncServiceImpl::OnUnregisterResult(
+    UnregisterCallback callback,
+    BackgroundSyncStatus status) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  std::move(callback).Run(
+      static_cast<blink::mojom::BackgroundSyncError>(status));
+}
+
+}  // namespace content
diff --git a/content/browser/background_sync/periodic_background_sync_service_impl.h b/content/browser/background_sync/periodic_background_sync_service_impl.h
new file mode 100644
index 0000000..d2d8b38
--- /dev/null
+++ b/content/browser/background_sync/periodic_background_sync_service_impl.h
@@ -0,0 +1,68 @@
+// 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 CONTENT_BROWSER_BACKGROUND_SYNC_PERIODIC_BACKGROUND_SYNC_SERVICE_IMPL_H_
+#define CONTENT_BROWSER_BACKGROUND_SYNC_PERIODIC_BACKGROUND_SYNC_SERVICE_IMPL_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/background_sync/background_sync_manager.h"
+#include "content/browser/background_sync/background_sync_registration_helper.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h"
+
+namespace content {
+
+class BackgroundSyncContextImpl;
+
+class CONTENT_EXPORT PeriodicBackgroundSyncServiceImpl
+    : public blink::mojom::PeriodicBackgroundSyncService {
+ public:
+  PeriodicBackgroundSyncServiceImpl(
+      BackgroundSyncContextImpl* background_sync_context,
+      mojo::InterfaceRequest<blink::mojom::PeriodicBackgroundSyncService>
+          request);
+
+  ~PeriodicBackgroundSyncServiceImpl() override;
+
+ private:
+  friend class PeriodicBackgroundSyncServiceImplTest;
+
+  // blink::mojom::BackgroundSyncService methods:
+  void Register(blink::mojom::SyncRegistrationOptionsPtr options,
+                int64_t sw_registration_id,
+                RegisterCallback callback) override;
+  void Unregister(int64_t sw_registration_id,
+                  const std::string& tag,
+                  UnregisterCallback callback) override;
+  void GetRegistrations(int64_t sw_registration_id,
+                        GetRegistrationsCallback callback) override;
+
+  void OnUnregisterResult(UnregisterCallback callback,
+                          BackgroundSyncStatus status);
+
+  // Called when an error is detected on |binding_|.
+  void OnConnectionError();
+
+  // |background_sync_context_| owns |this|.
+  BackgroundSyncContextImpl* background_sync_context_;
+
+  std::unique_ptr<BackgroundSyncRegistrationHelper> registration_helper_;
+  mojo::Binding<blink::mojom::PeriodicBackgroundSyncService> binding_;
+
+  base::WeakPtrFactory<PeriodicBackgroundSyncServiceImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PeriodicBackgroundSyncServiceImpl);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_BACKGROUND_SYNC_PERIODIC_BACKGROUND_SYNC_SERVICE_IMPL_H_
diff --git a/content/browser/background_sync/periodic_background_sync_service_impl_unittest.cc b/content/browser/background_sync/periodic_background_sync_service_impl_unittest.cc
new file mode 100644
index 0000000..e8290dd
--- /dev/null
+++ b/content/browser/background_sync/periodic_background_sync_service_impl_unittest.cc
@@ -0,0 +1,177 @@
+// 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 "content/browser/background_sync/periodic_background_sync_service_impl.h"
+
+#include "content/browser/background_sync/background_sync_service_impl_test_harness.h"
+
+namespace content {
+
+class PeriodicBackgroundSyncServiceImplTest
+    : public BackgroundSyncServiceImplTestHarness {
+ public:
+  PeriodicBackgroundSyncServiceImplTest() = default;
+
+  void SetUp() override {
+    BackgroundSyncServiceImplTestHarness::SetUp();
+    CreatePeriodicBackgroundSyncServiceImpl();
+  }
+
+ protected:
+  void CreatePeriodicBackgroundSyncServiceImpl() {
+    // Create a dummy mojo channel so that the PeriodicBackgroundSyncServiceImpl
+    // can be instantiated.
+    mojo::InterfaceRequest<blink::mojom::PeriodicBackgroundSyncService>
+        service_request = mojo::MakeRequest(&periodic_sync_service_ptr_);
+    // Create a new PeriodicBackgroundSyncServiceImpl bound to the dummy
+    // channel.
+    background_sync_context_->CreatePeriodicSyncService(
+        std::move(service_request));
+    base::RunLoop().RunUntilIdle();
+
+    // Since |background_sync_context_| is deleted after
+    // PeriodicBackgroundSyncServiceImplTest is, this is safe.
+    periodic_sync_service_impl_ =
+        background_sync_context_->periodic_sync_services_.begin()->get();
+    ASSERT_TRUE(periodic_sync_service_impl_);
+  }
+
+  // Helpers for testing *BackgroundSyncServiceImpl methods
+  void RegisterPeriodicSync(
+      blink::mojom::SyncRegistrationOptionsPtr sync,
+      blink::mojom::PeriodicBackgroundSyncService::RegisterCallback callback) {
+    periodic_sync_service_impl_->Register(std::move(sync), sw_registration_id_,
+                                          std::move(callback));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void UnregisterPeriodicSync(
+      const std::string& tag,
+      blink::mojom::PeriodicBackgroundSyncService::UnregisterCallback
+          callback) {
+    periodic_sync_service_impl_->Unregister(sw_registration_id_, tag,
+                                            std::move(callback));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void GetPeriodicSyncRegistrations(
+      blink::mojom::PeriodicBackgroundSyncService::GetRegistrationsCallback
+          callback) {
+    periodic_sync_service_impl_->GetRegistrations(sw_registration_id_,
+                                                  std::move(callback));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  blink::mojom::PeriodicBackgroundSyncServicePtr periodic_sync_service_ptr_;
+
+  // Owned by |background_sync_context_|
+  PeriodicBackgroundSyncServiceImpl* periodic_sync_service_impl_;
+};
+
+// Tests
+
+TEST_F(PeriodicBackgroundSyncServiceImplTest, RegisterPeriodicSync) {
+  bool called = false;
+  blink::mojom::BackgroundSyncError error;
+  blink::mojom::SyncRegistrationOptionsPtr reg;
+  auto to_register = default_sync_registration_.Clone();
+  to_register->min_interval = 3600;
+  RegisterPeriodicSync(
+      std::move(to_register),
+      base::BindOnce(&ErrorAndRegistrationCallback, &called, &error, &reg));
+  ASSERT_TRUE(called);
+  EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
+  EXPECT_EQ("", reg->tag);
+  EXPECT_EQ(3600, reg->min_interval);
+}
+
+TEST_F(PeriodicBackgroundSyncServiceImplTest,
+       GetPeriodicSyncRegistrationsNoSyncRegistered) {
+  bool called = false;
+  blink::mojom::BackgroundSyncError error;
+  unsigned long array_size = 0UL;
+  GetPeriodicSyncRegistrations(base::BindOnce(&ErrorAndRegistrationListCallback,
+                                              &called, &error, &array_size));
+  ASSERT_TRUE(called);
+  EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
+  EXPECT_EQ(0UL, array_size);
+}
+
+TEST_F(PeriodicBackgroundSyncServiceImplTest,
+       GetPeriodicSyncRegistrationsWithRegisteredSync) {
+  {
+    bool called = false;
+    blink::mojom::BackgroundSyncError error;
+    blink::mojom::SyncRegistrationOptionsPtr registered_reg;
+
+    auto to_register = default_sync_registration_.Clone();
+    to_register->min_interval = 3600;
+
+    RegisterPeriodicSync(std::move(to_register),
+                         base::BindOnce(&ErrorAndRegistrationCallback, &called,
+                                        &error, &registered_reg));
+    ASSERT_TRUE(called);
+    EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
+  }
+
+  {
+    bool called = false;
+    blink::mojom::BackgroundSyncError error;
+    unsigned long array_size = 0UL;
+    GetPeriodicSyncRegistrations(base::BindOnce(
+        &ErrorAndRegistrationListCallback, &called, &error, &array_size));
+    ASSERT_TRUE(called);
+    EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
+    EXPECT_EQ(1UL, array_size);
+  }
+}
+
+TEST_F(PeriodicBackgroundSyncServiceImplTest, Unregister) {
+  {
+    bool called = false;
+    blink::mojom::BackgroundSyncError error;
+
+    UnregisterPeriodicSync("non_existent",
+                           base::BindOnce(&ErrorCallback, &called, &error));
+    ASSERT_TRUE(called);
+    EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
+  }
+
+  {
+    bool called = false;
+    blink::mojom::BackgroundSyncError error;
+    blink::mojom::SyncRegistrationOptionsPtr reg;
+    auto to_register = default_sync_registration_.Clone();
+    to_register->tag = "shared_tag";
+    to_register->min_interval = 3600;
+    RegisterPeriodicSync(
+        std::move(to_register),
+        base::BindOnce(&ErrorAndRegistrationCallback, &called, &error, &reg));
+    ASSERT_TRUE(called);
+    EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
+    EXPECT_EQ("shared_tag", reg->tag);
+    EXPECT_EQ(3600, reg->min_interval);
+  }
+  {
+    bool called = false;
+    blink::mojom::BackgroundSyncError error;
+
+    UnregisterPeriodicSync("shared_tag",
+                           base::BindOnce(&ErrorCallback, &called, &error));
+    ASSERT_TRUE(called);
+    EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
+  }
+  {
+    bool called = false;
+    blink::mojom::BackgroundSyncError error;
+    unsigned long array_size = 0UL;
+    GetPeriodicSyncRegistrations(base::BindOnce(
+        &ErrorAndRegistrationListCallback, &called, &error, &array_size));
+    ASSERT_TRUE(called);
+    EXPECT_EQ(blink::mojom::BackgroundSyncError::NONE, error);
+    EXPECT_EQ(0UL, array_size);
+  }
+}
+
+}  // namespace content
diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index 7c85d7f9..5748f64 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -243,6 +243,7 @@
                               size_t approximate_event_count)>
           on_buffer_usage_callback) = 0;
   virtual bool HasTracingFailed() = 0;
+  virtual bool HasDataLossOccurred() = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TracingSession);
@@ -285,6 +286,7 @@
   }
 
   bool HasTracingFailed() override { return false; }
+  bool HasDataLossOccurred() override { return false; }
 };
 
 class TracingHandler::PerfettoTracingSession
@@ -338,6 +340,8 @@
 
   void ChangeTraceConfig(
       const base::trace_event::TraceConfig& chrome_config) override {
+    if (!tracing_session_host_)
+      return;
     auto perfetto_config = CreatePerfettoConfiguration(chrome_config);
     tracing_session_host_->ChangeTraceConfig(perfetto_config);
   }
@@ -358,9 +362,10 @@
     tracing_active_ = false;
 
     if (!tracing_session_host_) {
-      if (endpoint_)
+      if (endpoint_) {
+        // Will delete |this|.
         endpoint_->ReceiveTraceFinalContents(nullptr);
-
+      }
       return;
     }
 
@@ -391,28 +396,22 @@
   void GetBufferUsage(base::OnceCallback<void(float percent_full,
                                               size_t approximate_event_count)>
                           on_buffer_usage_callback) override {
+    if (!tracing_session_host_) {
+      std::move(on_buffer_usage_callback).Run(0.0f, 0);
+      return;
+    }
     DCHECK(on_buffer_usage_callback);
     tracing_session_host_->RequestBufferUsage(base::BindOnce(
         &PerfettoTracingSession::OnBufferUsage, base::Unretained(this),
         std::move(on_buffer_usage_callback)));
   }
 
-  void OnBufferUsage(base::OnceCallback<void(float percent_full,
-                                             size_t approximate_event_count)>
-                         on_buffer_usage_callback,
-                     bool success,
-                     float percent_full) {
-    if (!success) {
-      std::move(on_buffer_usage_callback).Run(0.0f, 0);
-      return;
-    }
-    std::move(on_buffer_usage_callback).Run(percent_full, 0);
-  }
-
   bool HasTracingFailed() override {
     return tracing_active_ && !tracing_session_host_;
   }
 
+  bool HasDataLossOccurred() override { return data_loss_; }
+
   // tracing::mojom::TracingSessionClient implementation:
   void OnTracingEnabled() override {
     if (on_recording_enabled_callback_) {
@@ -484,10 +483,25 @@
 
     if (endpoint_) {
       // TODO(oysteine): Signal to the client that tracing failed.
+      // Will delete |this|.
       endpoint_->ReceiveTraceFinalContents(nullptr);
     }
   }
 
+  void OnBufferUsage(base::OnceCallback<void(float percent_full,
+                                             size_t approximate_event_count)>
+                         on_buffer_usage_callback,
+                     bool success,
+                     float percent_full,
+                     bool data_loss) {
+    if (!success) {
+      std::move(on_buffer_usage_callback).Run(0.0f, 0);
+      return;
+    }
+    data_loss_ |= data_loss;
+    std::move(on_buffer_usage_callback).Run(percent_full, 0);
+  }
+
   // mojo::DataPipeDrainer::Client implementation:
   void OnDataAvailable(const void* data, size_t num_bytes) override {
     auto data_string = std::make_unique<std::string>(
@@ -507,10 +521,19 @@
 
   void MaybeTraceComplete() {
     if (read_buffers_complete_ && data_complete_ && endpoint_) {
-      endpoint_->ReceiveTraceFinalContents(nullptr);
+      // Request stats to check if data loss occurred.
+      GetBufferUsage(base::BindOnce(&PerfettoTracingSession::OnFinalBufferUsage,
+                                    base::Unretained(this)));
     }
   }
 
+  void OnFinalBufferUsage(float percent_full, size_t approximate_event_count) {
+    if (!endpoint_)
+      return;
+    // Will delete |this|.
+    endpoint_->ReceiveTraceFinalContents(nullptr);
+  }
+
   mojo::Binding<tracing::mojom::TracingSessionClient> binding_{this};
   tracing::mojom::TracingSessionHostPtr tracing_session_host_;
 
@@ -526,6 +549,7 @@
   bool data_complete_ = false;
   bool read_buffers_complete_ = false;
   bool tracing_active_ = false;
+  bool data_loss_ = false;
 
 #if DCHECK_IS_ON()
   base::trace_event::TraceConfig last_config_for_perfetto_;
@@ -630,8 +654,9 @@
   DCHECK(!trace_data_buffer_state_.in_string);
   DCHECK(!trace_data_buffer_state_.slashed);
 
+  bool data_loss = session_->HasDataLossOccurred();
   session_.reset();
-  frontend_->TracingComplete();
+  frontend_->TracingComplete(data_loss);
 }
 
 std::string TracingHandler::UpdateTraceDataBuffer(
@@ -693,13 +718,15 @@
 }
 
 void TracingHandler::OnTraceToStreamComplete(const std::string& stream_handle) {
+  bool data_loss = session_->HasDataLossOccurred();
   session_.reset();
   std::string stream_format = (proto_format_ ? Tracing::StreamFormatEnum::Proto
                                              : Tracing::StreamFormatEnum::Json);
   std::string stream_compression =
       (gzip_compression_ ? Tracing::StreamCompressionEnum::Gzip
                          : Tracing::StreamCompressionEnum::None);
-  frontend_->TracingComplete(stream_handle, stream_format, stream_compression);
+  frontend_->TracingComplete(data_loss, stream_handle, stream_format,
+                             stream_compression);
 }
 
 void TracingHandler::Start(Maybe<std::string> categories,
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index 639f749..ab14fcf 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -9893,4 +9893,54 @@
             shell()->web_contents()->GetLastCommittedURL());
 }
 
+// Tests a renderer aborting the navigation it started, while still waiting on a
+// long cross-process subframe beforeunload handler.
+// Regression test: https://crbug.com/972154
+IN_PROC_BROWSER_TEST_F(
+    NavigationControllerBrowserTest,
+    NavigationAbortDuringLongCrossProcessIframeBeforeUnload) {
+  // This test relies on the main frame and the iframe to live in different
+  // processes. This allows one renderer process to cancel a navigation while
+  // the other renderer process is busy executing its beforeunload handler.
+  if (!AreAllSitesIsolatedForTesting())
+    return;
+
+  const GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  const GURL navigated_url(
+      embedded_test_server()->GetURL("a.com", "/title1.html"));
+
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  FrameTreeNode* root = web_contents->GetFrameTree()->root();
+
+  // Have a first page with a cross process iframe.
+  // The iframe itself does have a dialog-showing beforeunload handler.
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  std::string script =
+      "window.addEventListener('beforeunload', function (event) {"
+      "  event.returnValue='blocked'"
+      "});";
+  EXPECT_TRUE(ExecJs(root->child_at(0), script));
+
+  TestNavigationObserver load_observer(web_contents);
+  NavigationHandleObserver abort_observer(web_contents, navigated_url);
+  BeforeUnloadBlockingDelegate beforeunload_pauser(web_contents);
+
+  // Navigate to any page, renderer initiated.
+  EXPECT_TRUE(ExecJs(shell(), "location.href = 'title1.html'"));
+
+  // The previous navigation is paused while the beforeunloadhandler dialog is
+  // shown to the user. In the meantime, the navigation is aborted:
+  beforeunload_pauser.Wait();
+  EXPECT_TRUE(ExecJs(shell(), "document.write()"));  // Cancel the navigation.
+
+  // Wait for javascript to get processed, and its consequences (aborting the
+  // navigation) to finish. To achieve that we simply wait for DidStopLoading.
+  load_observer.Wait();
+
+  // Verify that the navigation was aborted as expected.
+  EXPECT_FALSE(abort_observer.has_committed());
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index d08ff01..5fb77b4 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -264,10 +264,6 @@
   return navigation_request_->auth_challenge_info();
 }
 
-bool NavigationHandleImpl::IsWaitingToCommit() {
-  return state() == NavigationRequest::READY_TO_COMMIT;
-}
-
 bool NavigationHandleImpl::HasCommitted() {
   return state() == NavigationRequest::DID_COMMIT ||
          state() == NavigationRequest::DID_COMMIT_ERROR_PAGE;
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index 6519d7a97..282c7f2 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -216,11 +216,6 @@
     return navigation_request_->GetDeferringThrottleForTesting();
   }
 
-  // Whether the navigation was sent to be committed in a renderer by the
-  // RenderFrameHost. This can either be for the commit of a successful
-  // navigation or an error page.
-  bool IsWaitingToCommit();
-
   // Sets the READY_TO_COMMIT -> DID_COMMIT timeout.  Resets the timeout to the
   // default value if |timeout| is zero.
   static void SetCommitTimeoutForTesting(const base::TimeDelta& timeout);
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 988e426..2948001c 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -2335,7 +2335,7 @@
 }
 
 void NavigationRequest::OnRendererAbortedNavigation() {
-  if (navigation_handle_->IsWaitingToCommit()) {
+  if (IsWaitingToCommit()) {
     render_frame_host_->NavigationRequestCancelled(this);
   } else {
     frame_tree_node_->navigator()->CancelNavigation(frame_tree_node_, false);
@@ -2854,4 +2854,8 @@
   return std::move(appcache_handle_);
 }
 
+bool NavigationRequest::IsWaitingToCommit() {
+  return handle_state_ == NavigationRequest::READY_TO_COMMIT;
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index cb210ba..0048ff7 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -708,6 +708,11 @@
   // |handle_state_| and inform the delegate.
   void ReadyToCommitNavigation(bool is_error);
 
+  // Whether the navigation was sent to be committed in a renderer by the
+  // RenderFrameHost. This can either be for the commit of a successful
+  // navigation or an error page.
+  bool IsWaitingToCommit();
+
   FrameTreeNode* frame_tree_node_;
 
   // Invariant: At least one of |loader_| or |render_frame_host_| is null.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index bcd8236..644e87e9 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -477,6 +477,9 @@
       /* is_service_worker = */ false, process_id, frame_id);
 }
 
+// TODO(crbug.com/977040): Remove when no longer needed.
+const uint32_t kMaxCookieSameSiteDeprecationUrls = 20;
+
 }  // namespace
 
 class RenderFrameHostImpl::DroppedInterfaceRequestLogger
@@ -6706,8 +6709,11 @@
                                               std::move(navigation_request),
                                               is_same_document_navigation);
 
-  if (!is_same_document_navigation)
+  if (!is_same_document_navigation) {
     scheduler_tracked_features_ = 0;
+    cookie_no_samesite_deprecation_url_hashes_.clear();
+    cookie_samesite_none_insecure_deprecation_url_hashes_.clear();
+  }
 
   return true;
 }
@@ -7157,4 +7163,64 @@
                                         discard_duplicates));
 }
 
+void RenderFrameHostImpl::AddSameSiteCookieDeprecationMessage(
+    const std::string& cookie_url,
+    net::CanonicalCookie::CookieInclusionStatus status) {
+  std::string deprecation_message;
+  if (status == net::CanonicalCookie::CookieInclusionStatus::
+                    EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX) {
+    if (!ShouldAddCookieSameSiteDeprecationMessage(
+            cookie_url, &cookie_no_samesite_deprecation_url_hashes_)) {
+      return;
+    }
+    deprecation_message =
+        "[Deprecation] A cookie associated with a cross-site resource at " +
+        cookie_url +
+        " was set without the `SameSite` attribute. "
+        "A future release of Chrome will only deliver cookies with "
+        "cross-site requests if they are set with `SameSite=None`. You "
+        "can review cookies in developer tools under "
+        "Application>Storage>Cookies and see more details at "
+        "https://www.chromestatus.com/feature/5088147346030592.";
+  }
+  if (status == net::CanonicalCookie::CookieInclusionStatus::
+                    EXCLUDE_SAMESITE_NONE_INSECURE) {
+    if (!ShouldAddCookieSameSiteDeprecationMessage(
+            cookie_url,
+            &cookie_samesite_none_insecure_deprecation_url_hashes_)) {
+      return;
+    }
+    deprecation_message =
+        "[Deprecation] A cookie associated with a resource at " + cookie_url +
+        " was set with `SameSite=None` but without `Secure`. "
+        "A future release of Chrome will only deliver cookies marked "
+        "`SameSite=None` if they are also marked `Secure`. You "
+        "can review cookies in developer tools under "
+        "Application>Storage>Cookies and see more details at "
+        "https://www.chromestatus.com/feature/5633521622188032.";
+  }
+
+  if (deprecation_message.empty())
+    return;
+
+  AddUniqueMessageToConsole(blink::mojom::ConsoleMessageLevel::kWarning,
+                            deprecation_message);
+}
+
+bool RenderFrameHostImpl::ShouldAddCookieSameSiteDeprecationMessage(
+    const std::string& cookie_url,
+    base::circular_deque<size_t>* already_seen_url_hashes) {
+  DCHECK_LE(already_seen_url_hashes->size(), kMaxCookieSameSiteDeprecationUrls);
+  size_t cookie_url_hash = base::FastHash(cookie_url);
+  if (base::Contains(*already_seen_url_hashes, cookie_url_hash))
+    return false;
+
+  // Put most recent ones at the front because we are likely to have multiple
+  // consecutive cookies with the same URL.
+  if (already_seen_url_hashes->size() == kMaxCookieSameSiteDeprecationUrls)
+    already_seen_url_hashes->pop_back();
+  already_seen_url_hashes->push_front(cookie_url_hash);
+  return true;
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 34ca3ed..2316121 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -18,6 +18,7 @@
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/containers/circular_deque.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/id_map.h"
 #include "base/gtest_prod_util.h"
@@ -59,6 +60,7 @@
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/base/network_isolation_key.h"
+#include "net/cookies/canonical_cookie.h"
 #include "net/http/http_response_headers.h"
 #include "services/device/public/mojom/wake_lock_context.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
@@ -948,6 +950,12 @@
       blink::mojom::ConsoleMessageLevel level,
       const std::string& message);
 
+  // Add cookie SameSite deprecation messages to the DevTools console.
+  // TODO(crbug.com/977040): Remove when no longer needed.
+  void AddSameSiteCookieDeprecationMessage(
+      const std::string& cookie_url,
+      net::CanonicalCookie::CookieInclusionStatus status);
+
  protected:
   friend class RenderFrameHostFactory;
 
@@ -1653,6 +1661,13 @@
                                const std::string& message,
                                bool discard_duplicates);
 
+  // Returns whether a cookie SameSite deprecation message should be sent for
+  // the given cookie url.
+  // TODO(crbug.com/977040): Remove when no longer needed.
+  bool ShouldAddCookieSameSiteDeprecationMessage(
+      const std::string& cookie_url,
+      base::circular_deque<size_t>* already_seen_url_hashes);
+
   // For now, RenderFrameHosts indirectly keep RenderViewHosts alive via a
   // refcount that calls Shutdown when it reaches zero.  This allows each
   // RenderFrameHostManager to just care about RenderFrameHosts, while ensuring
@@ -2160,6 +2175,15 @@
   // every document commit.
   net::NetworkIsolationKey network_isolation_key_;
 
+  // Hold onto hashes of the last |kMaxCookieSameSiteDeprecationUrls| cookie
+  // URLs that we have seen since the last committed navigation, in order to
+  // partially deduplicate the corresponding cookie SameSite deprecation
+  // messages.
+  // TODO(crbug.com/977040): Remove when no longer needed.
+  base::circular_deque<size_t> cookie_no_samesite_deprecation_url_hashes_;
+  base::circular_deque<size_t>
+      cookie_samesite_none_insecure_deprecation_url_hashes_;
+
   // NOTE: This must be the last member.
   base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_;
 
diff --git a/content/browser/frame_host/render_frame_message_filter.cc b/content/browser/frame_host/render_frame_message_filter.cc
index 5548296..cf137f0 100644
--- a/content/browser/frame_host/render_frame_message_filter.cc
+++ b/content/browser/frame_host/render_frame_message_filter.cc
@@ -201,6 +201,7 @@
 
 // Send deprecation messages to the console about cookies that would be excluded
 // due to either SameSiteByDefaultCookies or CookiesWithoutSameSiteMustBeSecure.
+// TODO(crbug.com/977040): Remove when no longer needed.
 void SendDeprecationMessagesForSameSiteCookiesOnUI(
     int render_process_id,
     int render_frame_id,
@@ -234,39 +235,24 @@
       base::FeatureList::IsEnabled(features::kCookieDeprecationMessages);
 
   for (const auto& cookie_with_status : deprecated_cookies) {
+    std::string cookie_url = net::cookie_util::CookieOriginToURL(
+                                 cookie_with_status.cookie.Domain(),
+                                 cookie_with_status.cookie.IsSecure())
+                                 .possibly_invalid_spec();
     if (cookie_with_status.status ==
         net::CanonicalCookie::CookieInclusionStatus::
             EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX) {
       log_unspecified_treated_as_lax_metric = true;
-      if (emit_messages) {
-        root_frame_host->AddUniqueMessageToConsole(
-            blink::mojom::ConsoleMessageLevel::kWarning,
-            "[Deprecation] A cookie associated with a cross-site resource at " +
-                url.possibly_invalid_spec() +
-                " was set without the `SameSite` attribute. "
-                "A future release of Chrome will only deliver cookies with "
-                "cross-site requests if they are set with `SameSite=None`. You "
-                "can review cookies in developer tools under "
-                "Application>Storage>Cookies and see more details at "
-                "https://www.chromestatus.com/feature/5088147346030592.");
-      }
     }
     if (cookie_with_status.status ==
         net::CanonicalCookie::CookieInclusionStatus::
             EXCLUDE_SAMESITE_NONE_INSECURE) {
       log_none_insecure_metric = true;
-      if (emit_messages) {
-        root_frame_host->AddUniqueMessageToConsole(
-            blink::mojom::ConsoleMessageLevel::kWarning,
-            "[Deprecation] A cookie associated with a resource at " +
-                url.possibly_invalid_spec() +
-                " was set with `SameSite=None` but without `Secure`. "
-                "A future release of Chrome will only deliver cookies marked "
-                "`SameSite=None` if they are also marked `Secure`. You "
-                "can review cookies in developer tools under "
-                "Application>Storage>Cookies and see more details at "
-                "https://www.chromestatus.com/feature/5633521622188032.");
-      }
+    }
+
+    if (emit_messages) {
+      root_frame_host->AddSameSiteCookieDeprecationMessage(
+          cookie_url, cookie_with_status.status);
     }
   }
 
diff --git a/content/browser/network_service_client.cc b/content/browser/network_service_client.cc
index 5ec9f0b..19ba39a 100644
--- a/content/browser/network_service_client.cc
+++ b/content/browser/network_service_client.cc
@@ -427,6 +427,7 @@
 }
 #endif
 
+// TODO(crbug.com/977040): Remove when no longer needed.
 void DeprecateSameSiteCookies(int process_id,
                               int routing_id,
                               const net::CookieStatusList& excluded_cookies) {
@@ -480,37 +481,14 @@
         net::CanonicalCookie::CookieInclusionStatus::
             EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX) {
       samesite_treated_as_lax_cookies = true;
-
-      if (emit_messages) {
-        root_frame_host->AddUniqueMessageToConsole(
-            blink::mojom::ConsoleMessageLevel::kWarning,
-            "[Deprecation] A cookie associated with a cross-site resource at " +
-                cookie_url +
-                " was set without the `SameSite` attribute. "
-                "A future release of Chrome will only deliver cookies with "
-                "cross-site requests if they are set with `SameSite=None`. You "
-                "can review cookies in developer tools under "
-                "Application>Storage>Cookies and see more details at "
-                "https://www.chromestatus.com/feature/5088147346030592.");
-      }
     }
-
     if (excluded_cookie.status == net::CanonicalCookie::CookieInclusionStatus::
                                       EXCLUDE_SAMESITE_NONE_INSECURE) {
       samesite_none_insecure_cookies = true;
-
-      if (emit_messages) {
-        root_frame_host->AddUniqueMessageToConsole(
-            blink::mojom::ConsoleMessageLevel::kWarning,
-            "[Deprecation] A cookie associated with a resource at " +
-                cookie_url +
-                " was set with `SameSite=None` but without `Secure`. "
-                "A future release of Chrome will only deliver cookies marked "
-                "`SameSite=None` if they are also marked `Secure`. You "
-                "can review cookies in developer tools under "
-                "Application>Storage>Cookies and see more details at "
-                "https://www.chromestatus.com/feature/5633521622188032.");
-      }
+    }
+    if (emit_messages) {
+      root_frame_host->AddSameSiteCookieDeprecationMessage(
+          cookie_url, excluded_cookie.status);
     }
   }
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 568f32799..3fd664133 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -74,7 +74,8 @@
 #include "content/browser/appcache/chrome_appcache_service.h"
 #include "content/browser/background_fetch/background_fetch_context.h"
 #include "content/browser/background_fetch/background_fetch_service_impl.h"
-#include "content/browser/background_sync/background_sync_service_impl.h"
+#include "content/browser/background_sync/one_shot_background_sync_service_impl.h"
+#include "content/browser/background_sync/periodic_background_sync_service_impl.h"
 #include "content/browser/bad_message.h"
 #include "content/browser/blob_storage/blob_dispatcher_host.h"
 #include "content/browser/blob_storage/blob_registry_wrapper.h"
@@ -2032,7 +2033,13 @@
   AddUIThreadInterface(
       registry.get(),
       base::BindRepeating(
-          &BackgroundSyncContextImpl::CreateService,
+          &BackgroundSyncContextImpl::CreateOneShotSyncService,
+          base::Unretained(
+              storage_partition_impl_->GetBackgroundSyncContext())));
+  AddUIThreadInterface(
+      registry.get(),
+      base::BindRepeating(
+          &BackgroundSyncContextImpl::CreatePeriodicSyncService,
           base::Unretained(
               storage_partition_impl_->GetBackgroundSyncContext())));
   AddUIThreadInterface(
@@ -3123,20 +3130,12 @@
     renderer_cmd->AppendSwitch(switches::kDisableGpuCompositing);
   }
 #elif !defined(OS_CHROMEOS)
-#if !BUILDFLAG(ENABLE_MUS)
   // If gpu compositing is not being used, tell the renderer at startup. This
   // is inherently racey, as it may change while the renderer is being launched,
   // but the renderer will hear about the correct state eventually. This
   // optimizes the common case to avoid wasted work.
-  // Note: There is no ImageTransportFactory with Mus, but there is also no
-  // software compositing on platforms where Mus is used, e.g. ChromeOS, so
-  // no need to check this state and forward it.
   if (ImageTransportFactory::GetInstance()->IsGpuCompositingDisabled())
     renderer_cmd->AppendSwitch(switches::kDisableGpuCompositing);
-#else   // BUILDFLAG(ENABLE_MUS)
-// TODO(tonikitoo): Check if renderer should use software compositing
-// through some mechanism that isn't ImageTransportFactory with mus.
-#endif  // !BUILDFLAG(ENABLE_MUS)
 #endif  // defined(OS_ANDROID)
 
   // Add kWaitForDebugger to let renderer process wait for a debugger.
diff --git a/content/browser/scheduler/browser_io_task_environment.cc b/content/browser/scheduler/browser_io_task_environment.cc
index f899b38..75a18d9 100644
--- a/content/browser/scheduler/browser_io_task_environment.cc
+++ b/content/browser/scheduler/browser_io_task_environment.cc
@@ -33,9 +33,7 @@
   task_queues_ = std::make_unique<BrowserTaskQueues>(
       BrowserThread::IO, sequence_manager,
       sequence_manager->GetRealTimeDomain());
-  default_task_queue_ = sequence_manager->CreateTaskQueue(
-      TaskQueue::Spec("browser_io_task_environment_default_tq"));
-  default_task_runner_ = default_task_queue_->task_runner();
+  default_task_runner_ = task_queues_->CreateHandle().GetDefaultTaskRunner();
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
diff --git a/content/browser/scheduler/browser_io_task_environment.h b/content/browser/scheduler/browser_io_task_environment.h
index 9225196..7d47e10 100644
--- a/content/browser/scheduler/browser_io_task_environment.h
+++ b/content/browser/scheduler/browser_io_task_environment.h
@@ -64,7 +64,6 @@
   std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager_;
 
   std::unique_ptr<BrowserTaskQueues> task_queues_;
-  scoped_refptr<base::sequence_manager::TaskQueue> default_task_queue_;
   scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
 };
 
diff --git a/content/browser/site_per_process_unload_browsertest.cc b/content/browser/site_per_process_unload_browsertest.cc
index 639aa942..fa788b9 100644
--- a/content/browser/site_per_process_unload_browsertest.cc
+++ b/content/browser/site_per_process_unload_browsertest.cc
@@ -29,7 +29,6 @@
 #include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
-#include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
@@ -308,73 +307,6 @@
   EXPECT_TRUE(watcher.did_exit_normally());
 }
 
-class TestWCBeforeUnloadDelegate : public JavaScriptDialogManager,
-                                   public WebContentsDelegate {
- public:
-  explicit TestWCBeforeUnloadDelegate(WebContentsImpl* web_contents)
-      : web_contents_(web_contents) {
-    web_contents_->SetDelegate(this);
-  }
-
-  ~TestWCBeforeUnloadDelegate() override {
-    if (!callback_.is_null())
-      std::move(callback_).Run(true, base::string16());
-
-    web_contents_->SetDelegate(nullptr);
-    web_contents_->SetJavaScriptDialogManagerForTesting(nullptr);
-  }
-
-  void Wait() {
-    run_loop_->Run();
-    run_loop_ = std::make_unique<base::RunLoop>();
-  }
-
-  // WebContentsDelegate
-
-  JavaScriptDialogManager* GetJavaScriptDialogManager(
-      WebContents* source) override {
-    return this;
-  }
-
-  // JavaScriptDialogManager
-
-  void RunJavaScriptDialog(WebContents* web_contents,
-                           RenderFrameHost* render_frame_host,
-                           JavaScriptDialogType dialog_type,
-                           const base::string16& message_text,
-                           const base::string16& default_prompt_text,
-                           DialogClosedCallback callback,
-                           bool* did_suppress_message) override {
-    NOTREACHED();
-  }
-
-  void RunBeforeUnloadDialog(WebContents* web_contents,
-                             RenderFrameHost* render_frame_host,
-                             bool is_reload,
-                             DialogClosedCallback callback) override {
-    callback_ = std::move(callback);
-    run_loop_->Quit();
-  }
-
-  bool HandleJavaScriptDialog(WebContents* web_contents,
-                              bool accept,
-                              const base::string16* prompt_override) override {
-    NOTREACHED();
-    return true;
-  }
-
-  void CancelDialogs(WebContents* web_contents, bool reset_state) override {}
-
- private:
-  WebContentsImpl* web_contents_;
-
-  DialogClosedCallback callback_;
-
-  std::unique_ptr<base::RunLoop> run_loop_ = std::make_unique<base::RunLoop>();
-
-  DISALLOW_COPY_AND_ASSIGN(TestWCBeforeUnloadDelegate);
-};
-
 // This is a regression test for https://crbug.com/891423 in which tabs showing
 // beforeunload dialogs stalled navigation and triggered the "hung process"
 // dialog.
@@ -407,7 +339,7 @@
       base::string16());
 
   // Hang the first contents in a beforeunload dialog.
-  TestWCBeforeUnloadDelegate test_delegate(web_contents);
+  BeforeUnloadBlockingDelegate test_delegate(web_contents);
   EXPECT_TRUE(
       ExecJs(web_contents, "window.onbeforeunload=function(e){ return 'x' }"));
   EXPECT_TRUE(ExecJs(web_contents,
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index 264be9e0..8d28440 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -239,7 +239,7 @@
   class URLLoaderFactoryForBrowserProcess;
 
   friend class BackgroundSyncManagerTest;
-  friend class BackgroundSyncServiceImplTest;
+  friend class BackgroundSyncServiceImplTestHarness;
   friend class CookieStoreManagerTest;
   friend class PaymentAppContentUnitTestBase;
   friend class StoragePartitionImplMap;
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index a6513ded..3b7c4a0c 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -100,7 +100,7 @@
 class SavePackage;
 class ScreenOrientationProvider;
 class SiteInstance;
-class TestWCBeforeUnloadDelegate;  // site_per_process_browsertest.cc
+class BeforeUnloadBlockingDelegate;  // content_browser_test_utils_internal.h
 class
     TestWCDelegateForDialogsAndFullscreen;  // web_contents_impl_browsertest.cc
 class TestWebContents;
@@ -1047,7 +1047,7 @@
 
   friend class RenderFrameHostImplBeforeUnloadBrowserTest;
   friend class WebContentsImplBrowserTest;
-  friend class TestWCBeforeUnloadDelegate;
+  friend class BeforeUnloadBlockingDelegate;
   friend class TestWCDelegateForDialogsAndFullscreen;
 
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, NoJSMessageOnInterstitials);
diff --git a/content/public/app/BUILD.gn b/content/public/app/BUILD.gn
index 98feda5..b31fcd54 100644
--- a/content/public/app/BUILD.gn
+++ b/content/public/app/BUILD.gn
@@ -45,10 +45,6 @@
   "//services/service_manager/public/cpp",
 ]
 
-if (enable_mus) {
-  public_app_shared_deps += [ "//ui/aura" ]
-}
-
 if (is_component_build) {
   source_set("both_sources") {
     # Only the main content shared library can pull this in.
diff --git a/content/public/app/content_browser_manifest.cc b/content/public/app/content_browser_manifest.cc
index 44a2e08..bd656493 100644
--- a/content/public/app/content_browser_manifest.cc
+++ b/content/public/app/content_browser_manifest.cc
@@ -54,7 +54,6 @@
               "renderer",
               std::set<const char*>{
                   "blink.mojom.AppCacheBackend",
-                  "blink.mojom.BackgroundSyncService",
                   "blink.mojom.BlobRegistry",
                   "blink.mojom.BroadcastChannelProvider",
                   "blink.mojom.ClipboardHost",
@@ -66,6 +65,8 @@
                   "blink.mojom.Hyphenation",
                   "blink.mojom.MediaStreamTrackMetricsHost",
                   "blink.mojom.MimeRegistry",
+                  "blink.mojom.OneShotBackgroundSyncService",
+                  "blink.mojom.PeriodicBackgroundSyncService",
                   "blink.mojom.PluginRegistry",
                   "blink.mojom.PushMessaging",
                   "blink.mojom.ReportingServiceProxy",
diff --git a/content/public/browser/download_manager.h b/content/public/browser/download_manager.h
index 2a3db81..78eb41c 100644
--- a/content/public/browser/download_manager.h
+++ b/content/public/browser/download_manager.h
@@ -213,11 +213,6 @@
 
   virtual BrowserContext* GetBrowserContext() = 0;
 
-  // Checks whether downloaded files still exist. Updates state of downloads
-  // that refer to removed files. The check runs in the background and may
-  // finish asynchronously after this method returns.
-  virtual void CheckForHistoryFilesRemoval() = 0;
-
   // Called when download history query completes. Call
   // |load_history_downloads_cb| to load all the history downloads.
   virtual void OnHistoryQueryComplete(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 1c87c41..d605716 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1446,7 +1446,10 @@
     "../browser/background_sync/background_sync_launcher_unittest.cc",
     "../browser/background_sync/background_sync_manager_unittest.cc",
     "../browser/background_sync/background_sync_network_observer_unittest.cc",
-    "../browser/background_sync/background_sync_service_impl_unittest.cc",
+    "../browser/background_sync/background_sync_service_impl_test_harness.cc",
+    "../browser/background_sync/background_sync_service_impl_test_harness.h",
+    "../browser/background_sync/one_shot_background_sync_service_impl_unittest.cc",
+    "../browser/background_sync/periodic_background_sync_service_impl_unittest.cc",
     "../browser/blob_storage/blob_url_unittest.cc",
     "../browser/bluetooth/bluetooth_allowed_devices_unittest.cc",
     "../browser/bluetooth/bluetooth_blocklist_unittest.cc",
diff --git a/content/test/content_browser_test_utils_internal.cc b/content/test/content_browser_test_utils_internal.cc
index 20309b8..d411eb23 100644
--- a/content/test/content_browser_test_utils_internal.cc
+++ b/content/test/content_browser_test_utils_internal.cc
@@ -25,6 +25,7 @@
 #include "content/browser/frame_host/render_frame_host_delegate.h"
 #include "content/browser/frame_host/render_frame_proxy_host.h"
 #include "content/browser/renderer_host/delegated_frame_host.h"
+#include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_visual_properties.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -495,4 +496,56 @@
   run_loop_.Quit();
 }
 
+BeforeUnloadBlockingDelegate::BeforeUnloadBlockingDelegate(
+    WebContentsImpl* web_contents)
+    : web_contents_(web_contents) {
+  web_contents_->SetDelegate(this);
+}
+
+BeforeUnloadBlockingDelegate::~BeforeUnloadBlockingDelegate() {
+  if (!callback_.is_null())
+    std::move(callback_).Run(true, base::string16());
+
+  web_contents_->SetDelegate(nullptr);
+  web_contents_->SetJavaScriptDialogManagerForTesting(nullptr);
+}
+
+void BeforeUnloadBlockingDelegate::Wait() {
+  run_loop_->Run();
+  run_loop_ = std::make_unique<base::RunLoop>();
+}
+
+JavaScriptDialogManager*
+BeforeUnloadBlockingDelegate::GetJavaScriptDialogManager(WebContents* source) {
+  return this;
+}
+
+void BeforeUnloadBlockingDelegate::RunJavaScriptDialog(
+    WebContents* web_contents,
+    RenderFrameHost* render_frame_host,
+    JavaScriptDialogType dialog_type,
+    const base::string16& message_text,
+    const base::string16& default_prompt_text,
+    DialogClosedCallback callback,
+    bool* did_suppress_message) {
+  NOTREACHED();
+}
+
+void BeforeUnloadBlockingDelegate::RunBeforeUnloadDialog(
+    WebContents* web_contents,
+    RenderFrameHost* render_frame_host,
+    bool is_reload,
+    DialogClosedCallback callback) {
+  callback_ = std::move(callback);
+  run_loop_->Quit();
+}
+
+bool BeforeUnloadBlockingDelegate::HandleJavaScriptDialog(
+    WebContents* web_contents,
+    bool accept,
+    const base::string16* prompt_override) {
+  NOTREACHED();
+  return true;
+}
+
 }  // namespace content
diff --git a/content/test/content_browser_test_utils_internal.h b/content/test/content_browser_test_utils_internal.h
index 19a4cae0..07ff509 100644
--- a/content/test/content_browser_test_utils_internal.h
+++ b/content/test/content_browser_test_utils_internal.h
@@ -24,6 +24,7 @@
 #include "build/build_config.h"
 #include "content/browser/bad_message.h"
 #include "content/common/frame_messages.h"
+#include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/resource_dispatcher_host_delegate.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/test/browser_test_utils.h"
@@ -294,6 +295,51 @@
   DISALLOW_COPY_AND_ASSIGN(UnresponsiveRendererObserver);
 };
 
+// Helper class that overrides the JavaScriptDialogManager of a WebContents
+// to endlessly block on beforeunload.
+class BeforeUnloadBlockingDelegate : public JavaScriptDialogManager,
+                                     public WebContentsDelegate {
+ public:
+  explicit BeforeUnloadBlockingDelegate(WebContentsImpl* web_contents);
+  ~BeforeUnloadBlockingDelegate() override;
+  void Wait();
+
+  // WebContentsDelegate
+
+  JavaScriptDialogManager* GetJavaScriptDialogManager(
+      WebContents* source) override;
+
+  // JavaScriptDialogManager
+
+  void RunJavaScriptDialog(WebContents* web_contents,
+                           RenderFrameHost* render_frame_host,
+                           JavaScriptDialogType dialog_type,
+                           const base::string16& message_text,
+                           const base::string16& default_prompt_text,
+                           DialogClosedCallback callback,
+                           bool* did_suppress_message) override;
+
+  void RunBeforeUnloadDialog(WebContents* web_contents,
+                             RenderFrameHost* render_frame_host,
+                             bool is_reload,
+                             DialogClosedCallback callback) override;
+
+  bool HandleJavaScriptDialog(WebContents* web_contents,
+                              bool accept,
+                              const base::string16* prompt_override) override;
+
+  void CancelDialogs(WebContents* web_contents, bool reset_state) override {}
+
+ private:
+  WebContentsImpl* web_contents_;
+
+  DialogClosedCallback callback_;
+
+  std::unique_ptr<base::RunLoop> run_loop_ = std::make_unique<base::RunLoop>();
+
+  DISALLOW_COPY_AND_ASSIGN(BeforeUnloadBlockingDelegate);
+};
+
 }  // namespace content
 
 #endif  // CONTENT_TEST_CONTENT_BROWSER_TEST_UTILS_INTERNAL_H_
diff --git a/content/test/gpu/gpu_tests/gpu_helper.py b/content/test/gpu/gpu_tests/gpu_helper.py
index 4ec98cd..fd6231e 100644
--- a/content/test/gpu/gpu_tests/gpu_helper.py
+++ b/content/test/gpu/gpu_tests/gpu_helper.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import re
+import mock
 
 def _ParseANGLEGpuVendorString(device_string):
   if not device_string:
@@ -95,3 +96,24 @@
       if "UseSkiaRenderer" in o:
         return 'skia-renderer'
   return 'no-skia-renderer'
+
+# used by unittests to create a mock arguments object
+def GetMockArgs(is_asan=False, webgl_version='1.0.0'):
+  args = mock.MagicMock()
+  args.is_asan = is_asan
+  args.webgl_conformance_version = webgl_version
+  args.webgl2_only = False
+  args.url = 'https://www.google.com'
+  args.duration = 10
+  args.delay = 10
+  args.resolution = 100
+  args.fullscreen = False
+  args.underlay = False
+  args.logdir = '/tmp'
+  args.repeat = 1
+  args.outliers = 0
+  args.bypass_ipg = False
+  args.expected_vendor_id = 0
+  args.expected_device_id = 0
+  args.browser_options = []
+  return args
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
index 5b0ae9e..03f4fdd 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
@@ -11,47 +11,20 @@
 import sys
 import run_gpu_integration_test
 import gpu_project_config
-import inspect
-import itertools
 
-from collections import defaultdict
+from gpu_tests import context_lost_integration_test
+from gpu_tests import gpu_helper
+from gpu_tests import gpu_integration_test
+from gpu_tests import path_util
+from gpu_tests import webgl_conformance_integration_test
 
 from telemetry.testing import browser_test_runner
 from telemetry.testing import fakes
 from telemetry.internal.platform import system_info
 
-from gpu_tests import context_lost_integration_test
-from gpu_tests import gpu_integration_test
-from gpu_tests import path_util
-from gpu_tests import pixel_integration_test
-from gpu_tests import pixel_test_pages
-from gpu_tests import webgl_conformance_integration_test
-from gpu_tests import webgl_test_util
-
-from py_utils import discover
-from typ import expectations_parser
-from typ import json_results
-
 path_util.AddDirToPathIfNeeded(path_util.GetChromiumSrcDir(), 'tools', 'perf')
 from chrome_telemetry_build import chromium_config
 
-OS_CONDITIONS = ['win', 'mac', 'android']
-GPU_CONDITIONS = ['amd', 'arm', 'broadcom', 'hisilicon', 'intel', 'imagination',
-                  'nvidia', 'qualcomm', 'vivante']
-WIN_CONDITIONS = ['xp', 'vista', 'win7', 'win8', 'win10']
-MAC_CONDITIONS = ['leopard', 'snowleopard', 'lion', 'mountainlion',
-                  'mavericks', 'yosemite', 'sierra', 'highsierra', 'mojave']
-# These aren't expanded out into "lollipop", "marshmallow", etc.
-ANDROID_CONDITIONS = ['l', 'm', 'n', 'o', 'p', 'q']
-GENERIC_CONDITIONS = OS_CONDITIONS + GPU_CONDITIONS
-
-_map_specific_to_generic = {sos:'win' for sos in WIN_CONDITIONS}
-_map_specific_to_generic.update({sos:'mac' for sos in MAC_CONDITIONS})
-_map_specific_to_generic.update({sos:'android' for sos in ANDROID_CONDITIONS})
-
-_get_generic = lambda tags: set(
-    [_map_specific_to_generic.get(tag, tag) for tag in tags])
-
 VENDOR_NVIDIA = 0x10DE
 VENDOR_AMD = 0x1002
 VENDOR_INTEL = 0x8086
@@ -59,28 +32,6 @@
 VENDOR_STRING_IMAGINATION = 'Imagination Technologies'
 DEVICE_STRING_SGX = 'PowerVR SGX 554'
 
-ResultType = json_results.ResultType
-
-def _GetMockArgs(is_asan=False, webgl_version='1.0.0'):
-  args = mock.MagicMock()
-  args.is_asan = is_asan
-  args.webgl_conformance_version = webgl_version
-  args.webgl2_only = False
-  args.url = 'https://www.google.com'
-  args.duration = 10
-  args.delay = 10
-  args.resolution = 100
-  args.fullscreen = False
-  args.underlay = False
-  args.logdir = '/tmp'
-  args.repeat = 1
-  args.outliers = 0
-  args.bypass_ipg = False
-  args.expected_vendor_id = 0
-  args.expected_device_id = 0
-  args.browser_options = []
-  return args
-
 
 def _GetSystemInfo(
     gpu='', device='', vendor_string='',
@@ -108,7 +59,7 @@
   try:
     possible_browser = fakes.FakePossibleBrowser()
     possible_browser._returned_browser = browser
-    args = args or _GetMockArgs()
+    args = args or gpu_helper.GetMockArgs()
     ret = set(gpu_tests.GenerateTags(args, possible_browser))
   finally:
     gpu_tests.ExpectationsFiles = expectations_fn
@@ -131,202 +82,6 @@
   return ret
 
 
-def _CheckTestExpectationsAreForExistingTests(
-    test_class, mock_options, test_names=None):
-  test_names = test_names or [
-      args[0] for args in
-      test_class.GenerateGpuTests(mock_options)]
-  expectations_file = test_class.ExpectationsFiles()[0]
-  trie = {}
-  for test in test_names:
-    _trie = trie.setdefault(test[0], {})
-    for l in test[1:]:
-      _trie = _trie.setdefault(l, {})
-  f = open(expectations_file, 'r')
-  expectations_file = os.path.basename(expectations_file)
-  expectations = f.read()
-  f.close()
-  parser = expectations_parser.TaggedTestListParser(expectations)
-  for exp in parser.expectations:
-    _trie = trie
-    for l in exp.test:
-      if l == '*':
-        break
-      assert l in _trie, (
-        "%s:%d: Glob '%s' does not match with any tests in the %s test suite" %
-        (expectations_file, exp.lineno, exp.test, test_class.Name()))
-      _trie = _trie[l]
-
-
-def _IsCollision(s1, s2):
-  # s1 collides with s2 when s1 is a subset of s2
-  # A tag is in both sets if its a generic tag
-  # and its in one set while a specifc tag covered by the generic tag is in
-  # the other set.
-  for tag in s1:
-    if (not tag in s2 and not (
-        tag in GENERIC_CONDITIONS and
-        any(_map_specific_to_generic.get(t, t) == tag for t in s2)) and
-        not _map_specific_to_generic.get(tag, tag) in s2):
-      return False
-  return True
-
-
-def _ConflictLeaksRegression(possible_collision, exp):
-  reason_template = (
-      'Pattern \'{0}\' on line {1} has the %s expectation however the '
-      'the pattern on \'{2}\' line {3} has the Pass expectation'). format(
-          possible_collision.test, possible_collision.lineno, exp.test,
-          exp.lineno)
-  causes_regression = not(
-      ResultType.Failure in exp.results or ResultType.Skip in exp.results)
-  if (ResultType.Skip in possible_collision.results and
-      causes_regression):
-    return reason_template % 'Skip'
-  if (ResultType.Failure in possible_collision.results and
-      causes_regression):
-    return reason_template % 'Failure'
-  return ''
-
-
-def _MapGpuDevicesToVendors(tag_sets):
-  for tag_set in tag_sets:
-    if any(gpu in tag_set for gpu in GPU_CONDITIONS):
-      _map_specific_to_generic.update(
-          {t[0]: t[1] for t in
-           itertools.permutations(tag_set, 2) if (t[0] + '-').startswith(t[1])})
-      break
-
-
-def _CheckTestExpectationGlobsForCollision(expectations, file_name):
-  """This function looks for collisions between test expectations with patterns
-  that match with test expectation patterns that are globs. A test expectation
-  collides with another if its pattern matches with another's glob and if its
-  tags is a super set of the other expectation's tags. The less specific test
-  expectation must have a failure or skip expectation while the more specific
-  test expectation does not. The more specific test expectation will trump
-  the less specific test expectation and may cause an unexpected regression.
-
-  Args:
-  expectations: A string containing test expectations in the new format
-  file_name: Name of the file that the test expectations came from
-  """
-  master_conflicts_found = False
-  error_msg = ''
-  trie = {}
-  parser = expectations_parser.TaggedTestListParser(expectations)
-  globs_to_expectations = defaultdict(list)
-
-  _MapGpuDevicesToVendors(parser.tag_sets)
-
-  for exp in parser.expectations:
-    _trie = trie.setdefault(exp.test[0], {})
-    for l in exp.test[1:]:
-      _trie = _trie.setdefault(l, {})
-    _trie.setdefault('$', []).append(exp)
-
-  for exp in parser.expectations:
-    _trie = trie
-    glob = ''
-    for l in exp.test:
-      if '*' in _trie:
-        globs_to_expectations[glob + '*'].append(exp)
-      glob += l
-      if l in _trie:
-        _trie = _trie[l]
-      else:
-        break
-    if '*' in _trie:
-      globs_to_expectations[glob + '*'].append(exp)
-
-  for glob, expectations in globs_to_expectations.items():
-    conflicts_found = False
-    globs_to_match = [e for e in expectations if e.test == glob]
-    matched_to_globs = [e for e in expectations if e.test != glob]
-    for match in matched_to_globs:
-      for possible_collision in globs_to_match:
-        reason = _ConflictLeaksRegression(possible_collision, match)
-        if (reason and
-            _IsCollision(possible_collision.tags, match.tags)):
-          if not conflicts_found:
-            error_msg += ('\n\nFound conflicts for pattern %s in %s:\n' %
-                          (glob, file_name))
-          master_conflicts_found = conflicts_found = True
-          error_msg += ('  line %d conflicts with line %d: %s\n' %
-                        (possible_collision.lineno, match.lineno, reason))
-  assert not master_conflicts_found, error_msg
-
-
-def _CheckTestExpectationPatternsForCollision(expectations, file_name):
-  """This function makes sure that any test expectations that have the same
-  pattern do not collide with each other. They collide when one expectation's
-  tags are a subset of the other expectation's tags. If the expectation with
-  the larger tag set is active during a test run, then the expectation's whose
-  tag set is a subset of the tags will be active as well.
-
-  Args:
-  expectations: A string containing test expectations in the new format
-  file_name: Name of the file that the test expectations came from
-  """
-  parser = expectations_parser.TaggedTestListParser(expectations)
-  tests_to_exps = defaultdict(list)
-  master_conflicts_found = False
-  error_msg = ''
-  _MapGpuDevicesToVendors(parser.tag_sets)
-
-  for exp in parser.expectations:
-    tests_to_exps[exp.test].append(exp)
-  for pattern, exps in tests_to_exps.items():
-    conflicts_found = False
-    for e1, e2 in itertools.combinations(exps, 2):
-      if _IsCollision(e1.tags, e2.tags) or _IsCollision(e2.tags, e1.tags):
-        if not conflicts_found:
-          error_msg += ('\n\nFound conflicts for test %s in %s:\n' %
-                        (pattern, file_name))
-        master_conflicts_found = conflicts_found = True
-        error_msg += ('  line %d conflicts with line %d\n' %
-                      (e1.lineno, e2.lineno))
-  assert not master_conflicts_found, error_msg
-
-
-def _CheckPatternIsValid(pattern):
-  if not '*' in pattern and not 'WebglExtension_' in pattern:
-    full_path = os.path.normpath(os.path.join(
-        webgl_test_util.conformance_path, pattern))
-    if not os.path.exists(full_path):
-      raise Exception('The WebGL conformance test path specified in ' +
-        'expectation does not exist: ' + full_path)
-
-
-def _TestCheckTestExpectationsAreForExistingTests(expectations):
-  options = _GetMockArgs()
-  expectations_file = tempfile.NamedTemporaryFile(delete=False)
-  expectations_file.write(expectations)
-  expectations_file.close()
-  gpu_tests = gpu_integration_test.GpuIntegrationTest
-  generate_gpu_fn = gpu_tests.GenerateGpuTests
-  expectations_files_fn = gpu_tests.ExpectationsFiles
-  gpu_tests.GenerateGpuTests = mock.MagicMock(return_value=[('a/b/c', ())])
-  gpu_tests.ExpectationsFiles = mock.MagicMock(
-      return_value=[expectations_file.name])
-  try:
-    _CheckTestExpectationsAreForExistingTests(gpu_tests, options)
-  finally:
-    gpu_tests.GenerateGpuTests = generate_gpu_fn
-    gpu_tests.ExpectationsFiles = expectations_files_fn
-
-
-def _FindTestCases():
-  test_cases = []
-  for start_dir in gpu_project_config.CONFIG.start_dirs:
-    modules_to_classes = discover.DiscoverClasses(
-        start_dir,
-        gpu_project_config.CONFIG.top_level_dir,
-        base_class=gpu_integration_test.GpuIntegrationTest)
-    test_cases.extend(modules_to_classes.values())
-  return test_cases
-
-
 class GpuIntegrationTestUnittest(unittest.TestCase):
   def setUp(self):
     self._test_state = {}
@@ -352,232 +107,6 @@
     finally:
       temp_file.close()
 
-  def testCollisionInTestExpectationsWithSpecifcAndGenericOsTags(self):
-    test_expectations = '''# tags: [ mac win linux xp ]
-    # tags: [ intel amd nvidia ]
-    # tags: [ debug release ]
-    [ intel win ] a/b/c/d [ Failure ]
-    [ intel xp debug ] a/b/c/d [ Skip ]
-    '''
-    with self.assertRaises(AssertionError):
-      _CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
-
-  def testNoCollisionBetweenSpecificOsTags(self):
-    test_expectations = '''# tags: [ mac win linux xp win7 ]
-    # tags: [ intel amd nvidia ]
-    # tags: [ debug release ]
-    [ intel win7 ] a/b/c/d [ Failure ]
-    [ intel xp debug ] a/b/c/d [ Skip ]
-    '''
-    _CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
-
-  def testCollisionInTestExpectationsWithGpuVendorAndDeviceTags(self):
-    test_expectations = '''# tags: [ mac win linux xp ]
-    # tags: [ intel amd nvidia nvidia-0x01 ]
-    # tags: [ debug release ]
-    [ nvidia win ] a/b/c/d [ Failure ]
-    [ nvidia-0x01 xp debug ] a/b/c/d [ Skip ]
-    '''
-    with self.assertRaises(AssertionError):
-      _CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
-
-  def testCollisionInTestExpectationsWithGpuVendorAndDeviceTags2(self):
-    test_expectations = '''# tags: [ mac win linux xp win7 ]
-    # tags: [ intel amd nvidia nvidia-0x01 nvidia-0x02 ]
-    # tags: [ debug release ]
-    [ nvidia-0x01 win ] a/b/c/* [ Failure ]
-    [ nvidia win7 debug ] a/b/c/* [ Skip ]
-    '''
-    with self.assertRaises(AssertionError):
-      _CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
-
-  def testNoCollisionBetweenGpuDeviceTags(self):
-    test_expectations = '''# tags: [ mac win linux xp win7 ]
-    # tags: [ intel amd nvidia nvidia-0x01 nvidia-0x02 ]
-    # tags: [ debug release ]
-    [ nvidia-0x01 win7 ] a/b/c/d [ Failure ]
-    [ nvidia-0x02 win7 debug ] a/b/c/d [ Skip ]
-    [ nvidia win debug ] a/b/c/* [ Skip ]
-    '''
-    _CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
-
-  def testMixGenericandSpecificTagsInCollidingSets(self):
-    test_expectations = '''# tags: [ mac win linux xp win7 ]
-    # tags: [ intel amd nvidia nvidia-0x01 ]
-    # tags: [ debug release ]
-    [ nvidia-0x01 win ] a/b/c/d [ Failure ]
-    [ nvidia win7 debug ] a/b/c/d [ Skip ]
-    '''
-    with self.assertRaises(AssertionError):
-      _CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
-
-  def testCollisionInTestExpectationCausesAssertion(self):
-    test_expectations = '''# tags: [ mac win linux ]
-    # tags: [ intel amd nvidia ]
-    # tags: [ debug release ]
-    [ intel win ] a/b/c/d [ Failure ]
-    [ intel win debug ] a/b/c/d [ Skip ]
-    [ intel  ] a/b/c/d [ Failure ]
-    [ amd mac ] a/b [ RetryOnFailure ]
-    [ mac ] a/b [ Skip ]
-    [ amd mac ] a/b/c [ Failure ]
-    [ intel mac ] a/b/c [ Failure ]
-    '''
-    with self.assertRaises(AssertionError) as context:
-      _CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
-    self.assertIn("Found conflicts for test a/b/c/d in test.txt:",
-      str(context.exception))
-    self.assertIn('line 4 conflicts with line 5',
-      str(context.exception))
-    self.assertIn('line 4 conflicts with line 6',
-      str(context.exception))
-    self.assertIn('line 5 conflicts with line 6',
-      str(context.exception))
-    self.assertIn("Found conflicts for test a/b in test.txt:",
-      str(context.exception))
-    self.assertIn('line 7 conflicts with line 8',
-      str(context.exception))
-    self.assertNotIn("Found conflicts for test a/b/c in test.txt:",
-      str(context.exception))
-
-  def testCollisionWithGlobsWithFailureExpectation(self):
-    test_expectations = '''# tags: [ mac win linux ]
-    # tags: [ intel amd nvidia ]
-    # tags: [ debug release ]
-    [ intel debug ] a/b/c/d* [ Failure ]
-    [ intel debug mac ] a/b/c/d [ RetryOnFailure ]
-    [ intel debug mac ] a/b/c/d/e [ Failure ]
-    '''
-    with self.assertRaises(AssertionError) as context:
-      _CheckTestExpectationGlobsForCollision(test_expectations, 'test.txt')
-    self.assertIn('Found conflicts for pattern a/b/c/d* in test.txt:',
-        str(context.exception))
-    self.assertIn(("line 4 conflicts with line 5: Pattern 'a/b/c/d*' on line 4 "
-        "has the Failure expectation however the the pattern on 'a/b/c/d'"
-        " line 5 has the Pass expectation"),
-        str(context.exception))
-    self.assertNotIn('line 4 conflicts with line 6',
-        str(context.exception))
-
-  def testNoCollisionWithGlobsWithFailureExpectation(self):
-    test_expectations = '''# tags: [ mac win linux ]
-    # tags: [ intel amd nvidia ]
-    # tags: [ debug release ]
-    [ intel debug mac ] a/b/c/* [ Failure ]
-    [ intel debug ] a/b/c/d [ Failure ]
-    [ intel debug ] a/b/c/d [ Skip ]
-    '''
-    _CheckTestExpectationGlobsForCollision(test_expectations, 'test.txt')
-
-  def testCollisionWithGlobsWithSkipExpectation(self):
-    test_expectations = '''# tags: [ mac win linux ]
-    # tags: [ intel amd nvidia ]
-    # tags: [ debug release ]
-    [ intel debug ] a/b/c/d* [ Skip ]
-    [ intel debug mac ] a/b/c/d [ Failure ]
-    [ intel debug mac ] a/b/c/d/e [ RetryOnFailure ]
-    '''
-    with self.assertRaises(AssertionError) as context:
-      _CheckTestExpectationGlobsForCollision(test_expectations, 'test.txt')
-    self.assertIn('Found conflicts for pattern a/b/c/d* in test.txt:',
-        str(context.exception))
-    self.assertNotIn('line 4 conflicts with line 5',
-        str(context.exception))
-    self.assertIn('line 4 conflicts with line 6',
-        str(context.exception))
-    self.assertIn(("line 4 conflicts with line 6: Pattern 'a/b/c/d*' on line 4 "
-        "has the Skip expectation however the the pattern on 'a/b/c/d/e'"
-        " line 6 has the Pass expectation"),
-        str(context.exception))
-
-  def testNoCollisionWithGlobsWithSkipExpectation(self):
-    test_expectations = '''# tags: [ mac win linux ]
-    # tags: [ intel amd nvidia ]
-    # tags: [ debug release ]
-    [ intel debug mac ] a/b/c/* [ Skip ]
-    [ intel debug ] a/b/c/d [ Skip ]
-    [ intel debug ] a/b/c/d [ Skip ]
-    '''
-    _CheckTestExpectationGlobsForCollision(test_expectations, 'test.txt')
-
-  def testNoCollisionInTestExpectations(self):
-    test_expectations = '''# tags: [ mac win linux ]
-    # tags: [ intel amd nvidia ]
-    # tags: [ debug release ]
-    [ intel win release ] a/b/* [ Failure ]
-    [ intel debug ] a/b/c/d [ Failure ]
-    [ nvidia debug ] a/b/c/d [ Failure ]
-    '''
-    _CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
-
-  def testNoCollisionsForSameTestsInGpuTestExpectations(self):
-    webgl_conformance_test_class = (
-        webgl_conformance_integration_test.WebGLConformanceIntegrationTest)
-    for test_case in _FindTestCases():
-      if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__:
-        for i in xrange(1, 2 + (test_case == webgl_conformance_test_class)):
-          _ = list(test_case.GenerateGpuTests(
-              _GetMockArgs(webgl_version=('%d.0.0' % i))))
-          if test_case.ExpectationsFiles():
-            with open(test_case.ExpectationsFiles()[0]) as f:
-              _CheckTestExpectationPatternsForCollision(f.read(),
-              os.path.basename(f.name))
-
-  def testNoCollisionsWithGlobsInGpuTestExpectations(self):
-    webgl_conformance_test_class = (
-        webgl_conformance_integration_test.WebGLConformanceIntegrationTest)
-    for test_case in _FindTestCases():
-      if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__:
-        for i in xrange(1, 2 + (test_case == webgl_conformance_test_class)):
-          _ = list(test_case.GenerateGpuTests(
-              _GetMockArgs(webgl_version=('%d.0.0' % i))))
-          if test_case.ExpectationsFiles():
-            with open(test_case.ExpectationsFiles()[0]) as f:
-              _CheckTestExpectationGlobsForCollision(f.read(),
-              os.path.basename(f.name))
-
-  def testGpuTestExpectationsAreForExistingTests(self):
-    options = _GetMockArgs()
-    for test_case in _FindTestCases():
-      if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__:
-        if (test_case.Name() not in ('pixel', 'webgl_conformance')
-            and test_case.ExpectationsFiles()):
-          _CheckTestExpectationsAreForExistingTests(test_case, options)
-
-  def testWebglTestPathsExist(self):
-    webgl_test_class = (
-        webgl_conformance_integration_test.WebGLConformanceIntegrationTest)
-    for test_case in _FindTestCases():
-      if test_case == webgl_test_class:
-        for i in xrange(1, 3):
-          _ = list(test_case.GenerateGpuTests(
-              _GetMockArgs(webgl_version='%d.0.0' % i)))
-          with open(test_case.ExpectationsFiles()[0], 'r') as f:
-            expectations = expectations_parser.TaggedTestListParser(f.read())
-            for exp in expectations.expectations:
-              _CheckPatternIsValid(exp.test)
-
-  def testPixelTestsExpectationsAreForExistingTests(self):
-    pixel_test_names = []
-    for _, method in inspect.getmembers(
-        pixel_test_pages.PixelTestPages, predicate=inspect.isfunction):
-      pixel_test_names.extend(
-          [p.name for p in method(
-              pixel_integration_test.PixelIntegrationTest.test_base_name)])
-    _CheckTestExpectationsAreForExistingTests(
-        pixel_integration_test.PixelIntegrationTest,
-        _GetMockArgs(), pixel_test_names)
-
-  def testExpectationPatternNotInGeneratedTests(self):
-    with self.assertRaises(AssertionError) as context:
-      _TestCheckTestExpectationsAreForExistingTests('a/b/d [ Failure ]')
-    self.assertIn(("1: Glob 'a/b/d' does not match with any"
-                  " tests in the GpuIntegrationTest test suite"),
-                  str(context.exception))
-
-  def testGlobMatchesTestName(self):
-    _TestCheckTestExpectationsAreForExistingTests('a/b* [ Failure ]')
-
   def testOverrideDefaultRetryArgumentsinRunGpuIntegrationTests(self):
     self._RunGpuIntegrationTests(
         'run_tests_with_expectations_files', ['--retry-limit=1'])
@@ -600,7 +129,7 @@
     # we need to make sure that GenerateTags() returns an empty list if
     # there are no expectations files returned from ExpectationsFiles() or
     # else Typ will raise an exception
-    args = _GetMockArgs()
+    args = gpu_helper.GetMockArgs()
     possible_browser = mock.MagicMock()
     self.assertFalse(gpu_integration_test.GpuIntegrationTest.GenerateTags(
         args, possible_browser))
@@ -615,7 +144,7 @@
     return tag_set
 
   def testGenerateContextLostExampleTagsForAsan(self):
-    args = _GetMockArgs(is_asan=True)
+    args = gpu_helper.GetMockArgs(is_asan=True)
     tag_set = self._TestTagGenerationForMockPlatform(
         context_lost_integration_test.ContextLostIntegrationTest,
         args)
@@ -623,7 +152,7 @@
     self.assertNotIn('no-asan', tag_set)
 
   def testGenerateContextLostExampleTagsForNoAsan(self):
-    args = _GetMockArgs()
+    args = gpu_helper.GetMockArgs()
     tag_set = self._TestTagGenerationForMockPlatform(
         context_lost_integration_test.ContextLostIntegrationTest,
         args)
@@ -631,7 +160,7 @@
     self.assertNotIn('asan', tag_set)
 
   def testGenerateWebglConformanceExampleTagsForWebglVersion1andAsan(self):
-    args = _GetMockArgs(is_asan=True, webgl_version='1.0.0')
+    args = gpu_helper.GetMockArgs(is_asan=True, webgl_version='1.0.0')
     tag_set = self._TestTagGenerationForMockPlatform(
         webgl_conformance_integration_test.WebGLConformanceIntegrationTest,
         args)
@@ -639,7 +168,7 @@
     self.assertFalse(set(['no-asan', 'webgl-version-2']) & tag_set)
 
   def testGenerateWebglConformanceExampleTagsForWebglVersion2andNoAsan(self):
-    args = _GetMockArgs(is_asan=False, webgl_version='2.0.0')
+    args = gpu_helper.GetMockArgs(is_asan=False, webgl_version='2.0.0')
     tag_set = self._TestTagGenerationForMockPlatform(
         webgl_conformance_integration_test.WebGLConformanceIntegrationTest,
         args)
diff --git a/content/test/gpu/gpu_tests/test_expectations_unittest.py b/content/test/gpu/gpu_tests/test_expectations_unittest.py
new file mode 100644
index 0000000..1e29b9b
--- /dev/null
+++ b/content/test/gpu/gpu_tests/test_expectations_unittest.py
@@ -0,0 +1,469 @@
+# 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 gpu_project_config
+import inspect
+import itertools
+import mock
+import os
+import tempfile
+import unittest
+
+from collections import defaultdict
+
+from gpu_tests import gpu_helper
+from gpu_tests import gpu_integration_test
+from gpu_tests import pixel_integration_test
+from gpu_tests import pixel_test_pages
+from gpu_tests import webgl_conformance_integration_test
+from gpu_tests import webgl_test_util
+
+from py_utils import discover
+
+from typ import expectations_parser
+from typ import json_results
+
+OS_CONDITIONS = ['win', 'mac', 'android']
+GPU_CONDITIONS = ['amd', 'arm', 'broadcom', 'hisilicon', 'intel', 'imagination',
+                  'nvidia', 'qualcomm', 'vivante']
+WIN_CONDITIONS = ['xp', 'vista', 'win7', 'win8', 'win10']
+MAC_CONDITIONS = ['leopard', 'snowleopard', 'lion', 'mountainlion',
+                  'mavericks', 'yosemite', 'sierra', 'highsierra', 'mojave']
+# These aren't expanded out into "lollipop", "marshmallow", etc.
+ANDROID_CONDITIONS = ['l', 'm', 'n', 'o', 'p', 'q']
+GENERIC_CONDITIONS = OS_CONDITIONS + GPU_CONDITIONS
+
+_map_specific_to_generic = {sos:'win' for sos in WIN_CONDITIONS}
+_map_specific_to_generic.update({sos:'mac' for sos in MAC_CONDITIONS})
+_map_specific_to_generic.update({sos:'android' for sos in ANDROID_CONDITIONS})
+
+_get_generic = lambda tags: set(
+    [_map_specific_to_generic.get(tag, tag) for tag in tags])
+
+ResultType = json_results.ResultType
+
+def _ConflictLeaksRegression(possible_collision, exp):
+  reason_template = (
+      'Pattern \'{0}\' on line {1} has the %s expectation however the '
+      'the pattern on \'{2}\' line {3} has the Pass expectation'). format(
+          possible_collision.test, possible_collision.lineno, exp.test,
+          exp.lineno)
+  causes_regression = not(
+      ResultType.Failure in exp.results or ResultType.Skip in exp.results)
+  if (ResultType.Skip in possible_collision.results and
+      causes_regression):
+    return reason_template % 'Skip'
+  if (ResultType.Failure in possible_collision.results and
+      causes_regression):
+    return reason_template % 'Failure'
+  return ''
+
+
+def _IsCollision(s1, s2):
+  # s1 collides with s2 when s1 is a subset of s2
+  # A tag is in both sets if its a generic tag
+  # and its in one set while a specifc tag covered by the generic tag is in
+  # the other set.
+  for tag in s1:
+    if (not tag in s2 and not (
+        tag in GENERIC_CONDITIONS and
+        any(_map_specific_to_generic.get(t, t) == tag for t in s2)) and
+        not _map_specific_to_generic.get(tag, tag) in s2):
+      return False
+  return True
+
+
+def _MapGpuDevicesToVendors(tag_sets):
+  for tag_set in tag_sets:
+    if any(gpu in tag_set for gpu in GPU_CONDITIONS):
+      _map_specific_to_generic.update(
+          {t[0]: t[1] for t in
+           itertools.permutations(tag_set, 2) if (t[0] + '-').startswith(t[1])})
+      break
+
+
+def CheckTestExpectationsAreForExistingTests(
+    test_class, mock_options, test_names=None):
+  test_names = test_names or [
+      args[0] for args in
+      test_class.GenerateGpuTests(mock_options)]
+  expectations_file = test_class.ExpectationsFiles()[0]
+  trie = {}
+  for test in test_names:
+    _trie = trie.setdefault(test[0], {})
+    for l in test[1:]:
+      _trie = _trie.setdefault(l, {})
+  f = open(expectations_file, 'r')
+  expectations_file = os.path.basename(expectations_file)
+  expectations = f.read()
+  f.close()
+  parser = expectations_parser.TaggedTestListParser(expectations)
+  for exp in parser.expectations:
+    _trie = trie
+    for l in exp.test:
+      if l == '*':
+        break
+      assert l in _trie, (
+        "%s:%d: Glob '%s' does not match with any tests in the %s test suite" %
+        (expectations_file, exp.lineno, exp.test, test_class.Name()))
+      _trie = _trie[l]
+
+
+def CheckTestExpectationGlobsForCollision(expectations, file_name):
+  """This function looks for collisions between test expectations with patterns
+  that match with test expectation patterns that are globs. A test expectation
+  collides with another if its pattern matches with another's glob and if its
+  tags is a super set of the other expectation's tags. The less specific test
+  expectation must have a failure or skip expectation while the more specific
+  test expectation does not. The more specific test expectation will trump
+  the less specific test expectation and may cause an unexpected regression.
+
+  Args:
+  expectations: A string containing test expectations in the new format
+  file_name: Name of the file that the test expectations came from
+  """
+  master_conflicts_found = False
+  error_msg = ''
+  trie = {}
+  parser = expectations_parser.TaggedTestListParser(expectations)
+  globs_to_expectations = defaultdict(list)
+
+  _MapGpuDevicesToVendors(parser.tag_sets)
+
+  for exp in parser.expectations:
+    _trie = trie.setdefault(exp.test[0], {})
+    for l in exp.test[1:]:
+      _trie = _trie.setdefault(l, {})
+    _trie.setdefault('$', []).append(exp)
+
+  for exp in parser.expectations:
+    _trie = trie
+    glob = ''
+    for l in exp.test:
+      if '*' in _trie:
+        globs_to_expectations[glob + '*'].append(exp)
+      glob += l
+      if l in _trie:
+        _trie = _trie[l]
+      else:
+        break
+    if '*' in _trie:
+      globs_to_expectations[glob + '*'].append(exp)
+
+  for glob, expectations in globs_to_expectations.items():
+    conflicts_found = False
+    globs_to_match = [e for e in expectations if e.test == glob]
+    matched_to_globs = [e for e in expectations if e.test != glob]
+    for match in matched_to_globs:
+      for possible_collision in globs_to_match:
+        reason = _ConflictLeaksRegression(possible_collision, match)
+        if (reason and
+            _IsCollision(possible_collision.tags, match.tags)):
+          if not conflicts_found:
+            error_msg += ('\n\nFound conflicts for pattern %s in %s:\n' %
+                          (glob, file_name))
+          master_conflicts_found = conflicts_found = True
+          error_msg += ('  line %d conflicts with line %d: %s\n' %
+                        (possible_collision.lineno, match.lineno, reason))
+  assert not master_conflicts_found, error_msg
+
+
+def CheckTestExpectationPatternsForCollision(expectations, file_name):
+  """This function makes sure that any test expectations that have the same
+  pattern do not collide with each other. They collide when one expectation's
+  tags are a subset of the other expectation's tags. If the expectation with
+  the larger tag set is active during a test run, then the expectation's whose
+  tag set is a subset of the tags will be active as well.
+
+  Args:
+  expectations: A string containing test expectations in the new format
+  file_name: Name of the file that the test expectations came from
+  """
+  parser = expectations_parser.TaggedTestListParser(expectations)
+  tests_to_exps = defaultdict(list)
+  master_conflicts_found = False
+  error_msg = ''
+  _MapGpuDevicesToVendors(parser.tag_sets)
+
+  for exp in parser.expectations:
+    tests_to_exps[exp.test].append(exp)
+  for pattern, exps in tests_to_exps.items():
+    conflicts_found = False
+    for e1, e2 in itertools.combinations(exps, 2):
+      if _IsCollision(e1.tags, e2.tags) or _IsCollision(e2.tags, e1.tags):
+        if not conflicts_found:
+          error_msg += ('\n\nFound conflicts for test %s in %s:\n' %
+                        (pattern, file_name))
+        master_conflicts_found = conflicts_found = True
+        error_msg += ('  line %d conflicts with line %d\n' %
+                      (e1.lineno, e2.lineno))
+  assert not master_conflicts_found, error_msg
+
+
+def _TestCheckTestExpectationsAreForExistingTests(expectations):
+  options = gpu_helper.GetMockArgs()
+  expectations_file = tempfile.NamedTemporaryFile(delete=False)
+  expectations_file.write(expectations)
+  expectations_file.close()
+  gpu_tests = gpu_integration_test.GpuIntegrationTest
+  generate_gpu_fn = gpu_tests.GenerateGpuTests
+  expectations_files_fn = gpu_tests.ExpectationsFiles
+  gpu_tests.GenerateGpuTests = mock.MagicMock(return_value=[('a/b/c', ())])
+  gpu_tests.ExpectationsFiles = mock.MagicMock(
+      return_value=[expectations_file.name])
+  try:
+    CheckTestExpectationsAreForExistingTests(gpu_tests, options)
+  finally:
+    gpu_tests.GenerateGpuTests = generate_gpu_fn
+    gpu_tests.ExpectationsFiles = expectations_files_fn
+
+
+def _CheckPatternIsValid(pattern):
+  if not '*' in pattern and not 'WebglExtension_' in pattern:
+    full_path = os.path.normpath(os.path.join(
+        webgl_test_util.conformance_path, pattern))
+    if not os.path.exists(full_path):
+      raise Exception('The WebGL conformance test path specified in ' +
+        'expectation does not exist: ' + full_path)
+
+
+def _FindTestCases():
+  test_cases = []
+  for start_dir in gpu_project_config.CONFIG.start_dirs:
+    modules_to_classes = discover.DiscoverClasses(
+        start_dir,
+        gpu_project_config.CONFIG.top_level_dir,
+        base_class=gpu_integration_test.GpuIntegrationTest)
+    test_cases.extend(modules_to_classes.values())
+  return test_cases
+
+
+class GpuTestExpectationsValidation(unittest.TestCase):
+  def testNoCollisionsForSameTestsInGpuTestExpectations(self):
+    webgl_conformance_test_class = (
+        webgl_conformance_integration_test.WebGLConformanceIntegrationTest)
+    for test_case in _FindTestCases():
+      if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__:
+        for i in xrange(1, 2 + (test_case == webgl_conformance_test_class)):
+          _ = list(test_case.GenerateGpuTests(
+              gpu_helper.GetMockArgs(webgl_version=('%d.0.0' % i))))
+          if test_case.ExpectationsFiles():
+            with open(test_case.ExpectationsFiles()[0]) as f:
+              CheckTestExpectationPatternsForCollision(f.read(),
+              os.path.basename(f.name))
+
+  def testNoCollisionsWithGlobsInGpuTestExpectations(self):
+    webgl_conformance_test_class = (
+        webgl_conformance_integration_test.WebGLConformanceIntegrationTest)
+    for test_case in _FindTestCases():
+      if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__:
+        for i in xrange(1, 2 + (test_case == webgl_conformance_test_class)):
+          _ = list(test_case.GenerateGpuTests(
+              gpu_helper.GetMockArgs(webgl_version=('%d.0.0' % i))))
+          if test_case.ExpectationsFiles():
+            with open(test_case.ExpectationsFiles()[0]) as f:
+              CheckTestExpectationGlobsForCollision(f.read(),
+              os.path.basename(f.name))
+
+  def testGpuTestExpectationsAreForExistingTests(self):
+    options = gpu_helper.GetMockArgs()
+    for test_case in _FindTestCases():
+      if 'gpu_tests.gpu_integration_test_unittest' not in test_case.__module__:
+        if (test_case.Name() not in ('pixel', 'webgl_conformance')
+            and test_case.ExpectationsFiles()):
+          CheckTestExpectationsAreForExistingTests(test_case, options)
+
+  def testWebglTestPathsExist(self):
+    webgl_test_class = (
+        webgl_conformance_integration_test.WebGLConformanceIntegrationTest)
+    for test_case in _FindTestCases():
+      if test_case == webgl_test_class:
+        for i in xrange(1, 3):
+          _ = list(test_case.GenerateGpuTests(
+              gpu_helper.GetMockArgs(webgl_version='%d.0.0' % i)))
+          with open(test_case.ExpectationsFiles()[0], 'r') as f:
+            expectations = expectations_parser.TaggedTestListParser(f.read())
+            for exp in expectations.expectations:
+              _CheckPatternIsValid(exp.test)
+
+  def testPixelTestsExpectationsAreForExistingTests(self):
+    pixel_test_names = []
+    for _, method in inspect.getmembers(
+        pixel_test_pages.PixelTestPages, predicate=inspect.isfunction):
+      pixel_test_names.extend(
+          [p.name for p in method(
+              pixel_integration_test.PixelIntegrationTest.test_base_name)])
+    CheckTestExpectationsAreForExistingTests(
+        pixel_integration_test.PixelIntegrationTest,
+        gpu_helper.GetMockArgs(), pixel_test_names)
+
+
+class TestGpuTestExpectationsValidators(unittest.TestCase):
+  def testCollisionInTestExpectationsWithSpecifcAndGenericOsTags(self):
+    test_expectations = '''# tags: [ mac win linux xp ]
+    # tags: [ intel amd nvidia ]
+    # tags: [ debug release ]
+    [ intel win ] a/b/c/d [ Failure ]
+    [ intel xp debug ] a/b/c/d [ Skip ]
+    '''
+    with self.assertRaises(AssertionError):
+      CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
+
+  def testNoCollisionBetweenSpecificOsTags(self):
+    test_expectations = '''# tags: [ mac win linux xp win7 ]
+    # tags: [ intel amd nvidia ]
+    # tags: [ debug release ]
+    [ intel win7 ] a/b/c/d [ Failure ]
+    [ intel xp debug ] a/b/c/d [ Skip ]
+    '''
+    CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
+
+  def testCollisionInTestExpectationsWithGpuVendorAndDeviceTags(self):
+    test_expectations = '''# tags: [ mac win linux xp ]
+    # tags: [ intel amd nvidia nvidia-0x01 ]
+    # tags: [ debug release ]
+    [ nvidia win ] a/b/c/d [ Failure ]
+    [ nvidia-0x01 xp debug ] a/b/c/d [ Skip ]
+    '''
+    with self.assertRaises(AssertionError):
+      CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
+
+  def testCollisionInTestExpectationsWithGpuVendorAndDeviceTags2(self):
+    test_expectations = '''# tags: [ mac win linux xp win7 ]
+    # tags: [ intel amd nvidia nvidia-0x01 nvidia-0x02 ]
+    # tags: [ debug release ]
+    [ nvidia-0x01 win ] a/b/c/* [ Failure ]
+    [ nvidia win7 debug ] a/b/c/* [ Skip ]
+    '''
+    with self.assertRaises(AssertionError):
+      CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
+
+  def testNoCollisionBetweenGpuDeviceTags(self):
+    test_expectations = '''# tags: [ mac win linux xp win7 ]
+    # tags: [ intel amd nvidia nvidia-0x01 nvidia-0x02 ]
+    # tags: [ debug release ]
+    [ nvidia-0x01 win7 ] a/b/c/d [ Failure ]
+    [ nvidia-0x02 win7 debug ] a/b/c/d [ Skip ]
+    [ nvidia win debug ] a/b/c/* [ Skip ]
+    '''
+    CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
+
+  def testMixGenericandSpecificTagsInCollidingSets(self):
+    test_expectations = '''# tags: [ mac win linux xp win7 ]
+    # tags: [ intel amd nvidia nvidia-0x01 ]
+    # tags: [ debug release ]
+    [ nvidia-0x01 win ] a/b/c/d [ Failure ]
+    [ nvidia win7 debug ] a/b/c/d [ Skip ]
+    '''
+    with self.assertRaises(AssertionError):
+      CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
+
+  def testCollisionInTestExpectationCausesAssertion(self):
+    test_expectations = '''# tags: [ mac win linux ]
+    # tags: [ intel amd nvidia ]
+    # tags: [ debug release ]
+    [ intel win ] a/b/c/d [ Failure ]
+    [ intel win debug ] a/b/c/d [ Skip ]
+    [ intel  ] a/b/c/d [ Failure ]
+    [ amd mac ] a/b [ RetryOnFailure ]
+    [ mac ] a/b [ Skip ]
+    [ amd mac ] a/b/c [ Failure ]
+    [ intel mac ] a/b/c [ Failure ]
+    '''
+    with self.assertRaises(AssertionError) as context:
+      CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
+    self.assertIn("Found conflicts for test a/b/c/d in test.txt:",
+      str(context.exception))
+    self.assertIn('line 4 conflicts with line 5',
+      str(context.exception))
+    self.assertIn('line 4 conflicts with line 6',
+      str(context.exception))
+    self.assertIn('line 5 conflicts with line 6',
+      str(context.exception))
+    self.assertIn("Found conflicts for test a/b in test.txt:",
+      str(context.exception))
+    self.assertIn('line 7 conflicts with line 8',
+      str(context.exception))
+    self.assertNotIn("Found conflicts for test a/b/c in test.txt:",
+      str(context.exception))
+
+  def testCollisionWithGlobsWithFailureExpectation(self):
+    test_expectations = '''# tags: [ mac win linux ]
+    # tags: [ intel amd nvidia ]
+    # tags: [ debug release ]
+    [ intel debug ] a/b/c/d* [ Failure ]
+    [ intel debug mac ] a/b/c/d [ RetryOnFailure ]
+    [ intel debug mac ] a/b/c/d/e [ Failure ]
+    '''
+    with self.assertRaises(AssertionError) as context:
+      CheckTestExpectationGlobsForCollision(test_expectations, 'test.txt')
+    self.assertIn('Found conflicts for pattern a/b/c/d* in test.txt:',
+        str(context.exception))
+    self.assertIn(("line 4 conflicts with line 5: Pattern 'a/b/c/d*' on line 4 "
+        "has the Failure expectation however the the pattern on 'a/b/c/d'"
+        " line 5 has the Pass expectation"),
+        str(context.exception))
+    self.assertNotIn('line 4 conflicts with line 6',
+        str(context.exception))
+
+  def testNoCollisionWithGlobsWithFailureExpectation(self):
+    test_expectations = '''# tags: [ mac win linux ]
+    # tags: [ intel amd nvidia ]
+    # tags: [ debug release ]
+    [ intel debug mac ] a/b/c/* [ Failure ]
+    [ intel debug ] a/b/c/d [ Failure ]
+    [ intel debug ] a/b/c/d [ Skip ]
+    '''
+    CheckTestExpectationGlobsForCollision(test_expectations, 'test.txt')
+
+  def testCollisionWithGlobsWithSkipExpectation(self):
+    test_expectations = '''# tags: [ mac win linux ]
+    # tags: [ intel amd nvidia ]
+    # tags: [ debug release ]
+    [ intel debug ] a/b/c/d* [ Skip ]
+    [ intel debug mac ] a/b/c/d [ Failure ]
+    [ intel debug mac ] a/b/c/d/e [ RetryOnFailure ]
+    '''
+    with self.assertRaises(AssertionError) as context:
+      CheckTestExpectationGlobsForCollision(test_expectations, 'test.txt')
+    self.assertIn('Found conflicts for pattern a/b/c/d* in test.txt:',
+        str(context.exception))
+    self.assertNotIn('line 4 conflicts with line 5',
+        str(context.exception))
+    self.assertIn('line 4 conflicts with line 6',
+        str(context.exception))
+    self.assertIn(("line 4 conflicts with line 6: Pattern 'a/b/c/d*' on line 4 "
+        "has the Skip expectation however the the pattern on 'a/b/c/d/e'"
+        " line 6 has the Pass expectation"),
+        str(context.exception))
+
+  def testNoCollisionWithGlobsWithSkipExpectation(self):
+    test_expectations = '''# tags: [ mac win linux ]
+    # tags: [ intel amd nvidia ]
+    # tags: [ debug release ]
+    [ intel debug mac ] a/b/c/* [ Skip ]
+    [ intel debug ] a/b/c/d [ Skip ]
+    [ intel debug ] a/b/c/d [ Skip ]
+    '''
+    CheckTestExpectationGlobsForCollision(test_expectations, 'test.txt')
+
+  def testNoCollisionInTestExpectations(self):
+    test_expectations = '''# tags: [ mac win linux ]
+    # tags: [ intel amd nvidia ]
+    # tags: [ debug release ]
+    [ intel win release ] a/b/* [ Failure ]
+    [ intel debug ] a/b/c/d [ Failure ]
+    [ nvidia debug ] a/b/c/d [ Failure ]
+    '''
+    CheckTestExpectationPatternsForCollision(test_expectations, 'test.txt')
+
+  def testExpectationPatternNotInGeneratedTests(self):
+    with self.assertRaises(AssertionError) as context:
+      _TestCheckTestExpectationsAreForExistingTests('a/b/d [ Failure ]')
+    self.assertIn(("1: Glob 'a/b/d' does not match with any"
+                  " tests in the GpuIntegrationTest test suite"),
+                  str(context.exception))
+
+  def testGlobMatchesTestName(self):
+    _TestCheckTestExpectationsAreForExistingTests('a/b* [ Failure ]')
diff --git a/extensions/browser/api/messaging/message_service.cc b/extensions/browser/api/messaging/message_service.cc
index fdcf326e4..cba5581 100644
--- a/extensions/browser/api/messaging/message_service.cc
+++ b/extensions/browser/api/messaging/message_service.cc
@@ -416,7 +416,7 @@
   // any issues arise from it.
   std::unique_ptr<MessagePort> receiver(
       messaging_delegate_->CreateReceiverForNativeApp(
-          weak_factory_.GetWeakPtr(), source_rfh, extension->id(),
+          context_, weak_factory_.GetWeakPtr(), source_rfh, extension->id(),
           receiver_port_id, native_app_name,
           policy_permission == MessagingDelegate::PolicyPermission::ALLOW_ALL,
           &error));
diff --git a/extensions/browser/api/messaging/messaging_delegate.cc b/extensions/browser/api/messaging/messaging_delegate.cc
index 1714908c..56ead44 100644
--- a/extensions/browser/api/messaging/messaging_delegate.cc
+++ b/extensions/browser/api/messaging/messaging_delegate.cc
@@ -41,6 +41,7 @@
 }
 
 std::unique_ptr<MessagePort> MessagingDelegate::CreateReceiverForNativeApp(
+    content::BrowserContext* browser_context,
     base::WeakPtr<MessagePort::ChannelDelegate> channel_delegate,
     content::RenderFrameHost* source,
     const std::string& extension_id,
diff --git a/extensions/browser/api/messaging/messaging_delegate.h b/extensions/browser/api/messaging/messaging_delegate.h
index 7acae22..36d448b 100644
--- a/extensions/browser/api/messaging/messaging_delegate.h
+++ b/extensions/browser/api/messaging/messaging_delegate.h
@@ -68,6 +68,7 @@
   // Creates a MessagePort for a native app. If the port cannot be created,
   // returns nullptr and may populate |error_out|.
   virtual std::unique_ptr<MessagePort> CreateReceiverForNativeApp(
+      content::BrowserContext* browser_context,
       base::WeakPtr<MessagePort::ChannelDelegate> channel_delegate,
       content::RenderFrameHost* source,
       const std::string& extension_id,
diff --git a/extensions/browser/api/messaging/native_message_host.h b/extensions/browser/api/messaging/native_message_host.h
index 094083e..ebabefc 100644
--- a/extensions/browser/api/messaging/native_message_host.h
+++ b/extensions/browser/api/messaging/native_message_host.h
@@ -11,6 +11,10 @@
 #include "base/single_thread_task_runner.h"
 #include "ui/gfx/native_widget_types.h"
 
+namespace content {
+class BrowserContext;
+}  // namespace content
+
 namespace extensions {
 
 // An interface for receiving messages from MessageService (Chrome) using the
@@ -38,6 +42,7 @@
 
   // Creates the NativeMessageHost based on the |native_host_name|.
   static std::unique_ptr<NativeMessageHost> Create(
+      content::BrowserContext* browser_context,
       gfx::NativeView native_view,
       const std::string& source_extension_id,
       const std::string& native_host_name,
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn
index 364bd6e..c7a450a 100644
--- a/extensions/renderer/BUILD.gn
+++ b/extensions/renderer/BUILD.gn
@@ -26,8 +26,6 @@
     "api_definitions_natives.h",
     "app_window_custom_bindings.cc",
     "app_window_custom_bindings.h",
-    "async_scripts_run_info.cc",
-    "async_scripts_run_info.h",
     "binding_generating_native_handler.cc",
     "binding_generating_native_handler.h",
     "bindings/api_binding.cc",
diff --git a/extensions/renderer/async_scripts_run_info.cc b/extensions/renderer/async_scripts_run_info.cc
deleted file mode 100644
index 508518d2..0000000
--- a/extensions/renderer/async_scripts_run_info.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2017 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 "extensions/renderer/async_scripts_run_info.h"
-
-#include "base/metrics/histogram_macros.h"
-#include "content/public/renderer/render_frame.h"
-#include "extensions/renderer/scripts_run_info.h"
-
-namespace extensions {
-
-AsyncScriptsRunInfo::AsyncScriptsRunInfo(UserScript::RunLocation location)
-    : run_location_(location) {}
-
-AsyncScriptsRunInfo::~AsyncScriptsRunInfo() {}
-
-void AsyncScriptsRunInfo::WillExecute(const base::TimeTicks& timestamp) {
-  if (!last_completed_time_.is_null()) {
-    switch (run_location_) {
-      case UserScript::DOCUMENT_END:
-        UMA_HISTOGRAM_TIMES(
-            "Extensions.TimeYieldedBetweenContentScriptRuns.DocumentEnd",
-            timestamp - last_completed_time_);
-        break;
-      case UserScript::DOCUMENT_IDLE:
-        UMA_HISTOGRAM_TIMES(
-            "Extensions.TimeYieldedBetweenContentScriptRuns.DocumentIdle",
-            timestamp - last_completed_time_);
-        break;
-      // Currently document_start scripts are not async.
-      default:
-        break;
-    }
-  }
-}
-
-void AsyncScriptsRunInfo::OnCompleted(const base::TimeTicks& timestamp,
-                                      base::Optional<base::TimeDelta> elapsed) {
-  last_completed_time_ = timestamp;
-  if (elapsed) {
-    ScriptsRunInfo::LogLongInjectionTaskTime(run_location_, *elapsed);
-  }
-}
-
-}  // namespace extensions
diff --git a/extensions/renderer/async_scripts_run_info.h b/extensions/renderer/async_scripts_run_info.h
deleted file mode 100644
index d48d42c0..0000000
--- a/extensions/renderer/async_scripts_run_info.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 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 EXTENSIONS_RENDERER_ASYNC_SCRIPTS_RUN_INFO_H_
-#define EXTENSIONS_RENDERER_ASYNC_SCRIPTS_RUN_INFO_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/optional.h"
-
-#include "extensions/common/user_script.h"
-
-namespace extensions {
-
-// Collects information about asynchronously injected script runs for a
-// run_location.
-class AsyncScriptsRunInfo : public base::RefCounted<AsyncScriptsRunInfo> {
- public:
-  AsyncScriptsRunInfo(UserScript::RunLocation location);
-  void WillExecute(const base::TimeTicks& timestamp);
-  void OnCompleted(const base::TimeTicks& timestamp,
-                   base::Optional<base::TimeDelta> elapsed);
-
- private:
-  friend class base::RefCounted<AsyncScriptsRunInfo>;
-  ~AsyncScriptsRunInfo();
-
-  UserScript::RunLocation run_location_;
-
-  // Time stamp of the last OnCompleted() call.
-  base::TimeTicks last_completed_time_;
-
-  DISALLOW_COPY_AND_ASSIGN(AsyncScriptsRunInfo);
-};
-
-}  // namespace extensions
-
-#endif  // EXTENSIONS_RENDERER_SCRIPTS_RUN_INFO_H_
diff --git a/extensions/renderer/script_injection.cc b/extensions/renderer/script_injection.cc
index 4f8edf5..c54f9ea 100644
--- a/extensions/renderer/script_injection.cc
+++ b/extensions/renderer/script_injection.cc
@@ -19,7 +19,6 @@
 #include "extensions/common/extension_features.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/host_id.h"
-#include "extensions/renderer/async_scripts_run_info.h"
 #include "extensions/renderer/dom_activity_logger.h"
 #include "extensions/renderer/extension_frame_helper.h"
 #include "extensions/renderer/extensions_renderer_client.h"
@@ -88,14 +87,11 @@
 // This class manages its own lifetime.
 class TimedScriptInjectionCallback : public ScriptInjectionCallback {
  public:
-  TimedScriptInjectionCallback(
-      base::WeakPtr<ScriptInjection> injection,
-      scoped_refptr<AsyncScriptsRunInfo> async_run_info)
+  TimedScriptInjectionCallback(base::WeakPtr<ScriptInjection> injection)
       : ScriptInjectionCallback(
             base::Bind(&TimedScriptInjectionCallback::OnCompleted,
                        base::Unretained(this))),
-        injection_(injection),
-        async_run_info_(async_run_info) {}
+        injection_(injection) {}
   ~TimedScriptInjectionCallback() override {}
 
   void OnCompleted(const std::vector<v8::Local<v8::Value>>& result) {
@@ -105,25 +101,19 @@
       // If the script will never execute (such as if the context is destroyed),
       // willExecute() will not be called, but OnCompleted() will. Only log a
       // time for execution if the script, in fact, executed.
-      if (!start_time_.is_null()) {
+      if (!start_time_.is_null())
         elapsed = timestamp - start_time_;
-        if (async_run_info_)
-          async_run_info_->OnCompleted(timestamp, elapsed);
-      }
       injection_->OnJsInjectionCompleted(result, elapsed);
     }
   }
 
   void WillExecute() override {
     start_time_ = base::TimeTicks::Now();
-    if (async_run_info_)
-      async_run_info_->WillExecute(start_time_);
   }
 
  private:
   base::WeakPtr<ScriptInjection> injection_;
   base::TimeTicks start_time_;
-  scoped_refptr<AsyncScriptsRunInfo> async_run_info_;
 };
 
 }  // namespace
@@ -190,7 +180,6 @@
 ScriptInjection::InjectionResult ScriptInjection::TryToInject(
     UserScript::RunLocation current_location,
     ScriptsRunInfo* scripts_run_info,
-    scoped_refptr<AsyncScriptsRunInfo> async_run_info,
     const CompletionCallback& async_completion_callback) {
   if (current_location < run_location_)
     return INJECTION_WAITING;  // Wait for the right location.
@@ -216,8 +205,7 @@
       RequestPermissionFromBrowser();
       return INJECTION_WAITING;  // Wait around for permission.
     case PermissionsData::PageAccess::kAllowed:
-      InjectionResult result =
-          Inject(scripts_run_info, std::move(async_run_info));
+      InjectionResult result = Inject(scripts_run_info);
       // If the injection is blocked, we need to set the manager so we can
       // notify it upon completion.
       if (result == INJECTION_BLOCKED)
@@ -236,7 +224,7 @@
     return INJECTION_FINISHED;
   }
 
-  return Inject(scripts_run_info, nullptr);
+  return Inject(scripts_run_info);
 }
 
 void ScriptInjection::OnHostRemoved() {
@@ -259,8 +247,7 @@
 }
 
 ScriptInjection::InjectionResult ScriptInjection::Inject(
-    ScriptsRunInfo* scripts_run_info,
-    scoped_refptr<AsyncScriptsRunInfo> async_run_info) {
+    ScriptsRunInfo* scripts_run_info) {
   DCHECK(injection_host_);
   DCHECK(scripts_run_info);
   DCHECK(!complete_);
@@ -278,7 +265,7 @@
 
   if (should_inject_js)
     InjectJs(&(scripts_run_info->executing_scripts[host_id().id()]),
-             &(scripts_run_info->num_js), std::move(async_run_info));
+             &(scripts_run_info->num_js));
   if (should_inject_css)
     InjectCss(&(scripts_run_info->injected_stylesheets[host_id().id()]),
               &(scripts_run_info->num_css));
@@ -295,10 +282,8 @@
   return complete_ ? INJECTION_FINISHED : INJECTION_BLOCKED;
 }
 
-void ScriptInjection::InjectJs(
-    std::set<std::string>* executing_scripts,
-    size_t* num_injected_js_scripts,
-    scoped_refptr<AsyncScriptsRunInfo> async_run_info) {
+void ScriptInjection::InjectJs(std::set<std::string>* executing_scripts,
+                               size_t* num_injected_js_scripts) {
   DCHECK(!did_inject_js_);
   blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame();
   std::vector<blink::WebScriptSource> sources = injector_->GetJsSources(
@@ -312,8 +297,7 @@
   bool is_user_gesture = injector_->IsUserGesture();
 
   std::unique_ptr<blink::WebScriptExecutionCallback> callback(
-      new TimedScriptInjectionCallback(weak_ptr_factory_.GetWeakPtr(),
-                                       std::move(async_run_info)));
+      new TimedScriptInjectionCallback(weak_ptr_factory_.GetWeakPtr()));
 
   base::ElapsedTimer exec_timer;
   if (injection_host_->id().type() == HostID::EXTENSIONS && log_activity_)
diff --git a/extensions/renderer/script_injection.h b/extensions/renderer/script_injection.h
index 66a1c0a..3070e7a5 100644
--- a/extensions/renderer/script_injection.h
+++ b/extensions/renderer/script_injection.h
@@ -30,7 +30,6 @@
 }
 
 namespace extensions {
-class AsyncScriptsRunInfo;
 struct ScriptsRunInfo;
 
 // A script wrapper which is aware of whether or not it is allowed to execute,
@@ -69,7 +68,6 @@
   InjectionResult TryToInject(
       UserScript::RunLocation current_location,
       ScriptsRunInfo* scripts_run_info,
-      scoped_refptr<AsyncScriptsRunInfo> async_run_info,
       const CompletionCallback& async_completion_callback);
 
   // Called when permission for the given injection has been granted.
@@ -100,13 +98,11 @@
 
   // Injects the script. Returns INJECTION_FINISHED if injection has finished,
   // otherwise INJECTION_BLOCKED.
-  InjectionResult Inject(ScriptsRunInfo* scripts_run_info,
-                         scoped_refptr<AsyncScriptsRunInfo> async_run_info);
+  InjectionResult Inject(ScriptsRunInfo* scripts_run_info);
 
   // Inject any JS scripts into the frame for the injection.
   void InjectJs(std::set<std::string>* executing_scripts,
-                size_t* num_injected_js_scripts,
-                scoped_refptr<AsyncScriptsRunInfo> async_run_info);
+                size_t* num_injected_js_scripts);
 
   // Inject any CSS source into the frame for the injection.
   void InjectCss(std::set<std::string>* injected_stylesheets,
diff --git a/extensions/renderer/script_injection_manager.cc b/extensions/renderer/script_injection_manager.cc
index 6f42300a..e395f32a 100644
--- a/extensions/renderer/script_injection_manager.cc
+++ b/extensions/renderer/script_injection_manager.cc
@@ -20,7 +20,6 @@
 #include "extensions/common/extension_features.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/extension_set.h"
-#include "extensions/renderer/async_scripts_run_info.h"
 #include "extensions/renderer/extension_frame_helper.h"
 #include "extensions/renderer/extension_injection_host.h"
 #include "extensions/renderer/programmatic_script_injector.h"
@@ -404,8 +403,6 @@
   active_injection_frames_.insert(frame);
 
   ScriptsRunInfo scripts_run_info(frame, run_location);
-  scoped_refptr<AsyncScriptsRunInfo> async_run_info =
-      base::MakeRefCounted<AsyncScriptsRunInfo>(run_location);
 
   for (auto iter = frame_injections.begin(); iter != frame_injections.end();) {
     // It's possible for the frame to be invalidated in the course of injection
@@ -414,8 +411,7 @@
       break;
     std::unique_ptr<ScriptInjection> injection(std::move(*iter));
     iter = frame_injections.erase(iter);
-    TryToInject(std::move(injection), run_location, &scripts_run_info,
-                async_run_info);
+    TryToInject(std::move(injection), run_location, &scripts_run_info);
   }
 
   // We are done running in the frame.
@@ -427,15 +423,14 @@
 void ScriptInjectionManager::TryToInject(
     std::unique_ptr<ScriptInjection> injection,
     UserScript::RunLocation run_location,
-    ScriptsRunInfo* scripts_run_info,
-    scoped_refptr<AsyncScriptsRunInfo> async_run_info) {
+    ScriptsRunInfo* scripts_run_info) {
   // Try to inject the script. If the injection is waiting (i.e., for
   // permission), add it to the list of pending injections. If the injection
   // has blocked, add it to the list of running injections.
   // The Unretained below is safe because this object owns all the
   // ScriptInjections, so is guaranteed to outlive them.
   switch (injection->TryToInject(
-      run_location, scripts_run_info, std::move(async_run_info),
+      run_location, scripts_run_info,
       base::Bind(&ScriptInjectionManager::OnInjectionFinished,
                  base::Unretained(this)))) {
     case ScriptInjection::INJECTION_WAITING:
@@ -472,7 +467,7 @@
       iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second;
 
   ScriptsRunInfo scripts_run_info(render_frame, run_location);
-  TryToInject(std::move(injection), run_location, &scripts_run_info, nullptr);
+  TryToInject(std::move(injection), run_location, &scripts_run_info);
 }
 
 void ScriptInjectionManager::HandleExecuteDeclarativeScript(
@@ -488,7 +483,7 @@
     ScriptsRunInfo scripts_run_info(render_frame, UserScript::BROWSER_DRIVEN);
     // TODO(markdittmer): Use return value of TryToInject for error handling.
     TryToInject(std::move(injection), UserScript::BROWSER_DRIVEN,
-                &scripts_run_info, nullptr);
+                &scripts_run_info);
 
     scripts_run_info.LogRun(activity_logging_enabled_);
   }
diff --git a/extensions/renderer/script_injection_manager.h b/extensions/renderer/script_injection_manager.h
index ef00d56..fb01535 100644
--- a/extensions/renderer/script_injection_manager.h
+++ b/extensions/renderer/script_injection_manager.h
@@ -81,8 +81,7 @@
   // Try to inject and store injection if it has not finished.
   void TryToInject(std::unique_ptr<ScriptInjection> injection,
                    UserScript::RunLocation run_location,
-                   ScriptsRunInfo* scripts_run_info,
-                   scoped_refptr<AsyncScriptsRunInfo> async_run_info);
+                   ScriptsRunInfo* scripts_run_info);
 
   // Handle the ExecuteCode extension message.
   void HandleExecuteCode(const ExtensionMsg_ExecuteCode_Params& params,
diff --git a/extensions/renderer/scripts_run_info.cc b/extensions/renderer/scripts_run_info.cc
index 8bd717c..087b406 100644
--- a/extensions/renderer/scripts_run_info.cc
+++ b/extensions/renderer/scripts_run_info.cc
@@ -45,7 +45,6 @@
                                  num_blocking_js);
       } else if (num_css || num_js) {
         UMA_HISTOGRAM_TIMES("Extensions.InjectStart_Time", elapsed);
-        LogLongInjectionTaskTime(run_location_, elapsed);
       }
       break;
     case UserScript::DOCUMENT_END:
@@ -55,7 +54,6 @@
                                  num_blocking_js);
       } else if (num_js) {
         UMA_HISTOGRAM_TIMES("Extensions.InjectEnd_Time", elapsed);
-        LogLongInjectionTaskTime(run_location_, elapsed);
       }
       break;
     case UserScript::DOCUMENT_IDLE:
@@ -65,7 +63,6 @@
                                  num_blocking_js);
       } else if (num_js) {
         UMA_HISTOGRAM_TIMES("Extensions.InjectIdle_Time", elapsed);
-        LogLongInjectionTaskTime(run_location_, elapsed);
       }
       break;
     case UserScript::RUN_DEFERRED:
@@ -78,33 +75,4 @@
   }
 }
 
-void ScriptsRunInfo::LogLongInjectionTaskTime(
-    UserScript::RunLocation run_location,
-    const base::TimeDelta& elapsed) {
-  // We only record tasks longer than 50 milliseconds. This threshold aligns
-  // with the definition of "long task" in Long Tasks API
-  // (https://w3c.github.io/longtasks/).
-  const base::TimeDelta kLongTaskThreshold =
-      base::TimeDelta::FromMilliseconds(50);
-  if (elapsed < kLongTaskThreshold)
-    return;
-
-  switch (run_location) {
-    case UserScript::DOCUMENT_START:
-      UMA_HISTOGRAM_TIMES("Extensions.LongInjectionTaskTime.DocumentStart",
-                          elapsed);
-      break;
-    case UserScript::DOCUMENT_END:
-      UMA_HISTOGRAM_TIMES("Extensions.LongInjectionTaskTime.DocumentEnd",
-                          elapsed);
-      break;
-    case UserScript::DOCUMENT_IDLE:
-      UMA_HISTOGRAM_TIMES("Extensions.LongInjectionTaskTime.DocumentIdle",
-                          elapsed);
-      break;
-    default:
-      break;
-  }
-}
-
 }  // namespace extensions
diff --git a/extensions/shell/test/shell_test.cc b/extensions/shell/test/shell_test.cc
index 888a5351..c7e70435 100644
--- a/extensions/shell/test/shell_test.cc
+++ b/extensions/shell/test/shell_test.cc
@@ -14,7 +14,6 @@
 #include "extensions/shell/browser/desktop_controller.h"
 #include "extensions/shell/browser/shell_content_browser_client.h"
 #include "extensions/shell/browser/shell_extension_system.h"
-#include "ui/base/ui_base_features.h"
 
 #if defined(OS_CHROMEOS)
 #include "content/public/test/network_connection_change_simulator.h"
@@ -22,14 +21,7 @@
 
 namespace extensions {
 
-AppShellTest::AppShellTest()
-    : browser_context_(nullptr),
-      extension_system_(nullptr) {
-  // Disable mash until app_shell test infra is updated to use Window Service.
-  scoped_feature_list_.InitWithFeatures(
-      {} /* enabled */,
-      {features::kMash, features::kSingleProcessMash} /* disabled */);
-
+AppShellTest::AppShellTest() {
   CreateTestServer(base::FilePath(FILE_PATH_LITERAL("extensions/test/data")));
 }
 
diff --git a/extensions/shell/test/shell_test.h b/extensions/shell/test/shell_test.h
index 8d85a01..ead304c 100644
--- a/extensions/shell/test/shell_test.h
+++ b/extensions/shell/test/shell_test.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 
-#include "base/test/scoped_feature_list.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_base.h"
 
@@ -33,9 +32,8 @@
   content::BrowserContext* browser_context() { return browser_context_; }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
-  content::BrowserContext* browser_context_;
-  ShellExtensionSystem* extension_system_;
+  content::BrowserContext* browser_context_ = nullptr;
+  ShellExtensionSystem* extension_system_ = nullptr;
 };
 
 }  // namespace extensions
diff --git a/google_apis/BUILD.gn b/google_apis/BUILD.gn
index 84abf02..ef0da3d 100644
--- a/google_apis/BUILD.gn
+++ b/google_apis/BUILD.gn
@@ -112,6 +112,7 @@
       "gaia/oauth2_access_token_fetcher_impl.h",
       "gaia/oauth2_access_token_manager.cc",
       "gaia/oauth2_access_token_manager.h",
+      "gaia/oauth2_access_token_manager_diagnostics_observer.h",
       "gaia/oauth2_api_call_flow.cc",
       "gaia/oauth2_api_call_flow.h",
       "gaia/oauth2_id_token_decoder.cc",
diff --git a/google_apis/gaia/oauth2_access_token_manager.cc b/google_apis/gaia/oauth2_access_token_manager.cc
index c6cef64..c0769a1 100644
--- a/google_apis/gaia/oauth2_access_token_manager.cc
+++ b/google_apis/gaia/oauth2_access_token_manager.cc
@@ -14,6 +14,16 @@
 
 OAuth2AccessTokenManager::~OAuth2AccessTokenManager() = default;
 
+void OAuth2AccessTokenManager::AddDiagnosticsObserver(
+    AccessTokenDiagnosticsObserver* observer) {
+  diagnostics_observer_list_.AddObserver(observer);
+}
+
+void OAuth2AccessTokenManager::RemoveDiagnosticsObserver(
+    AccessTokenDiagnosticsObserver* observer) {
+  diagnostics_observer_list_.RemoveObserver(observer);
+}
+
 void OAuth2AccessTokenManager::RegisterTokenResponse(
     const std::string& client_id,
     const CoreAccountId& account_id,
@@ -43,7 +53,7 @@
 void OAuth2AccessTokenManager::ClearCache() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   for (const auto& entry : token_cache_) {
-    for (auto& observer : token_service_->GetDiagnicsObservers())
+    for (auto& observer : diagnostics_observer_list_)
       observer.OnAccessTokenRemoved(entry.first.account_id, entry.first.scopes);
   }
 
@@ -57,7 +67,7 @@
        iter != token_cache_.end();
        /* iter incremented in body */) {
     if (iter->first.account_id == account_id) {
-      for (auto& observer : token_service_->GetDiagnicsObservers())
+      for (auto& observer : diagnostics_observer_list_)
         observer.OnAccessTokenRemoved(account_id, iter->first.scopes);
       token_cache_.erase(iter++);
     } else {
@@ -74,7 +84,7 @@
       token_cache_.find(request_parameters);
   if (token_iterator != token_cache_.end() &&
       token_iterator->second.access_token == token_to_remove) {
-    for (auto& observer : token_service_->GetDiagnicsObservers()) {
+    for (auto& observer : diagnostics_observer_list_) {
       observer.OnAccessTokenRemoved(request_parameters.account_id,
                                     request_parameters.scopes);
     }
diff --git a/google_apis/gaia/oauth2_access_token_manager.h b/google_apis/gaia/oauth2_access_token_manager.h
index 95bda00..58ae62e 100644
--- a/google_apis/gaia/oauth2_access_token_manager.h
+++ b/google_apis/gaia/oauth2_access_token_manager.h
@@ -21,6 +21,10 @@
   explicit OAuth2AccessTokenManager(OAuth2TokenService* token_service);
   virtual ~OAuth2AccessTokenManager();
 
+  // Add or remove observers of this token manager.
+  void AddDiagnosticsObserver(AccessTokenDiagnosticsObserver* observer);
+  void RemoveDiagnosticsObserver(AccessTokenDiagnosticsObserver* observer);
+
   // Add a new entry to the cache.
   void RegisterTokenResponse(
       const std::string& client_id,
@@ -58,6 +62,9 @@
 
   // The cache of currently valid tokens.
   OAuth2TokenService::TokenCache token_cache_;
+  // List of observers to notify when access token status changes.
+  base::ObserverList<AccessTokenDiagnosticsObserver, true>::Unchecked
+      diagnostics_observer_list_;
   // TODO(https://crbug.com/967598): Remove this once OAuth2AccessTokenManager
   // fully manages access tokens independently of OAuth2TokenService.
   OAuth2TokenService* token_service_;
diff --git a/google_apis/gaia/oauth2_access_token_manager_diagnostics_observer.h b/google_apis/gaia/oauth2_access_token_manager_diagnostics_observer.h
new file mode 100644
index 0000000..fec1189e
--- /dev/null
+++ b/google_apis/gaia/oauth2_access_token_manager_diagnostics_observer.h
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GOOGLE_APIS_GAIA_OAUTH2_ACCESS_TOKEN_MANAGER_DIAGNOSTICS_OBSERVER_H_
+#define GOOGLE_APIS_GAIA_OAUTH2_ACCESS_TOKEN_MANAGER_DIAGNOSTICS_OBSERVER_H_
+
+#include "base/time/time.h"
+#include "google_apis/gaia/core_account_id.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+
+// TODO(https://crbug.com/967598): Move this class to OAuth2AccessTokenManager
+// and discard this file once it's not referred by OAuth2TokenService.
+
+// Classes that want to monitor status of access token and access token
+// request should implement this interface and register with the
+// AddOAccessTokenDiagnosticsObserver() call.
+class AccessTokenDiagnosticsObserver {
+ public:
+  // A set of scopes in OAuth2 authentication.
+  typedef std::set<std::string> ScopeSet;
+
+  // Called when receiving request for access token.
+  virtual void OnAccessTokenRequested(const CoreAccountId& account_id,
+                                      const std::string& consumer_id,
+                                      const ScopeSet& scopes) {}
+
+  // Called when access token fetching finished successfully or
+  // unsuccessfully. |expiration_time| are only valid with
+  // successful completion.
+  virtual void OnFetchAccessTokenComplete(const CoreAccountId& account_id,
+                                          const std::string& consumer_id,
+                                          const ScopeSet& scopes,
+                                          GoogleServiceAuthError error,
+                                          base::Time expiration_time) {}
+
+  // Called when an access token was removed.
+  virtual void OnAccessTokenRemoved(const CoreAccountId& account_id,
+                                    const ScopeSet& scopes) {}
+};
+
+#endif  // GOOGLE_APIS_GAIA_OAUTH2_ACCESS_TOKEN_MANAGER_DIAGNOSTICS_OBSERVER_H_
diff --git a/google_apis/gaia/oauth2_token_service.cc b/google_apis/gaia/oauth2_token_service.cc
index cda4d8b..38ab53e 100644
--- a/google_apis/gaia/oauth2_token_service.cc
+++ b/google_apis/gaia/oauth2_token_service.cc
@@ -412,6 +412,11 @@
   return delegate_.get();
 }
 
+const base::ObserverList<AccessTokenDiagnosticsObserver, true>::Unchecked&
+OAuth2TokenService::GetAccessTokenDiagnosticsObservers() {
+  return token_manager_->diagnostics_observer_list_;
+}
+
 OAuth2TokenService::TokenCache& OAuth2TokenService::token_cache() {
   return token_manager_->token_cache();
 }
@@ -433,6 +438,16 @@
   diagnostics_observer_list_.RemoveObserver(observer);
 }
 
+void OAuth2TokenService::AddAccessTokenDiagnosticsObserver(
+    AccessTokenDiagnosticsObserver* observer) {
+  token_manager_->AddDiagnosticsObserver(observer);
+}
+
+void OAuth2TokenService::RemoveAccessTokenDiagnosticsObserver(
+    AccessTokenDiagnosticsObserver* observer) {
+  token_manager_->RemoveDiagnosticsObserver(observer);
+}
+
 std::unique_ptr<OAuth2TokenService::Request>
 OAuth2TokenService::StartRequestForMultilogin(
     const CoreAccountId& account_id,
@@ -510,13 +525,13 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   std::unique_ptr<RequestImpl> request(new RequestImpl(account_id, consumer));
-  for (auto& observer : diagnostics_observer_list_)
+  for (auto& observer : token_manager_->diagnostics_observer_list_)
     observer.OnAccessTokenRequested(account_id, consumer->id(), scopes);
 
   if (!RefreshTokenIsAvailable(account_id)) {
     GoogleServiceAuthError error(GoogleServiceAuthError::USER_NOT_SIGNED_UP);
 
-    for (auto& observer : diagnostics_observer_list_) {
+    for (auto& observer : token_manager_->diagnostics_observer_list_) {
       observer.OnFetchAccessTokenComplete(account_id, consumer->id(), scopes,
                                           error, base::Time());
     }
@@ -581,7 +596,7 @@
     RequestImpl* request,
     const RequestParameters& request_parameters) {
   DCHECK(cache_token_response && cache_token_response->access_token.length());
-  for (auto& observer : diagnostics_observer_list_) {
+  for (auto& observer : token_manager_->diagnostics_observer_list_) {
     observer.OnFetchAccessTokenComplete(
         request_parameters.account_id, request->GetConsumerId(),
         request_parameters.scopes, GoogleServiceAuthError::AuthErrorNone(),
@@ -704,7 +719,7 @@
       GetCachedTokenResponse(request_param);
   for (const base::WeakPtr<RequestImpl>& req : fetcher->waiting_requests()) {
     if (req) {
-      for (auto& observer : diagnostics_observer_list_) {
+      for (auto& observer : token_manager_->diagnostics_observer_list_) {
         observer.OnFetchAccessTokenComplete(
             req->GetAccountId(), req->GetConsumerId(), fetcher->GetScopeSet(),
             fetcher->error(), entry ? entry->expiration_time : base::Time());
diff --git a/google_apis/gaia/oauth2_token_service.h b/google_apis/gaia/oauth2_token_service.h
index 47c7c2e7..13e2378 100644
--- a/google_apis/gaia/oauth2_token_service.h
+++ b/google_apis/gaia/oauth2_token_service.h
@@ -24,6 +24,7 @@
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "google_apis/gaia/oauth2_access_token_consumer.h"
 #include "google_apis/gaia/oauth2_access_token_fetcher.h"
+#include "google_apis/gaia/oauth2_access_token_manager_diagnostics_observer.h"
 #include "google_apis/gaia/oauth2_token_service_observer.h"
 
 namespace network {
@@ -92,27 +93,11 @@
     std::string id_;
   };
 
-  // Classes that want to monitor status of access token and access token
+  // Classes that want to monitor status of refresh token and refresh token
   // request should implement this interface and register with the
   // AddDiagnosticsObserver() call.
   class DiagnosticsObserver {
    public:
-    // Called when receiving request for access token.
-    virtual void OnAccessTokenRequested(const CoreAccountId& account_id,
-                                        const std::string& consumer_id,
-                                        const ScopeSet& scopes) {}
-    // Called when access token fetching finished successfully or
-    // unsuccessfully. |expiration_time| are only valid with
-    // successful completion.
-    virtual void OnFetchAccessTokenComplete(const CoreAccountId& account_id,
-                                            const std::string& consumer_id,
-                                            const ScopeSet& scopes,
-                                            GoogleServiceAuthError error,
-                                            base::Time expiration_time) {}
-    // Called when an access token was removed.
-    virtual void OnAccessTokenRemoved(const CoreAccountId& account_id,
-                                      const ScopeSet& scopes) {}
-
     // Caled when a new refresh token is available. Contains diagnostic
     // information about the source of the update credentials operation.
     virtual void OnRefreshTokenAvailableFromSource(
@@ -158,6 +143,14 @@
   void AddDiagnosticsObserver(DiagnosticsObserver* observer);
   void RemoveDiagnosticsObserver(DiagnosticsObserver* observer);
 
+  // TODO(https://crbug.com/967598): Remove these APIs once we can use
+  // OAuth2AccessTokenManager without OAuth2TokenService.
+  // Add or remove observers of access token manager.
+  void AddAccessTokenDiagnosticsObserver(
+      AccessTokenDiagnosticsObserver* observer);
+  void RemoveAccessTokenDiagnosticsObserver(
+      AccessTokenDiagnosticsObserver* observer);
+
   // Checks in the cache for a valid access token for a specified |account_id|
   // and |scopes|, and if not found starts a request for an OAuth2 access token
   // using the OAuth2 refresh token maintained by this instance for that
@@ -260,10 +253,13 @@
   OAuth2TokenService::TokenCache& token_cache();
 
   const base::ObserverList<DiagnosticsObserver, true>::Unchecked&
-  GetDiagnicsObservers() {
+  GetDiagnosticsObservers() {
     return diagnostics_observer_list_;
   }
 
+  const base::ObserverList<AccessTokenDiagnosticsObserver, true>::Unchecked&
+  GetAccessTokenDiagnosticsObservers();
+
  protected:
   // Implements a cancelable |OAuth2TokenService::Request|, which should be
   // operated on the UI thread.
diff --git a/google_apis/gaia/oauth_multilogin_result.cc b/google_apis/gaia/oauth_multilogin_result.cc
index 42d2b0c..312679e 100644
--- a/google_apis/gaia/oauth_multilogin_result.cc
+++ b/google_apis/gaia/oauth_multilogin_result.cc
@@ -39,14 +39,14 @@
     const OAuthMultiloginResult& other) {
   status_ = other.status();
   cookies_ = other.cookies();
-  failed_accounts_ = other.failed_accounts();
+  failed_gaia_ids_ = other.failed_gaia_ids();
 }
 
 OAuthMultiloginResult& OAuthMultiloginResult::operator=(
     const OAuthMultiloginResult& other) {
   status_ = other.status();
   cookies_ = other.cookies();
-  failed_accounts_ = other.failed_accounts();
+  failed_gaia_ids_ = other.failed_gaia_ids();
   return *this;
 }
 
@@ -74,9 +74,9 @@
     const std::string* gaia_id = account.FindStringKey("obfuscated_id");
     const std::string* status = account.FindStringKey("status");
     if (status && gaia_id && *status != "OK")
-      failed_accounts_.push_back(*gaia_id);
+      failed_gaia_ids_.push_back(*gaia_id);
   }
-  if (failed_accounts_.empty())
+  if (failed_gaia_ids_.empty())
     status_ = OAuthMultiloginResponseStatus::kUnknownStatus;
 }
 
@@ -97,6 +97,7 @@
     base::Optional<bool> is_http_only = cookie.FindBoolKey("isHttpOnly");
     const std::string* priority = cookie.FindStringKey("priority");
     base::Optional<double> expiration_delta = cookie.FindDoubleKey("maxAge");
+    const std::string* same_site = cookie.FindStringKey("sameSite");
 
     base::TimeDelta before_expiration =
         base::TimeDelta::FromSecondsD(expiration_delta.value_or(0.0));
@@ -113,7 +114,8 @@
         path ? *path : "", /*creation=*/base::Time::Now(),
         base::Time::Now() + before_expiration,
         /*last_access=*/base::Time::Now(), is_secure.value_or(true),
-        is_http_only.value_or(true), net::CookieSameSite::NO_RESTRICTION,
+        is_http_only.value_or(true),
+        net::StringToCookieSameSite(same_site ? *same_site : ""),
         net::StringToCookiePriority(priority ? *priority : "medium"));
     if (new_cookie.IsCanonical()) {
       cookies_.push_back(std::move(new_cookie));
diff --git a/google_apis/gaia/oauth_multilogin_result.h b/google_apis/gaia/oauth_multilogin_result.h
index 8fb264a..dc019c0 100644
--- a/google_apis/gaia/oauth_multilogin_result.h
+++ b/google_apis/gaia/oauth_multilogin_result.h
@@ -67,7 +67,7 @@
   ~OAuthMultiloginResult();
 
   std::vector<net::CanonicalCookie> cookies() const { return cookies_; }
-  std::vector<std::string> failed_accounts() const { return failed_accounts_; }
+  std::vector<std::string> failed_gaia_ids() const { return failed_gaia_ids_; }
   OAuthMultiloginResponseStatus status() const { return status_; }
 
  private:
@@ -84,7 +84,7 @@
   void TryParseFailedAccountsFromValue(base::Value* json_value);
 
   std::vector<net::CanonicalCookie> cookies_;
-  std::vector<std::string> failed_accounts_;
+  std::vector<std::string> failed_gaia_ids_;
   OAuthMultiloginResponseStatus status_;
 };
 
diff --git a/google_apis/gaia/oauth_multilogin_result_unittest.cc b/google_apis/gaia/oauth_multilogin_result_unittest.cc
index 674f0a13..3cd83ed 100644
--- a/google_apis/gaia/oauth_multilogin_result_unittest.cc
+++ b/google_apis/gaia/oauth_multilogin_result_unittest.cc
@@ -45,17 +45,18 @@
               "value":"vAlUe2",
               "host":"google.com",
               "path":"/",
-              "isSecure":true,
-              "isHttpOnly":false,
+              "isSecure":false,
+              "isHttpOnly":true,
               "priority":"HIGH",
-              "maxAge":63070000
+              "maxAge":63070000,
+              "sameSite":"Lax"
             },
             {
               "name":"SSID",
               "value":"vAlUe3",
               "domain":".google.de",
               "path":"path",
-              "sSecure":true,
+              "isSecure":true,
               "isHttpOnly":false,
               "priority":"HIGH",
               "maxAge":63070000
@@ -65,10 +66,9 @@
               "value":"vAlUe4",
               "host":".google.fr",
               "path":"/",
-              "isSecure":true,
-              "isHttpOnly":false,
               "priority":"HIGH",
-              "maxAge":0
+              "maxAge":0,
+              "sameSite":"Strict"
             }
           ]
         }
@@ -85,15 +85,16 @@
   double expiration = expiration_time.ToDoubleT();
   const std::vector<CanonicalCookie> cookies = {
       CanonicalCookie("SID", "vAlUe1", ".google.ru", "/", time_now, time_now,
-                      expiration_time, true, false,
-                      net::CookieSameSite::NO_RESTRICTION,
+                      expiration_time, /*is_secure=*/true,
+                      /*is_http_only=*/false, net::CookieSameSite::UNSPECIFIED,
                       net::CookiePriority::COOKIE_PRIORITY_HIGH),
       CanonicalCookie("APISID", "vAlUe2", "google.com", "/", time_now, time_now,
-                      expiration_time, true, false,
-                      net::CookieSameSite::NO_RESTRICTION,
+                      expiration_time, /*is_secure=*/false,
+                      /*is_http_only=*/true, net::CookieSameSite::LAX_MODE,
                       net::CookiePriority::COOKIE_PRIORITY_HIGH),
       CanonicalCookie("HSID", "vAlUe4", "", "/", time_now, time_now, time_now,
-                      true, false, net::CookieSameSite::NO_RESTRICTION,
+                      /*is_secure=*/true, /*is_http_only=*/true,
+                      net::CookieSameSite::STRICT_MODE,
                       net::CookiePriority::COOKIE_PRIORITY_HIGH)};
 
   EXPECT_EQ((int)result.cookies().size(), 3);
@@ -117,19 +118,19 @@
                           Property(&CanonicalCookie::IsCanonical, Eq(true))));
   EXPECT_THAT(result.cookies(),
               ElementsAre(Property(&CanonicalCookie::IsHttpOnly, Eq(false)),
-                          Property(&CanonicalCookie::IsHttpOnly, Eq(false)),
-                          Property(&CanonicalCookie::IsHttpOnly, Eq(false))));
+                          Property(&CanonicalCookie::IsHttpOnly, Eq(true)),
+                          Property(&CanonicalCookie::IsHttpOnly, Eq(true))));
   EXPECT_THAT(result.cookies(),
               ElementsAre(Property(&CanonicalCookie::IsSecure, Eq(true)),
-                          Property(&CanonicalCookie::IsSecure, Eq(true)),
+                          Property(&CanonicalCookie::IsSecure, Eq(false)),
                           Property(&CanonicalCookie::IsSecure, Eq(true))));
   EXPECT_THAT(result.cookies(),
               ElementsAre(Property(&CanonicalCookie::SameSite,
-                                   Eq(net::CookieSameSite::NO_RESTRICTION)),
+                                   Eq(net::CookieSameSite::UNSPECIFIED)),
                           Property(&CanonicalCookie::SameSite,
-                                   Eq(net::CookieSameSite::NO_RESTRICTION)),
+                                   Eq(net::CookieSameSite::LAX_MODE)),
                           Property(&CanonicalCookie::SameSite,
-                                   Eq(net::CookieSameSite::NO_RESTRICTION))));
+                                   Eq(net::CookieSameSite::STRICT_MODE))));
   EXPECT_THAT(
       result.cookies(),
       ElementsAre(Property(&CanonicalCookie::Priority,
@@ -303,7 +304,7 @@
       )";
   OAuthMultiloginResult result4(data_error_invalid_credentials);
   EXPECT_EQ(result4.status(), OAuthMultiloginResponseStatus::kInvalidTokens);
-  EXPECT_THAT(result4.failed_accounts(), ElementsAre(Eq("account1")));
+  EXPECT_THAT(result4.failed_gaia_ids(), ElementsAre(Eq("account1")));
 
   // Unknown status.
   OAuthMultiloginResult unknown_status(R"()]}'
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index a5d1001..dbc42c2 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -2752,9 +2752,10 @@
     }
     builders {
       name: "Libfuzzer Upload Linux UBSan"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "VR Linux"
@@ -2784,15 +2785,17 @@
     }
     builders {
       name: "Libfuzzer Upload Linux ASan Debug"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Libfuzzer Upload Linux V8-ARM64 ASan Debug"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Closure Compilation Linux"
@@ -2831,39 +2834,45 @@
     }
     builders {
       name: "Libfuzzer Upload Linux V8-ARM64 ASan"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Libfuzzer Upload Linux32 ASan Debug"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Libfuzzer Upload Linux32 ASan"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Libfuzzer Upload Linux32 V8-ARM ASan Debug"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Libfuzzer Upload Linux32 V8-ARM ASan"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Libfuzzer Upload Chrome OS ASan"
-      dimensions: "os:Ubuntu-14.04"
       mixins: "fuzz-ci"
       mixins: "libfuzzer"
+      mixins: "linux-xenial"
+      mixins: "builderless"
     }
     builders {
       name: "Android CFI"
diff --git a/ios/web/favicon/BUILD.gn b/ios/web/favicon/BUILD.gn
index f39a52b8..d8a81cb 100644
--- a/ios/web/favicon/BUILD.gn
+++ b/ios/web/favicon/BUILD.gn
@@ -8,8 +8,11 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   deps = [
     "//ios/web/public",
+    "//ios/web/web_state:web_state_impl_header",
   ]
   sources = [
+    "favicon_manager.h",
+    "favicon_manager.mm",
     "favicon_status.cc",
     "favicon_url.cc",
     "favicon_util.h",
diff --git a/ios/web/favicon/favicon_manager.h b/ios/web/favicon/favicon_manager.h
new file mode 100644
index 0000000..d1044f5
--- /dev/null
+++ b/ios/web/favicon/favicon_manager.h
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_FAVICON_FAVICON_MANAGER_H_
+#define IOS_WEB_FAVICON_FAVICON_MANAGER_H_
+
+#include "base/macros.h"
+#include "base/values.h"
+
+class GURL;
+namespace web {
+class WebStateImpl;
+class WebFrame;
+
+// Handles "favicon.favicons" message from injected JavaScript and notifies
+// WebStateImpl if message contains favicon URLs.
+class FaviconManager final {
+ public:
+  explicit FaviconManager(WebStateImpl* web_state);
+  ~FaviconManager();
+
+ private:
+  bool OnJsMessage(const base::DictionaryValue& message,
+                   const GURL& page_url,
+                   bool has_user_gesture,
+                   bool in_main_frame,
+                   WebFrame* sender_frame);
+
+  WebStateImpl* web_state_impl_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(FaviconManager);
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_FAVICON_FAVICON_MANAGER_H_
diff --git a/ios/web/favicon/favicon_manager.mm b/ios/web/favicon/favicon_manager.mm
new file mode 100644
index 0000000..be68f98
--- /dev/null
+++ b/ios/web/favicon/favicon_manager.mm
@@ -0,0 +1,52 @@
+// 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/web/favicon/favicon_manager.h"
+
+#import "ios/web/favicon/favicon_util.h"
+#import "ios/web/web_state/web_state_impl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+const char kCommandPrefix[] = "favicon";
+}
+
+namespace web {
+
+FaviconManager::FaviconManager(WebStateImpl* web_state)
+    : web_state_impl_(web_state) {
+  web_state_impl_->AddScriptCommandCallback(
+      base::BindRepeating(&FaviconManager::OnJsMessage, base::Unretained(this)),
+      kCommandPrefix);
+}
+
+FaviconManager::~FaviconManager() {
+  web_state_impl_->RemoveScriptCommandCallback(kCommandPrefix);
+}
+
+bool FaviconManager::OnJsMessage(const base::DictionaryValue& message,
+                                 const GURL& page_url,
+                                 bool has_user_gesture,
+                                 bool form_in_main_frame,
+                                 WebFrame* sender_frame) {
+  DCHECK(sender_frame->IsMainFrame());
+
+  const std::string* command = message.FindStringKey("command");
+  if (!command) {
+    return false;
+  }
+
+  std::vector<FaviconURL> URLs;
+  if (!ExtractFaviconURL(&message, page_url, &URLs))
+    return false;
+
+  if (!URLs.empty())
+    web_state_impl_->OnFaviconUrlUpdated(URLs);
+  return true;
+}
+
+}  // namespace web
diff --git a/ios/web/favicon/resources/favicon.js b/ios/web/favicon/resources/favicon.js
index 8ccb890a..7834440 100644
--- a/ios/web/favicon/resources/favicon.js
+++ b/ios/web/favicon/resources/favicon.js
@@ -16,7 +16,7 @@
 (function() {
 
 __gCrWeb.message.invokeOnHost({
-  'command': 'document.favicons',
+  'command': 'favicon.favicons',
   'favicons': __gCrWeb.common.getFavicons()
 });
 }());  // End of anonymous object
diff --git a/ios/web/navigation/resources/restore_session.html b/ios/web/navigation/resources/restore_session.html
index cd4d85c..6301e793 100644
--- a/ios/web/navigation/resources/restore_session.html
+++ b/ios/web/navigation/resources/restore_session.html
@@ -120,15 +120,10 @@
     }
 
     /**
-     * Sends error message to native controller.
+     * print error and show blank page.
      */
     function handleError(message) {
       console.log("Error: " + message);
-      if (__gCrWeb.message) {
-        __gCrWeb.message.invokeOnHost(
-          {"command": "restoresession.error", "message": message});
-        __gCrWeb.message.invokeQueues();
-      }
       window.location.replace("about:blank");
     }
   </script>
diff --git a/ios/web/web_state/js/resources/navigation.js b/ios/web/web_state/js/resources/navigation.js
index fe50f56..b7770d4 100644
--- a/ios/web/web_state/js/resources/navigation.js
+++ b/ios/web/web_state/js/resources/navigation.js
@@ -109,7 +109,7 @@
   // Because hash changes don't trigger __gCrWeb.didFinishNavigation, so fetch
   // favicons for the new page manually.
   __gCrWeb.message.invokeOnHost({
-    'command': 'document.favicons',
+    'command': 'favicon.favicons',
     'favicons': __gCrWeb.common.getFavicons()
   });
 
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index e4577ed..5f2e9077 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -48,7 +48,7 @@
 #include "ios/web/common/features.h"
 #include "ios/web/common/referrer_util.h"
 #include "ios/web/common/url_util.h"
-#import "ios/web/favicon/favicon_util.h"
+#import "ios/web/favicon/favicon_manager.h"
 #import "ios/web/find_in_page/find_in_page_manager_impl.h"
 #include "ios/web/history_state_util.h"
 #import "ios/web/js_messaging/crw_js_injector.h"
@@ -78,7 +78,6 @@
 #import "ios/web/public/deprecated/crw_native_content_provider.h"
 #include "ios/web/public/deprecated/url_verification_constants.h"
 #import "ios/web/public/download/download_controller.h"
-#include "ios/web/public/favicon/favicon_url.h"
 #import "ios/web/public/java_script_dialog_presenter.h"
 #include "ios/web/public/js_messaging/web_frame.h"
 #include "ios/web/public/js_messaging/web_frame_util.h"
@@ -217,6 +216,9 @@
 
   // State of user interaction with web content.
   web::UserInteractionState _userInteractionState;
+
+  // Manager for favicon JavaScript messages.
+  std::unique_ptr<web::FaviconManager> _faviconManager;
 }
 
 // The WKNavigationDelegate handler class.
@@ -403,6 +405,7 @@
     web::BrowsingDataRemover::FromBrowserState(browserState)->AddObserver(self);
     web::WebFramesManagerImpl::CreateForWebState(_webStateImpl);
     web::FindInPageManagerImpl::CreateForWebState(_webStateImpl);
+    _faviconManager = std::make_unique<web::FaviconManager>(_webStateImpl);
     _legacyNativeController =
         [[CRWLegacyNativeContentController alloc] initWithWebState:webState];
     _legacyNativeController.delegate = self;
@@ -711,6 +714,7 @@
   _SSLStatusUpdater = nil;
   [self.UIHandler close];
   [self.JSNavigationHandler close];
+  _faviconManager.reset();
 
   self.swipeRecognizerProvider = nil;
   [self.legacyNativeController close];
@@ -955,96 +959,21 @@
 - (void)loadData:(NSData*)data
         MIMEType:(NSString*)MIMEType
           forURL:(const GURL&)URL {
-  [self stopLoading];
-  web::NavigationItemImpl* item =
-      self.navigationManagerImpl->GetLastCommittedItemImpl();
-  auto navigationContext = web::NavigationContextImpl::CreateNavigationContext(
-      self.webStateImpl, URL,
-      /*has_user_gesture=*/true, item->GetTransitionType(),
-      /*is_renderer_initiated=*/false);
-  self.navigationHandler.navigationState = web::WKNavigationState::REQUESTED;
-  navigationContext->SetNavigationItemUniqueID(item->GetUniqueID());
-
-  item->SetNavigationInitiationType(
-      web::NavigationInitiationType::BROWSER_INITIATED);
-  // The error_retry_state_machine may still be in the
-  // |kDisplayingWebErrorForFailedNavigation| from the navigation that is being
-  // replaced. As the navigation is now successful, the error can be cleared.
-  item->error_retry_state_machine().SetNoNavigationError();
-  // The load data call will replace the current navigation and the webView URL
-  // of the navigation will be replaced by |URL|. Set the URL of the
-  // navigationItem to keep them synced.
-  // Note: it is possible that the URL in item already match |url|. But item can
-  // also contain a placeholder URL intended to be replaced.
-  item->SetURL(URL);
-  navigationContext->SetMimeType(MIMEType);
-  if (item->GetUserAgentType() == web::UserAgentType::NONE &&
-      URLNeedsUserAgentType(URL)) {
-    item->SetUserAgentType(web::UserAgentType::MOBILE);
-  }
-
-  WKNavigation* navigation =
-      [self.webView loadData:data
-                       MIMEType:MIMEType
-          characterEncodingName:base::SysUTF8ToNSString(base::kCodepageUTF8)
-                        baseURL:net::NSURLWithGURL(URL)];
-
-  [self.navigationHandler.navigationStates
-         setContext:std::move(navigationContext)
-      forNavigation:navigation];
-  [self.navigationHandler.navigationStates
-           setState:web::WKNavigationState::REQUESTED
-      forNavigation:navigation];
+  [_requestController loadData:data
+                       webView:self.webView
+                      MIMEType:MIMEType
+                        forURL:URL];
 }
 
 // Loads the HTML into the page at the given URL. Only for testing purpose.
 - (void)loadHTML:(NSString*)HTML forURL:(const GURL&)URL {
-  DCHECK(HTML.length);
-  // Remove the transient content view.
-  self.webStateImpl->ClearTransientContent();
-
-  self.navigationHandler.navigationState = web::WKNavigationState::REQUESTED;
-
   // Web View should not be created for App Specific URLs.
   if (!web::GetWebClient()->IsAppSpecificURL(URL)) {
     [self ensureWebViewCreated];
     DCHECK(self.webView) << "self.webView null while trying to load HTML";
   }
-  WKNavigation* navigation =
-      [self.webView loadHTMLString:HTML baseURL:net::NSURLWithGURL(URL)];
-  [self.navigationHandler.navigationStates
-           setState:web::WKNavigationState::REQUESTED
-      forNavigation:navigation];
-  std::unique_ptr<web::NavigationContextImpl> context;
-  const ui::PageTransition loadHTMLTransition =
-      ui::PageTransition::PAGE_TRANSITION_TYPED;
-  if (self.webStateImpl->HasWebUI()) {
-    // WebUI uses |loadHTML:forURL:| to feed the content to web view. This
-    // should not be treated as a navigation, but WKNavigationDelegate callbacks
-    // still expect a valid context.
-    context = web::NavigationContextImpl::CreateNavigationContext(
-        self.webStateImpl, URL, /*has_user_gesture=*/true, loadHTMLTransition,
-        /*is_renderer_initiated=*/false);
-    context->SetNavigationItemUniqueID(self.currentNavItem->GetUniqueID());
-    if (web::features::StorePendingItemInContext()) {
-      // Transfer pending item ownership to NavigationContext.
-      // NavigationManager owns pending item after navigation is requested and
-      // until navigation context is created.
-      context->SetItem(self.navigationManagerImpl->ReleasePendingItem());
-    }
-  } else {
-    context = [self registerLoadRequestForURL:URL
-                                     referrer:web::Referrer()
-                                   transition:loadHTMLTransition
-                       sameDocumentNavigation:NO
-                               hasUserGesture:YES
-                            rendererInitiated:NO
-                        placeholderNavigation:NO];
-  }
-  context->SetLoadingHtmlString(true);
-  context->SetMimeType(@"text/html");
-  [self.navigationHandler.navigationStates setContext:std::move(context)
-                                        forNavigation:navigation];
+
+  [_requestController loadHTML:HTML webView:self.webView forURL:URL];
 }
 
 - (void)requirePageReconstruction {
@@ -1806,11 +1735,7 @@
   dispatch_once(&onceToken, ^{
     handlers = new std::map<std::string, SEL>();
     (*handlers)["chrome.send"] = @selector(handleChromeSendMessage:context:);
-    (*handlers)["document.favicons"] =
-        @selector(handleDocumentFaviconsMessage:context:);
     (*handlers)["window.error"] = @selector(handleWindowErrorMessage:context:);
-    (*handlers)["restoresession.error"] =
-        @selector(handleRestoreSessionErrorMessage:context:);
   });
   DCHECK(handlers);
   auto iter = handlers->find(command);
@@ -1964,27 +1889,6 @@
   return NO;
 }
 
-// Handles 'document.favicons' message.
-- (BOOL)handleDocumentFaviconsMessage:(base::DictionaryValue*)message
-                              context:(NSDictionary*)context {
-  if (![context[kIsMainFrame] boolValue])
-    return NO;
-
-  std::vector<web::FaviconURL> URLs;
-  GURL originGURL;
-  id origin = context[kOriginURLKey];
-  if (origin) {
-    NSURL* originNSURL = base::mac::ObjCCastStrict<NSURL>(origin);
-    originGURL = net::GURLWithNSURL(originNSURL);
-  }
-  if (!web::ExtractFaviconURL(message, originGURL, &URLs))
-    return NO;
-
-  if (!URLs.empty())
-    self.webStateImpl->OnFaviconUrlUpdated(URLs);
-  return YES;
-}
-
 // Handles 'window.error' message.
 - (BOOL)handleWindowErrorMessage:(base::DictionaryValue*)message
                          context:(NSDictionary*)context {
@@ -1998,28 +1902,6 @@
   return YES;
 }
 
-// Handles 'restoresession.error' message.
-- (BOOL)handleRestoreSessionErrorMessage:(base::DictionaryValue*)message
-                                 context:(NSDictionary*)context {
-  if (![context[kIsMainFrame] boolValue])
-    return NO;
-  std::string errorMessage;
-  if (!message->GetString("message", &errorMessage)) {
-    DLOG(WARNING) << "JS message parameter not found: message";
-    return NO;
-  }
-
-  // Restore session error is likely a result of coding error. Log diagnostics
-  // information that is sent back by the page to aid debugging.
-  NOTREACHED()
-      << "Session restore failed unexpectedly with error: " << errorMessage
-      << ". Web view URL: "
-      << (self.webView
-              ? net::GURLWithNSURL(self.webView.URL).possibly_invalid_spec()
-              : " N/A");
-  return YES;
-}
-
 #pragma mark - WebUI
 
 // Sets up WebUI for URL.
diff --git a/ios/web/web_state/ui/crw_web_request_controller.h b/ios/web/web_state/ui/crw_web_request_controller.h
index c009c1f..ef132a4 100644
--- a/ios/web/web_state/ui/crw_web_request_controller.h
+++ b/ios/web/web_state/ui/crw_web_request_controller.h
@@ -72,6 +72,19 @@
                                           (web::WKBackForwardListItemHolder*)
                                               holder;
 
+// Loads |data| of type |MIMEType| and replaces last committed URL with the
+// given |URL|.
+- (void)loadData:(NSData*)data
+         webView:(WKWebView*)webView
+        MIMEType:(NSString*)MIMEType
+          forURL:(const GURL&)URL;
+
+// Loads |HTML| into the page and use |URL| to resolve relative URLs within the
+// document.
+- (void)loadHTML:(NSString*)HTML
+         webView:(WKWebView*)webView
+          forURL:(const GURL&)URL;
+
 @end
 
 #endif  // IOS_WEB_WEB_STATE_UI_CRW_WEB_REQUEST_CONTROLLER_H_
diff --git a/ios/web/web_state/ui/crw_web_request_controller.mm b/ios/web/web_state/ui/crw_web_request_controller.mm
index 8586e01..d88ba4c 100644
--- a/ios/web/web_state/ui/crw_web_request_controller.mm
+++ b/ios/web/web_state/ui/crw_web_request_controller.mm
@@ -7,6 +7,7 @@
 #import <WebKit/WebKit.h>
 
 #include "base/feature_list.h"
+#include "base/i18n/i18n_constants.h"
 #import "base/ios/block_types.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
@@ -32,6 +33,7 @@
 using web::wk_navigation_util::ExtractUrlFromPlaceholderUrl;
 using web::wk_navigation_util::IsPlaceholderUrl;
 using web::wk_navigation_util::kReferrerHeaderName;
+using web::wk_navigation_util::URLNeedsUserAgentType;
 
 namespace {
 // Values for the histogram that counts slow/fast back/forward navigations.
@@ -256,6 +258,99 @@
       }));
 }
 
+- (void)loadData:(NSData*)data
+         webView:(WKWebView*)webView
+        MIMEType:(NSString*)MIMEType
+          forURL:(const GURL&)URL {
+  [_delegate webRequestControllerStopLoading:self];
+  web::NavigationItemImpl* item =
+      self.navigationManagerImpl->GetLastCommittedItemImpl();
+  auto navigationContext = web::NavigationContextImpl::CreateNavigationContext(
+      self.webState, URL,
+      /*has_user_gesture=*/true, item->GetTransitionType(),
+      /*is_renderer_initiated=*/false);
+  self.navigationHandler.navigationState = web::WKNavigationState::REQUESTED;
+  navigationContext->SetNavigationItemUniqueID(item->GetUniqueID());
+
+  item->SetNavigationInitiationType(
+      web::NavigationInitiationType::BROWSER_INITIATED);
+  // The error_retry_state_machine may still be in the
+  // |kDisplayingWebErrorForFailedNavigation| from the navigation that is being
+  // replaced. As the navigation is now successful, the error can be cleared.
+  item->error_retry_state_machine().SetNoNavigationError();
+  // The load data call will replace the current navigation and the webView URL
+  // of the navigation will be replaced by |URL|. Set the URL of the
+  // navigationItem to keep them synced.
+  // Note: it is possible that the URL in item already match |url|. But item can
+  // also contain a placeholder URL intended to be replaced.
+  item->SetURL(URL);
+  navigationContext->SetMimeType(MIMEType);
+  if (item->GetUserAgentType() == web::UserAgentType::NONE &&
+      URLNeedsUserAgentType(URL)) {
+    item->SetUserAgentType(web::UserAgentType::MOBILE);
+  }
+
+  WKNavigation* navigation =
+      [webView loadData:data
+                       MIMEType:MIMEType
+          characterEncodingName:base::SysUTF8ToNSString(base::kCodepageUTF8)
+                        baseURL:net::NSURLWithGURL(URL)];
+
+  [self.navigationHandler.navigationStates
+         setContext:std::move(navigationContext)
+      forNavigation:navigation];
+  [self.navigationHandler.navigationStates
+           setState:web::WKNavigationState::REQUESTED
+      forNavigation:navigation];
+}
+
+- (void)loadHTML:(NSString*)HTML
+         webView:(WKWebView*)webView
+          forURL:(const GURL&)URL {
+  DCHECK(HTML.length);
+  // Remove the transient content view.
+  self.webState->ClearTransientContent();
+
+  self.navigationHandler.navigationState = web::WKNavigationState::REQUESTED;
+
+  WKNavigation* navigation = [webView loadHTMLString:HTML
+                                             baseURL:net::NSURLWithGURL(URL)];
+  [self.navigationHandler.navigationStates
+           setState:web::WKNavigationState::REQUESTED
+      forNavigation:navigation];
+  std::unique_ptr<web::NavigationContextImpl> context;
+  const ui::PageTransition loadHTMLTransition =
+      ui::PageTransition::PAGE_TRANSITION_TYPED;
+  if (self.webState->HasWebUI()) {
+    // WebUI uses |loadHTML:forURL:| to feed the content to web view. This
+    // should not be treated as a navigation, but WKNavigationDelegate callbacks
+    // still expect a valid context.
+    context = web::NavigationContextImpl::CreateNavigationContext(
+        self.webState, URL, /*has_user_gesture=*/true, loadHTMLTransition,
+        /*is_renderer_initiated=*/false);
+    context->SetNavigationItemUniqueID(self.currentNavItem->GetUniqueID());
+    if (web::features::StorePendingItemInContext()) {
+      // Transfer pending item ownership to NavigationContext.
+      // NavigationManager owns pending item after navigation is requested and
+      // until navigation context is created.
+      context->SetItem(self.navigationManagerImpl->ReleasePendingItem());
+    }
+  } else {
+    context = [_delegate webRequestController:self
+                    registerLoadRequestForURL:URL
+                                     referrer:web::Referrer()
+                                   transition:loadHTMLTransition
+                       sameDocumentNavigation:NO
+                               hasUserGesture:YES
+                            rendererInitiated:NO
+                        placeholderNavigation:NO];
+  }
+  context->SetLoadingHtmlString(true);
+  context->SetMimeType(@"text/html");
+  [self.navigationHandler.navigationStates setContext:std::move(context)
+                                        forNavigation:navigation];
+}
+
 // Reports Navigation.IOSWKWebViewSlowFastBackForward UMA. No-op if pending
 // navigation is not back forward navigation.
 - (void)reportBackForwardNavigationTypeForFastNavigation:(BOOL)isFast {
diff --git a/media/audio/cras/audio_manager_cras.cc b/media/audio/cras/audio_manager_cras.cc
index 58e91b02..376989bf 100644
--- a/media/audio/cras/audio_manager_cras.cc
+++ b/media/audio/cras/audio_manager_cras.cc
@@ -91,8 +91,7 @@
     device_names->emplace_back(kInternalOutputVirtualDevice,
                                base::NumberToString(device_list[0].id));
   } else {
-    DCHECK(device_list[0].type == chromeos::AUDIO_TYPE_INTERNAL_MIC ||
-           device_list[1].type == chromeos::AUDIO_TYPE_INTERNAL_MIC);
+    DCHECK(device_list[0].IsInternalMic() || device_list[1].IsInternalMic());
     device_names->emplace_back(kInternalInputVirtualDevice,
                                base::NumberToString(device_list[0].id));
   }
diff --git a/media/blink/resource_multibuffer_data_provider.cc b/media/blink/resource_multibuffer_data_provider.cc
index 551351c..e39a17f6 100644
--- a/media/blink/resource_multibuffer_data_provider.cc
+++ b/media/blink/resource_multibuffer_data_provider.cc
@@ -251,23 +251,8 @@
   destination_url_data->set_valid_until(base::Time::Now() +
                                         GetCacheValidUntil(response));
 
-  uint32_t reasons = GetReasonsForUncacheability(response);
-  destination_url_data->set_cacheable(reasons == 0);
-  UMA_HISTOGRAM_BOOLEAN("Media.CacheUseful", reasons == 0);
-  int shift = 0;
-  int max_enum = base::bits::Log2Ceiling(kMaxReason);
-  while (reasons) {
-    DCHECK_LT(shift, max_enum);  // Sanity check.
-    if (reasons & 0x1) {
-      // Note: this uses an exact linear UMA to fake an enum UMA, as the actual
-      // enum is a bitmask.
-      UMA_HISTOGRAM_EXACT_LINEAR("Media.UncacheableReason", shift,
-                                 max_enum);  // PRESUBMIT_IGNORE_UMA_MAX
-    }
-
-    reasons >>= 1;
-    ++shift;
-  }
+  destination_url_data->set_cacheable(GetReasonsForUncacheability(response) ==
+                                      0);
 
   // Expected content length can be |kPositionNotSpecified|, in that case
   // |content_length_| is not specified and this is a streaming response.
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc
index 3e830d11..e372ad1 100644
--- a/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -130,7 +130,7 @@
     return;
   }
 
-  if (!decoder_thread_.Start()) {
+  if (!decoder_thread_.IsRunning() && !decoder_thread_.Start()) {
     std::move(init_cb).Run(false);
     return;
   }
@@ -149,9 +149,32 @@
                                        InitCB init_cb,
                                        OutputCB output_cb) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
-  DCHECK_EQ(state_, State::kUninitialized);
+  DCHECK(state_ == State::kUninitialized || state_ == State::kWaitingForInput);
   DVLOGF(3);
 
+  // Reinitializing the decoder is allowed if there are no pending decodes.
+  if (current_decode_task_ || !decode_task_queue_.empty()) {
+    VLOGF(1) << "Don't call Initialize() while there are pending decode tasks";
+    client_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(init_cb), false));
+    return;
+  }
+
+  // We expect the decoder to have released all output buffers (by the client
+  // triggering a flush or reset), even if the media::VideoDecoder API doesn't
+  // explicitly specify this.
+  DCHECK(output_frames_.empty());
+
+  if (state_ != State::kUninitialized) {
+    DVLOGF(3) << "Reinitializing decoder";
+    if (decoder_) {
+      decoder_->Reset();
+      decoder_ = nullptr;
+    }
+    vaapi_wrapper_ = nullptr;
+    SetState(State::kUninitialized);
+  }
+
   // Initialize VAAPI wrapper.
   VideoCodecProfile profile = config.profile();
   vaapi_wrapper_ = VaapiWrapper::CreateForVideoCodec(
@@ -361,8 +384,13 @@
 
   // Get a video frame from the video frame pool.
   scoped_refptr<VideoFrame> frame = frame_pool_->GetFrame();
-  if (!frame)
+  if (!frame) {
+    // Ask the video frame pool to notify us when new frames are available, so
+    // we can retry the current decode task.
+    frame_pool_->NotifyWhenFrameAvailable(base::BindOnce(
+        &VaapiVideoDecoder::NotifyFrameAvailableTask, weak_this_));
     return nullptr;
+  }
 
   frame->set_timestamp(current_decode_task_->buffer_->timestamp());
 
@@ -401,13 +429,6 @@
   DCHECK_EQ(output_frames_.count(surface_id), 0u);
   output_frames_[surface_id] = frame;
 
-  // When the video frame is returned to the pool we need to be notified, so we
-  // can start decoding again if we are waiting for output buffers.
-  // TODO(dstaessens@): Don't make use of BindToCurrentLoop.
-  base::OnceClosure delete_frame_cb = BindToCurrentLoop(
-      base::BindOnce(&VaapiVideoDecoder::NotifyFrameAvailableTask, weak_this_));
-  frame->AddDestructionObserver(std::move(delete_frame_cb));
-
   // When the last reference to the VASurface is dropped ReleaseFrameTask() will
   // be called. This means the decoder no longer needs the frame for output or
   // reference, so we can safely remove it from |output_frames_| and destroy the
@@ -595,9 +616,8 @@
 
   // Check whether the state change is valid.
   switch (state) {
-    case State::kDecoding:
-      DCHECK(state_ == State::kWaitingForInput ||
-             state_ == State::kWaitingForOutput);
+    case State::kUninitialized:
+      DCHECK_EQ(state_, State::kWaitingForInput);
       break;
     case State::kWaitingForInput:
       DCHECK(decode_task_queue_.empty());
@@ -609,6 +629,10 @@
       DCHECK(current_decode_task_);
       DCHECK_EQ(state_, State::kDecoding);
       break;
+    case State::kDecoding:
+      DCHECK(state_ == State::kWaitingForInput ||
+             state_ == State::kWaitingForOutput);
+      break;
     case State::kResetting:
       DCHECK(state_ == State::kWaitingForInput ||
              state_ == State::kWaitingForOutput || state_ == State::kDecoding);
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index a5ace70..3f9ebba 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -539,20 +539,11 @@
                                       coded_size.height());
       gfx::Size uv_tex_size(u_width, u_height);
 
+      DCHECK_EQ(frame_resources_.size(),
+                VideoFrame::NumPlanes(frame->format()));
       if (frame->HasTextures()) {
-        if (frame->format() == PIXEL_FORMAT_NV12) {
-          DCHECK_EQ(2u, frame_resources_.size());
-        } else {
-          DCHECK_EQ(PIXEL_FORMAT_I420, frame->format());
-          DCHECK_EQ(3u,
-                    frame_resources_.size());  // Alpha is not supported yet.
-        }
-      } else {
-        DCHECK_GE(frame_resources_.size(), 3u);
-        DCHECK(frame_resources_.size() <= 3 ||
-               ya_tex_size == VideoFrame::PlaneSize(frame->format(),
-                                                    VideoFrame::kAPlane,
-                                                    coded_size));
+        DCHECK(frame->format() == PIXEL_FORMAT_NV12 ||
+               frame->format() == PIXEL_FORMAT_I420);
       }
 
       // Compute the UV sub-sampling factor based on the ratio between
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index 29b4d3c..87965c8 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -243,6 +243,35 @@
   serialized_ = true;
 }
 
+Message::Message(base::span<const uint8_t> payload,
+                 base::span<ScopedHandle> handles) {
+  MojoResult rv = mojo::CreateMessage(&handle_);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+  DCHECK(handle_.is_valid());
+
+  void* buffer;
+  uint32_t buffer_size;
+  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(payload.size()));
+  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(handles.size()));
+  MojoAppendMessageDataOptions options;
+  options.struct_size = sizeof(options);
+  options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE;
+  rv = MojoAppendMessageData(
+      handle_->value(), static_cast<uint32_t>(payload.size()),
+      reinterpret_cast<MojoHandle*>(handles.data()),
+      static_cast<uint32_t>(handles.size()), &options, &buffer, &buffer_size);
+  DCHECK_EQ(MOJO_RESULT_OK, rv);
+  // Handle ownership has been taken by MojoAppendMessageData.
+  for (auto& handle : handles)
+    ignore_result(handle.release());
+
+  payload_buffer_ = internal::Buffer(buffer, payload.size(), payload.size());
+  std::copy(payload.begin(), payload.end(),
+            static_cast<uint8_t*>(payload_buffer_.data()));
+  transferable_ = true;
+  serialized_ = true;
+}
+
 // static
 Message Message::CreateFromMessageHandle(ScopedMessageHandle* message_handle) {
   DCHECK(message_handle);
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
index 4ff8a6b..3be96de 100644
--- a/mojo/public/cpp/bindings/message.h
+++ b/mojo/public/cpp/bindings/message.h
@@ -16,6 +16,7 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/component_export.h"
+#include "base/containers/span.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "mojo/public/cpp/bindings/lib/buffer.h"
@@ -68,6 +69,14 @@
           size_t payload_interface_id_count,
           std::vector<ScopedHandle>* handles);
 
+  // Constructs a new serialized Message object from a fully populated message
+  // payload (including a well-formed message header) and an optional set of
+  // handle attachments. This Message may not be extended with additional
+  // payload or handles once constructed, but its payload remains mutable as
+  // long as the Message is not moved and neither |Reset()| nor
+  // |TakeMojoMessage()| is called.
+  Message(base::span<const uint8_t> payload, base::span<ScopedHandle> handles);
+
   // Constructs a new serialized Message object from an existing
   // ScopedMessageHandle; e.g., one read from a message pipe.
   //
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
index 789872b..91d54b39 100644
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -24,6 +24,7 @@
     "map_unittest.cc",
     "message_queue.cc",
     "message_queue.h",
+    "message_unittest.cc",
     "multiplex_router_unittest.cc",
     "native_struct_unittest.cc",
     "new_endpoint_types_unittest.cc",
diff --git a/mojo/public/cpp/bindings/tests/message_unittest.cc b/mojo/public/cpp/bindings/tests/message_unittest.cc
new file mode 100644
index 0000000..6c68fdf
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/message_unittest.cc
@@ -0,0 +1,81 @@
+// 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 <stdint.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+constexpr int32_t kTestMessageName = 42;
+constexpr int32_t kTestMessageFlags = 7;
+constexpr uint32_t kTestPayloadSize = 32;
+
+void CreateTestMessagePayload(std::vector<uint8_t>* bytes,
+                              std::vector<ScopedHandle>* handles) {
+  Message message(kTestMessageName, kTestMessageFlags, 0, kTestPayloadSize,
+                  nullptr);
+  message.header()->trace_id = 0;
+  bytes->resize(message.data_num_bytes());
+  std::copy(message.data(), message.data() + message.data_num_bytes(),
+            bytes->begin());
+
+  MessagePipe pipe;
+  handles->resize(2);
+  handles->at(0) = ScopedHandle(std::move(pipe.handle0));
+  handles->at(1) = ScopedHandle(std::move(pipe.handle1));
+}
+
+TEST(BindingsMessageTest, ConstructFromPayload) {
+  // Verifies that Message objects constructed directly from a raw payload look
+  // the same on the wire as raw messages constructed with lower level APIs.
+  MessagePipe pipe;
+
+  // First feed the raw message data directly into the pipe.
+  std::vector<uint8_t> in_bytes1;
+  std::vector<ScopedHandle> in_handles1;
+  CreateTestMessagePayload(&in_bytes1, &in_handles1);
+  WriteMessageRaw(pipe.handle0.get(), in_bytes1.data(), in_bytes1.size(),
+                  reinterpret_cast<const MojoHandle*>(in_handles1.data()),
+                  in_handles1.size(), MOJO_WRITE_MESSAGE_FLAG_NONE);
+  for (auto& handle : in_handles1)
+    ignore_result(handle.release());
+
+  // Now construct a Message object from the same payload and feed that into the
+  // pipe.
+  std::vector<uint8_t> in_bytes2;
+  std::vector<ScopedHandle> in_handles2;
+  CreateTestMessagePayload(&in_bytes2, &in_handles2);
+  Message message(in_bytes2, in_handles2);
+  WriteMessageNew(pipe.handle0.get(), message.TakeMojoMessage(),
+                  MOJO_WRITE_MESSAGE_FLAG_NONE);
+
+  // Now read both messages and ensure that they're identical.
+  // NOTE: The handles themselves cannot be identical, but the same number of
+  // handles should be attached.
+  std::vector<uint8_t> out_bytes1;
+  std::vector<ScopedHandle> out_handles1;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            ReadMessageRaw(pipe.handle1.get(), &out_bytes1, &out_handles1,
+                           MOJO_READ_MESSAGE_FLAG_NONE));
+  std::vector<uint8_t> out_bytes2;
+  std::vector<ScopedHandle> out_handles2;
+  ASSERT_EQ(MOJO_RESULT_OK,
+            ReadMessageRaw(pipe.handle1.get(), &out_bytes2, &out_handles2,
+                           MOJO_READ_MESSAGE_FLAG_NONE));
+
+  EXPECT_EQ(out_bytes1, out_bytes2);
+  EXPECT_EQ(out_handles1.size(), out_handles2.size());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace mojo
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index f5a5564..43fd4d3d 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -1416,10 +1416,6 @@
   request_creation_time_ = base::Time();
 
   UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpTimeToFirstByte", to_start);
-  if (request_info_.upload_data_stream &&
-      request_info_.upload_data_stream->size() > 1024 * 1024) {
-    UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpTimeToFirstByte.LargeUpload", to_start);
-  }
 }
 
 void URLRequestHttpJob::ResetTimer() {
diff --git a/services/identity/public/cpp/access_token_fetcher_unittest.cc b/services/identity/public/cpp/access_token_fetcher_unittest.cc
index 4fe4632..c1da225 100644
--- a/services/identity/public/cpp/access_token_fetcher_unittest.cc
+++ b/services/identity/public/cpp/access_token_fetcher_unittest.cc
@@ -45,7 +45,7 @@
 }  // namespace
 
 class AccessTokenFetcherTest : public testing::Test,
-                               public OAuth2TokenService::DiagnosticsObserver {
+                               public AccessTokenDiagnosticsObserver {
  public:
   using TestTokenCallback =
       StrictMock<MockCallback<AccessTokenFetcher::TokenCallback>>;
@@ -61,11 +61,11 @@
     account_tracker_ = std::make_unique<AccountTrackerService>();
     account_tracker_->Initialize(&pref_service_, base::FilePath());
 
-    token_service_.AddDiagnosticsObserver(this);
+    token_service_.AddAccessTokenDiagnosticsObserver(this);
   }
 
   ~AccessTokenFetcherTest() override {
-    token_service_.RemoveDiagnosticsObserver(this);
+    token_service_.RemoveAccessTokenDiagnosticsObserver(this);
   }
 
   std::string AddAccount(const std::string& gaia_id, const std::string& email) {
@@ -107,7 +107,7 @@
   AccessTokenInfo access_token_info() { return access_token_info_; }
 
  private:
-  // OAuth2TokenService::DiagnosticsObserver:
+  // OAuth2AccessTokenManagerDiagnosticsObserver:
   void OnAccessTokenRequested(
       const CoreAccountId& account_id,
       const std::string& consumer_id,
diff --git a/services/identity/public/cpp/identity_manager.cc b/services/identity/public/cpp/identity_manager.cc
index d19f7e8..0588de90 100644
--- a/services/identity/public/cpp/identity_manager.cc
+++ b/services/identity/public/cpp/identity_manager.cc
@@ -67,6 +67,7 @@
   primary_account_manager_->SetObserver(this);
   token_service_->AddDiagnosticsObserver(this);
   token_service_->AddObserver(this);
+  token_service_->AddAccessTokenDiagnosticsObserver(this);
 
   // IdentityManager owns the ATS and GCMS instances and will outlive them, so
   // base::Unretained is safe.
@@ -100,6 +101,7 @@
   primary_account_manager_->ClearObserver();
   token_service_->RemoveObserver(this);
   token_service_->RemoveDiagnosticsObserver(this);
+  token_service_->RemoveAccessTokenDiagnosticsObserver(this);
 }
 
 void IdentityManager::AddObserver(Observer* observer) {
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h
index cbf867a7..8514908 100644
--- a/services/identity/public/cpp/identity_manager.h
+++ b/services/identity/public/cpp/identity_manager.h
@@ -53,6 +53,7 @@
 // ./README.md for detailed documentation.
 class IdentityManager : public PrimaryAccountManager::Observer,
                         public OAuth2TokenService::DiagnosticsObserver,
+                        public AccessTokenDiagnosticsObserver,
                         public OAuth2TokenServiceObserver {
  public:
   class Observer {
@@ -596,7 +597,7 @@
       const GoogleServiceAuthError& error);
   void OnGaiaCookieDeletedByUserAction();
 
-  // OAuth2TokenService::DiagnosticsObserver:
+  // AccessTokenDiagnosticsObserver
   void OnAccessTokenRequested(const CoreAccountId& account_id,
                               const std::string& consumer_id,
                               const ScopeSet& scopes) override;
@@ -607,6 +608,7 @@
                                   base::Time expiration_time) override;
   void OnAccessTokenRemoved(const CoreAccountId& account_id,
                             const ScopeSet& scopes) override;
+  // OAuth2TokenService::DiagnosticsObserver:
   void OnRefreshTokenAvailableFromSource(const CoreAccountId& account_id,
                                          bool is_refresh_token_valid,
                                          const std::string& source) override;
diff --git a/services/identity/public/cpp/identity_manager_unittest.cc b/services/identity/public/cpp/identity_manager_unittest.cc
index 939637db..517ff9b 100644
--- a/services/identity/public/cpp/identity_manager_unittest.cc
+++ b/services/identity/public/cpp/identity_manager_unittest.cc
@@ -103,7 +103,7 @@
 
       // It should trigger OnAccessTokenRemovedFromCache from
       // IdentityManager::DiagnosticsObserver.
-      for (auto& observer : GetDiagnicsObservers())
+      for (auto& observer : GetAccessTokenDiagnosticsObservers())
         observer.OnAccessTokenRemoved(account_id, scopes);
 
       std::move(on_access_token_invalidated_callback_).Run();
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc
index 5b172ca5..4bff8ed 100644
--- a/services/network/cors/cors_url_loader.cc
+++ b/services/network/cors/cors_url_loader.cc
@@ -154,6 +154,16 @@
     return;
   }
 
+  // Does not allow modifying headers that are stored in |cors_exempt_headers|.
+  for (const auto& header : modified_headers.GetHeaderVector()) {
+    if (request_.cors_exempt_headers.HasHeader(header.key)) {
+      LOG(WARNING) << "A client is trying to modify header value for '"
+                   << header.key << "', but it is not permitted.";
+      HandleComplete(URLLoaderCompletionStatus(net::ERR_INVALID_ARGUMENT));
+      return;
+    }
+  }
+
   for (const auto& name : removed_headers) {
     request_.headers.RemoveHeader(name);
     request_.cors_exempt_headers.RemoveHeader(name);
diff --git a/services/network/cors/cors_url_loader_unittest.cc b/services/network/cors/cors_url_loader_unittest.cc
index c62c701..9ee1956 100644
--- a/services/network/cors/cors_url_loader_unittest.cc
+++ b/services/network/cors/cors_url_loader_unittest.cc
@@ -1227,6 +1227,36 @@
       GetRequest().cors_exempt_headers.HasHeader(kTestCorsExemptHeader));
 }
 
+TEST_F(CorsURLLoaderTest, CorsExemptHeaderModificationOnRedirects) {
+  ResourceRequest request;
+  request.url = GURL("https://example.com/foo.png");
+  request.cors_exempt_headers.SetHeader(kTestCorsExemptHeader, "test-value");
+  CreateLoaderAndStart(request);
+  EXPECT_EQ(1, num_created_loaders());
+
+  NotifyLoaderClientOnReceiveRedirect(
+      CreateRedirectInfo(301, "GET", GURL("https://example.com/bar.png")));
+  RunUntilRedirectReceived();
+
+  ASSERT_TRUE(IsNetworkLoaderStarted());
+  EXPECT_TRUE(client().has_received_redirect());
+  EXPECT_FALSE(client().has_received_response());
+  EXPECT_FALSE(client().has_received_completion());
+  EXPECT_TRUE(
+      GetRequest().cors_exempt_headers.HasHeader(kTestCorsExemptHeader));
+
+  net::HttpRequestHeaders modified_headers;
+  modified_headers.SetHeader(kTestCorsExemptHeader, "test-modified");
+  FollowRedirect({}, modified_headers);
+  RunUntilComplete();
+
+  ASSERT_EQ(1, num_created_loaders());
+  EXPECT_FALSE(client().has_received_response());
+  EXPECT_TRUE(client().has_received_completion());
+  ASSERT_TRUE(
+      GetRequest().cors_exempt_headers.HasHeader(kTestCorsExemptHeader));
+}
+
 // Tests if OriginAccessList is actually used to decide the cors flag.
 // Details for the OriginAccessList behaviors are verified in
 // OriginAccessListTest, but this test intends to verify if CorsURlLoader calls
diff --git a/services/network/public/cpp/resource_request_body.h b/services/network/public/cpp/resource_request_body.h
index eae9bc8..292aeb6 100644
--- a/services/network/public/cpp/resource_request_body.h
+++ b/services/network/public/cpp/resource_request_body.h
@@ -19,6 +19,12 @@
 #include "services/network/public/mojom/url_loader.mojom-shared.h"
 #include "url/gurl.h"
 
+namespace blink {
+namespace mojom {
+class FetchAPIRequestBodyDataView;
+}  // namespace mojom
+}  // namespace blink
+
 namespace network {
 
 // ResourceRequestBody represents body (i.e. upload data) of a HTTP request.
@@ -98,6 +104,8 @@
 
  private:
   friend class base::RefCountedThreadSafe<ResourceRequestBody>;
+  friend struct mojo::StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
+                                   scoped_refptr<network::ResourceRequestBody>>;
   friend struct mojo::StructTraits<network::mojom::URLRequestBodyDataView,
                                    scoped_refptr<network::ResourceRequestBody>>;
   ~ResourceRequestBody();
diff --git a/services/tracing/perfetto/consumer_host.cc b/services/tracing/perfetto/consumer_host.cc
index 79c2b6f..a3c16c77 100644
--- a/services/tracing/perfetto/consumer_host.cc
+++ b/services/tracing/perfetto/consumer_host.cc
@@ -330,7 +330,7 @@
 void ConsumerHost::TracingSession::RequestBufferUsage(
     RequestBufferUsageCallback callback) {
   if (!request_buffer_usage_callback_.is_null()) {
-    std::move(callback).Run(false, 0);
+    std::move(callback).Run(false, 0, false);
     return;
   }
 
@@ -433,7 +433,7 @@
   }
 
   if (!success || stats.buffer_stats_size() != 1) {
-    std::move(request_buffer_usage_callback_).Run(false, 0.0f);
+    std::move(request_buffer_usage_callback_).Run(false, 0.0f, false);
     return;
   }
 
@@ -445,7 +445,10 @@
   double percent_full =
       bytes_in_buffer / static_cast<double>(buf_stats.buffer_size());
   percent_full = std::min(std::max(0.0, percent_full), 1.0);
-  std::move(request_buffer_usage_callback_).Run(true, percent_full);
+  bool data_loss = buf_stats.chunks_overwritten() > 0 ||
+                   buf_stats.chunks_discarded() > 0 ||
+                   buf_stats.abi_violations() > 0;
+  std::move(request_buffer_usage_callback_).Run(true, percent_full, data_loss);
 }
 
 void ConsumerHost::TracingSession::Flush(
diff --git a/services/tracing/public/mojom/perfetto_service.mojom b/services/tracing/public/mojom/perfetto_service.mojom
index aad114c..b9241b0 100644
--- a/services/tracing/public/mojom/perfetto_service.mojom
+++ b/services/tracing/public/mojom/perfetto_service.mojom
@@ -226,8 +226,10 @@
   ReadBuffers(handle<data_pipe_producer> stream) => ();
 
   // Request current trace buffer usage of the active session. Will be returned
-  // as percentage value between 0.0f and 1.0f.
-  RequestBufferUsage() => (bool success, float percent_full);
+  // as percentage value between 0.0f and 1.0f. |data_loss| indicates whether
+  // any trace data is known to have been lost, e.g. because the ring buffer
+  // wrapped and data were overridden.
+  RequestBufferUsage() => (bool success, float percent_full, bool data_loss);
 
   // Disables tracing and converts the collected trace data converted into the
   // legacy JSON format before returning it via the data pipe. If
diff --git a/storage/browser/quota/quota_database_unittest.cc b/storage/browser/quota/quota_database_unittest.cc
index 288049b..a1911ff 100644
--- a/storage/browser/quota/quota_database_unittest.cc
+++ b/storage/browser/quota/quota_database_unittest.cc
@@ -13,8 +13,8 @@
 #include "base/callback.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/message_loop/message_loop.h"
 #include "base/stl_util.h"
+#include "base/test/scoped_task_environment.h"
 #include "sql/database.h"
 #include "sql/meta_table.h"
 #include "sql/statement.h"
@@ -575,7 +575,7 @@
     db->CommitTransaction();
   }
 
-  base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
 };
 
 TEST_F(QuotaDatabaseTest, LazyOpen) {
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index 0a0fbd0..aa5ca9e 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -33,6 +33,7 @@
     "feature_policy/feature_policy.cc",
     "feature_policy/policy_value.cc",
     "features.cc",
+    "fetch/fetch_api_request_body_mojom_traits.cc",
     "frame/frame_policy.cc",
     "frame/from_ad_state.cc",
     "frame/user_activation_state.cc",
diff --git a/third_party/blink/common/fetch/OWNERS b/third_party/blink/common/fetch/OWNERS
new file mode 100644
index 0000000..b2e47ad
--- /dev/null
+++ b/third_party/blink/common/fetch/OWNERS
@@ -0,0 +1,7 @@
+file://third_party/blink/renderer/core/fetch/OWNERS
+
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
+
+# TEAM: blink-network-dev@chromium.org
+# COMPONENT: Blink>Network>FetchAPI
diff --git a/third_party/blink/common/fetch/fetch_api_request_body_mojom_traits.cc b/third_party/blink/common/fetch/fetch_api_request_body_mojom_traits.cc
new file mode 100644
index 0000000..b4b4a6a
--- /dev/null
+++ b/third_party/blink/common/fetch/fetch_api_request_body_mojom_traits.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 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 "third_party/blink/public/common/fetch/fetch_api_request_body_mojom_traits.h"
+
+#include "services/network/public/cpp/url_request_mojom_traits.h"
+
+namespace mojo {
+
+bool StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
+                  scoped_refptr<network::ResourceRequestBody>>::
+    Read(blink::mojom::FetchAPIRequestBodyDataView data,
+         scoped_refptr<network::ResourceRequestBody>* out) {
+  auto body = base::MakeRefCounted<network::ResourceRequestBody>();
+  if (!data.ReadElements(&(body->elements_)))
+    return false;
+  body->set_identifier(data.identifier());
+  body->set_contains_sensitive_info(data.contains_sensitive_info());
+  *out = std::move(body);
+  return true;
+}
+
+}  // namespace mojo
diff --git a/third_party/blink/public/common/DEPS b/third_party/blink/public/common/DEPS
index 6f3146b3..727f80b9 100644
--- a/third_party/blink/public/common/DEPS
+++ b/third_party/blink/public/common/DEPS
@@ -11,6 +11,7 @@
     "+net",
     "+media",
     "+mojo",
+    "+services/network/public/cpp/resource_request_body.h",
     "+services/network/public/cpp/shared_url_loader_factory.h",
     "+services/network/public/mojom/url_loader_factory.mojom.h",
     "+skia/public/interfaces",
diff --git a/third_party/blink/public/common/fetch/fetch_api_request_body.typemap b/third_party/blink/public/common/fetch/fetch_api_request_body.typemap
new file mode 100644
index 0000000..b57c723
--- /dev/null
+++ b/third_party/blink/public/common/fetch/fetch_api_request_body.typemap
@@ -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.
+
+mojom = "//third_party/blink/public/mojom/fetch/fetch_api_request.mojom"
+public_headers = [
+  "//base/memory/scoped_refptr.h",
+  "//services/network/public/cpp/resource_request_body.h",
+]
+traits_headers = [ "//third_party/blink/public/common/fetch/fetch_api_request_body_mojom_traits.h" ]
+public_deps = [
+  "//base",
+  "//services/network/public/cpp:cpp_base",
+]
+type_mappings = [ "blink.mojom.FetchAPIRequestBody=scoped_refptr<network::ResourceRequestBody>[nullable_is_same_type,copyable_pass_by_value]" ]
diff --git a/third_party/blink/public/common/fetch/fetch_api_request_body_mojom_traits.h b/third_party/blink/public/common/fetch/fetch_api_request_body_mojom_traits.h
new file mode 100644
index 0000000..e8b3d20
--- /dev/null
+++ b/third_party/blink/public/common/fetch/fetch_api_request_body_mojom_traits.h
@@ -0,0 +1,48 @@
+// 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 THIRD_PARTY_BLINK_PUBLIC_COMMON_FETCH_FETCH_API_REQUEST_BODY_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_FETCH_FETCH_API_REQUEST_BODY_MOJOM_TRAITS_H_
+
+#include <utility>
+
+#include "base/memory/scoped_refptr.h"
+#include "services/network/public/cpp/resource_request_body.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
+                    scoped_refptr<network::ResourceRequestBody>> {
+  static bool IsNull(const scoped_refptr<network::ResourceRequestBody>& r) {
+    return !r;
+  }
+
+  static void SetToNull(scoped_refptr<network::ResourceRequestBody>* out) {
+    out->reset();
+  }
+
+  static const std::vector<network::DataElement>& elements(
+      const scoped_refptr<network::ResourceRequestBody>& r) {
+    return *r->elements();
+  }
+
+  static uint64_t identifier(
+      const scoped_refptr<network::ResourceRequestBody>& r) {
+    return r->identifier_;
+  }
+
+  static bool contains_sensitive_info(
+      const scoped_refptr<network::ResourceRequestBody>& r) {
+    return r->contains_sensitive_info_;
+  }
+
+  static bool Read(blink::mojom::FetchAPIRequestBodyDataView data,
+                   scoped_refptr<network::ResourceRequestBody>* out);
+};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_FETCH_FETCH_API_REQUEST_BODY_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/mojom/background_sync/background_sync.mojom b/third_party/blink/public/mojom/background_sync/background_sync.mojom
index 3ba75ec15..55bf6f1 100644
--- a/third_party/blink/public/mojom/background_sync/background_sync.mojom
+++ b/third_party/blink/public/mojom/background_sync/background_sync.mojom
@@ -50,10 +50,37 @@
   BackgroundSyncType sync_type;
 };
 
-interface BackgroundSyncService {
-  Register(SyncRegistrationOptions options, int64 service_worker_registration_id)
-      => (BackgroundSyncError err, SyncRegistrationOptions options);
+// Used by SyncManager for one-shot Background Sync.
+interface OneShotBackgroundSyncService {
+  // Used to register one-shot Background Sync tasks.
+  Register(SyncRegistrationOptions options,
+           int64 service_worker_registration_id)
+      => (BackgroundSyncError err, SyncRegistrationOptions? options);
+
+  // Used to indicate that the register() promise has been resolved by
+  // JavaScript code.
   DidResolveRegistration(BackgroundSyncRegistrationInfo registration_info);
-  GetOneShotSyncRegistrations(int64 service_worker_registration_id)
-      => (BackgroundSyncError err, array<SyncRegistrationOptions> registrations);
+
+  // Returns a list of all one-shot Background Sync registrations.
+  GetRegistrations(int64 service_worker_registration_id)
+      => (BackgroundSyncError err,
+          array<SyncRegistrationOptions> registrations);
+};
+
+// Used by PeriodicSyncManager for periodic Background Sync.
+interface PeriodicBackgroundSyncService {
+  // Used to register periodic Background Sync tasks.
+  Register(SyncRegistrationOptions options,
+           int64 service_worker_registration_id)
+      => (BackgroundSyncError err, SyncRegistrationOptions? options);
+
+  // Used to unregister periodic Background Sync tasks. Silently succeeds
+  // if called for non-existent periodic Background Sync registrations.
+  Unregister(int64 service_worker_registration_id, string tag)
+      => (BackgroundSyncError err);
+
+  // Returns a list of all periodic Background Sync registrations.
+  GetRegistrations(int64 service_worker_registration_id)
+      => (BackgroundSyncError err,
+          array<SyncRegistrationOptions> registrations);
 };
diff --git a/third_party/blink/public/mojom/fetch/fetch_api_request.mojom b/third_party/blink/public/mojom/fetch/fetch_api_request.mojom
index 44f7c7d..7f766b5 100644
--- a/third_party/blink/public/mojom/fetch/fetch_api_request.mojom
+++ b/third_party/blink/public/mojom/fetch/fetch_api_request.mojom
@@ -108,6 +108,28 @@
   map<string, string> headers;
 };
 
+// Struct representing a Body for a Request:
+// https://fetch.spec.whatwg.org/#body
+// This has the same members definition with network.mojom.URLRequestBody, which
+// aims to pass around body for network.mojom.URLRequest. Both of them are
+// typemapped to scoped_refptr<network::ResourceRequestBody> for the default
+// variant, the only difference is for the Blink variant that
+// network.mojom.URLRequestBody still is typemapped to
+// scoped_refptr<network::ResourceRequestBody> but this is typemapped to
+// scoped_refptr<blink::EncodedFormData>.
+struct FetchAPIRequestBody {
+  // Store upload bodies
+  array<network.mojom.DataElement> elements;
+
+  // Identifies a particular upload instance, which is used by the cache to
+  // formulate a cache key.
+  uint64 identifier;
+
+  // Indicates whether the post data contains sensitive information like
+  // passwords.
+  bool contains_sensitive_info;
+};
+
 // Struct representing a Request:
 // https://fetch.spec.whatwg.org/#request-class
 // Compared to network.mojom.URLRequest which is kind of internal data in the
@@ -133,7 +155,7 @@
   // service workers.
   // TODO(crbug.com/911930): Remove |blob| and use |body| instead everywhere.
   SerializedBlob? blob;
-  network.mojom.URLRequestBody? body;
+  FetchAPIRequestBody? body;
 
   Referrer? referrer;
   network.mojom.CredentialsMode credentials_mode =
diff --git a/third_party/blink/public/public_typemaps.gni b/third_party/blink/public/public_typemaps.gni
index 0b8379e..396c5dc 100644
--- a/third_party/blink/public/public_typemaps.gni
+++ b/third_party/blink/public/public_typemaps.gni
@@ -4,6 +4,7 @@
 
 # These are typemaps which are exposed by Blink to its embedder.
 typemaps = [
+  "//third_party/blink/public/common/fetch/fetch_api_request_body.typemap",
   "//third_party/blink/public/common/fetch/fetch_api_request_headers.typemap",
   "//third_party/blink/public/common/indexeddb/indexed_db_default.typemap",
   "//third_party/blink/public/common/loader/url_loader_factory_bundle.typemap",
diff --git a/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc b/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc
index f4d42df..4875efe 100644
--- a/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc
+++ b/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc
@@ -32,28 +32,32 @@
   v8::Isolate* isolate = context->GetIsolate();
   v8::Local<v8::Primitive> base_url_value =
       host_defined_options->Get(isolate, kBaseURL);
+  SECURITY_CHECK(base_url_value->IsString());
   String base_url_string =
-      ToCoreStringWithNullCheck(v8::Local<v8::String>::Cast(base_url_value));
+      ToCoreString(v8::Local<v8::String>::Cast(base_url_value));
   KURL base_url = base_url_string.IsEmpty() ? KURL() : KURL(base_url_string);
   DCHECK(base_url.IsNull() || base_url.IsValid());
 
   v8::Local<v8::Primitive> credentials_mode_value =
       host_defined_options->Get(isolate, kCredentialsMode);
+  SECURITY_CHECK(credentials_mode_value->IsUint32());
   auto credentials_mode = static_cast<network::mojom::CredentialsMode>(
       credentials_mode_value->IntegerValue(context).ToChecked());
 
   v8::Local<v8::Primitive> nonce_value =
       host_defined_options->Get(isolate, kNonce);
-  String nonce =
-      ToCoreStringWithNullCheck(v8::Local<v8::String>::Cast(nonce_value));
+  SECURITY_CHECK(nonce_value->IsString());
+  String nonce = ToCoreString(v8::Local<v8::String>::Cast(nonce_value));
 
   v8::Local<v8::Primitive> parser_state_value =
       host_defined_options->Get(isolate, kParserState);
+  SECURITY_CHECK(parser_state_value->IsUint32());
   ParserDisposition parser_state = static_cast<ParserDisposition>(
       parser_state_value->IntegerValue(context).ToChecked());
 
   v8::Local<v8::Primitive> referrer_policy_value =
       host_defined_options->Get(isolate, kReferrerPolicy);
+  SECURITY_CHECK(referrer_policy_value->IsUint32());
   network::mojom::ReferrerPolicy referrer_policy =
       static_cast<network::mojom::ReferrerPolicy>(
           referrer_policy_value->IntegerValue(context).ToChecked());
diff --git a/third_party/blink/renderer/core/animation/interpolation_effect_test.cc b/third_party/blink/renderer/core/animation/interpolation_effect_test.cc
index 2ed3176..ea6fb6f 100644
--- a/third_party/blink/renderer/core/animation/interpolation_effect_test.cc
+++ b/third_party/blink/renderer/core/animation/interpolation_effect_test.cc
@@ -67,6 +67,10 @@
   interpolation_effect->GetActiveInterpolations(3, kInterpolationTestDuration,
                                                 active_interpolations);
   EXPECT_EQ(0ul, active_interpolations.size());
+
+  interpolation_effect->GetActiveInterpolations(0, kInterpolationTestDuration,
+                                                active_interpolations);
+  EXPECT_EQ(1ul, active_interpolations.size());
 }
 
 TEST(AnimationInterpolationEffectTest, MultipleInterpolations) {
diff --git a/third_party/blink/renderer/core/clipboard/data_transfer.cc b/third_party/blink/renderer/core/clipboard/data_transfer.cc
index 0769fa7..b7160ec 100644
--- a/third_party/blink/renderer/core/clipboard/data_transfer.cc
+++ b/third_party/blink/renderer/core/clipboard/data_transfer.cc
@@ -101,10 +101,9 @@
     // object contains transparency and there are other elements in the same
     // stacking context which stacked below.
     PaintLayer* layer = dragged_layout_object->EnclosingLayer();
-    if (!layer->GetLayoutObject().StyleRef().IsStackingContext()) {
-      layer =
-          PaintLayerStackingNode::AncestorStackingContextNode(layer)->Layer();
-    }
+    if (!layer->GetLayoutObject().StyleRef().IsStackingContext())
+      layer = layer->AncestorStackingContext();
+
     IntRect absolute_bounding_box =
         dragged_layout_object->AbsoluteBoundingBoxRectIncludingDescendants();
     // TODO(chrishtr): consider using the root frame's visible rect instead
diff --git a/third_party/blink/renderer/core/css/remote_font_face_source.cc b/third_party/blink/renderer/core/css/remote_font_face_source.cc
index 9ddc913..3241fe0 100644
--- a/third_party/blink/renderer/core/css/remote_font_face_source.cc
+++ b/third_party/blink/renderer/core/css/remote_font_face_source.cc
@@ -359,13 +359,6 @@
     DCHECK_NE(load_start_time_, 0);
     int duration = static_cast<int>(CurrentTimeMS() - load_start_time_);
     RecordLoadTimeHistogram(font, duration);
-
-    enum { kCorsFail, kCorsSuccess, kCorsEnumMax };
-    int cors_value =
-        font->GetResponse().IsCorsSameOrigin() ? kCorsSuccess : kCorsFail;
-    DEFINE_THREAD_SAFE_STATIC_LOCAL(EnumerationHistogram, cors_histogram,
-                                    ("WebFont.CORSSuccess", kCorsEnumMax));
-    cors_histogram.Count(cors_value);
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index 1a0b2673..08efdcc 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -672,5 +672,17 @@
       style.SetTextOverflow(text_control->ValueForTextOverflow());
     }
   }
+
+  if (RuntimeEnabledFeatures::LayoutNGBlockFragmentationEnabled()) {
+    // When establishing a block fragmentation context for LayoutNG, we require
+    // that everything fragmentable inside can be laid out by NG natively, since
+    // NG and legacy layout cannot cooperate within the same fragmentation
+    // context. Set a flag, so that we can quickly determine whether we need to
+    // check that an element is compatible with the NG block fragmentation
+    // machinery.
+    if (style.SpecifiesColumns() ||
+        (element && element->GetDocument().Printing()))
+      style.SetInsideNGFragmentationContext(true);
+  }
 }
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 0359576f..15474ea 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -339,6 +339,18 @@
   if (style.HasTextCombine() && !style.IsHorizontalWritingMode())
     return true;
 
+  if (style.InsideNGFragmentationContext()) {
+    // If we're inside an NG block fragmentation context, all fragmentable boxes
+    // must be laid out by NG natively. We only allow legacy layout objects if
+    // they are monolithic (e.g. replaced content, inline-table, and so
+    // on). Inline display types end up on a line, and are therefore monolithic,
+    // so we can allow those.
+    if (!style.IsDisplayInlineType()) {
+      if (style.IsDisplayTableType() || style.IsDisplayFlexibleOrGridBox())
+        return true;
+    }
+  }
+
   return false;
 }
 
@@ -3733,6 +3745,15 @@
     // forced legacy layout in the ancestry, e.g. if this element no longer
     // establishes a new formatting context.
     ForceLegacyLayoutInFormattingContext(new_style);
+
+    // If we're inside an NG fragmentation context, we also need the entire
+    // fragmentation context to fall back to legacy layout. Note that once this
+    // has happened, the fragmentation context will be locked to legacy layout,
+    // even if all the reasons for requiring it in the first place disappear
+    // (e.g. if the only reason was a table, and that table is removed, we'll
+    // still be using legacy layout).
+    if (new_style.InsideNGFragmentationContext())
+      ForceLegacyLayoutInFragmentationContext(new_style);
   } else if (old_force) {
     // TODO(mstensho): If we have ancestors that got legacy layout just because
     // of this child, we should clean it up, and switch the subtree back to NG,
@@ -3760,6 +3781,38 @@
   }
 }
 
+void Element::ForceLegacyLayoutInFragmentationContext(
+    const ComputedStyle& new_style) {
+  // This element cannot be laid out natively by LayoutNG. We now need to switch
+  // all enclosing block fragmentation contexts over to using legacy
+  // layout. Find the element that establishes the fragmentation context, and
+  // switch it over to legacy layout. Note that we walk the parent chain here,
+  // and not the containing block chain. This means that we may get false
+  // positives; e.g. if there's an absolutely positioned table, whose containing
+  // block of the table is on the outside of the fragmentation context, we're
+  // still going to fall back to legacy.
+  Element* parent;
+  for (Element* walker = this; walker; walker = parent) {
+    parent = ToElementOrNull(LayoutTreeBuilderTraversal::Parent(*walker));
+    if (!walker->GetComputedStyle()->SpecifiesColumns())
+      continue;
+
+    // Found an element that establishes a fragmentation context. Force it to do
+    // legacy layout. Keep looking for outer fragmentation contexts, since we
+    // need to force them over to legacy as well.
+    walker->SetShouldForceLegacyLayoutForChild(true);
+    walker->SetNeedsReattachLayoutTree();
+    if (parent && !parent->GetComputedStyle()->InsideNGFragmentationContext())
+      return;
+  }
+  DCHECK(GetDocument().Printing());
+  // Force legacy layout on the entire document, since we're printing, and
+  // there's some fragmentable box that needs legacy layout inside somewhere.
+  Element* root = GetDocument().documentElement();
+  root->SetShouldForceLegacyLayoutForChild(true);
+  root->SetNeedsReattachLayoutTree();
+}
+
 bool Element::IsFocusedElementInDocument() const {
   return this == GetDocument().FocusedElement();
 }
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index d14474df..fd76212 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -1170,6 +1170,12 @@
   // within a formatting context.
   void ForceLegacyLayoutInFormattingContext(const ComputedStyle& new_style);
 
+  // If this element requires legacy layout, and we're inside a fragmentation
+  // context, we need to force legacy layout for the entire fragmentation
+  // context. LayoutNG block fragmentation and legacy block fragmentation cannot
+  // cooperate within a fragmentation context.
+  void ForceLegacyLayoutInFragmentationContext(const ComputedStyle& new_style);
+
   Member<ElementData> element_data_;
 };
 
diff --git a/third_party/blink/renderer/core/frame/location.cc b/third_party/blink/renderer/core/frame/location.cc
index 2eb0735..06e790f 100644
--- a/third_party/blink/renderer/core/frame/location.cc
+++ b/third_party/blink/renderer/core/frame/location.cc
@@ -201,10 +201,7 @@
   if (hash[0] == '#')
     new_fragment_identifier = hash.Substring(1);
   url.SetFragmentIdentifier(new_fragment_identifier);
-  // Note that by parsing the URL and *then* comparing fragments, we are
-  // comparing fragments post-canonicalization, and so this handles the
-  // cases where fragment identifiers are ignored or invalid.
-  if (EqualIgnoringNullity(old_fragment_identifier, url.FragmentIdentifier()))
+  if (old_fragment_identifier == url.FragmentIdentifier())
     return;
   SetLocation(url.GetString(), IncumbentDOMWindow(isolate),
               EnteredDOMWindow(isolate), &exception_state);
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 9fad4285..2675c6b 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -6614,6 +6614,9 @@
   # delivered via dataCollected events.
   event tracingComplete
     parameters
+      # Indicates whether some trace data is known to have been lost, e.g. because the trace ring
+      # buffer wrapped around.
+      boolean dataLossOccurred
       # A handle of the stream that holds resulting trace data.
       optional IO.StreamHandle stream
       # Trace data format of returned stream.
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
index eb6c249..a93cb08 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
@@ -37,9 +37,8 @@
 #include "third_party/blink/renderer/core/layout/layout_text.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_stacking_node.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.h"
 #include "v8/include/v8-inspector.h"
 
 namespace blink {
@@ -1059,12 +1058,9 @@
   DCHECK(!embedded_document || !layer->FirstChild());
 
   if (!embedded_document) {
-    if (PaintLayerStackingNode* node = layer->StackingNode()) {
-      PaintLayerStackingNodeIterator iterator(*node, kAllChildren);
-      while (PaintLayer* child_layer = iterator.Next()) {
-        VisitPaintLayer(child_layer);
-      }
-    }
+    PaintLayerPaintOrderIterator iterator(*layer, kAllChildren);
+    while (PaintLayer* child_layer = iterator.Next())
+      VisitPaintLayer(child_layer);
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index b1d20c0..edee4a8 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -5716,7 +5716,8 @@
       (Parent() && IsWritingModeRoot()) ||
       (IsOutOfFlowPositioned() &&
        StyleRef().GetPosition() == EPosition::kFixed) ||
-      ShouldApplySizeContainment() || DisplayLockInducesSizeContainment())
+      ShouldApplySizeContainment() || DisplayLockInducesSizeContainment() ||
+      IsFrameSet())
     return kForbidBreaks;
 
   EBreakInside break_value = BreakInside();
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index 8e2bda7..988c24d4 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -220,6 +220,8 @@
 
 void LayoutImage::PaintReplaced(const PaintInfo& paint_info,
                                 const PhysicalOffset& paint_offset) const {
+  if (PaintBlockedByDisplayLock(DisplayLockContext::kChildren))
+    return;
   ImagePainter(*this).PaintReplaced(paint_info, paint_offset);
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_tree_as_text.cc b/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
index ebc4716..ea3585eb 100644
--- a/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
@@ -60,6 +60,7 @@
 #include "third_party/blink/renderer/core/page/print_context.h"
 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
 #include "third_party/blink/renderer/platform/wtf/text/character_names.h"
@@ -695,14 +696,12 @@
     Write(ts, layer.GetLayoutObject(), indent + 1, behavior);
 }
 
-static PaintLayerStackingNode::PaintLayers NormalFlowListFor(
-    PaintLayerStackingNode* node) {
-  PaintLayerStackingNode::PaintLayers vector;
-  if (node) {
-    PaintLayerStackingNodeIterator it(*node, kNormalFlowChildren);
-    while (PaintLayer* normal_flow_child = it.Next())
-      vector.push_back(normal_flow_child);
-  }
+static Vector<PaintLayer*> ChildLayers(const PaintLayer* layer,
+                                       PaintLayerIteration which_children) {
+  Vector<PaintLayer*> vector;
+  PaintLayerPaintOrderIterator it(*layer, which_children);
+  while (PaintLayer* child = it.Next())
+    vector.push_back(child);
   return vector;
 }
 
@@ -749,29 +748,23 @@
   }
 #endif
 
-  bool paints_background_separately = false;
-  if (layer->StackingNode()) {
-    PaintLayerStackingNode::PaintLayers* neg_list =
-        layer->StackingNode()->NegZOrderList();
-    paints_background_separately = neg_list && neg_list->size() > 0;
-    if (should_paint && paints_background_separately) {
-      Write(ts, *layer, layer_bounds, damage_rect.Rect(),
-            clip_rect_to_apply.Rect(), kLayerPaintPhaseBackground, indent,
-            behavior, marked_layer);
-    }
+  const auto& neg_list = ChildLayers(layer, kNegativeZOrderChildren);
+  bool paints_background_separately = !neg_list.IsEmpty();
+  if (should_paint && paints_background_separately) {
+    Write(ts, *layer, layer_bounds, damage_rect.Rect(),
+          clip_rect_to_apply.Rect(), kLayerPaintPhaseBackground, indent,
+          behavior, marked_layer);
+  }
 
-    if (neg_list) {
-      int curr_indent = indent;
-      if (behavior & kLayoutAsTextShowLayerNesting) {
-        WriteIndent(ts, indent);
-        ts << " negative z-order list(" << neg_list->size() << ")\n";
-        ++curr_indent;
-      }
-      for (unsigned i = 0; i != neg_list->size(); ++i) {
-        WriteLayers(ts, root_layer, neg_list->at(i), curr_indent, behavior,
-                    marked_layer);
-      }
+  if (!neg_list.IsEmpty()) {
+    int curr_indent = indent;
+    if (behavior & kLayoutAsTextShowLayerNesting) {
+      WriteIndent(ts, indent);
+      ts << " negative z-order list(" << neg_list.size() << ")\n";
+      ++curr_indent;
     }
+    for (auto* layer : neg_list)
+      WriteLayers(ts, root_layer, layer, curr_indent, behavior, marked_layer);
   }
 
   if (should_paint) {
@@ -782,35 +775,28 @@
           indent, behavior, marked_layer);
   }
 
-  if (layer->StackingNode()) {
-    PaintLayerStackingNode::PaintLayers normal_flow_list =
-        NormalFlowListFor(layer->StackingNode());
-    if (!normal_flow_list.IsEmpty()) {
-      int curr_indent = indent;
-      if (behavior & kLayoutAsTextShowLayerNesting) {
-        WriteIndent(ts, indent);
-        ts << " normal flow list(" << normal_flow_list.size() << ")\n";
-        ++curr_indent;
-      }
-      for (unsigned i = 0; i != normal_flow_list.size(); ++i) {
-        WriteLayers(ts, root_layer, normal_flow_list.at(i), curr_indent,
-                    behavior, marked_layer);
-      }
+  const auto& normal_flow_list = ChildLayers(layer, kNormalFlowChildren);
+  if (!normal_flow_list.IsEmpty()) {
+    int curr_indent = indent;
+    if (behavior & kLayoutAsTextShowLayerNesting) {
+      WriteIndent(ts, indent);
+      ts << " normal flow list(" << normal_flow_list.size() << ")\n";
+      ++curr_indent;
     }
+    for (auto* layer : normal_flow_list)
+      WriteLayers(ts, root_layer, layer, curr_indent, behavior, marked_layer);
+  }
 
-    if (PaintLayerStackingNode::PaintLayers* pos_list =
-            layer->StackingNode()->PosZOrderList()) {
-      int curr_indent = indent;
-      if (behavior & kLayoutAsTextShowLayerNesting) {
-        WriteIndent(ts, indent);
-        ts << " positive z-order list(" << pos_list->size() << ")\n";
-        ++curr_indent;
-      }
-      for (unsigned i = 0; i != pos_list->size(); ++i) {
-        WriteLayers(ts, root_layer, pos_list->at(i), curr_indent, behavior,
-                    marked_layer);
-      }
+  const auto& pos_list = ChildLayers(layer, kPositiveZOrderChildren);
+  if (!pos_list.IsEmpty()) {
+    int curr_indent = indent;
+    if (behavior & kLayoutAsTextShowLayerNesting) {
+      WriteIndent(ts, indent);
+      ts << " positive z-order list(" << pos_list.size() << ")\n";
+      ++curr_indent;
     }
+    for (auto* layer : pos_list)
+      WriteLayers(ts, root_layer, layer, curr_indent, behavior, marked_layer);
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 66455df9..80313e52 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -949,6 +949,12 @@
   DCHECK(!box_->IsLayoutBlock() ||
          To<LayoutBlock>(box_)->CreatesNewFormattingContext());
 
+  // We cannot enter legacy layout for something fragmentable if we're inside an
+  // NG block fragmentation context. LayoutNG and legacy block fragmentation
+  // cannot cooperate within the same fragmentation context.
+  DCHECK(!constraint_space.HasBlockFragmentation() ||
+         box_->GetPaginationBreakability() == LayoutBox::kForbidBreaks);
+
   scoped_refptr<const NGLayoutResult> layout_result =
       box_->GetCachedLayoutResult();
 
diff --git a/third_party/blink/renderer/core/layout/paint_containment_test.cc b/third_party/blink/renderer/core/layout/paint_containment_test.cc
index 3b3ba26e..3e088f1 100644
--- a/third_party/blink/renderer/core/layout/paint_containment_test.cc
+++ b/third_party/blink/renderer/core/layout/paint_containment_test.cc
@@ -28,8 +28,7 @@
   // clipping and stacking performed by paint containment.
   DCHECK(obj.Layer());
   PaintLayer* layer = obj.Layer();
-  EXPECT_TRUE(layer->StackingNode() &&
-              layer->GetLayoutObject().StyleRef().IsStackingContext());
+  EXPECT_TRUE(layer->GetLayoutObject().StyleRef().IsStackingContext());
 }
 
 TEST_F(PaintContainmentTest, BlockPaintContainment) {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
index 65ac466..a3152eb7 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
@@ -264,6 +264,8 @@
 
 void LayoutSVGRoot::PaintReplaced(const PaintInfo& paint_info,
                                   const PhysicalOffset& paint_offset) const {
+  if (PaintBlockedByDisplayLock(DisplayLockContext::kChildren))
+    return;
   SVGRootPainter(*this).PaintReplaced(paint_info, paint_offset);
 }
 
diff --git a/third_party/blink/renderer/core/mojo/DEPS b/third_party/blink/renderer/core/mojo/DEPS
index a3ad469..a3bebdc 100644
--- a/third_party/blink/renderer/core/mojo/DEPS
+++ b/third_party/blink/renderer/core/mojo/DEPS
@@ -1,3 +1,5 @@
 include_rules = [
-  "+mojo/public/cpp/system",
+  "+base/numerics/safe_math.h",
+
+  "+mojo/public",
 ]
diff --git a/third_party/blink/renderer/core/mojo/mojo_handle.cc b/third_party/blink/renderer/core/mojo/mojo_handle.cc
index b74bf25..2951ad1 100644
--- a/third_party/blink/renderer/core/mojo/mojo_handle.cc
+++ b/third_party/blink/renderer/core/mojo/mojo_handle.cc
@@ -4,6 +4,9 @@
 
 #include "third_party/blink/renderer/core/mojo/mojo_handle.h"
 
+#include "base/numerics/safe_math.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
@@ -50,11 +53,10 @@
 MojoResult MojoHandle::writeMessage(
     ArrayBufferOrArrayBufferView& buffer,
     const HeapVector<Member<MojoHandle>>& handles) {
-  // mojo::WriteMessageRaw takes ownership of the handles, so release them here.
-  Vector<::MojoHandle, kHandleVectorInlineCapacity> raw_handles(handles.size());
-  std::transform(
-      handles.begin(), handles.end(), raw_handles.begin(),
-      [](MojoHandle* handle) { return handle->handle_.release().value(); });
+  Vector<mojo::ScopedHandle, kHandleVectorInlineCapacity> scoped_handles(
+      handles.size());
+  std::transform(handles.begin(), handles.end(), scoped_handles.begin(),
+                 [](MojoHandle* handle) { return std::move(handle->handle_); });
 
   const void* bytes = nullptr;
   size_t num_bytes = 0;
@@ -68,9 +70,13 @@
     num_bytes = view->byteLength();
   }
 
-  return mojo::WriteMessageRaw(
-      mojo::MessagePipeHandle(handle_.get().value()), bytes, num_bytes,
-      raw_handles.data(), raw_handles.size(), MOJO_WRITE_MESSAGE_FLAG_NONE);
+  auto message = mojo::Message(
+      base::make_span(static_cast<const uint8_t*>(bytes), num_bytes),
+      base::make_span(scoped_handles));
+  DCHECK(!message.IsNull());
+  return mojo::WriteMessageNew(mojo::MessagePipeHandle(handle_.get().value()),
+                               message.TakeMojoMessage(),
+                               MOJO_WRITE_MESSAGE_FLAG_NONE);
 }
 
 MojoReadMessageResult* MojoHandle::readMessage(
diff --git a/third_party/blink/renderer/core/paint/BUILD.gn b/third_party/blink/renderer/core/paint/BUILD.gn
index ae24cb6..65a4f8c 100644
--- a/third_party/blink/renderer/core/paint/BUILD.gn
+++ b/third_party/blink/renderer/core/paint/BUILD.gn
@@ -157,6 +157,8 @@
     "paint_layer_clipper.cc",
     "paint_layer_clipper.h",
     "paint_layer_fragment.h",
+    "paint_layer_paint_order_iterator.cc",
+    "paint_layer_paint_order_iterator.h",
     "paint_layer_painter.cc",
     "paint_layer_painter.h",
     "paint_layer_painting_info.h",
@@ -166,8 +168,6 @@
     "paint_layer_scrollable_area.h",
     "paint_layer_stacking_node.cc",
     "paint_layer_stacking_node.h",
-    "paint_layer_stacking_node_iterator.cc",
-    "paint_layer_stacking_node_iterator.h",
     "paint_phase.cc",
     "paint_phase.h",
     "paint_property_tree_builder.cc",
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index 00050711..d9796621 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -65,9 +65,9 @@
 #include "third_party/blink/renderer/core/paint/frame_paint_timing.h"
 #include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_painter.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.h"
 #include "third_party/blink/renderer/core/paint/scrollable_area_painter.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
@@ -402,8 +402,7 @@
   bool isolate = owning_layer_.ShouldIsolateCompositedDescendants();
 
   // non stacking context layers should never isolate
-  DCHECK((owning_layer_.StackingNode() &&
-          owning_layer_.GetLayoutObject().StyleRef().IsStackingContext()) ||
+  DCHECK(owning_layer_.GetLayoutObject().StyleRef().IsStackingContext() ||
          !isolate);
 
   graphics_layer_->SetIsRootForIsolatedGroup(isolate);
@@ -2774,16 +2773,8 @@
   if (!parent->HasVisibleDescendant())
     return false;
 
-  if (!parent->StackingNode())
-    return false;
-
-#if DCHECK_IS_ON()
-  LayerListMutationDetector mutation_checker(parent->StackingNode());
-#endif
-
-  PaintLayerStackingNodeIterator normal_flow_iterator(*parent->StackingNode(),
-                                                      kAllChildren);
-  while (PaintLayer* child_layer = normal_flow_iterator.Next()) {
+  PaintLayerPaintOrderIterator iterator(*parent, kAllChildren);
+  while (PaintLayer* child_layer = iterator.Next()) {
     if (child_layer->HasCompositedLayerMapping())
       continue;
     if (child_layer->HasVisibleContent() ||
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc b/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc
index 0714fb3..f5379aa 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 
 namespace blink {
@@ -339,10 +340,8 @@
     }
   }
 
-  if (layer->StackingDescendantNeedsCompositingLayerAssignment() &&
-      layer->GetLayoutObject().StyleRef().IsStackingContext()) {
-    PaintLayerStackingNodeIterator iterator(*layer->StackingNode(),
-                                            kNegativeZOrderChildren);
+  if (layer->StackingDescendantNeedsCompositingLayerAssignment()) {
+    PaintLayerPaintOrderIterator iterator(*layer, kNegativeZOrderChildren);
     while (PaintLayer* child_node = iterator.Next()) {
       AssignLayersToBackingsInternal(child_node, squashing_state,
                                      layers_needing_paint_invalidation);
@@ -359,10 +358,9 @@
         layers_needing_paint_invalidation);
   }
 
-  if (layer->StackingNode() &&
-      layer->StackingDescendantNeedsCompositingLayerAssignment()) {
-    PaintLayerStackingNodeIterator iterator(
-        *layer->StackingNode(), kNormalFlowChildren | kPositiveZOrderChildren);
+  if (layer->StackingDescendantNeedsCompositingLayerAssignment()) {
+    PaintLayerPaintOrderIterator iterator(*layer,
+                                          kNormalFlowAndPositiveZOrderChildren);
     while (PaintLayer* curr_layer = iterator.Next()) {
       AssignLayersToBackingsInternal(curr_layer, squashing_state,
                                      layers_needing_paint_invalidation);
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc b/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc
index aa4e40db..b3ae108 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_requirements_updater.cc
@@ -31,9 +31,8 @@
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_stacking_node.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 
 namespace blink {
@@ -232,11 +231,7 @@
 
 #if DCHECK_IS_ON()
 static void CheckSubtreeHasNoCompositing(PaintLayer* layer) {
-  if (!layer->StackingNode())
-    return;
-  PaintLayerStackingNodeIterator iterator(
-      *layer->StackingNode(),
-      kNegativeZOrderChildren | kNormalFlowChildren | kPositiveZOrderChildren);
+  PaintLayerPaintOrderIterator iterator(*layer, kAllChildren);
   while (PaintLayer* cur_layer = iterator.Next()) {
     DCHECK(cur_layer->GetCompositingState() == kNotComposited);
     DCHECK(!cur_layer->DirectCompositingReasons() ||
@@ -394,9 +389,7 @@
   }
 
 #if DCHECK_IS_ON()
-  base::Optional<LayerListMutationDetector> mutation_checker;
-  if (layer->StackingNode())
-    mutation_checker.emplace(layer->StackingNode());
+  PaintLayerListMutationDetector mutation_checker(*layer);
 #endif
 
   bool any_descendant_has3d_transform = false;
@@ -426,10 +419,8 @@
       !layer->HasCompositingDescendant() &&
       !layer->DescendantMayNeedCompositingRequirementsUpdate();
 
-  if (!skip_children &&
-      layer->GetLayoutObject().StyleRef().IsStackingContext()) {
-    PaintLayerStackingNodeIterator iterator(*layer->StackingNode(),
-                                            kNegativeZOrderChildren);
+  if (!skip_children) {
+    PaintLayerPaintOrderIterator iterator(*layer, kNegativeZOrderChildren);
     while (PaintLayer* child_layer = iterator.Next()) {
       IntRect absolute_child_descendant_bounding_box;
       UpdateRecursive(layer, child_layer, overlap_map, child_recursion_data,
@@ -478,9 +469,9 @@
     child_recursion_data.testing_overlap_ = true;
   }
 
-  if (!skip_children && layer->StackingNode()) {
-    PaintLayerStackingNodeIterator iterator(
-        *layer->StackingNode(), kNormalFlowChildren | kPositiveZOrderChildren);
+  if (!skip_children) {
+    PaintLayerPaintOrderIterator iterator(*layer,
+                                          kNormalFlowAndPositiveZOrderChildren);
     while (PaintLayer* child_layer = iterator.Next()) {
       IntRect absolute_child_descendant_bounding_box;
       UpdateRecursive(layer, child_layer, overlap_map, child_recursion_data,
diff --git a/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc b/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc
index 2c797e0..d27f2aa 100644
--- a/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_builder.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
 
 namespace blink {
 
@@ -85,14 +86,11 @@
                                    : &pending_reparents;
 
 #if DCHECK_IS_ON()
-  base::Optional<LayerListMutationDetector> mutation_checker;
-  if (layer.StackingNode())
-    mutation_checker.emplace(layer.StackingNode());
+  PaintLayerListMutationDetector mutation_checker(layer);
 #endif
 
-  if (style.IsStackingContext()) {
-    PaintLayerStackingNodeIterator iterator(*layer.StackingNode(),
-                                            kNegativeZOrderChildren);
+  if (layer.IsStackingContextWithNegativeZOrderChildren()) {
+    PaintLayerPaintOrderIterator iterator(layer, kNegativeZOrderChildren);
     while (PaintLayer* child_layer = iterator.Next()) {
       RebuildRecursive(*child_layer, *layer_vector_for_children,
                        *pending_reparents_for_children);
@@ -107,9 +105,9 @@
     }
   }
 
-  if (layer.StackingNode()) {
-    PaintLayerStackingNodeIterator iterator(
-        *layer.StackingNode(), kNormalFlowChildren | kPositiveZOrderChildren);
+  {
+    PaintLayerPaintOrderIterator iterator(layer,
+                                          kNormalFlowAndPositiveZOrderChildren);
     while (PaintLayer* child_layer = iterator.Next()) {
       RebuildRecursive(*child_layer, *layer_vector_for_children,
                        *pending_reparents_for_children);
diff --git a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
index c5ced0c8..c6d3d0b 100644
--- a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
+++ b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
@@ -839,8 +839,7 @@
     const PaintLayer* layer) const {
   if (!layer->HasCompositingDescendant())
     return false;
-  return layer->StackingNode() &&
-         layer->StackingNode()->HasNegativeZOrderList();
+  return layer->IsStackingContextWithNegativeZOrderChildren();
 }
 
 static void UpdateTrackingRasterInvalidationsRecursive(
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index ef0a0be..e73a815 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -77,6 +77,7 @@
 #include "third_party/blink/renderer/core/paint/filter_effect_builder.h"
 #include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_painter.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/style/reference_clip_path_operation.h"
@@ -164,6 +165,7 @@
       has_fixed_position_descendant_(false),
       has_sticky_position_descendant_(false),
       has_non_contained_absolute_position_descendant_(false),
+      has_stacked_descendant_in_current_stacking_context_(false),
       self_painting_status_changed_(false),
       filter_on_effect_node_dirty_(false),
       backdrop_filter_on_effect_node_dirty_(false),
@@ -176,6 +178,9 @@
       descendant_needs_compositing_layer_assignment_(false),
       has_self_painting_layer_descendant_(false),
       is_non_stacked_with_in_flow_stacked_descendant_(false),
+#if DCHECK_IS_ON()
+      layer_list_mutation_allowed_(true),
+#endif
       layout_object_(layout_object),
       parent_(nullptr),
       previous_(nullptr),
@@ -655,6 +660,7 @@
     has_fixed_position_descendant_ = false;
     has_sticky_position_descendant_ = false;
     has_non_contained_absolute_position_descendant_ = false;
+    has_stacked_descendant_in_current_stacking_context_ = false;
     has_self_painting_layer_descendant_ = false;
     is_non_stacked_with_in_flow_stacked_descendant_ = false;
 
@@ -662,7 +668,6 @@
         GetLayoutObject().CanContainAbsolutePositionObjects();
 
     const ComputedStyle& style = GetLayoutObject().StyleRef();
-    bool needs_stacking_node = style.IsStackingContext();
     bool is_stacked = style.IsStacked();
 
     for (PaintLayer* child = FirstChild(); child;
@@ -675,9 +680,9 @@
         has_visible_descendant_ = true;
 
       has_non_isolated_descendant_with_blend_mode_ |=
-          (!child->GetLayoutObject().StyleRef().IsStackingContext() &&
+          (!child_style.IsStackingContext() &&
            child->HasNonIsolatedDescendantWithBlendMode()) ||
-          child->GetLayoutObject().StyleRef().HasBlendMode();
+          child_style.HasBlendMode();
 
       has_descendant_with_clip_path_ |= child->HasDescendantWithClipPath() ||
                                         child->GetLayoutObject().HasClipPath();
@@ -695,7 +700,14 @@
              child_style.GetPosition() == EPosition::kAbsolute);
       }
 
-      needs_stacking_node = needs_stacking_node || !child_style.IsStacked();
+      if (!has_stacked_descendant_in_current_stacking_context_) {
+        if (child_style.IsStacked()) {
+          has_stacked_descendant_in_current_stacking_context_ = true;
+        } else if (!child_style.IsStackingContext()) {
+          has_stacked_descendant_in_current_stacking_context_ =
+              child->has_stacked_descendant_in_current_stacking_context_;
+        }
+      }
 
       has_self_painting_layer_descendant_ =
           has_self_painting_layer_descendant_ ||
@@ -711,7 +723,7 @@
       }
     }
 
-    UpdateStackingNode(needs_stacking_node);
+    UpdateStackingNode();
 
     if (old_has_non_isolated_descendant_with_blend_mode !=
         static_cast<bool>(has_non_isolated_descendant_with_blend_mode_))
@@ -775,13 +787,9 @@
 void PaintLayer::Update3DTransformedDescendantStatus() {
   has3d_transformed_descendant_ = false;
 
-  if (!stacking_node_)
-    return;
-
   // Transformed or preserve-3d descendants can only be in the z-order lists,
   // not in the normal flow list, so we only need to check those.
-  PaintLayerStackingNodeIterator iterator(
-      *stacking_node_.get(), kPositiveZOrderChildren | kNegativeZOrderChildren);
+  PaintLayerPaintOrderIterator iterator(*this, kStackedChildren);
   while (PaintLayer* child_layer = iterator.Next()) {
     bool child_has3d = false;
     // If the child lives in a 3d hierarchy, then the layer at the root of
@@ -988,9 +996,15 @@
     return Parent();
   if (!GetLayoutObject().StyleRef().IsStacked())
     return IsSelfPaintingLayer() ? Parent() : ContainingLayer();
-  if (PaintLayerStackingNode* ancestor_stacking_node =
-          PaintLayerStackingNode::AncestorStackingContextNode(this))
-    return ancestor_stacking_node->Layer();
+  return AncestorStackingContext();
+}
+
+PaintLayer* PaintLayer::AncestorStackingContext() const {
+  for (PaintLayer* ancestor = Parent(); ancestor;
+       ancestor = ancestor->Parent()) {
+    if (ancestor->GetLayoutObject().StyleRef().IsStackingContext())
+      return ancestor;
+  }
   return nullptr;
 }
 
@@ -1313,12 +1327,16 @@
 }
 
 void PaintLayer::AddChild(PaintLayer* child, PaintLayer* before_child) {
+#if DCHECK_IS_ON()
+  DCHECK(layer_list_mutation_allowed_);
+#endif
+
   PaintLayer* prev_sibling =
       before_child ? before_child->PreviousSibling() : LastChild();
   if (prev_sibling) {
     child->SetPreviousSibling(prev_sibling);
     prev_sibling->SetNextSibling(child);
-    DCHECK(prev_sibling != child);
+    DCHECK_NE(prev_sibling, child);
   } else {
     SetFirstChild(child);
   }
@@ -1326,7 +1344,7 @@
   if (before_child) {
     before_child->SetPreviousSibling(child);
     child->SetNextSibling(before_child);
-    DCHECK(before_child != child);
+    DCHECK_NE(before_child, child);
   } else {
     SetLastChild(child);
   }
@@ -1351,7 +1369,7 @@
     // ancestorStackingContextNode() can be null in the case where we're
     // building up generated content layers. This is ok, since the lists will
     // start off dirty in that case anyway.
-    PaintLayerStackingNode::DirtyStackingContextZOrderLists(child);
+    PaintLayerStackingNode::DirtyStackingContextZOrderLists(*child);
     MarkAncestorChainForFlagsUpdate();
   }
 
@@ -1368,7 +1386,11 @@
   child->SetNeedsRepaint();
 }
 
-PaintLayer* PaintLayer::RemoveChild(PaintLayer* old_child) {
+void PaintLayer::RemoveChild(PaintLayer* old_child) {
+#if DCHECK_IS_ON()
+  DCHECK(layer_list_mutation_allowed_);
+#endif
+
   old_child->MarkCompositingContainerChainForNeedsRepaint();
 
   if (old_child->PreviousSibling())
@@ -1390,7 +1412,7 @@
         Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
     }
     // Dirty the z-order list in which we are contained.
-    PaintLayerStackingNode::DirtyStackingContextZOrderLists(old_child);
+    PaintLayerStackingNode::DirtyStackingContextZOrderLists(*old_child);
     SetNeedsCompositingInputsUpdate();
   }
 
@@ -1410,8 +1432,6 @@
 
   if (old_child->EnclosingPaginationLayer())
     old_child->ClearPaginationRecursive();
-
-  return old_child;
 }
 
 void PaintLayer::ClearClipRects(ClipRectsCacheSlot cache_slot) {
@@ -1425,7 +1445,7 @@
     return;
 
   if (old_style && old_style->IsStacked()) {
-    PaintLayerStackingNode::DirtyStackingContextZOrderLists(this);
+    PaintLayerStackingNode::DirtyStackingContextZOrderLists(*this);
     MarkAncestorChainForFlagsUpdate();
   }
 
@@ -1619,10 +1639,18 @@
   UpdateSelfPaintingLayer();
 }
 
-void PaintLayer::UpdateStackingNode(bool needs_stacking_node) {
+void PaintLayer::UpdateStackingNode() {
+#if DCHECK_IS_ON()
+  DCHECK(layer_list_mutation_allowed_);
+#endif
+
+  bool needs_stacking_node =
+      has_stacked_descendant_in_current_stacking_context_ &&
+      GetLayoutObject().StyleRef().IsStackingContext();
+
   if (needs_stacking_node != !!stacking_node_) {
     if (needs_stacking_node)
-      stacking_node_ = std::make_unique<PaintLayerStackingNode>(this);
+      stacking_node_ = std::make_unique<PaintLayerStackingNode>(*this);
     else
       stacking_node_ = nullptr;
   }
@@ -2390,7 +2418,7 @@
 }
 
 PaintLayer* PaintLayer::HitTestChildren(
-    ChildrenIteration childrento_visit,
+    PaintLayerIteration children_to_visit,
     PaintLayer* root_layer,
     HitTestResult& result,
     const HitTestRecursionData& recursion_data,
@@ -2402,9 +2430,6 @@
   if (!HasSelfPaintingLayerDescendant())
     return nullptr;
 
-  if (!stacking_node_)
-    return nullptr;
-
   if (GetLayoutObject().PaintBlockedByDisplayLock(
           DisplayLockContext::kChildren))
     return nullptr;
@@ -2413,8 +2438,7 @@
   PaintLayer* stop_layer = stop_node ? stop_node->PaintingLayer() : nullptr;
 
   PaintLayer* result_layer = nullptr;
-  PaintLayerStackingNodeReverseIterator iterator(*stacking_node_,
-                                                 childrento_visit);
+  PaintLayerPaintOrderReverseIterator iterator(*this, children_to_visit);
   while (PaintLayer* child_layer = iterator.Next()) {
     if (child_layer->IsReplacedNormalFlowStacking())
       continue;
@@ -2637,18 +2661,7 @@
     const PaintLayer& composited_layer,
     PhysicalRect& result,
     PaintLayer::CalculateBoundsOptions options) const {
-  if (!StackingNode())
-    return;
-
-  DCHECK(GetLayoutObject().StyleRef().IsStackingContext() ||
-         !StackingNode()->HasPositiveZOrderList());
-
-#if DCHECK_IS_ON()
-  LayerListMutationDetector mutation_checker(
-      const_cast<PaintLayer*>(this)->StackingNode());
-#endif
-
-  PaintLayerStackingNodeIterator iterator(*StackingNode(), kAllChildren);
+  PaintLayerPaintOrderIterator iterator(*this, kAllChildren);
   while (PaintLayer* child_layer = iterator.Next()) {
     // Here we exclude both directly composited layers and squashing layers
     // because those Layers don't paint into the graphics layer
@@ -2965,12 +2978,7 @@
 
 bool PaintLayer::ChildBackgroundIsKnownToBeOpaqueInRect(
     const PhysicalRect& local_rect) const {
-  if (!stacking_node_)
-    return false;
-
-  PaintLayerStackingNodeReverseIterator reverse_iterator(
-      *stacking_node_,
-      kPositiveZOrderChildren | kNormalFlowChildren | kNegativeZOrderChildren);
+  PaintLayerPaintOrderReverseIterator reverse_iterator(*this, kAllChildren);
   while (PaintLayer* child_layer = reverse_iterator.Next()) {
     // Stop at composited paint boundaries and non-self-painting layers.
     if (child_layer->IsPaintInvalidationContainer())
@@ -3191,7 +3199,7 @@
     return;
   }
 
-  if (PaintLayerStackingNode::StyleDidChange(this, old_style))
+  if (PaintLayerStackingNode::StyleDidChange(*this, old_style))
     MarkAncestorChainForFlagsUpdate();
 
   if (RequiresScrollableArea()) {
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index 3f01ce60..36921b3 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -56,7 +56,6 @@
 #include "third_party/blink/renderer/core/paint/paint_layer_fragment.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_resource_info.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_stacking_node.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.h"
 #include "third_party/blink/renderer/core/paint/paint_result.h"
 #include "third_party/blink/renderer/platform/graphics/compositing_reasons.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
@@ -87,6 +86,23 @@
   kCompositingQueriesAreOnlyAllowedInCertainDocumentLifecyclePhases
 };
 
+// Used in PaintLayerPaintOrderIterator.
+enum PaintLayerIteration {
+  kNegativeZOrderChildren = 1,
+  // Normal flow children are not mandated by CSS 2.1 but are an artifact of
+  // our implementation: we allocate PaintLayers for elements that
+  // are not treated as stacking contexts and thus we need to walk them
+  // during painting and hit-testing.
+  kNormalFlowChildren = 1 << 1,
+  kPositiveZOrderChildren = 1 << 2,
+
+  kStackedChildren = kNegativeZOrderChildren | kPositiveZOrderChildren,
+  kNormalFlowAndPositiveZOrderChildren =
+      kNormalFlowChildren | kPositiveZOrderChildren,
+  kAllChildren =
+      kNegativeZOrderChildren | kNormalFlowChildren | kPositiveZOrderChildren
+};
+
 // FIXME: remove this once the compositing query DCHECKS are no longer hit.
 class CORE_EXPORT DisableCompositingQueryAsserts {
   STACK_ALLOCATED();
@@ -250,9 +266,10 @@
   // good but we can't use it for now because it conflicts with
   // PaintInfo::paintContainer.
   PaintLayer* CompositingContainer() const;
+  PaintLayer* AncestorStackingContext() const;
 
   void AddChild(PaintLayer* new_child, PaintLayer* before_child = nullptr);
-  PaintLayer* RemoveChild(PaintLayer*);
+  void RemoveChild(PaintLayer*);
 
   void ClearClipRects(ClipRectsCacheSlot = kNumberOfClipRectsCacheSlots);
 
@@ -329,9 +346,9 @@
                       : PhysicalOffset();
   }
 
-  PaintLayerStackingNode* StackingNode() { return stacking_node_.get(); }
-  const PaintLayerStackingNode* StackingNode() const {
-    return stacking_node_.get();
+  bool IsStackingContextWithNegativeZOrderChildren() const {
+    DCHECK(!stacking_node_ || GetLayoutObject().StyleRef().IsStackingContext());
+    return stacking_node_ && !stacking_node_->NegZOrderList().IsEmpty();
   }
 
   bool SubtreeIsInvisible() const {
@@ -1086,6 +1103,7 @@
   }
   PaintLayerStackingNode* StackingParent() { return stacking_parent_; }
   bool IsInStackingParentZOrderLists() const;
+  bool LayerListMutationAllowed() const { return layer_list_mutation_allowed_; }
 #endif
 
   void SetNeedsCompositingLayerAssignment();
@@ -1146,7 +1164,7 @@
       double* z_offset = nullptr,
       const PhysicalOffset& translation_offset = PhysicalOffset());
   PaintLayer* HitTestChildren(
-      ChildrenIteration,
+      PaintLayerIteration,
       PaintLayer* root_layer,
       HitTestResult&,
       const HitTestRecursionData& recursion_data,
@@ -1187,7 +1205,7 @@
 
   bool ShouldBeSelfPaintingLayer() const;
 
-  void UpdateStackingNode(bool needs_stacking_node);
+  void UpdateStackingNode();
 
   FilterOperations FilterOperationsIncludingReflection() const;
 
@@ -1259,6 +1277,10 @@
     return *ancestor_dependent_compositing_inputs_;
   }
 
+  // This is private because PaintLayerStackingNode is only for PaintLayer and
+  // PaintLayerPaintOrderIterator.
+  PaintLayerStackingNode* StackingNode() const { return stacking_node_.get(); }
+
   // Self-painting layer is an optimization where we avoid the heavy Layer
   // painting machinery for a Layer allocated only to handle the overflow clip
   // case.
@@ -1316,6 +1338,7 @@
   unsigned has_fixed_position_descendant_ : 1;
   unsigned has_sticky_position_descendant_ : 1;
   unsigned has_non_contained_absolute_position_descendant_ : 1;
+  unsigned has_stacked_descendant_in_current_stacking_context_ : 1;
 
   unsigned self_painting_status_changed_ : 1;
 
@@ -1340,6 +1363,10 @@
   unsigned has_self_painting_layer_descendant_ : 1;
   unsigned is_non_stacked_with_in_flow_stacked_descendant_ : 1;
 
+#if DCHECK_IS_ON()
+  mutable unsigned layer_list_mutation_allowed_ : 1;
+#endif
+
   LayoutBoxModelObject& layout_object_;
 
   PaintLayer* parent_;
@@ -1382,6 +1409,14 @@
   PaintLayerStackingNode* stacking_parent_;
 #endif
 
+  // For layer_list_mutation_allowed_.
+  friend class PaintLayerListMutationDetector;
+
+  // For stacking_node_ to avoid exposing it publicly.
+  friend class PaintLayerPaintOrderIterator;
+  friend class PaintLayerPaintOrderReverseIterator;
+  friend class PaintLayerStackingNode;
+
   FRIEND_TEST_ALL_PREFIXES(PaintLayerTest,
                            DescendantDependentFlagsStopsAtThrottledFrames);
   FRIEND_TEST_ALL_PREFIXES(PaintLayerTest,
@@ -1390,6 +1425,27 @@
   DISALLOW_COPY_AND_ASSIGN(PaintLayer);
 };
 
+#if DCHECK_IS_ON()
+class PaintLayerListMutationDetector {
+  STACK_ALLOCATED();
+
+ public:
+  explicit PaintLayerListMutationDetector(const PaintLayer& layer)
+      : layer_(layer),
+        previous_mutation_allowed_state_(layer.layer_list_mutation_allowed_) {
+    layer.layer_list_mutation_allowed_ = false;
+  }
+
+  ~PaintLayerListMutationDetector() {
+    layer_.layer_list_mutation_allowed_ = previous_mutation_allowed_state_;
+  }
+
+ private:
+  const PaintLayer& layer_;
+  bool previous_mutation_allowed_state_;
+};
+#endif
+
 }  // namespace blink
 
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.cc b/third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.cc
new file mode 100644
index 0000000..0456b7a
--- /dev/null
+++ b/third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
+
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_stacking_node.h"
+
+namespace blink {
+
+PaintLayer* PaintLayerPaintOrderIterator::Next() {
+  if (remaining_children_ & kNegativeZOrderChildren) {
+    if (root_.StackingNode()) {
+      const auto& neg_z_order_list = root_.StackingNode()->NegZOrderList();
+      if (index_ < neg_z_order_list.size())
+        return neg_z_order_list[index_++];
+    }
+
+    index_ = 0;
+    remaining_children_ &= ~kNegativeZOrderChildren;
+  }
+
+  if (remaining_children_ & kNormalFlowChildren) {
+    for (; current_normal_flow_child_;
+         current_normal_flow_child_ =
+             current_normal_flow_child_->NextSibling()) {
+      if (current_normal_flow_child_->GetLayoutObject().StyleRef().IsStacked())
+        continue;
+
+      PaintLayer* normal_flow_child = current_normal_flow_child_;
+      current_normal_flow_child_ = current_normal_flow_child_->NextSibling();
+      return normal_flow_child;
+    }
+
+    // We reset the iterator in case we reuse it.
+    current_normal_flow_child_ = root_.FirstChild();
+    remaining_children_ &= ~kNormalFlowChildren;
+  }
+
+  if (remaining_children_ & kPositiveZOrderChildren) {
+    if (root_.StackingNode()) {
+      const auto& pos_z_order_list = root_.StackingNode()->PosZOrderList();
+      if (index_ < pos_z_order_list.size())
+        return pos_z_order_list[index_++];
+    }
+
+    index_ = 0;
+    remaining_children_ &= ~kPositiveZOrderChildren;
+  }
+
+  return nullptr;
+}
+
+PaintLayer* PaintLayerPaintOrderReverseIterator::Next() {
+  if (remaining_children_ & kNegativeZOrderChildren) {
+    if (root_.StackingNode()) {
+      const auto& neg_z_order_list = root_.StackingNode()->NegZOrderList();
+      if (index_ >= 0)
+        return neg_z_order_list[index_--];
+    }
+
+    remaining_children_ &= ~kNegativeZOrderChildren;
+    SetIndexToLastItem();
+  }
+
+  if (remaining_children_ & kNormalFlowChildren) {
+    for (; current_normal_flow_child_;
+         current_normal_flow_child_ =
+             current_normal_flow_child_->PreviousSibling()) {
+      if (current_normal_flow_child_->GetLayoutObject().StyleRef().IsStacked())
+        continue;
+
+      PaintLayer* normal_flow_child = current_normal_flow_child_;
+      current_normal_flow_child_ =
+          current_normal_flow_child_->PreviousSibling();
+      return normal_flow_child;
+    }
+
+    remaining_children_ &= ~kNormalFlowChildren;
+    SetIndexToLastItem();
+  }
+
+  if (remaining_children_ & kPositiveZOrderChildren) {
+    if (root_.StackingNode()) {
+      const auto& pos_z_order_list = root_.StackingNode()->PosZOrderList();
+      if (index_ >= 0)
+        return pos_z_order_list[index_--];
+    }
+
+    remaining_children_ &= ~kPositiveZOrderChildren;
+    SetIndexToLastItem();
+  }
+
+  return nullptr;
+}
+
+void PaintLayerPaintOrderReverseIterator::SetIndexToLastItem() {
+  if (remaining_children_ & kNegativeZOrderChildren) {
+    if (root_.StackingNode()) {
+      const auto& neg_z_order_list = root_.StackingNode()->NegZOrderList();
+      if (!neg_z_order_list.IsEmpty()) {
+        index_ = neg_z_order_list.size() - 1;
+        return;
+      }
+    }
+
+    remaining_children_ &= ~kNegativeZOrderChildren;
+  }
+
+  if (remaining_children_ & kNormalFlowChildren) {
+    current_normal_flow_child_ = root_.LastChild();
+    return;
+  }
+
+  if (remaining_children_ & kPositiveZOrderChildren) {
+    if (root_.StackingNode()) {
+      const auto& pos_z_order_list = root_.StackingNode()->PosZOrderList();
+      if (!pos_z_order_list.IsEmpty()) {
+        index_ = pos_z_order_list.size() - 1;
+        return;
+      }
+    }
+
+    remaining_children_ &= ~kPositiveZOrderChildren;
+  }
+
+  // No more list to visit.
+  DCHECK(!remaining_children_);
+  index_ = -1;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h b/third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h
new file mode 100644
index 0000000..32ee38c
--- /dev/null
+++ b/third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_PAINT_ORDER_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_PAINT_ORDER_ITERATOR_H_
+
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+
+namespace blink {
+
+// This iterator walks the PaintLayer descendants in the following paint order:
+// NegativeZOrderChildren -> NormalFlowChildren -> PositiveZOrderChildren.
+class CORE_EXPORT PaintLayerPaintOrderIterator {
+  STACK_ALLOCATED();
+
+ public:
+  PaintLayerPaintOrderIterator(const PaintLayer& root,
+                               PaintLayerIteration which_children)
+      : root_(root),
+        remaining_children_(which_children),
+        index_(0),
+        current_normal_flow_child_(root.FirstChild())
+#if DCHECK_IS_ON()
+        ,
+        mutation_detector_(root)
+#endif
+  {
+  }
+
+  PaintLayer* Next();
+
+ private:
+  const PaintLayer& root_;
+  unsigned remaining_children_;
+  unsigned index_;
+  PaintLayer* current_normal_flow_child_;
+#if DCHECK_IS_ON()
+  PaintLayerListMutationDetector mutation_detector_;
+#endif
+  DISALLOW_COPY_AND_ASSIGN(PaintLayerPaintOrderIterator);
+};
+
+// This iterator is similar to PaintLayerPaintOrderIterator but it walks the
+// lists in reverse order (from the last item to the first one).
+class CORE_EXPORT PaintLayerPaintOrderReverseIterator {
+  STACK_ALLOCATED();
+
+ public:
+  PaintLayerPaintOrderReverseIterator(const PaintLayer& root,
+                                      unsigned which_children)
+      : root_(root),
+        remaining_children_(which_children)
+#if DCHECK_IS_ON()
+        ,
+        mutation_detector_(root)
+#endif
+  {
+    SetIndexToLastItem();
+  }
+
+  PaintLayer* Next();
+
+ private:
+  void SetIndexToLastItem();
+
+  const PaintLayer& root_;
+  unsigned remaining_children_;
+  int index_;
+  PaintLayer* current_normal_flow_child_;
+#if DCHECK_IS_ON()
+  PaintLayerListMutationDetector mutation_detector_;
+#endif
+  DISALLOW_COPY_AND_ASSIGN(PaintLayerPaintOrderReverseIterator);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_PAINT_ORDER_ITERATOR_H_
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.cc b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
index 58f6b6ff..0f91221 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/paint/object_paint_properties.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
 #include "third_party/blink/renderer/core/paint/scrollable_area_painter.h"
 #include "third_party/blink/renderer/platform/geometry/float_point_3d.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
@@ -550,7 +551,7 @@
             paint_layer_,
             DisplayItem::kLayerChunkNormalFlowAndPositiveZOrderChildren);
       }
-      if (PaintChildren(kNormalFlowChildren | kPositiveZOrderChildren, context,
+      if (PaintChildren(kNormalFlowAndPositiveZOrderChildren, context,
                         painting_info, paint_flags) == kMayBeClippedByCullRect)
         result = kMayBeClippedByCullRect;
     }
@@ -669,30 +670,21 @@
 }
 
 PaintResult PaintLayerPainter::PaintChildren(
-    unsigned children_to_visit,
+    PaintLayerIteration children_to_visit,
     GraphicsContext& context,
     const PaintLayerPaintingInfo& painting_info,
     PaintLayerFlags paint_flags) {
   PaintResult result = kFullyPainted;
   if (!paint_layer_.HasSelfPaintingLayerDescendant())
     return result;
-  if (!paint_layer_.StackingNode())
-    return result;
+
   if (paint_layer_.GetLayoutObject().PaintBlockedByDisplayLock(
           DisplayLockContext::kChildren)) {
     return result;
   }
 
-#if DCHECK_IS_ON()
-  LayerListMutationDetector mutation_checker(paint_layer_.StackingNode());
-#endif
-
-  PaintLayerStackingNodeIterator iterator(*paint_layer_.StackingNode(),
-                                          children_to_visit);
-  PaintLayer* child = iterator.Next();
-  if (!child)
-    return result;
-  for (; child; child = iterator.Next()) {
+  PaintLayerPaintOrderIterator iterator(paint_layer_, children_to_visit);
+  while (PaintLayer* child = iterator.Next()) {
     // If this Layer should paint into its own backing or a grouped backing,
     // that will be done via CompositedLayerMapping::PaintContents() and
     // CompositedLayerMapping::DoPaintTask().
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.h b/third_party/blink/renderer/core/paint/paint_layer_painter.h
index 7c48860..c16f278 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_PAINTER_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_fragment.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_painting_info.h"
 #include "third_party/blink/renderer/core/paint/paint_result.h"
@@ -17,7 +18,6 @@
 class ClipRect;
 class ComputedStyle;
 class DisplayItemClient;
-class PaintLayer;
 class GraphicsContext;
 struct PhysicalOffset;
 
@@ -60,7 +60,7 @@
  private:
   friend class PaintLayerPainterTest;
 
-  PaintResult PaintChildren(unsigned children_to_visit,
+  PaintResult PaintChildren(PaintLayerIteration children_to_visit,
                             GraphicsContext&,
                             const PaintLayerPaintingInfo&,
                             PaintLayerFlags);
diff --git a/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc b/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc
index fd5c114..7598786 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_stacking_node.cc
@@ -59,101 +59,66 @@
 // FIXME: This should not require PaintLayer. There is currently a cycle where
 // in order to determine if we isStacked() we have to ask the paint
 // layer about some of its state.
-PaintLayerStackingNode::PaintLayerStackingNode(PaintLayer* layer)
-    : layer_(layer)
-#if DCHECK_IS_ON()
-      ,
-      layer_list_mutation_allowed_(true)
-#endif
-{
-  // Non-stacking contexts should have empty z-order lists. As this is already
-  // the case, there is no need to dirty / recompute these lists.
-  z_order_lists_dirty_ =
-      layer->GetLayoutObject().StyleRef().IsStackingContext();
+PaintLayerStackingNode::PaintLayerStackingNode(PaintLayer& layer)
+    : layer_(layer), z_order_lists_dirty_(true) {
+  DCHECK(layer.GetLayoutObject().StyleRef().IsStackingContext());
 }
 
 PaintLayerStackingNode::~PaintLayerStackingNode() {
 #if DCHECK_IS_ON()
-  if (!Layer()->GetLayoutObject().DocumentBeingDestroyed())
+  if (!layer_.GetLayoutObject().DocumentBeingDestroyed())
     UpdateStackingParentForZOrderLists(nullptr);
 #endif
 }
 
-PaintLayerStackingNode::PaintLayers* PaintLayerStackingNode::PosZOrderList()
-    const {
-  DCHECK(!z_order_lists_dirty_);
-  DCHECK(Layer()->GetLayoutObject().StyleRef().IsStackingContext() ||
-         !pos_z_order_list_);
-  return pos_z_order_list_.get();
-}
-
-PaintLayerStackingNode::PaintLayers* PaintLayerStackingNode::NegZOrderList()
-    const {
-  DCHECK(!z_order_lists_dirty_);
-  DCHECK(Layer()->GetLayoutObject().StyleRef().IsStackingContext() ||
-         !neg_z_order_list_);
-  return neg_z_order_list_.get();
-}
-
-bool PaintLayerStackingNode::IsDirtyStackingContext() const {
-  return z_order_lists_dirty_ &&
-         Layer()->GetLayoutObject().StyleRef().IsStackingContext();
-}
-
 PaintLayerCompositor* PaintLayerStackingNode::Compositor() const {
-  DCHECK(Layer()->GetLayoutObject().View());
-  if (!Layer()->GetLayoutObject().View())
+  DCHECK(layer_.GetLayoutObject().View());
+  if (!layer_.GetLayoutObject().View())
     return nullptr;
-  return Layer()->GetLayoutObject().View()->Compositor();
+  return layer_.GetLayoutObject().View()->Compositor();
 }
 
 void PaintLayerStackingNode::DirtyZOrderLists() {
 #if DCHECK_IS_ON()
-  DCHECK(layer_list_mutation_allowed_);
-#endif
-  DCHECK(Layer()->GetLayoutObject().StyleRef().IsStackingContext());
-
-#if DCHECK_IS_ON()
+  DCHECK(layer_.LayerListMutationAllowed());
   UpdateStackingParentForZOrderLists(nullptr);
 #endif
 
-  if (pos_z_order_list_)
-    pos_z_order_list_->clear();
-  if (neg_z_order_list_)
-    neg_z_order_list_->clear();
+  pos_z_order_list_.clear();
+  neg_z_order_list_.clear();
   z_order_lists_dirty_ = true;
 
-  if (!Layer()->GetLayoutObject().DocumentBeingDestroyed() && Compositor())
+  if (!layer_.GetLayoutObject().DocumentBeingDestroyed() && Compositor())
     Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
 }
 
 void PaintLayerStackingNode::DirtyStackingContextZOrderLists(
-    PaintLayer* layer) {
-  if (PaintLayerStackingNode* stacking_node =
-          AncestorStackingContextNode(layer)) {
-    // This invalidation code intentionally refers to stale state.
-    DisableCompositingQueryAsserts disabler;
+    PaintLayer& layer) {
+  auto* stacking_context = layer.AncestorStackingContext();
+  if (!stacking_context)
+    return;
 
-    // Changes of stacking may result in graphics layers changing size
-    // due to new contents painting into them.
-    PaintLayer* ancestor_layer = stacking_node->Layer();
-    if (auto* mapping = ancestor_layer->GetCompositedLayerMapping()) {
-      mapping->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
-    }
+  // This invalidation code intentionally refers to stale state.
+  DisableCompositingQueryAsserts disabler;
 
-    stacking_node->DirtyZOrderLists();
-  }
+  // Changes of stacking may result in graphics layers changing size
+  // due to new contents painting into them.
+  if (auto* mapping = stacking_context->GetCompositedLayerMapping())
+    mapping->SetNeedsGraphicsLayerUpdate(kGraphicsLayerUpdateSubtree);
+
+  if (stacking_context->StackingNode())
+    stacking_context->StackingNode()->DirtyZOrderLists();
 }
 
 void PaintLayerStackingNode::RebuildZOrderLists() {
 #if DCHECK_IS_ON()
-  DCHECK(layer_list_mutation_allowed_);
+  DCHECK(layer_.LayerListMutationAllowed());
 #endif
-  DCHECK(IsDirtyStackingContext());
+  DCHECK(z_order_lists_dirty_);
 
-  for (PaintLayer* child = Layer()->FirstChild(); child;
+  for (PaintLayer* child = layer_.FirstChild(); child;
        child = child->NextSibling())
-    CollectLayers(child, pos_z_order_list_, neg_z_order_list_);
+    CollectLayers(*child);
 
   auto CompareZIndex = [](PaintLayer* first, PaintLayer* second) {
     return first->GetLayoutObject().StyleRef().ZIndex() <
@@ -161,20 +126,17 @@
   };
 
   // Sort the two lists.
-  if (pos_z_order_list_)
-    std::stable_sort(pos_z_order_list_->begin(), pos_z_order_list_->end(),
-                     CompareZIndex);
-
-  if (neg_z_order_list_)
-    std::stable_sort(neg_z_order_list_->begin(), neg_z_order_list_->end(),
-                     CompareZIndex);
+  std::stable_sort(pos_z_order_list_.begin(), pos_z_order_list_.end(),
+                   CompareZIndex);
+  std::stable_sort(neg_z_order_list_.begin(), neg_z_order_list_.end(),
+                   CompareZIndex);
 
   // Append layers for top layer elements after normal layer collection, to
   // ensure they are on top regardless of z-indexes.  The layoutObjects of top
   // layer elements are children of the view, sorted in top layer stacking
   // order.
-  if (Layer()->IsRootLayer()) {
-    LayoutBlockFlow* root_block = Layer()->GetLayoutObject().View();
+  if (layer_.IsRootLayer()) {
+    LayoutBlockFlow* root_block = layer_.GetLayoutObject().View();
     // If the viewport is paginated, everything (including "top-layer" elements)
     // gets redirected to the flow thread. So that's where we have to look, in
     // that case.
@@ -187,15 +149,9 @@
           (child->GetNode() && child->GetNode()->IsElementNode())
               ? ToElement(child->GetNode())
               : nullptr;
-      if (child_element && child_element->IsInTopLayer()) {
-        PaintLayer* layer = ToLayoutBoxModelObject(child)->Layer();
-        if (layer->StackingNode()) {
-          // Create the buffer if it doesn't exist yet.
-          if (!pos_z_order_list_) {
-            pos_z_order_list_ = std::make_unique<PaintLayers>();
-          }
-          pos_z_order_list_->push_back(layer);
-        }
+      if (child_element && child_element->IsInTopLayer() &&
+          child->StyleRef().IsStacked()) {
+        pos_z_order_list_.push_back(ToLayoutBoxModelObject(child)->Layer());
       }
     }
   }
@@ -207,48 +163,37 @@
   z_order_lists_dirty_ = false;
 }
 
-void PaintLayerStackingNode::CollectLayers(
-    PaintLayer* paint_layer,
-    std::unique_ptr<PaintLayers>& pos_buffer,
-    std::unique_ptr<PaintLayers>& neg_buffer) {
-  if (paint_layer->IsInTopLayer())
+void PaintLayerStackingNode::CollectLayers(PaintLayer& paint_layer) {
+  if (paint_layer.IsInTopLayer())
     return;
 
-  const ComputedStyle& style = paint_layer->GetLayoutObject().StyleRef();
+  const ComputedStyle& style = paint_layer.GetLayoutObject().StyleRef();
 
   if (style.IsStacked()) {
-    std::unique_ptr<PaintLayers>& buffer =
-        (style.ZIndex() >= 0) ? pos_buffer : neg_buffer;
-    if (!buffer)
-      buffer = std::make_unique<PaintLayers>();
-    buffer->push_back(paint_layer);
+    auto& list = style.ZIndex() >= 0 ? pos_z_order_list_ : neg_z_order_list_;
+    list.push_back(&paint_layer);
   }
 
-  if (!style.IsStackingContext()) {
-    for (PaintLayer* child = paint_layer->FirstChild(); child;
-         child = child->NextSibling()) {
-      CollectLayers(child, pos_buffer, neg_buffer);
-    }
-  }
+  if (style.IsStackingContext())
+    return;
+
+  for (PaintLayer* child = paint_layer.FirstChild(); child;
+       child = child->NextSibling())
+    CollectLayers(*child);
 }
 
 #if DCHECK_IS_ON()
 void PaintLayerStackingNode::UpdateStackingParentForZOrderLists(
     PaintLayerStackingNode* stacking_parent) {
-  if (pos_z_order_list_) {
-    for (wtf_size_t i = 0; i < pos_z_order_list_->size(); ++i)
-      pos_z_order_list_->at(i)->SetStackingParent(stacking_parent);
-  }
-
-  if (neg_z_order_list_) {
-    for (wtf_size_t i = 0; i < neg_z_order_list_->size(); ++i)
-      neg_z_order_list_->at(i)->SetStackingParent(stacking_parent);
-  }
+  for (auto* layer : pos_z_order_list_)
+    layer->SetStackingParent(stacking_parent);
+  for (auto* layer : neg_z_order_list_)
+    layer->SetStackingParent(stacking_parent);
 }
 
 #endif
 
-bool PaintLayerStackingNode::StyleDidChange(PaintLayer* paint_layer,
+bool PaintLayerStackingNode::StyleDidChange(PaintLayer& paint_layer,
                                             const ComputedStyle* old_style) {
   bool was_stacking_context = false;
   bool was_stacked = false;
@@ -259,7 +204,7 @@
     was_stacked = old_style->IsStacked();
   }
 
-  const ComputedStyle& new_style = paint_layer->GetLayoutObject().StyleRef();
+  const ComputedStyle& new_style = paint_layer.GetLayoutObject().StyleRef();
 
   bool should_be_stacking_context = new_style.IsStackingContext();
   bool should_be_stacked = new_style.IsStacked();
@@ -268,58 +213,25 @@
     return false;
 
   // Need to force requirements update, due to change of stacking order.
-  paint_layer->SetNeedsCompositingRequirementsUpdate();
+  paint_layer.SetNeedsCompositingRequirementsUpdate();
   DirtyStackingContextZOrderLists(paint_layer);
 
-  if (paint_layer->StackingNode()) {
-    if (should_be_stacking_context)
-      paint_layer->StackingNode()->DirtyZOrderLists();
-    else
-      paint_layer->StackingNode()->ClearZOrderLists();
-  }
+  if (paint_layer.StackingNode())
+    paint_layer.StackingNode()->DirtyZOrderLists();
 
   if (was_stacked != should_be_stacked) {
-    if (!paint_layer->GetLayoutObject().DocumentBeingDestroyed() &&
-        !paint_layer->IsRootLayer() && paint_layer->Compositor()) {
-      paint_layer->Compositor()->SetNeedsCompositingUpdate(
+    if (!paint_layer.GetLayoutObject().DocumentBeingDestroyed() &&
+        !paint_layer.IsRootLayer() && paint_layer.Compositor()) {
+      paint_layer.Compositor()->SetNeedsCompositingUpdate(
           kCompositingUpdateRebuildTree);
     }
   }
   return true;
 }
 
-PaintLayerStackingNode* PaintLayerStackingNode::AncestorStackingContextNode(
-    const PaintLayer* layer) {
-  for (PaintLayer* ancestor = layer->Parent(); ancestor;
-       ancestor = ancestor->Parent()) {
-    if (ancestor->GetLayoutObject().StyleRef().IsStackingContext())
-      return ancestor->StackingNode();
-  }
-  return nullptr;
-}
-
-void PaintLayerStackingNode::ClearZOrderLists() {
-  DCHECK(!Layer()->GetLayoutObject().StyleRef().IsStackingContext());
-
-#if DCHECK_IS_ON()
-  UpdateStackingParentForZOrderLists(nullptr);
-#endif
-
-  pos_z_order_list_.reset();
-  neg_z_order_list_.reset();
-}
-
 void PaintLayerStackingNode::UpdateZOrderLists() {
-  if (!z_order_lists_dirty_)
-    return;
-
-  if (!Layer()->GetLayoutObject().StyleRef().IsStackingContext()) {
-    ClearZOrderLists();
-    z_order_lists_dirty_ = false;
-    return;
-  }
-
-  RebuildZOrderLists();
+  if (z_order_lists_dirty_)
+    RebuildZOrderLists();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h b/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
index 3439dec..fb9288f4 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
@@ -58,6 +58,9 @@
 class PaintLayerCompositor;
 class ComputedStyle;
 
+// This class is only for PaintLayer, PaintLayerPaintOrderIterator and
+// PaintLayerPaintOrderReverseIterator. Other classes should not use this class.
+//
 // PaintLayerStackingNode represents a stacked element which is either a
 // stacking context or a positioned element.
 // See
@@ -74,131 +77,70 @@
 //
 // Stacked elements form a subtree over the layout tree. Ideally we would want
 // objects of this class to be a node in this tree but there are potential
-// issues with stale pointers so we rely on PaintLayer's tree
-// structure.
+// issues with stale pointers so we rely on PaintLayer's tree structure.
 //
 // This class's purpose is to represent a node in the stacked element tree
 // (aka paint tree). It currently caches the z-order lists for painting and
 // hit-testing.
 //
-// To implement any z-order list iterations, use
-// PaintLayerStackingNodeIterator and
-// PaintLayerStackingNodeReverseIterator.
+// To implement any paint order iterations, use PaintLayerPaintOrderIterator and
+// PaintLayerZOrderReverseIterator.
 //
-// Only a real stacking context can have non-empty z-order lists thus contain
-// child nodes in the tree. The z-order lists of a positioned element with auto
-// z-index are always empty (i.e. it's a leaf of the stacked element tree).
-// A real stacking context can also be a leaf if it doesn't contain any stacked
-// elements.
+// We create PaintLayerStackingNode only for real stacking contexts with stacked
+// children. PaintLayerPaintOrder[Reverse]Iterator can iterate normal flow
+// children in paint order with or without a stacking node.
 class CORE_EXPORT PaintLayerStackingNode {
   USING_FAST_MALLOC(PaintLayerStackingNode);
 
  public:
-  explicit PaintLayerStackingNode(PaintLayer*);
+  explicit PaintLayerStackingNode(PaintLayer&);
   ~PaintLayerStackingNode();
 
-  bool ZOrderListsDirty() const { return z_order_lists_dirty_; }
-  void DirtyZOrderLists();
   void UpdateZOrderLists();
-  void ClearZOrderLists();
-  static void DirtyStackingContextZOrderLists(PaintLayer*);
-
-  bool HasPositiveZOrderList() const {
-    return PosZOrderList() && PosZOrderList()->size();
-  }
-  bool HasNegativeZOrderList() const {
-    return NegZOrderList() && NegZOrderList()->size();
-  }
+  static void DirtyStackingContextZOrderLists(PaintLayer&);
 
   // Returns whether a relevant style changed.
-  static bool StyleDidChange(PaintLayer* paint_layer,
+  static bool StyleDidChange(PaintLayer& paint_layer,
                              const ComputedStyle* old_style);
 
-  PaintLayer* Layer() const { return layer_; }
-
-#if DCHECK_IS_ON()
-  bool LayerListMutationAllowed() const { return layer_list_mutation_allowed_; }
-  void SetLayerListMutationAllowed(bool flag) {
-    layer_list_mutation_allowed_ = flag;
-  }
-#endif
-
-  static PaintLayerStackingNode* AncestorStackingContextNode(const PaintLayer*);
   using PaintLayers = Vector<PaintLayer*>;
 
+  const PaintLayers& PosZOrderList() const {
+    DCHECK(!z_order_lists_dirty_);
+    return pos_z_order_list_;
+  }
+  const PaintLayers& NegZOrderList() const {
+    DCHECK(!z_order_lists_dirty_);
+    return neg_z_order_list_;
+  }
+
  private:
-  friend class PaintLayerStackingNodeIterator;
-  friend class PaintLayerStackingNodeReverseIterator;
-  friend class LayoutTreeAsText;
-
-#if DCHECK_IS_ON()
-  friend class PaintLayer;
-#endif
-
-  PaintLayers* PosZOrderList() const;
-
-  PaintLayers* NegZOrderList() const;
-
+  void DirtyZOrderLists();
   void RebuildZOrderLists();
 
-  static void CollectLayers(PaintLayer*,
-                            std::unique_ptr<PaintLayers>& pos_z_order_list,
-                            std::unique_ptr<PaintLayers>& neg_z_order_list);
+  void CollectLayers(PaintLayer&);
 
 #if DCHECK_IS_ON()
   void UpdateStackingParentForZOrderLists(
       PaintLayerStackingNode* stacking_parent);
 #endif
 
-  bool IsDirtyStackingContext() const;
-
   PaintLayerCompositor* Compositor() const;
 
-  PaintLayer* layer_;
+  PaintLayer& layer_;
 
-  // m_posZOrderList holds a sorted list of all the descendant nodes within
-  // that have z-indices of 0 (or is treated as 0 for positioned objects) or
-  // greater.
-  std::unique_ptr<PaintLayers> pos_z_order_list_;
-  // m_negZOrderList holds descendants within our stacking context with
-  // negative z-indices.
-  std::unique_ptr<PaintLayers> neg_z_order_list_;
+  // Holds a sorted list of all the descendant nodes within that have z-indices
+  // of 0 (or is treated as 0 for positioned objects) or greater.
+  PaintLayers pos_z_order_list_;
+  // Holds descendants within our stacking context with negative z-indices.
+  PaintLayers neg_z_order_list_;
 
-  // This boolean caches whether the z-order lists above are dirty.
-  // It is only ever set for stacking contexts, as no other element can
-  // have z-order lists.
+  // Indicates whether the z-order lists above are dirty.
   bool z_order_lists_dirty_ : 1;
 
-#if DCHECK_IS_ON()
-  bool layer_list_mutation_allowed_ : 1;
-#endif
-
   DISALLOW_COPY_AND_ASSIGN(PaintLayerStackingNode);
 };
 
-#if DCHECK_IS_ON()
-class LayerListMutationDetector {
-  STACK_ALLOCATED();
-
- public:
-  explicit LayerListMutationDetector(PaintLayerStackingNode* stacking_node)
-      : stacking_node_(stacking_node),
-        previous_mutation_allowed_state_(
-            stacking_node->LayerListMutationAllowed()) {
-    stacking_node_->SetLayerListMutationAllowed(false);
-  }
-
-  ~LayerListMutationDetector() {
-    stacking_node_->SetLayerListMutationAllowed(
-        previous_mutation_allowed_state_);
-  }
-
- private:
-  PaintLayerStackingNode* stacking_node_;
-  bool previous_mutation_allowed_state_;
-};
-#endif
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_STACKING_NODE_H_
diff --git a/third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.cc b/third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.cc
deleted file mode 100644
index 076e289..0000000
--- a/third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.h"
-
-// FIXME: We should build our primitive on top of
-// PaintLayerStackingNode and remove this include.
-#include "third_party/blink/renderer/core/paint/paint_layer.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_stacking_node.h"
-
-namespace blink {
-
-PaintLayerStackingNodeIterator::PaintLayerStackingNodeIterator(
-    const PaintLayerStackingNode& root,
-    unsigned which_children)
-    : root_(root), remaining_children_(which_children), index_(0) {
-  current_normal_flow_child_ = root.Layer()->FirstChild();
-}
-
-PaintLayer* PaintLayerStackingNodeIterator::Next() {
-  if (remaining_children_ & kNegativeZOrderChildren) {
-    PaintLayerStackingNode::PaintLayers* neg_z_order_list =
-        root_.NegZOrderList();
-    if (neg_z_order_list && index_ < neg_z_order_list->size())
-      return neg_z_order_list->at(index_++);
-
-    index_ = 0;
-    remaining_children_ &= ~kNegativeZOrderChildren;
-  }
-
-  if (remaining_children_ & kNormalFlowChildren) {
-    for (; current_normal_flow_child_;
-         current_normal_flow_child_ =
-             current_normal_flow_child_->NextSibling()) {
-      if (!current_normal_flow_child_->GetLayoutObject()
-               .StyleRef()
-               .IsStacked()) {
-        PaintLayer* normal_flow_child = current_normal_flow_child_;
-        current_normal_flow_child_ = current_normal_flow_child_->NextSibling();
-        return normal_flow_child;
-      }
-    }
-
-    // We reset the iterator in case we reuse it.
-    current_normal_flow_child_ = root_.Layer()->FirstChild();
-    remaining_children_ &= ~kNormalFlowChildren;
-  }
-
-  if (remaining_children_ & kPositiveZOrderChildren) {
-    PaintLayerStackingNode::PaintLayers* pos_z_order_list =
-        root_.PosZOrderList();
-    if (pos_z_order_list && index_ < pos_z_order_list->size())
-      return pos_z_order_list->at(index_++);
-
-    index_ = 0;
-    remaining_children_ &= ~kPositiveZOrderChildren;
-  }
-
-  return nullptr;
-}
-
-PaintLayer* PaintLayerStackingNodeReverseIterator::Next() {
-  if (remaining_children_ & kNegativeZOrderChildren) {
-    PaintLayerStackingNode::PaintLayers* neg_z_order_list =
-        root_.NegZOrderList();
-    if (neg_z_order_list && index_ >= 0)
-      return neg_z_order_list->at(index_--);
-
-    remaining_children_ &= ~kNegativeZOrderChildren;
-    SetIndexToLastItem();
-  }
-
-  if (remaining_children_ & kNormalFlowChildren) {
-    for (; current_normal_flow_child_;
-         current_normal_flow_child_ =
-             current_normal_flow_child_->PreviousSibling()) {
-      if (!current_normal_flow_child_->GetLayoutObject()
-               .StyleRef()
-               .IsStacked()) {
-        PaintLayer* normal_flow_child = current_normal_flow_child_;
-        current_normal_flow_child_ =
-            current_normal_flow_child_->PreviousSibling();
-        return normal_flow_child;
-      }
-    }
-
-    remaining_children_ &= ~kNormalFlowChildren;
-    SetIndexToLastItem();
-  }
-
-  if (remaining_children_ & kPositiveZOrderChildren) {
-    PaintLayerStackingNode::PaintLayers* pos_z_order_list =
-        root_.PosZOrderList();
-    if (pos_z_order_list && index_ >= 0)
-      return pos_z_order_list->at(index_--);
-
-    remaining_children_ &= ~kPositiveZOrderChildren;
-    SetIndexToLastItem();
-  }
-
-  return nullptr;
-}
-
-void PaintLayerStackingNodeReverseIterator::SetIndexToLastItem() {
-  if (remaining_children_ & kNegativeZOrderChildren) {
-    PaintLayerStackingNode::PaintLayers* neg_z_order_list =
-        root_.NegZOrderList();
-    if (neg_z_order_list) {
-      index_ = neg_z_order_list->size() - 1;
-      return;
-    }
-
-    remaining_children_ &= ~kNegativeZOrderChildren;
-  }
-
-  if (remaining_children_ & kNormalFlowChildren) {
-    current_normal_flow_child_ = root_.Layer()->LastChild();
-    return;
-  }
-
-  if (remaining_children_ & kPositiveZOrderChildren) {
-    PaintLayerStackingNode::PaintLayers* pos_z_order_list =
-        root_.PosZOrderList();
-    if (pos_z_order_list) {
-      index_ = pos_z_order_list->size() - 1;
-      return;
-    }
-
-    remaining_children_ &= ~kPositiveZOrderChildren;
-  }
-
-  // No more list to visit.
-  DCHECK(!remaining_children_);
-  index_ = -1;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.h b/third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.h
deleted file mode 100644
index a002e2cf..0000000
--- a/third_party/blink/renderer/core/paint/paint_layer_stacking_node_iterator.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_STACKING_NODE_ITERATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_STACKING_NODE_ITERATOR_H_
-
-#include "base/macros.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-enum ChildrenIteration {
-  kNegativeZOrderChildren = 1,
-  // Normal flow children are not mandated by CSS 2.1 but are an artifact of
-  // our implementation: we allocate PaintLayers for elements that
-  // are not treated as stacking contexts and thus we need to walk them
-  // during painting and hit-testing.
-  kNormalFlowChildren = 1 << 1,
-  kPositiveZOrderChildren = 1 << 2,
-  kAllChildren =
-      kNegativeZOrderChildren | kNormalFlowChildren | kPositiveZOrderChildren
-};
-
-class PaintLayerStackingNode;
-class PaintLayer;
-
-// This iterator walks the PaintLayerStackingNode lists in the following order:
-// NegativeZOrderChildren -> NormalFlowChildren -> PositiveZOrderChildren.
-class PaintLayerStackingNodeIterator {
-  STACK_ALLOCATED();
-
- public:
-  PaintLayerStackingNodeIterator(const PaintLayerStackingNode& root,
-                                 unsigned which_children);
-
-  PaintLayer* Next();
-
- private:
-  const PaintLayerStackingNode& root_;
-  unsigned remaining_children_;
-  unsigned index_;
-  PaintLayer* current_normal_flow_child_;
-  DISALLOW_COPY_AND_ASSIGN(PaintLayerStackingNodeIterator);
-};
-
-// This iterator is similar to PaintLayerStackingNodeIterator but it walks the
-// lists in reverse order (from the last item to the first one).
-class PaintLayerStackingNodeReverseIterator {
-  STACK_ALLOCATED();
-
- public:
-  PaintLayerStackingNodeReverseIterator(const PaintLayerStackingNode& root,
-                                        unsigned which_children)
-      : root_(root), remaining_children_(which_children) {
-    SetIndexToLastItem();
-  }
-
-  PaintLayer* Next();
-
- private:
-  void SetIndexToLastItem();
-
-  const PaintLayerStackingNode& root_;
-  unsigned remaining_children_;
-  int index_;
-  PaintLayer* current_normal_flow_child_;
-  DISALLOW_COPY_AND_ASSIGN(PaintLayerStackingNodeReverseIterator);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_STACKING_NODE_ITERATOR_H_
diff --git a/third_party/blink/renderer/core/paint/paint_layer_test.cc b/third_party/blink/renderer/core/paint/paint_layer_test.cc
index 386a489..2bc5d6b 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_test.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_paint_order_iterator.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
@@ -588,15 +589,19 @@
 
   PaintLayer* target = GetPaintLayerByElementId("target");
 
-  EXPECT_TRUE(target->StackingNode()->HasNegativeZOrderList());
-  EXPECT_FALSE(target->StackingNode()->HasPositiveZOrderList());
+  EXPECT_TRUE(
+      PaintLayerPaintOrderIterator(*target, kNegativeZOrderChildren).Next());
+  EXPECT_FALSE(
+      PaintLayerPaintOrderIterator(*target, kPositiveZOrderChildren).Next());
 
   GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr,
                                                       "z-index: 1");
   UpdateAllLifecyclePhasesForTest();
 
-  EXPECT_FALSE(target->StackingNode()->HasNegativeZOrderList());
-  EXPECT_TRUE(target->StackingNode()->HasPositiveZOrderList());
+  EXPECT_FALSE(
+      PaintLayerPaintOrderIterator(*target, kNegativeZOrderChildren).Next());
+  EXPECT_TRUE(
+      PaintLayerPaintOrderIterator(*target, kPositiveZOrderChildren).Next());
 }
 
 TEST_P(PaintLayerTest, HasDescendantWithClipPath) {
diff --git a/third_party/blink/renderer/core/script/layered_api_resources.h b/third_party/blink/renderer/core/script/layered_api_resources.h
index 3f2c821..ccd501c 100644
--- a/third_party/blink/renderer/core/script/layered_api_resources.h
+++ b/third_party/blink/renderer/core/script/layered_api_resources.h
@@ -29,8 +29,12 @@
 const LayeredAPIResource kLayeredAPIResources[] = {
     {"blank/index.mjs", IDR_LAYERED_API_BLANK_INDEX_MJS, Module::kBlank},
 
+    {"elements/switch/face_utils.mjs",
+     IDR_LAYERED_API_ELEMENTS_SWITCH_FACE_UTILS_MJS, Module::kElementsSwitch},
     {"elements/switch/index.mjs", IDR_LAYERED_API_ELEMENTS_SWITCH_INDEX_MJS,
      Module::kElementsSwitch},
+    {"elements/switch/reflection.mjs",
+     IDR_LAYERED_API_ELEMENTS_SWITCH_REFLECTION_MJS, Module::kElementsSwitch},
 
     {"kv-storage/async_iterator.mjs",
      IDR_LAYERED_API_KV_STORAGE_ASYNC_ITERATOR_MJS, Module::kKvStorage},
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/face_utils.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/face_utils.mjs
new file mode 100644
index 0000000..f8e4685d
--- /dev/null
+++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/face_utils.mjs
@@ -0,0 +1,75 @@
+// 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.
+
+/**
+ * @file Utilities for form-associated custom elements
+ */
+
+import * as reflection from './reflection.mjs';
+
+function installGetter(proto, propName, getter) {
+  Object.defineProperty(
+      getter, 'name',
+      {configurable: true, enumerable: false, value: 'get ' + propName});
+  Object.defineProperty(
+      proto, propName, {configurable: true, enumerable: true, get: getter});
+}
+
+/**
+ * Add the following properties to |proto|.
+ *   - disabled
+ *   - name
+ *   - type
+ *   - form
+ *   - willValidate
+ *   - validity
+ *   - validationMessage
+ *   - labels
+ *   - checkValidity()
+ *   - reportValidity()
+ *   - setCustomValidity(error)
+ *
+ * @param {!Object} proto An Element prototype which will have properties
+ * @param {!Symbol} internals A Symbol of the ElementInternals property of the
+ *     element
+ */
+export function installPropertiesAndFunctions(proto, internals) {
+  reflection.installBool(proto, 'disabled');
+  reflection.installString(proto, 'name');
+  installGetter(proto, 'type', function() {
+    if (!(this instanceof proto.constructor)) {
+      throw TypeError(
+          'The context object is not an instance of ' + proto.contructor.name);
+    }
+    return this.localName;
+  });
+
+  installGetter(proto, 'form', function() {
+    return this[internals].form;
+  });
+  installGetter(proto, 'willValidate', function() {
+    return this[internals].willValidate;
+  });
+  installGetter(proto, 'validity', function() {
+    return this[internals].validity;
+  });
+  installGetter(proto, 'validationMessage', function() {
+    return this[internals].validationMessage;
+  });
+  installGetter(proto, 'labels', function() {
+    return this[internals].labels;
+  });
+  proto.checkValidity = function() {
+    return this[internals].checkValidity();
+  };
+  proto.reportValidity = function() {
+    return this[internals].reportValidity();
+  };
+  proto.setCustomValidity = function(error) {
+    if (error === undefined) {
+      throw new TypeError('Too few arguments');
+    }
+    this[internals].setValidity({customError: true}, error);
+  };
+}
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs
index 062f30e..1fe4608 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs
+++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs
@@ -2,5 +2,39 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// TODO(tkent): Add real code.
+import * as face from './face_utils.mjs';
+import * as reflection from './reflection.mjs';
 
+// https://github.com/tkent-google/std-switch/issues/2
+const STATE_ATTR = 'on';
+
+// Private property symbols
+// TODO(tkent): Use a private field.
+const _internals = Symbol();
+
+export class StdSwitchElement extends HTMLElement {
+  static get formAssociated() {
+    return true;
+  }
+
+  constructor() {
+    super();
+    this[_internals] = this.attachInternals();
+  }
+}
+
+reflection.installBool(StdSwitchElement.prototype, STATE_ATTR);
+reflection.installBool(
+    StdSwitchElement.prototype, 'default' + STATE_ATTR,
+    'default' + STATE_ATTR.charAt(0).toUpperCase() + STATE_ATTR.substring(1));
+face.installPropertiesAndFunctions(StdSwitchElement.prototype, _internals);
+
+// This is necessary for anyObject.toString.call(switchInstance).
+Object.defineProperty(StdSwitchElement.prototype, Symbol.toStringTag, {
+  configurable: true,
+  enumerable: false,
+  value: 'StdSwitchElement',
+  writable: false
+});
+
+customElements.define('std-switch', StdSwitchElement);
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/reflection.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/reflection.mjs
new file mode 100644
index 0000000..989a4aa
--- /dev/null
+++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/reflection.mjs
@@ -0,0 +1,63 @@
+// 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.
+
+/**
+ * @file Manage attribute-property reflections.
+ *     https://html.spec.whatwg.org/C/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes
+ */
+
+/**
+ * Add a bool reflection property to the specified prototype for the specified
+ * attribute.
+ *
+ * @param {!Object} proto An element prototype
+ * @param {string} attrName An attribute name
+ * @param {string} propName An optional property name. attrName will be used if
+ *     this argument is omitted.
+ */
+export function installBool(proto, attrName, propName = attrName) {
+  let getter = function() {
+    return this.hasAttribute(attrName);
+  };
+  let setter = function(value) {
+    this.toggleAttribute(attrName, Boolean(value));
+  };
+  Object.defineProperty(
+      getter, 'name',
+      {configurable: true, enumerable: false, value: 'get ' + propName});
+  Object.defineProperty(
+      setter, 'name',
+      {configurable: true, enumerable: false, value: 'set ' + propName});
+  Object.defineProperty(
+      proto, propName,
+      {configurable: true, enumerable: true, get: getter, set: setter});
+}
+
+/**
+ * Add a DOMString reflection property to the specified prototype for the
+ * specified attribute.
+ *
+ * @param {!Element} element An element prototype
+ * @param {string} attrName An attribute name
+ * @param {string} propName An optional property name. attrName will be used if
+ *     this argument is omitted.
+ */
+export function installString(proto, attrName, propName = attrName) {
+  let getter = function() {
+    let value = this.getAttribute(attrName);
+    return value === null ? '' : value;
+  };
+  let setter = function(value) {
+    this.setAttribute(attrName, value);
+  };
+  Object.defineProperty(
+      getter, 'name',
+      {configurable: true, enumerable: false, value: 'get ' + propName});
+  Object.defineProperty(
+      setter, 'name',
+      {configurable: true, enumerable: false, value: 'set ' + propName});
+  Object.defineProperty(
+      proto, propName,
+      {configurable: true, enumerable: true, get: getter, set: setter});
+}
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/resources.grdp b/third_party/blink/renderer/core/script/resources/layered_api/resources.grdp
index 7b97d28..1c8bb89 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/resources.grdp
+++ b/third_party/blink/renderer/core/script/resources/layered_api/resources.grdp
@@ -7,7 +7,9 @@
        third_party/blink/public/blink_resources.grd.
     -->
   <include name="IDR_LAYERED_API_BLANK_INDEX_MJS" file="../renderer/core/script/resources/layered_api/blank/index.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
+  <include name="IDR_LAYERED_API_ELEMENTS_SWITCH_FACE_UTILS_MJS" file="../renderer/core/script/resources/layered_api/elements/switch/face_utils.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
   <include name="IDR_LAYERED_API_ELEMENTS_SWITCH_INDEX_MJS" file="../renderer/core/script/resources/layered_api/elements/switch/index.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
+  <include name="IDR_LAYERED_API_ELEMENTS_SWITCH_REFLECTION_MJS" file="../renderer/core/script/resources/layered_api/elements/switch/reflection.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
   <include name="IDR_LAYERED_API_KV_STORAGE_ASYNC_ITERATOR_MJS" file="../renderer/core/script/resources/layered_api/kv-storage/async_iterator.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
   <include name="IDR_LAYERED_API_KV_STORAGE_IDB_UTILS_MJS" file="../renderer/core/script/resources/layered_api/kv-storage/idb_utils.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
   <include name="IDR_LAYERED_API_KV_STORAGE_INDEX_MJS" file="../renderer/core/script/resources/layered_api/kv-storage/index.mjs" type="BINDATA" skip_minify="true" compress="gzip"/>
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/toast/index.mjs b/third_party/blink/renderer/core/script/resources/layered_api/toast/index.mjs
index d79d4e4..36d24e5e 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/toast/index.mjs
+++ b/third_party/blink/renderer/core/script/resources/layered_api/toast/index.mjs
@@ -50,14 +50,14 @@
   #shadow = this.attachShadow({mode: 'closed'});
   #timeoutID;
 
-  constructor(message = '') {
+  constructor(message) {
     super();
 
     this.#shadow.adoptedStyleSheets = [generateStylesheet()];
 
     this.#shadow.innerHTML = `<slot></slot>`;
-    if (!this.textContent) {
-      this.textContent = `${message}`;
+    if (message !== undefined) {
+      this.textContent = message;
     }
   }
 
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
index e9af136..c0e5ab30 100644
--- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -113,6 +113,14 @@
       keywords: ["not-inside-link", "inside-unvisited-link", "inside-visited-link"],
       inherited: true,
     },
+    // Whether we're inside an NG block fragmentation context.
+    {
+      name: "InsideNGFragmentationContext",
+      field_template: "primitive",
+      default_value: "false",
+      type_name: "bool",
+      inherited: true,
+    },
     // Style has content property with attr() values.
     {
       name: "HasAttrContent",
diff --git a/third_party/blink/renderer/modules/background_sync/sync_manager.cc b/third_party/blink/renderer/modules/background_sync/sync_manager.cc
index 8e13b62d..311b6d4 100644
--- a/third_party/blink/renderer/modules/background_sync/sync_manager.cc
+++ b/third_party/blink/renderer/modules/background_sync/sync_manager.cc
@@ -53,7 +53,7 @@
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
 
-  GetBackgroundSyncServicePtr()->GetOneShotSyncRegistrations(
+  GetBackgroundSyncServicePtr()->GetRegistrations(
       registration_->RegistrationId(),
       WTF::Bind(&SyncManager::GetRegistrationsCallback,
                 WrapPersistent(resolver)));
@@ -61,7 +61,7 @@
   return promise;
 }
 
-const mojom::blink::BackgroundSyncServicePtr&
+const mojom::blink::OneShotBackgroundSyncServicePtr&
 SyncManager::GetBackgroundSyncServicePtr() {
   if (!background_sync_service_.get()) {
     Platform::Current()->GetInterfaceProvider()->GetInterface(
@@ -133,7 +133,7 @@
     case mojom::blink::BackgroundSyncError::NOT_ALLOWED:
     case mojom::blink::BackgroundSyncError::PERMISSION_DENIED:
       // These errors should never be returned from
-      // BackgroundSyncManager::GetOneShotSyncRegistrations
+      // BackgroundSyncManager::GetRegistrations
       NOTREACHED();
       break;
     case mojom::blink::BackgroundSyncError::STORAGE:
diff --git a/third_party/blink/renderer/modules/background_sync/sync_manager.h b/third_party/blink/renderer/modules/background_sync/sync_manager.h
index 5eedef11..a7d5356 100644
--- a/third_party/blink/renderer/modules/background_sync/sync_manager.h
+++ b/third_party/blink/renderer/modules/background_sync/sync_manager.h
@@ -39,10 +39,11 @@
   enum { kUnregisteredSyncID = -1 };
 
  private:
-  // Returns an initialized BackgroundSyncServicePtr. A connection with the
-  // the browser's BackgroundSyncService is created the first time this method
-  // is called.
-  const mojom::blink::BackgroundSyncServicePtr& GetBackgroundSyncServicePtr();
+  // Returns an initialized OneShotBackgroundSyncServicePtr. A connection with
+  // the browser's OneShotBackgroundSyncService is created the first time this
+  // method is called.
+  const mojom::blink::OneShotBackgroundSyncServicePtr&
+  GetBackgroundSyncServicePtr();
 
   // Callbacks
   void RegisterCallback(ScriptPromiseResolver*,
@@ -55,7 +56,7 @@
 
   Member<ServiceWorkerRegistration> registration_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  mojom::blink::BackgroundSyncServicePtr background_sync_service_;
+  mojom::blink::OneShotBackgroundSyncServicePtr background_sync_service_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/native_file_system/file_system_directory_handle.idl b/third_party/blink/renderer/modules/native_file_system/file_system_directory_handle.idl
index ca9e5b0..02dbcf2 100644
--- a/third_party/blink/renderer/modules/native_file_system/file_system_directory_handle.idl
+++ b/third_party/blink/renderer/modules/native_file_system/file_system_directory_handle.idl
@@ -4,6 +4,7 @@
 
 // https://wicg.github.io/native-file-system/#filesystemdirectoryhandle
 [
+    Exposed=(Window,Worker),
     RuntimeEnabled=NativeFileSystem,
     ImplementedAs=NativeFileSystemDirectoryHandle
 ] interface FileSystemDirectoryHandle : FileSystemHandle {
diff --git a/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl b/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl
index e432309..bd4170e7 100644
--- a/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl
+++ b/third_party/blink/renderer/modules/native_file_system/file_system_file_handle.idl
@@ -4,6 +4,7 @@
 
 // https://wicg.github.io/native-file-system/#filesystemfilehandle
 [
+    Exposed=(Window,Worker),
     RuntimeEnabled=NativeFileSystem,
     ImplementedAs=NativeFileSystemFileHandle
 ] interface FileSystemFileHandle : FileSystemHandle {
diff --git a/third_party/blink/renderer/modules/native_file_system/file_system_handle.idl b/third_party/blink/renderer/modules/native_file_system/file_system_handle.idl
index 3572ab7..c96dfd6 100644
--- a/third_party/blink/renderer/modules/native_file_system/file_system_handle.idl
+++ b/third_party/blink/renderer/modules/native_file_system/file_system_handle.idl
@@ -4,6 +4,7 @@
 
 // https://wicg.github.io/native-file-system/#filesystemhandle
 [
+    Exposed=(Window,Worker),
     RuntimeEnabled=NativeFileSystem,
     ImplementedAs=NativeFileSystemHandle
 ] interface FileSystemHandle {
diff --git a/third_party/blink/renderer/modules/native_file_system/file_system_writer.idl b/third_party/blink/renderer/modules/native_file_system/file_system_writer.idl
index 72a5dcb..52ebb44e 100644
--- a/third_party/blink/renderer/modules/native_file_system/file_system_writer.idl
+++ b/third_party/blink/renderer/modules/native_file_system/file_system_writer.idl
@@ -4,6 +4,7 @@
 
 // https://wicg.github.io/native-file-system/#filesystemwriter
 [
+    Exposed=(Window,Worker),
     ImplementedAs=NativeFileSystemWriter,
     RuntimeEnabled=NativeFileSystem
 ] interface FileSystemWriter {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.idl b/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.idl
index 30ca23d..b6f8e86 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_sender.idl
@@ -25,6 +25,7 @@
 
 // http://w3c.github.io/webrtc-pc/#rtcdtmfsender
 
+[Exposed=Window]
 interface RTCDTMFSender : EventTarget {
     [RaisesException] void insertDTMF(DOMString tones, optional long duration, optional long interToneGap);
     attribute EventHandler ontonechange;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_tone_change_event.idl b/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_tone_change_event.idl
index 0aa9bd9..69a67ae 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_tone_change_event.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_dtmf_tone_change_event.idl
@@ -26,6 +26,7 @@
 // https://w3c.github.io/webrtc-pc/#rtcdtmftonechangeevent
 
 [
+    Exposed=Window,
     Constructor(DOMString type, RTCDTMFToneChangeEventInit eventInitDict)
 ] interface RTCDTMFToneChangeEvent : Event {
     readonly attribute DOMString tone;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_error.idl b/third_party/blink/renderer/modules/peerconnection/rtc_error.idl
index f4de9ac..27bb63f4 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_error.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_error.idl
@@ -23,6 +23,7 @@
 
 // https://w3c.github.io/webrtc-pc/#dfn-rtcerror
 [
+    Exposed=Window,
     Constructor(RTCErrorInit init, optional DOMString message = "")
 ] interface RTCError : DOMException {
     readonly attribute RTCErrorDetailType errorDetail;
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 30e42ab..694c997 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
@@ -49,8 +49,6 @@
     const GLenum ATOMIC_COUNTER_BARRIER_BIT             = 0x00001000;
     const GLenum SHADER_STORAGE_BARRIER_BIT             = 0x00002000;
     const GLenum ALL_BARRIER_BITS                       = 0xFFFFFFFF;
-    const GLenum FALSE = 0;
-    const GLenum TRUE = 1;
     const GLenum READ_ONLY                              = 0x88B8;
     const GLenum WRITE_ONLY                             = 0x88B9;
     const GLenum READ_WRITE                             = 0x88BA;
diff --git a/third_party/blink/renderer/platform/animation/animation_utilities.h b/third_party/blink/renderer/platform/animation/animation_utilities.h
index 183f3ee..07cdcb9 100644
--- a/third_party/blink/renderer/platform/animation/animation_utilities.h
+++ b/third_party/blink/renderer/platform/animation/animation_utilities.h
@@ -34,8 +34,11 @@
 // Calculates the accuracy for evaluating a timing function for an animation
 // with the specified duration.
 inline double AccuracyForDuration(double duration) {
-  double accuracy = 1.0 / (200.0 * duration);
   double default_epsilon = gfx::CubicBezier::GetDefaultEpsilon();
+  if (duration == 0) {
+    return default_epsilon;
+  }
+  double accuracy = 1.0 / (200.0 * duration);
   // Avoid min()/max() from std here in the header, because that would require
   // inclusion of <algorithm>, which is slow to compile.
   return accuracy > default_epsilon ? accuracy : default_epsilon;
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index de7b2ef0..a9b68c0 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -112,6 +112,7 @@
     "heap_thread_test.cc",
     "heap_traits_test.cc",
     "incremental_marking_test.cc",
+    "marking_verifier_test.cc",
     "name_trait_test.cc",
     "object_start_bitmap_test.cc",
     "persistent_test.cc",
diff --git a/third_party/blink/renderer/platform/heap/heap_page.cc b/third_party/blink/renderer/platform/heap/heap_page.cc
index 398a426..56d46af 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.cc
+++ b/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -110,6 +110,11 @@
   return gc_info->finalize;
 }
 
+const char* HeapObjectHeader::Name() const {
+  const GCInfo* gc_info = GCInfoTable::Get().GCInfoFromIndex(GcInfoIndex());
+  return gc_info->name(Payload()).value;
+}
+
 BaseArena::BaseArena(ThreadState* state, int index)
     : thread_state_(state), index_(index) {}
 
@@ -573,7 +578,10 @@
   // for incremental marking the application is running between steps and
   // might set up a new area.
   SetAllocationPoint(nullptr, 0);
-  for (BasePage* page : swept_pages_) {
+  DCHECK(swept_unfinalized_pages_.IsEmpty());
+  DCHECK(swept_unfinalized_empty_pages_.IsEmpty());
+  // There may be objects on swept_pages_ as pre-finalizers may allocate.
+  for (BasePage* page : unswept_pages_) {
     static_cast<NormalPage*>(page)->VerifyMarking();
   }
 #endif  // DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/platform/heap/heap_page.h b/third_party/blink/renderer/platform/heap/heap_page.h
index 8fb72ee..cb8f094 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.h
+++ b/third_party/blink/renderer/platform/heap/heap_page.h
@@ -235,15 +235,18 @@
 
   // The payload starts directly after the HeapObjectHeader, and the payload
   // size does not include the sizeof(HeapObjectHeader).
-  Address Payload();
+  Address Payload() const;
   size_t PayloadSize();
-  Address PayloadEnd();
+  Address PayloadEnd() const;
 
   void Finalize(Address, size_t);
 
   // Returns true if object has finalizer.
   bool HasNonTrivialFinalizer() const;
 
+  // Returns a human-readable name of this object.
+  const char* Name() const;
+
   // Returns true if magic number is valid.
   bool IsValid() const;
   // Returns true if magic number is valid or zapped.
@@ -1061,12 +1064,14 @@
 #endif
 }
 
-inline Address HeapObjectHeader::Payload() {
-  return reinterpret_cast<Address>(this) + sizeof(HeapObjectHeader);
+inline Address HeapObjectHeader::Payload() const {
+  return reinterpret_cast<Address>(const_cast<HeapObjectHeader*>(this)) +
+         sizeof(HeapObjectHeader);
 }
 
-inline Address HeapObjectHeader::PayloadEnd() {
-  return reinterpret_cast<Address>(this) + size();
+inline Address HeapObjectHeader::PayloadEnd() const {
+  return reinterpret_cast<Address>(const_cast<HeapObjectHeader*>(this)) +
+         size();
 }
 
 NO_SANITIZE_ADDRESS inline size_t HeapObjectHeader::PayloadSize() {
diff --git a/third_party/blink/renderer/platform/heap/heap_test_utilities.h b/third_party/blink/renderer/platform/heap/heap_test_utilities.h
index 805cfb63..e4d705e 100644
--- a/third_party/blink/renderer/platform/heap/heap_test_utilities.h
+++ b/third_party/blink/renderer/platform/heap/heap_test_utilities.h
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "third_party/blink/renderer/platform/heap/blink_gc.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/trace_traits.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
@@ -106,6 +107,22 @@
   void Trace(Visitor* visitor) override { Mixin::Trace(visitor); }
 };
 
+// Simple linked object to be used in tests.
+class LinkedObject : public GarbageCollected<LinkedObject> {
+ public:
+  LinkedObject() = default;
+  explicit LinkedObject(LinkedObject* next) : next_(next) {}
+
+  void set_next(LinkedObject* next) { next_ = next; }
+  LinkedObject* next() const { return next_; }
+  Member<LinkedObject>& next_ref() { return next_; }
+
+  void Trace(Visitor* visitor) { visitor->Trace(next_); }
+
+ private:
+  Member<LinkedObject> next_;
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_UTILITIES_H_
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index b0cda7d..4dbe630 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -221,23 +221,16 @@
   std::vector<std::pair<HeapObjectHeader*, bool /* was marked */>> headers_;
 };
 
-class Object : public GarbageCollected<Object> {
+class Object : public LinkedObject {
  public:
-  Object() : next_(nullptr) {}
-  explicit Object(Object* next) : next_(next) {}
-
-  void set_next(Object* next) { next_ = next; }
+  Object() = default;
+  explicit Object(Object* next) : LinkedObject(next) {}
 
   bool IsMarked() const {
     return HeapObjectHeader::FromPayload(this)->IsMarked();
   }
 
-  virtual void Trace(blink::Visitor* visitor) { visitor->Trace(next_); }
-
-  Member<Object>& next_ref() { return next_; }
-
- private:
-  Member<Object> next_;
+  void Trace(Visitor* visitor) { LinkedObject::Trace(visitor); }
 };
 
 // =============================================================================
diff --git a/third_party/blink/renderer/platform/heap/marking_verifier.h b/third_party/blink/renderer/platform/heap/marking_verifier.h
index 567227b6..5775e19 100644
--- a/third_party/blink/renderer/platform/heap/marking_verifier.h
+++ b/third_party/blink/renderer/platform/heap/marking_verifier.h
@@ -28,6 +28,7 @@
         !info->has_v_table || blink::VTableInitialized(header->Payload());
     if (can_verify) {
       CHECK(header->IsValid());
+      parent_ = header;
       info->trace(this, header->Payload());
     }
   }
@@ -76,8 +77,23 @@
     // state meaning that there are unmarked objects reachable from marked
     // ones.
     CHECK(child_header);
-    CHECK(child_header->IsMarked());
+    if (!child_header->IsMarked()) {
+      // Pre-finalizers may allocate. In that case the newly allocated objects
+      // reside on a page that is not scheduled for sweeping.
+      if (PageFromObject(child_header->Payload())->HasBeenSwept())
+        return;
+
+      LOG(FATAL)
+          << "MarkingVerifier: Encountered unmarked object. " << std::endl
+          << std::endl
+          << "Hint (use v8_enable_raw_heap_snapshots for better naming): "
+          << std::endl
+          << parent_->Name() << std::endl
+          << "\\-> " << child_header->Name() << std::endl;
+    }
   }
+
+  HeapObjectHeader* parent_ = nullptr;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/marking_verifier_test.cc b/third_party/blink/renderer/platform/heap/marking_verifier_test.cc
new file mode 100644
index 0000000..497c3f9
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/marking_verifier_test.cc
@@ -0,0 +1,91 @@
+// 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 "third_party/blink/renderer/platform/heap/marking_verifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+
+namespace blink {
+
+namespace {
+
+class ResurrectingPreFinalizer
+    : public GarbageCollected<ResurrectingPreFinalizer> {
+  USING_PRE_FINALIZER(ResurrectingPreFinalizer, Dispose);
+
+ public:
+  enum TestType { kMember, kWeakMember };
+
+  class GlobalStorage : public GarbageCollected<GlobalStorage> {
+   public:
+    void Trace(Visitor* visitor) {
+      visitor->Trace(strong);
+      visitor->Trace(weak);
+    }
+
+    Member<LinkedObject> strong;
+    WeakMember<LinkedObject> weak;
+  };
+
+  ResurrectingPreFinalizer(TestType test_type,
+                           GlobalStorage* storage,
+                           LinkedObject* object_that_dies)
+      : test_type_(test_type),
+        storage_(storage),
+        object_that_dies_(object_that_dies) {}
+
+  void Trace(Visitor* visitor) {
+    visitor->Trace(storage_);
+    visitor->Trace(object_that_dies_);
+  }
+
+ private:
+  void Dispose() {
+    switch (test_type_) {
+      case TestType::kMember:
+        storage_->strong = object_that_dies_;
+        break;
+      case TestType::kWeakMember:
+        storage_->weak = object_that_dies_;
+        break;
+    }
+  }
+
+  TestType test_type_;
+  Member<GlobalStorage> storage_;
+  Member<LinkedObject> object_that_dies_;
+};
+
+}  // namespace
+
+TEST(MarkingVerifierDeathTest, DiesOnResurrectedMember) {
+  if (!ThreadState::Current()->VerifyMarkingEnabled())
+    return;
+
+  Persistent<ResurrectingPreFinalizer::GlobalStorage> storage(
+      MakeGarbageCollected<ResurrectingPreFinalizer::GlobalStorage>());
+  MakeGarbageCollected<ResurrectingPreFinalizer>(
+      ResurrectingPreFinalizer::kMember, storage.Get(),
+      MakeGarbageCollected<LinkedObject>());
+  ASSERT_DEATH_IF_SUPPORTED(PreciselyCollectGarbage(),
+                            "MarkingVerifier: Encountered unmarked object.");
+}
+
+TEST(MarkingVerifierDeathTest, DiesOnResurrectedWeakMember) {
+  if (!ThreadState::Current()->VerifyMarkingEnabled())
+    return;
+
+  Persistent<ResurrectingPreFinalizer::GlobalStorage> storage(
+      MakeGarbageCollected<ResurrectingPreFinalizer::GlobalStorage>());
+  MakeGarbageCollected<ResurrectingPreFinalizer>(
+      ResurrectingPreFinalizer::kWeakMember, storage.Get(),
+      MakeGarbageCollected<LinkedObject>());
+  ASSERT_DEATH_IF_SUPPORTED(PreciselyCollectGarbage(),
+                            "MarkingVerifier: Encountered unmarked object.");
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index bca3dad..eb0747518 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -802,6 +802,9 @@
     Heap().Compaction()->FilterNonLiveSlots();
   }
 
+  // Last point where all mark bits are present.
+  VerifyMarking(marking_type);
+
   EagerSweep();
 
   {
@@ -1614,7 +1617,7 @@
       deadline);
 }
 
-bool ThreadState::ShouldVerifyMarking() const {
+bool ThreadState::VerifyMarkingEnabled() const {
   bool should_verify_marking =
       RuntimeEnabledFeatures::HeapIncrementalMarkingStressEnabled();
 #if BUILDFLAG(BLINK_HEAP_VERIFICATION)
@@ -1641,9 +1644,6 @@
   const size_t marked_bytes = current_gc_data_.visitor->marked_bytes();
   current_gc_data_.visitor.reset();
 
-  if (ShouldVerifyMarking())
-    VerifyMarking(marking_type);
-
   Heap().stats_collector()->NotifyMarkingCompleted(marked_bytes);
 
   WTF::Partitions::ReportMemoryUsageHistogram();
@@ -1661,12 +1661,10 @@
 }
 
 void ThreadState::VerifyMarking(BlinkGC::MarkingType marking_type) {
-  // Marking for snapshot does not clear unreachable weak fields prohibiting
-  // verification of markbits as we leave behind non-marked non-cleared weak
-  // fields.
-  if (marking_type == BlinkGC::kTakeSnapshot)
-    return;
-  Heap().VerifyMarking();
+  DCHECK_NE(BlinkGC::kTakeSnapshot, marking_type);
+
+  if (VerifyMarkingEnabled())
+    Heap().VerifyMarking();
 }
 
 void ThreadState::CollectAllGarbageForTesting(BlinkGC::StackState stack_state) {
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 68137405..4e7490f 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -426,6 +426,8 @@
   // Implementation for RAILModeObserver
   void OnRAILModeChanged(RAILMode new_mode) override;
 
+  bool VerifyMarkingEnabled() const;
+
  private:
   // Stores whether some ThreadState is currently in incremental marking.
   static AtomicEntryFlag incremental_marking_flag_;
@@ -475,8 +477,6 @@
   bool MarkPhaseAdvanceMarking(base::TimeTicks deadline);
   void VerifyMarking(BlinkGC::MarkingType);
 
-  bool ShouldVerifyMarking() const;
-
   // ShouldForceConservativeGC
   // implements the heuristics that are used to determine when to collect
   // garbage.
diff --git a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
index cd20c6f..21cb9e5 100644
--- a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
+++ b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
@@ -15,6 +15,7 @@
   "//third_party/blink/renderer/platform/mojo/security_origin.typemap",
   "//third_party/blink/renderer/platform/mojo/string.typemap",
   "//third_party/blink/renderer/platform/mojo/time.typemap",
+  "//third_party/blink/renderer/platform/network/encoded_form_data_element.typemap",
   "//third_party/blink/renderer/platform/network/encoded_form_data.typemap",
   "//third_party/blink/renderer/platform/network/http_request_headers.typemap",
   "//third_party/blink/public/common/manifest/display_mode.typemap",
diff --git a/third_party/blink/renderer/platform/network/BUILD.gn b/third_party/blink/renderer/platform/network/BUILD.gn
index 2ef1999..2bc375f 100644
--- a/third_party/blink/renderer/platform/network/BUILD.gn
+++ b/third_party/blink/renderer/platform/network/BUILD.gn
@@ -31,6 +31,8 @@
     "content_security_policy_response_headers.h",
     "encoded_form_data.cc",
     "encoded_form_data.h",
+    "encoded_form_data_element_mojom_traits.cc",
+    "encoded_form_data_element_mojom_traits.h",
     "encoded_form_data_mojom_traits.cc",
     "encoded_form_data_mojom_traits.h",
     "form_data_encoder.cc",
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data.h b/third_party/blink/renderer/platform/network/encoded_form_data.h
index 65f317bf..f47b14e 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data.h
+++ b/third_party/blink/renderer/platform/network/encoded_form_data.h
@@ -38,14 +38,12 @@
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
-namespace network {
-namespace mojom {
-class URLRequestBodyDataView;
-}  // namespace mojom
-}  // namespace network
-
 namespace blink {
 
+namespace mojom {
+class FetchAPIRequestBodyDataView;
+}  // namespace mojom
+
 class BlobDataHandle;
 class WrappedDataPipeGetter;
 
@@ -153,7 +151,7 @@
   bool IsSafeToSendToAnotherThread() const;
 
  private:
-  friend struct mojo::StructTraits<network::mojom::URLRequestBodyDataView,
+  friend struct mojo::StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
                                    scoped_refptr<blink::EncodedFormData>>;
   EncodedFormData();
   EncodedFormData(const EncodedFormData&);
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data.typemap b/third_party/blink/renderer/platform/network/encoded_form_data.typemap
index 55b4887..9c35be2 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data.typemap
+++ b/third_party/blink/renderer/platform/network/encoded_form_data.typemap
@@ -2,12 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//services/network/public/mojom/url_loader.mojom"
+mojom = "//third_party/blink/public/mojom/fetch/fetch_api_request.mojom"
 public_headers =
     [ "//third_party/blink/renderer/platform/network/encoded_form_data.h" ]
 traits_headers = [ "//third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h" ]
 
-type_mappings = [
-  "network.mojom.DataElement=::blink::FormDataElement[move_only]",
-  "network.mojom.URLRequestBody=scoped_refptr<::blink::EncodedFormData>[nullable_is_same_type,copyable_pass_by_value]",
-]
+type_mappings = [ "blink.mojom.FetchAPIRequestBody=scoped_refptr<::blink::EncodedFormData>[nullable_is_same_type,copyable_pass_by_value]" ]
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data_element.typemap b/third_party/blink/renderer/platform/network/encoded_form_data_element.typemap
new file mode 100644
index 0000000..7ee387a
--- /dev/null
+++ b/third_party/blink/renderer/platform/network/encoded_form_data_element.typemap
@@ -0,0 +1,11 @@
+# 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.
+
+mojom = "//services/network/public/mojom/url_loader.mojom"
+public_headers =
+    [ "//third_party/blink/renderer/platform/network/encoded_form_data.h" ]
+traits_headers = [ "//third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h" ]
+
+type_mappings =
+    [ "network.mojom.DataElement=::blink::FormDataElement[move_only]" ]
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc b/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc
new file mode 100644
index 0000000..0c906c9
--- /dev/null
+++ b/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc
@@ -0,0 +1,174 @@
+// 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 <utility>
+
+#include "third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h"
+
+#include "base/feature_list.h"
+#include "mojo/public/cpp/base/file_mojom_traits.h"
+#include "mojo/public/cpp/base/file_path_mojom_traits.h"
+#include "mojo/public/cpp/base/time_mojom_traits.h"
+#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
+#include "mojo/public/cpp/bindings/string_traits_wtf.h"
+#include "services/network/public/cpp/features.h"
+#include "services/network/public/mojom/data_pipe_getter.mojom-blink.h"
+#include "third_party/blink/public/mojom/blob/blob.mojom-blink.h"
+#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h"
+
+namespace mojo {
+
+// static
+network::mojom::DataElementType
+StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::type(
+    const blink::FormDataElement& data) {
+  switch (data.type_) {
+    case blink::FormDataElement::kData:
+      return network::mojom::DataElementType::kBytes;
+    case blink::FormDataElement::kEncodedFile:
+      return network::mojom::DataElementType::kFile;
+    case blink::FormDataElement::kEncodedBlob: {
+      if (data.optional_blob_data_handle_)
+        return network::mojom::DataElementType::kDataPipe;
+      return network::mojom::DataElementType::kBlob;
+    }
+    case blink::FormDataElement::kDataPipe:
+      return network::mojom::DataElementType::kDataPipe;
+  }
+  NOTREACHED();
+  return network::mojom::DataElementType::kUnknown;
+}
+
+// static
+base::span<const uint8_t>
+StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::buf(
+    const blink::FormDataElement& data) {
+  return base::make_span(reinterpret_cast<const uint8_t*>(data.data_.data()),
+                         data.data_.size());
+}
+
+// static
+base::File
+StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::file(
+    const blink::FormDataElement& data) {
+  return base::File();
+}
+
+// static
+base::FilePath
+StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::path(
+    const blink::FormDataElement& data) {
+  return base::FilePath::FromUTF8Unsafe(data.filename_.Utf8());
+}
+
+// static
+network::mojom::blink::DataPipeGetterPtrInfo
+StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::
+    data_pipe_getter(const blink::FormDataElement& data) {
+  if (data.type_ == blink::FormDataElement::kDataPipe) {
+    if (!data.data_pipe_getter_)
+      return nullptr;
+    network::mojom::blink::DataPipeGetterPtr data_pipe_getter;
+    (*data.data_pipe_getter_->GetPtr())
+        ->Clone(mojo::MakeRequest(&data_pipe_getter));
+    return data_pipe_getter.PassInterface();
+  }
+  if (data.type_ == blink::FormDataElement::kEncodedBlob) {
+    if (data.optional_blob_data_handle_) {
+      blink::mojom::blink::BlobPtr blob_ptr(blink::mojom::blink::BlobPtrInfo(
+          data.optional_blob_data_handle_->CloneBlobPtr()
+              .PassInterface()
+              .PassHandle(),
+          blink::mojom::blink::Blob::Version_));
+      network::mojom::blink::DataPipeGetterPtr data_pipe_getter_ptr;
+      blob_ptr->AsDataPipeGetter(MakeRequest(&data_pipe_getter_ptr));
+      return data_pipe_getter_ptr.PassInterface();
+    }
+  }
+  return nullptr;
+}
+
+// static
+base::Time
+StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::
+    expected_modification_time(const blink::FormDataElement& data) {
+  if (data.type_ == blink::FormDataElement::kEncodedFile)
+    return base::Time::FromDoubleT(data.expected_file_modification_time_);
+  return base::Time();
+}
+
+// static
+bool StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::
+    Read(network::mojom::DataElementDataView data,
+         blink::FormDataElement* out) {
+  network::mojom::DataElementType data_type;
+  if (!data.ReadType(&data_type)) {
+    return false;
+  }
+  out->file_start_ = data.offset();
+  out->file_length_ = data.length();
+
+  switch (data_type) {
+    case network::mojom::DataElementType::kBytes: {
+      out->type_ = blink::FormDataElement::kData;
+      // TODO(richard.li): Delete this workaround when type of
+      // blink::FormDataElement::data_ is changed to WTF::Vector<uint8_t>
+      WTF::Vector<uint8_t> buf;
+      if (!data.ReadBuf(&buf)) {
+        return false;
+      }
+      out->data_.AppendRange(buf.begin(), buf.end());
+      break;
+    }
+    case network::mojom::DataElementType::kFile: {
+      out->type_ = blink::FormDataElement::kEncodedFile;
+      base::FilePath file_path;
+      base::Time expected_time;
+      if (!data.ReadPath(&file_path) ||
+          !data.ReadExpectedModificationTime(&expected_time)) {
+        return false;
+      }
+      out->expected_file_modification_time_ = expected_time.ToDoubleT();
+      out->filename_ =
+          WTF::String(file_path.value().data(), file_path.value().size());
+      break;
+    }
+    case network::mojom::DataElementType::kBlob: {
+      // Blobs are actually passed around as kDataPipe elements when network
+      // service is enabled, which keeps the blobs alive.
+      DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService));
+      out->type_ = blink::FormDataElement::kEncodedBlob;
+      if (!data.ReadBlobUuid(&out->blob_uuid_)) {
+        return false;
+      }
+      out->optional_blob_data_handle_ = blink::BlobDataHandle::Create(
+          out->blob_uuid_, "" /* type is not necessary */, data.length());
+      break;
+    }
+    case network::mojom::DataElementType::kDataPipe: {
+      out->type_ = blink::FormDataElement::kDataPipe;
+      auto data_pipe_ptr_info = data.TakeDataPipeGetter<
+          network::mojom::blink::DataPipeGetterPtrInfo>();
+      DCHECK(data_pipe_ptr_info.is_valid());
+
+      network::mojom::blink::DataPipeGetterPtr data_pipe_getter;
+      data_pipe_getter.Bind(std::move(data_pipe_ptr_info));
+      out->data_pipe_getter_ =
+          base::MakeRefCounted<blink::WrappedDataPipeGetter>(
+              std::move(data_pipe_getter));
+      break;
+    }
+    case network::mojom::DataElementType::kUnknown:
+    case network::mojom::DataElementType::kChunkedDataPipe:
+    case network::mojom::DataElementType::kRawFile:
+      NOTREACHED();
+      return false;
+  }
+  return true;
+}
+
+}  // namespace mojo
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h b/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h
new file mode 100644
index 0000000..8ed20e2
--- /dev/null
+++ b/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h
@@ -0,0 +1,58 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_ELEMENT_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_ELEMENT_MOJOM_TRAITS_H_
+
+#include "services/network/public/mojom/url_loader.mojom-blink.h"
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+
+namespace mojo {
+
+template <>
+struct PLATFORM_EXPORT
+    StructTraits<network::mojom::DataElementDataView, blink::FormDataElement> {
+  static network::mojom::DataElementType type(
+      const blink::FormDataElement& data);
+
+  static base::span<const uint8_t> buf(const blink::FormDataElement& data);
+
+  static base::File file(const blink::FormDataElement& data);
+
+  static base::FilePath path(const blink::FormDataElement& data);
+
+  static const WTF::String& blob_uuid(const blink::FormDataElement& data) {
+    return data.blob_uuid_;
+  }
+
+  static network::mojom::blink::DataPipeGetterPtrInfo data_pipe_getter(
+      const blink::FormDataElement& data);
+
+  static network::mojom::blink::ChunkedDataPipeGetterPtrInfo
+  chunked_data_pipe_getter(const blink::FormDataElement& data) {
+    return nullptr;
+  }
+
+  static uint64_t offset(const blink::FormDataElement& data) {
+    return data.file_start_;
+  }
+
+  static uint64_t length(const blink::FormDataElement& data) {
+    if (data.type_ == blink::FormDataElement::kEncodedBlob &&
+        data.optional_blob_data_handle_) {
+      return data.optional_blob_data_handle_->size();
+    }
+    return data.file_length_;
+  }
+
+  static base::Time expected_modification_time(
+      const blink::FormDataElement& data);
+
+  static bool Read(network::mojom::DataElementDataView data,
+                   blink::FormDataElement* out);
+};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_ELEMENT_MOJOM_TRAITS_H_
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.cc b/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.cc
index ab4901e..4389fce 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.cc
+++ b/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.cc
@@ -2,180 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <utility>
-
 #include "third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h"
 
-#include "base/feature_list.h"
-#include "mojo/public/cpp/base/file_mojom_traits.h"
-#include "mojo/public/cpp/base/file_path_mojom_traits.h"
-#include "mojo/public/cpp/base/time_mojom_traits.h"
 #include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
-#include "mojo/public/cpp/bindings/string_traits_wtf.h"
-#include "services/network/public/cpp/features.h"
-#include "services/network/public/mojom/data_pipe_getter.mojom-blink.h"
-#include "third_party/blink/public/mojom/blob/blob.mojom-blink.h"
-#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
-#include "third_party/blink/public/platform/interface_provider.h"
-#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h"
 #include "third_party/blink/renderer/platform/network/form_data_encoder.h"
-#include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h"
 
 namespace mojo {
 
 // static
-network::mojom::DataElementType
-StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::type(
-    const blink::FormDataElement& data) {
-  switch (data.type_) {
-    case blink::FormDataElement::kData:
-      return network::mojom::DataElementType::kBytes;
-    case blink::FormDataElement::kEncodedFile:
-      return network::mojom::DataElementType::kFile;
-    case blink::FormDataElement::kEncodedBlob: {
-      if (data.optional_blob_data_handle_)
-        return network::mojom::DataElementType::kDataPipe;
-      return network::mojom::DataElementType::kBlob;
-    }
-    case blink::FormDataElement::kDataPipe:
-      return network::mojom::DataElementType::kDataPipe;
-  }
-  NOTREACHED();
-  return network::mojom::DataElementType::kUnknown;
-}
-
-// static
-base::span<const uint8_t>
-StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::buf(
-    const blink::FormDataElement& data) {
-  return base::make_span(reinterpret_cast<const uint8_t*>(data.data_.data()),
-                         data.data_.size());
-}
-
-// static
-base::File
-StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::file(
-    const blink::FormDataElement& data) {
-  return base::File();
-}
-
-// static
-base::FilePath
-StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::path(
-    const blink::FormDataElement& data) {
-  return base::FilePath::FromUTF8Unsafe(data.filename_.Utf8());
-}
-
-// static
-network::mojom::blink::DataPipeGetterPtrInfo
-StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::
-    data_pipe_getter(const blink::FormDataElement& data) {
-  if (data.type_ == blink::FormDataElement::kDataPipe) {
-    if (!data.data_pipe_getter_)
-      return nullptr;
-    network::mojom::blink::DataPipeGetterPtr data_pipe_getter;
-    (*data.data_pipe_getter_->GetPtr())
-        ->Clone(mojo::MakeRequest(&data_pipe_getter));
-    return data_pipe_getter.PassInterface();
-  }
-  if (data.type_ == blink::FormDataElement::kEncodedBlob) {
-    if (data.optional_blob_data_handle_) {
-      blink::mojom::blink::BlobPtr blob_ptr(blink::mojom::blink::BlobPtrInfo(
-          data.optional_blob_data_handle_->CloneBlobPtr()
-              .PassInterface()
-              .PassHandle(),
-          blink::mojom::blink::Blob::Version_));
-      network::mojom::blink::DataPipeGetterPtr data_pipe_getter_ptr;
-      blob_ptr->AsDataPipeGetter(MakeRequest(&data_pipe_getter_ptr));
-      return data_pipe_getter_ptr.PassInterface();
-    }
-  }
-  return nullptr;
-}
-
-// static
-base::Time
-StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::
-    expected_modification_time(const blink::FormDataElement& data) {
-  if (data.type_ == blink::FormDataElement::kEncodedFile)
-    return base::Time::FromDoubleT(data.expected_file_modification_time_);
-  return base::Time();
-}
-
-// static
-bool StructTraits<network::mojom::DataElementDataView, blink::FormDataElement>::
-    Read(network::mojom::DataElementDataView data,
-         blink::FormDataElement* out) {
-  network::mojom::DataElementType data_type;
-  if (!data.ReadType(&data_type)) {
-    return false;
-  }
-  out->file_start_ = data.offset();
-  out->file_length_ = data.length();
-
-  switch (data_type) {
-    case network::mojom::DataElementType::kBytes: {
-      out->type_ = blink::FormDataElement::kData;
-      // TODO(richard.li): Delete this workaround when type of
-      // blink::FormDataElement::data_ is changed to WTF::Vector<uint8_t>
-      WTF::Vector<uint8_t> buf;
-      if (!data.ReadBuf(&buf)) {
-        return false;
-      }
-      out->data_.AppendRange(buf.begin(), buf.end());
-      break;
-    }
-    case network::mojom::DataElementType::kFile: {
-      out->type_ = blink::FormDataElement::kEncodedFile;
-      base::FilePath file_path;
-      base::Time expected_time;
-      if (!data.ReadPath(&file_path) ||
-          !data.ReadExpectedModificationTime(&expected_time)) {
-        return false;
-      }
-      out->expected_file_modification_time_ = expected_time.ToDoubleT();
-      out->filename_ =
-          WTF::String(file_path.value().data(), file_path.value().size());
-      break;
-    }
-    case network::mojom::DataElementType::kBlob: {
-      // Blobs are actually passed around as kDataPipe elements when network
-      // service is enabled, which keeps the blobs alive.
-      DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService));
-      out->type_ = blink::FormDataElement::kEncodedBlob;
-      if (!data.ReadBlobUuid(&out->blob_uuid_)) {
-        return false;
-      }
-      out->optional_blob_data_handle_ = blink::BlobDataHandle::Create(
-          out->blob_uuid_, "" /* type is not necessary */, data.length());
-      break;
-    }
-    case network::mojom::DataElementType::kDataPipe: {
-      out->type_ = blink::FormDataElement::kDataPipe;
-      auto data_pipe_ptr_info = data.TakeDataPipeGetter<
-          network::mojom::blink::DataPipeGetterPtrInfo>();
-      DCHECK(data_pipe_ptr_info.is_valid());
-
-      network::mojom::blink::DataPipeGetterPtr data_pipe_getter;
-      data_pipe_getter.Bind(std::move(data_pipe_ptr_info));
-      out->data_pipe_getter_ =
-          base::MakeRefCounted<blink::WrappedDataPipeGetter>(
-              std::move(data_pipe_getter));
-      break;
-    }
-    case network::mojom::DataElementType::kUnknown:
-    case network::mojom::DataElementType::kChunkedDataPipe:
-    case network::mojom::DataElementType::kRawFile:
-      NOTREACHED();
-      return false;
-  }
-  return true;
-}
-
-// static
-bool StructTraits<network::mojom::URLRequestBodyDataView,
+bool StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
                   scoped_refptr<blink::EncodedFormData>>::
-    Read(network::mojom::URLRequestBodyDataView in,
+    Read(blink::mojom::FetchAPIRequestBodyDataView in,
          scoped_refptr<blink::EncodedFormData>* out) {
   *out = blink::EncodedFormData::Create();
   if (!in.ReadElements(&((*out)->elements_))) {
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h b/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h
index c2aed21..0a403da6 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h
+++ b/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h
@@ -5,58 +5,13 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_MOJOM_TRAITS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_MOJOM_TRAITS_H_
 
-#include <string>
-
-#include "services/network/public/mojom/url_loader.mojom-blink.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
 
 namespace mojo {
 
 template <>
-struct PLATFORM_EXPORT
-    StructTraits<network::mojom::DataElementDataView, blink::FormDataElement> {
-  static network::mojom::DataElementType type(
-      const blink::FormDataElement& data);
-
-  static base::span<const uint8_t> buf(const blink::FormDataElement& data);
-
-  static base::File file(const blink::FormDataElement& data);
-
-  static base::FilePath path(const blink::FormDataElement& data);
-
-  static const WTF::String& blob_uuid(const blink::FormDataElement& data) {
-    return data.blob_uuid_;
-  }
-
-  static network::mojom::blink::DataPipeGetterPtrInfo data_pipe_getter(
-      const blink::FormDataElement& data);
-
-  static network::mojom::blink::ChunkedDataPipeGetterPtrInfo
-  chunked_data_pipe_getter(const blink::FormDataElement& data) {
-    return nullptr;
-  }
-
-  static uint64_t offset(const blink::FormDataElement& data) {
-    return data.file_start_;
-  }
-
-  static uint64_t length(const blink::FormDataElement& data) {
-    if (data.type_ == blink::FormDataElement::kEncodedBlob &&
-        data.optional_blob_data_handle_) {
-      return data.optional_blob_data_handle_->size();
-    }
-    return data.file_length_;
-  }
-
-  static base::Time expected_modification_time(
-      const blink::FormDataElement& data);
-
-  static bool Read(network::mojom::DataElementDataView data,
-                   blink::FormDataElement* out);
-};
-
-template <>
-struct PLATFORM_EXPORT StructTraits<network::mojom::URLRequestBodyDataView,
+struct PLATFORM_EXPORT StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
                                     scoped_refptr<blink::EncodedFormData>> {
   static bool IsNull(const scoped_refptr<blink::EncodedFormData>& data) {
     return !data;
@@ -76,7 +31,7 @@
     return data->contains_password_data_;
   }
 
-  static bool Read(network::mojom::URLRequestBodyDataView in,
+  static bool Read(blink::mojom::FetchAPIRequestBodyDataView in,
                    scoped_refptr<blink::EncodedFormData>* out);
 };
 
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data_test.cc b/third_party/blink/renderer/platform/network/encoded_form_data_test.cc
index c71f053..31cbbcd 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data_test.cc
+++ b/third_party/blink/renderer/platform/network/encoded_form_data_test.cc
@@ -15,6 +15,7 @@
 #include "services/network/public/mojom/url_loader.mojom-blink.h"
 #include "third_party/blink/public/mojom/blob/blob.mojom-blink.h"
 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+#include "third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h"
 #include "third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h"
 #include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h"
 
@@ -174,7 +175,7 @@
   original1->SetContainsPasswordData(true);
   scoped_refptr<EncodedFormData> copied1 = EncodedFormData::Create();
   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
-              network::mojom::blink::URLRequestBody>(&original1, &copied1));
+              blink::mojom::blink::FetchAPIRequestBody>(&original1, &copied1));
   EXPECT_EQ(original1->Identifier(), copied1->Identifier());
   EXPECT_EQ(original1->ContainsPasswordData(), copied1->ContainsPasswordData());
 }
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index cdceabcf..95ca9054 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1375,7 +1375,6 @@
 crbug.com/797591 virtual/layout_ng_experimental/fast/multicol/newmulticol/regular-block-becomes-multicol.html [ Failure ]
 
 ### Missing refs, these are also skipped for existing layout.
-crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/balance-table-with-fractional-height-row.html [ Failure ]
 crbug.com/967329 virtual/layout_ng_experimental/external/wpt/css/css-multicol/columnfill-auto-max-height-002.html [ Failure ]
 crbug.com/967329 virtual/layout_ng_experimental/external/wpt/css/css-multicol/columnfill-auto-max-height-001.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/filter-with-abspos.html [ Failure ]
@@ -1517,10 +1516,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-margin-nested-firstchild-001.xht [ Failure ]
 crbug.com/792446 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-float-001.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-none-001.xht [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-table-cell-001.xht [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-table-cell-height-001.xht [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-table-cell-height-002.xht [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-table-cell-vertical-align-001.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-under-vertical-rl-scroll.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-width-001.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-width-002.xht [ Failure ]
@@ -1546,8 +1541,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/balance-line-overflow.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/balance-line-underflow-1.html [ Crash ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/balance-line-underflow-2.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/balance-repeating-table-headers.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/balance-table-with-border-spacing.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/basic-rtl.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/break-before-first-line-in-first-child.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/break-in-scrollable.html [ Failure ]
@@ -1558,10 +1551,10 @@
 crbug.com/714962 virtual/layout_ng_experimental/fast/multicol/change-height.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/client-rect-after-spanner.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/client-rect-nested.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/client-rects-crossing-boundaries.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/client-rects-crossing-boundaries-nested.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/client-rects.html [ Crash Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/client-rects-rtl.html [ Crash Failure ]
+crbug.com/829028 virtual/layout_ng_experimental/fast/multicol/clone-block-children-inline-mismatch-crash.html [ Crash ]
 crbug.com/874506 virtual/layout_ng_experimental/fast/multicol/column-break-with-balancing.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/column-count-with-rules.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/column-rules.html [ Failure ]
@@ -1628,7 +1621,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/first-line-in-float-below-next-column-top.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/first-line-in-float-with-margin.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/fixedpos-child-becomes-static.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/flexbox.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/flipped-blocks-border-after.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/flipped-blocks-hit-test.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/float-after-break-after.html [ Failure ]
@@ -1652,7 +1644,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/forced-break-after-last-block-before-spanner.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/forced-break-in-nested-columns.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/forced-break-too-short-column.html [ Failure ]
-crbug.com/874506 virtual/layout_ng_experimental/fast/multicol/foreignObject.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/hit-test-above-or-below.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/hit-test-end-of-column.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/hit-test-end-of-column-with-line-height.html [ Crash Failure ]
@@ -1661,8 +1652,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/inline-block-baseline.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/inline-getclientrects.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/inner-multicol-in-second-column.html [ Failure ]
-crbug.com/714962 virtual/layout_ng_experimental/fast/multicol/input-type-number.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/input-with-overflow-second-column.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/insane-column-gap.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/layers-in-multicol.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/line-in-next-row-in-fourth-inner-multicol.html [ Failure ]
@@ -1671,7 +1660,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/many-lines-overflow-in-single-row-inner.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/mixed-opacity-test.html [ Crash ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/mixed-positioning-stacking-order.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/multicol-with-child-renderLayer-for-input.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-3-multicols-fixed-height.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-auto-height-extra-block-inbetween.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-auto-height.html [ Failure ]
@@ -1708,7 +1696,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/hide-box-vertical-rl.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/list-item.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/orphans-and-widows-balance.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/newmulticol/table-cell.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/outlines-at-column-boundaries.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/out-of-flow/abspos-auto-position.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/out-of-flow/abspos-auto-position-on-line-at-boundary.html [ Failure ]
@@ -1776,6 +1763,7 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/overflow-on-multicol.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/overflow-on-viewport.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/percent-margins.html [ Failure ]
+crbug.com/829028 virtual/layout_ng_experimental/fast/multicol/span/positioned-objects-not-removed-crash.html [ Crash ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/preferred-widths-with-column-content.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/pseudo-after.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/pseudo-after-then-content.html [ Failure ]
@@ -1793,11 +1781,9 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-first.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-img.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-last.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-table.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-with-margin.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-with-margins-between-margins.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/spanner-with-relpos-child.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/summary-split.html [ Failure ]
 crbug.com/824918 virtual/layout_ng_experimental/fast/multicol/span/trailing-margin-around-spanner.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/trailing-margin-before-spanner.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/span/two-rows-then-spanner-then-two-rows.html [ Failure ]
@@ -1808,8 +1794,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/static-child-becomes-fixedpos.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/svg-change-column-crash.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/table-cell-content-change.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/table-cell-content-change-with-decorations.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/table-margin-collapse.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/tall-content-in-inner-with-fixed-height.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_experimental/fast/multicol/tall-float1.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/tall-float2.html [ Failure ]
@@ -1872,12 +1856,7 @@
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/become-unfragmented-with-lines.html [ Crash Pass ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/become-unfragmented-with-unbreakable-blocks.html [ Crash Pass ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/block-after-float-first-child.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/border-spacing-break-before-unbreakable-row.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/break-in-first-table-row-only.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/break-in-second-table-section.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/break-inside-avoid-with-forced-break.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/break-in-tbody-after-caption.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/cells-dont-fit-on-page-paginated.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/change-fragmentainer-height-block-float-2.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/change-fragmentainer-height-block-float.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/change-fragmentainer-height-inline-float.html [ Failure ]
@@ -1888,82 +1867,16 @@
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/float-pushed-to-next-fragmentainer-by-floats.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/forced-break-clearance-unsplittable-content.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_experimental/fragmentation/forced-break-inside-float.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/fragmented-rowspan-alignment.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/fragmented-rowspan.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/fragmented-table-cell.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/fragmented-table-with-fixed-height.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/image-block-as-first-child.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/multi-line-cells.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/multi-line-cells-paginated.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/nested-repeating-thead-2.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/nested-repeating-thead-3.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/nested-repeating-thead-4.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/nested-repeating-thead-tfoot-4.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/nested-repeating-thead-tfoot.html [ Failure ]
-crbug.com/829028 virtual/layout_ng_experimental/fragmentation/no-repeating-table-header-after-sections.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/overflow-crossing-boundary.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/relayout-abspos.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/remove-unbreakable-block-in-line-float.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/repeating-thead-exceeds-page-size.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/repeating-thead-multiple-tables.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/repeating-thead-multiple-tables-page-border.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/repeating-thead-no-room-for-content-row-on-first-page.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/repeating-thead-tfoot-different-fragment-height.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/repeating-thead-under-repeating-thead.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-cell-repeating-thead-break-inside-avoid-content.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-cell-repeating-thead-break-inside-content-first-line.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-cell-repeating-thead-break-inside-content.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-cells-multiple-tables-no-repeating-thead.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-large-cell-with-header.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-in-multiple-table-sections.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row-2.html [ Crash ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row-3.html [ Crash ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row-4.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-nested-repeating-thead-2.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-nested-repeating-thead-3.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-nested-repeating-thead-4.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-nested-repeating-thead.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-nested-repeating-thead-nested-repeating-tfoot.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-nested-repeating-thead-tfoot-2.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-nested-repeating-thead-tfoot-3.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-nested-repeating-thead-tfoot-4.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-nested-repeating-thead-tfoot.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-paginated.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-paginated-with-text.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-tfoot-rows-allowing-break.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-cell-straddles-page.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-cell-straddles-page-unsplittable-div.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-starts-middle-of-page-break-after-avoid-2.html [ Crash ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-starts-middle-of-page-break-after-avoid-3.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-starts-middle-of-page-break-after-avoid.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-starts-middle-of-page.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-tfoot.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-tfoot-starts-middle-of-page-break-after-avoid-2.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-tfoot-starts-middle-of-page-break-after-avoid-3.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-tfoot-starts-middle-of-page-break-after-avoid.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-tfoot-starts-middle-of-page.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-tfoot-with-border-spacing-at-top-of-row.html [ Crash ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-tfoot-with-caption.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-tfoot-with-two-captions.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-with-border-spacing-at-top-of-row.html [ Crash ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-with-caption.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/single-line-cells-repeating-thead-with-two-captions.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/table-in-subpixel-fragmentainer.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/table-overlapping-rowspan.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/table-row-dimensions-break-freely.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/table-row-dimensions.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/table-row-dimensions-with-thead.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/table-row-page-break-collapsed-border.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fragmentation/thead-under-repeating-thead.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/unbreakable-tall-float-before-block.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fragmentation/unbreakable-tall-float-before-line.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/printing/absolute-positioned.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/printing/absolute-position-headers-and-footers.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/printing/allowed-page-breaks.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/printing/composited-thead-tfoot-repeat.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/printing/css2.1/page-break-after-000.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/printing/css2.1/page-break-after-002.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/printing/css2.1/page-break-after-003.html [ Failure ]
@@ -1995,12 +1908,6 @@
 crbug.com/591099 virtual/layout_ng_experimental/printing/respect-layout-overflow-from-pagination.html [ Failure ]
 crbug.com/824918 virtual/layout_ng_experimental/printing/setPrinting.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_experimental/printing/single-line-must-not-be-split-into-two-pages.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/printing/tfoot-repeats-at-bottom-of-each-page-multiple-tables.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/printing/thead-repeat-in-nested-table.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/printing/thead-repeats-at-top-of-each-page.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/printing/thead-repeats-at-top-of-each-page-multiple-tables.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/printing/thead-repeats-with-translucent-text-and-borders.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/printing/thead-under-multicol.html [ Failure ]
 crbug.com/829804 virtual/layout_ng_experimental/printing/webgl-oversized-printing.html [ Skip ]
 crbug.com/824918 virtual/layout_ng_experimental/printing/width-overflow.html [ Failure ]
 Bug(none) virtual/layout_ng_experimental/fragmentation/scrolling-contents-scroll.html [ Crash Failure ]
@@ -3310,6 +3217,8 @@
 crbug.com/968164 external/wpt/css/css-ui/webkit-appearance-menulist-button-001.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.12 ] external/wpt/webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/mediaElementAudioSourceToScriptProcessorTest.html [ Timeout ]
+crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-mode.html [ Timeout ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/ready-states/autoplay-hidden.optional.html [ Timeout ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-timestamp-events.html [ Timeout ]
 crbug.com/626703 external/wpt/webvtt/rendering/cues-with-video/processing-model/bidi/start_alignment.html [ Failure ]
@@ -6152,7 +6061,6 @@
 
 # Sheriff 2019-05-20
 crbug.com/963739 [ Fuchsia ] synthetic_gestures/smooth-scroll-tiny-delta.html [ Pass Timeout ]
-crbug.com/964158 [ Linux ] external/wpt/payment-handler/change-payment-method.https.html [ Pass Crash ]
 crbug.com/964239 external/wpt/css/css-scroll-snap/scroll-margin.html [ Pass Failure ]
 crbug.com/965389 [ Mac ] media/track/track-cue-rendering-position-auto.html [ Pass Failure ]
 
@@ -6237,16 +6145,14 @@
 crbug.com/973769 [ Linux ] http/tests/media/video-seek-to-middle.html [ Pass Failure ]
 crbug.com/973769 [ Linux ] media/controls/controls-cast-do-not-fade-out.html [ Pass Timeout ]
 crbug.com/973769 [ Linux ] media/video-pause-immediately.html [ Pass Failure ]
-crbug.com/973769 [ Linux ] media/video-played-ranges-1.html [ Pass Failure ]
-crbug.com/973769 [ Linux ] media/video-played-reset.html [ Pass Failure ]
 crbug.com/973769 [ Linux ] media/W3C/audio/events/event_timeupdate_manual.html [ Pass Failure ]
 
 # Flaky on Linux
 crbug.com/974660 [ Linux ] http/tests/devtools/elements/highlight/highlight-node-vertical-rl.js [ Pass Failure ]
 
-# Failing on windows with lazy-feedback-allocation feature in V8. See the bug
-# for more details.
-crbug.com/976587 [ Win Mac ] netinfo/gc-unused-listeners.html [ Pass Failure ]
+# Failing with lazy-feedback-allocation feature in V8. See the bug for more
+# details.
+crbug.com/976587 netinfo/gc-unused-listeners.html [ Pass Failure ]
 
 # TODO (michaelludwig): Remove after Skia roll and rebaseline
 crbug.com/976743 virtual/gpu-rasterization/images/color-profile-background-image-cover.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index be5080b..0f85382b 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -6211,6 +6211,18 @@
      {}
     ]
    ],
+   "payment-handler/change-payment-method-manual.https.html": [
+    [
+     "payment-handler/change-payment-method-manual.https.html",
+     {}
+    ]
+   ],
+   "payment-handler/payment-request-event-manual.https.html": [
+    [
+     "payment-handler/payment-request-event-manual.https.html",
+     {}
+    ]
+   ],
    "payment-method-basic-card/empty-data-manual.https.html": [
     [
      "payment-method-basic-card/empty-data-manual.https.html",
@@ -42667,6 +42679,18 @@
      {}
     ]
    ],
+   "css/css-flexbox/flex-minimum-height-flex-items-015.html": [
+    [
+     "css/css-flexbox/flex-minimum-height-flex-items-015.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-flexbox/flex-minimum-width-flex-items-001.xht": [
     [
      "css/css-flexbox/flex-minimum-width-flex-items-001.xht",
@@ -97775,6 +97799,18 @@
      {}
     ]
    ],
+   "css/filter-effects/backdrop-filter-plus-mask.html": [
+    [
+     "css/filter-effects/backdrop-filter-plus-mask.html",
+     [
+      [
+       "/css/filter-effects/backdrop-filter-plus-mask-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/filter-effects/backdrop-filter-reference-filter.html": [
     [
      "css/filter-effects/backdrop-filter-reference-filter.html",
@@ -138644,9 +138680,6 @@
    "css/css-shapes/spec-examples/reference/shape-outside-008-ref.html": [
     []
    ],
-   "css/css-shapes/spec-examples/shape-outside-018-expected.txt": [
-    []
-   ],
    "css/css-shapes/spec-examples/support/circle-no-shadow.png": [
     []
    ],
@@ -142442,6 +142475,15 @@
    "css/css-ui/appearance-cssom-001-expected.txt": [
     []
    ],
+   "css/css-ui/appearance-parsing-expected.txt": [
+    []
+   ],
+   "css/css-ui/appearance-property-expected.txt": [
+    []
+   ],
+   "css/css-ui/appearance-serialization-expected.txt": [
+    []
+   ],
    "css/css-ui/inheritance-expected.txt": [
     []
    ],
@@ -143285,6 +143327,15 @@
    "css/css-ui/tools/appearance-build-webkit-reftests.py": [
     []
    ],
+   "css/css-ui/webkit-appearance-parsing-expected.txt": [
+    []
+   ],
+   "css/css-ui/webkit-appearance-property-expected.txt": [
+    []
+   ],
+   "css/css-ui/webkit-appearance-serialization-expected.txt": [
+    []
+   ],
    "css/css-values/META.yml": [
     []
    ],
@@ -145685,6 +145736,9 @@
    "css/filter-effects/backdrop-filter-plus-filter-ref.html": [
     []
    ],
+   "css/filter-effects/backdrop-filter-plus-mask-ref.html": [
+    []
+   ],
    "css/filter-effects/backdrop-filter-update-ref.html": [
     []
    ],
@@ -161342,6 +161396,9 @@
    "payment-handler/payment-request-event-constructor.https.serviceworker.js": [
     []
    ],
+   "payment-handler/payment-request-event-manual.https-expected.txt": [
+    []
+   ],
    "payment-handler/payment-request-event.https-expected.txt": [
     []
    ],
@@ -166529,33 +166586,18 @@
    "subresource-integrity/alternate.css": [
     []
    ],
-   "subresource-integrity/crossorigin-anon-script.js": [
-    []
-   ],
-   "subresource-integrity/crossorigin-anon-script.js.headers": [
-    []
-   ],
    "subresource-integrity/crossorigin-anon-style.css": [
     []
    ],
    "subresource-integrity/crossorigin-anon-style.css.headers": [
     []
    ],
-   "subresource-integrity/crossorigin-creds-script.js": [
-    []
-   ],
-   "subresource-integrity/crossorigin-creds-script.js.sub.headers": [
-    []
-   ],
    "subresource-integrity/crossorigin-creds-style.css": [
     []
    ],
    "subresource-integrity/crossorigin-creds-style.css.sub.headers": [
     []
    ],
-   "subresource-integrity/crossorigin-ineligible-script.js": [
-    []
-   ],
    "subresource-integrity/crossorigin-ineligible-style.css": [
     []
    ],
@@ -166631,10 +166673,10 @@
    "subresource-integrity/ed25519-style.css.headers": [
     []
    ],
-   "subresource-integrity/matching-digest.js": [
+   "subresource-integrity/script.js": [
     []
    ],
-   "subresource-integrity/non-matching-digest.js": [
+   "subresource-integrity/sri-test-helpers.sub.js": [
     []
    ],
    "subresource-integrity/style.css": [
@@ -195892,6 +195934,12 @@
      {}
     ]
    ],
+   "css/CSS2/floats/floats-saturated-position-crash.html": [
+    [
+     "css/CSS2/floats/floats-saturated-position-crash.html",
+     {}
+    ]
+   ],
    "css/CSS2/floats/list-item-taller-than-opportunity-001.html": [
     [
      "css/CSS2/floats/list-item-taller-than-opportunity-001.html",
@@ -202030,6 +202078,12 @@
      {}
     ]
    ],
+   "css/css-position/position-absolute-crash-chrome-009.html": [
+    [
+     "css/css-position/position-absolute-crash-chrome-009.html",
+     {}
+    ]
+   ],
    "css/css-position/position-absolute-dynamic-containing-block.html": [
     [
      "css/css-position/position-absolute-dynamic-containing-block.html",
@@ -208740,6 +208794,24 @@
      {}
     ]
    ],
+   "css/css-ui/appearance-parsing.html": [
+    [
+     "css/css-ui/appearance-parsing.html",
+     {}
+    ]
+   ],
+   "css/css-ui/appearance-property.html": [
+    [
+     "css/css-ui/appearance-property.html",
+     {}
+    ]
+   ],
+   "css/css-ui/appearance-serialization.html": [
+    [
+     "css/css-ui/appearance-serialization.html",
+     {}
+    ]
+   ],
    "css/css-ui/box-sizing-027.html": [
     [
      "css/css-ui/box-sizing-027.html",
@@ -209004,6 +209076,24 @@
      {}
     ]
    ],
+   "css/css-ui/webkit-appearance-parsing.html": [
+    [
+     "css/css-ui/webkit-appearance-parsing.html",
+     {}
+    ]
+   ],
+   "css/css-ui/webkit-appearance-property.html": [
+    [
+     "css/css-ui/webkit-appearance-property.html",
+     {}
+    ]
+   ],
+   "css/css-ui/webkit-appearance-serialization.html": [
+    [
+     "css/css-ui/webkit-appearance-serialization.html",
+     {}
+    ]
+   ],
    "css/css-values/absolute-length-units-001.html": [
     [
      "css/css-values/absolute-length-units-001.html",
@@ -259861,14 +259951,6 @@
      {}
     ]
    ],
-   "payment-handler/change-payment-method.https.html": [
-    [
-     "payment-handler/change-payment-method.https.html",
-     {
-      "testdriver": true
-     }
-    ]
-   ],
    "payment-handler/idlharness.https.any.js": [
     [
      "payment-handler/idlharness.https.any.html",
@@ -259987,12 +260069,6 @@
      {}
     ]
    ],
-   "payment-handler/payment-request-event.https.html": [
-    [
-     "payment-handler/payment-request-event.https.html",
-     {}
-    ]
-   ],
    "payment-handler/same-object-attributes.https.html": [
     [
      "payment-handler/same-object-attributes.https.html",
@@ -281392,9 +281468,9 @@
      {}
     ]
    ],
-   "subresource-integrity/subresource-integrity.sub.html": [
+   "subresource-integrity/subresource-integrity.html": [
     [
-     "subresource-integrity/subresource-integrity.sub.html",
+     "subresource-integrity/subresource-integrity.html",
      {}
     ]
    ],
@@ -325480,6 +325556,10 @@
    "0ebd9a621db450a70e23af3d236b79a32aa4b56a",
    "reftest"
   ],
+  "css/CSS2/floats/floats-saturated-position-crash.html": [
+   "08a400df569c2f42670de1963724d0e028bac150",
+   "testharness"
+  ],
   "css/CSS2/floats/floats-wrap-bfc-001-left-overflow-ref.xht": [
    "9d4c968d1d687c42932cfa205775f300de4fe343",
    "support"
@@ -345456,6 +345536,10 @@
    "b130ff5561f721d69058902f9c5081b3f6471ace",
    "reftest"
   ],
+  "css/css-flexbox/flex-minimum-height-flex-items-015.html": [
+   "81fd54915f52177649ff0e0d4d1a5fb657ddccfa",
+   "reftest"
+  ],
   "css/css-flexbox/flex-minimum-width-flex-items-001.xht": [
    "cd18483ba414160c46e30bc282dec0c2fcd2f418",
    "reftest"
@@ -364332,6 +364416,10 @@
    "3d1c90930cf3f0b794ea34fcdbcb347cb6d3863c",
    "testharness"
   ],
+  "css/css-position/position-absolute-crash-chrome-009.html": [
+   "23df49cfaae5cdba6225c981d652b491f9df6784",
+   "testharness"
+  ],
   "css/css-position/position-absolute-dynamic-containing-block.html": [
    "3968f685849663574ca213fcb90dc5fb3eaffaa3",
    "testharness"
@@ -367388,10 +367476,6 @@
    "b16362153e512f9cccdeb5059f9bf9fa71c98c43",
    "testharness"
   ],
-  "css/css-shapes/spec-examples/shape-outside-018-expected.txt": [
-   "50c5fb4c6d830e013ffd2484141a0735d144c1f7",
-   "support"
-  ],
   "css/css-shapes/spec-examples/shape-outside-018.html": [
    "058e5793e5c2e6110f51299b1041da94cb3cdc79",
    "testharness"
@@ -384000,10 +384084,26 @@
    "da6f1ffa6cacd3ac0e77b343ffaa33ab44f4c2c2",
    "reftest"
   ],
+  "css/css-ui/appearance-parsing-expected.txt": [
+   "c748b8aeb1f562e25ea64ef11239f4b670fc0759",
+   "support"
+  ],
+  "css/css-ui/appearance-parsing.html": [
+   "8d6973231c0cb33fa0bd1ff83f54f98487d844e8",
+   "testharness"
+  ],
   "css/css-ui/appearance-progress-bar-001.html": [
    "1573f69911566fc4410f6d49959f83dad140ce3e",
    "reftest"
   ],
+  "css/css-ui/appearance-property-expected.txt": [
+   "90dcd4612b5f0193432bd81d19e7e89c790a9305",
+   "support"
+  ],
+  "css/css-ui/appearance-property.html": [
+   "53b98ba6055f81b7dd8510cd5be5f85b1ff4a01f",
+   "testharness"
+  ],
   "css/css-ui/appearance-push-button-001.html": [
    "3aecbb079809bb0f1e8c2a0786dee51bc6eb61d4",
    "reftest"
@@ -384016,6 +384116,14 @@
    "78741411551c667c3f3f317eeb475db883d9d7d4",
    "reftest"
   ],
+  "css/css-ui/appearance-serialization-expected.txt": [
+   "edcbb8b50929b54000a972029bf7c292cf4b5f9d",
+   "support"
+  ],
+  "css/css-ui/appearance-serialization.html": [
+   "ed968afee609b4beab0a67d7bc9fc11c686ecb33",
+   "testharness"
+  ],
   "css/css-ui/appearance-slider-horizontal-001.html": [
    "4d5b1a94fc9485b3aad8744d09dfe7d8c4ea12eb",
    "reftest"
@@ -386608,10 +386716,26 @@
    "05fb5ce25f24df1bf8fd2c72323a9deea9d0e112",
    "reftest"
   ],
+  "css/css-ui/webkit-appearance-parsing-expected.txt": [
+   "c748b8aeb1f562e25ea64ef11239f4b670fc0759",
+   "support"
+  ],
+  "css/css-ui/webkit-appearance-parsing.html": [
+   "0f08eab222493a123fd5a44afce3689385a42390",
+   "testharness"
+  ],
   "css/css-ui/webkit-appearance-progress-bar-001.html": [
    "b989502902d2b8716f7fcc0d8f18e124ec8cb94e",
    "reftest"
   ],
+  "css/css-ui/webkit-appearance-property-expected.txt": [
+   "d0e76b0a11629e39dc97e7e3ac72e1f14132b827",
+   "support"
+  ],
+  "css/css-ui/webkit-appearance-property.html": [
+   "fb3f7df2d4885c801711f91f7d1e1f7ba09a3951",
+   "testharness"
+  ],
   "css/css-ui/webkit-appearance-push-button-001.html": [
    "6f44498d4660f58e6df2b32efd218c729edc0fc8",
    "reftest"
@@ -386624,6 +386748,14 @@
    "8abd91e3c8faa9f5e02a8af6d2f4e66e1a0c6c29",
    "reftest"
   ],
+  "css/css-ui/webkit-appearance-serialization-expected.txt": [
+   "3ff4fe3e611739b95b9d38a2acffbd3559d044f2",
+   "support"
+  ],
+  "css/css-ui/webkit-appearance-serialization.html": [
+   "b325fd5d6c647063c880046804afbac23c617d7f",
+   "testharness"
+  ],
   "css/css-ui/webkit-appearance-slider-horizontal-001.html": [
    "a9375b5088ca995c545fb441cccef281242836dd",
    "reftest"
@@ -395592,6 +395724,14 @@
    "3a2d8feaeefc82c20afd3de2c2cf9ce9bf6aed11",
    "reftest"
   ],
+  "css/filter-effects/backdrop-filter-plus-mask-ref.html": [
+   "15786e7ac873c6cbb4b5e6919f8f0d02bd522a79",
+   "support"
+  ],
+  "css/filter-effects/backdrop-filter-plus-mask.html": [
+   "e1390af38d47b1eb6eec2df85299e279030aafd8",
+   "reftest"
+  ],
   "css/filter-effects/backdrop-filter-reference-filter.html": [
    "6c61a9620aa82d23375fd31900d87bcfd873a5e1",
    "reftest"
@@ -415565,7 +415705,7 @@
    "testharness"
   ],
   "html/browsers/history/the-location-interface/location_hash.html": [
-   "74d2d01543086e841cdfd39c95a2f956a9dd1f32",
+   "99f64ddea248a2fc77eb3faaa51f12ee97843eed",
    "testharness"
   ],
   "html/browsers/history/the-location-interface/location_host.html": [
@@ -448028,14 +448168,14 @@
    "7c09f5d407e890c0ed02df1217d85f2f36d722bc",
    "testharness"
   ],
+  "payment-handler/change-payment-method-manual.https.html": [
+   "56690d2b26ed671f773a16853463126e57735f77",
+   "manual"
+  ],
   "payment-handler/change-payment-method.https-expected.txt": [
    "099831a8ee28b7c1c1137beae8c7a4c916bc1d38",
    "support"
   ],
-  "payment-handler/change-payment-method.https.html": [
-   "56690d2b26ed671f773a16853463126e57735f77",
-   "testharness"
-  ],
   "payment-handler/idlharness.https.any.js": [
    "878114ec7ed878d3e754341cbffa138092fee129",
    "testharness"
@@ -448084,13 +448224,17 @@
    "fdb71aa845179cf63344d9370b1a138ef8924746",
    "testharness"
   ],
-  "payment-handler/payment-request-event.https-expected.txt": [
+  "payment-handler/payment-request-event-manual.https-expected.txt": [
    "b76179f0c74a04d903ba1911be97252db6552851",
    "support"
   ],
-  "payment-handler/payment-request-event.https.html": [
+  "payment-handler/payment-request-event-manual.https.html": [
    "3c8deb3b92fe00d7b4bdf1c57f1e33ab553acee3",
-   "testharness"
+   "manual"
+  ],
+  "payment-handler/payment-request-event.https-expected.txt": [
+   "b76179f0c74a04d903ba1911be97252db6552851",
+   "support"
   ],
   "payment-handler/register-and-activate-service-worker.js": [
    "fb54c5c06488f319b0b4152837cc6a1b530cab7a",
@@ -448541,7 +448685,7 @@
    "manual"
   ],
   "payment-request/rejects_if_not_active.https-expected.txt": [
-   "0d8fb1a5d178a8848d827d781c90d92fe18ddeaa",
+   "1fa19f3e96160290eca310571cce99cfbc513395",
    "support"
   ],
   "payment-request/rejects_if_not_active.https.html": [
@@ -449121,11 +449265,11 @@
    "testharness"
   ],
   "pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture.html": [
-   "3c768ef0574346a38a05b6153f7133c3adbb2be0",
+   "c66584a939420ca5470226b1e66d18d41cfe5d1e",
    "testharness"
   ],
   "pointerevents/pointerlock/pointerevent_pointerlock_supercedes_capture.html": [
-   "98fec4e74f15984a31714d3b908c87b426aecc5c",
+   "f3f61cf0d03fed40b44c5f2c10043bcf6a7e611f",
    "testharness"
   ],
   "pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html": [
@@ -463585,7 +463729,7 @@
    "support"
   ],
   "service-workers/service-worker/resources/worker-interception-iframe.https.html": [
-   "1ae1674a2b42be26b9f5d4b336fd9527f075cc75",
+   "87b0ed3f2498552d995cc5086fcc15b90d47097f",
    "support"
   ],
   "service-workers/service-worker/resources/worker-interception-redirect-serviceworker.js": [
@@ -463829,11 +463973,11 @@
    "testharness"
   ],
   "service-workers/service-worker/worker-interception.https-expected.txt": [
-   "e63c0271e6c98156254de5715b72e7a95b9bb5ab",
+   "3fb502afc0169f40220963532b91a9df672caedf",
    "support"
   ],
   "service-workers/service-worker/worker-interception.https.html": [
-   "f30d6e9ac5460e2b255ecdbbba381ad0540f1cec",
+   "6b66fb0f02296410f4d96076c68deb798024e7b9",
    "testharness"
   ],
   "service-workers/service-worker/xhr-response-url.https.html": [
@@ -465121,7 +465265,7 @@
    "testharness"
   ],
   "sms/interceptor.https.html": [
-   "4a6772f4416ab0550e451c710da1b48bbd9303d6",
+   "b7e8fd53bff98c6c64554149e0cd71c5f1f45686",
    "testharness"
   ],
   "sms/resources/iframe.html": [
@@ -465133,7 +465277,7 @@
    "testharness"
   ],
   "sms/sms_provider.js": [
-   "a4759419a88670ec6e3f1cbd4ac3dcb3500ad57a",
+   "eaa31ebea3113db8ecac51d6d0dec08d2d234444",
    "support"
   ],
   "sms/sms_receiver.idl": [
@@ -465261,7 +465405,7 @@
    "testharness"
   ],
   "std-toast/options.html": [
-   "bd091a128e6474a1d6b6ca4a061ff6f5bafe8595",
+   "65d3a3ae37394bc137ce1baf570226b0b7cde098",
    "testharness"
   ],
   "std-toast/resources/helpers.js": [
@@ -465876,14 +466020,6 @@
    "0ea6d22ec772aed88ec1926e7e1a23a4ba81375c",
    "support"
   ],
-  "subresource-integrity/crossorigin-anon-script.js": [
-   "8493585f1b189c680fd4f6853a2cc9072ff962f7",
-   "support"
-  ],
-  "subresource-integrity/crossorigin-anon-script.js.headers": [
-   "cb762eff806849df46dc758ef7b98b63f27f54c9",
-   "support"
-  ],
   "subresource-integrity/crossorigin-anon-style.css": [
    "3cde4df12c45211dd3637cf79524bad1f6d09c55",
    "support"
@@ -465892,14 +466028,6 @@
    "cb762eff806849df46dc758ef7b98b63f27f54c9",
    "support"
   ],
-  "subresource-integrity/crossorigin-creds-script.js": [
-   "6f39e25b40b9b552b6632123eab95e761e896558",
-   "support"
-  ],
-  "subresource-integrity/crossorigin-creds-script.js.sub.headers": [
-   "d6af1f0deabbf20cdad47e8c084db6fbf42a42b7",
-   "support"
-  ],
   "subresource-integrity/crossorigin-creds-style.css": [
    "3cde4df12c45211dd3637cf79524bad1f6d09c55",
    "support"
@@ -465908,10 +466036,6 @@
    "d6af1f0deabbf20cdad47e8c084db6fbf42a42b7",
    "support"
   ],
-  "subresource-integrity/crossorigin-ineligible-script.js": [
-   "dd2f968ef6a3ed8c51b1c338dcaa2a8669747b32",
-   "support"
-  ],
   "subresource-integrity/crossorigin-ineligible-style.css": [
    "3cde4df12c45211dd3637cf79524bad1f6d09c55",
    "support"
@@ -466016,12 +466140,12 @@
    "d2997e562fadfd208d202ce3d60d11cd937ea779",
    "testharness"
   ],
-  "subresource-integrity/matching-digest.js": [
-   "ec41325e4e9cde1231f44aaa5343c5e2689228e4",
+  "subresource-integrity/script.js": [
+   "bfca1efbf3fb10fc98dd39600966fc97817586ee",
    "support"
   ],
-  "subresource-integrity/non-matching-digest.js": [
-   "1b4943ee016b366bf7c07401da5a87dcbbb537a9",
+  "subresource-integrity/sri-test-helpers.sub.js": [
+   "22c9e9c5dd9af81d988f9c1570645925a558d070",
    "support"
   ],
   "subresource-integrity/style.css": [
@@ -466040,8 +466164,8 @@
    "1f091ebd5694f9d6e2e09a6b36c19bffcfb0ac72",
    "testharness"
   ],
-  "subresource-integrity/subresource-integrity.sub.html": [
-   "658ea6fd92eb4c7ced3f21f1051615de606e5ceb",
+  "subresource-integrity/subresource-integrity.html": [
+   "f317b0e423ec26a12196b69de84bf10cae088d5a",
    "testharness"
   ],
   "subresource-integrity/tools/ed25519.py": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-parsing-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-parsing-expected.txt
new file mode 100644
index 0000000..c748b8a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-parsing-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL parsing via attribute change steps of CSS declaration block's owner node assert_equals: expected "none" but got ""
+FAIL parsing via modification of cssText assert_equals: expected "none" but got ""
+FAIL parsing via creation of CSS declaration block assert_equals: expected "none" but got ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-parsing.html
new file mode 100644
index 0000000..8d697323
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-parsing.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Parsing of `appearance`</title>
+  <link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+test(function() {
+  var input = document.createElement('input');
+  input.setAttribute('style', 'appearance: none;');
+
+  assert_equals(input.style.getPropertyValue('appearance'), 'none');
+}, 'parsing via attribute change steps of CSS declaration block\'s owner node');
+
+test(function() {
+  var input = document.createElement('input');
+  input.style.cssText = 'appearance: none;';
+
+  assert_equals(input.style.getPropertyValue('appearance'), 'none');
+}, 'parsing via modification of cssText');
+
+test(function(t) {
+  var style = document.createElement('style');
+  style.appendChild(
+    document.createTextNode('#foo { appearance: none; }')
+  );
+  document.body.appendChild(style);
+  t.add_cleanup(function() {
+    document.body.removeChild(style);
+  });
+
+  assert_equals(style.sheet.cssRules.length, 1);
+  assert_equals(
+    style.sheet.cssRules[0].style.getPropertyValue('appearance'),
+    'none'
+  );
+}, 'parsing via creation of CSS declaration block');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-property-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-property-expected.txt
new file mode 100644
index 0000000..90dcd46
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-property-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+FAIL setProperty assert_equals: expected (string) "none" but got (undefined) undefined
+FAIL removeProperty assert_equals: expected (string) "" but got (undefined) undefined
+PASS property assignment
+FAIL getPropertyValue assert_equals: expected "none" but got ""
+FAIL property access assert_equals: expected (string) "none" but got (undefined) undefined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-property.html b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-property.html
new file mode 100644
index 0000000..53b98ba6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-property.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Property references to `appearance`</title>
+  <link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+function create(initialValue) {
+  var style = document.createElement('input').style;
+
+  style.setProperty('appearance', initialValue);
+
+  return style;
+}
+
+test(function() {
+  var style = create('');
+
+  style.setProperty('appearance', 'none');
+
+  assert_equals(style.appearance, 'none');
+}, 'setProperty');
+
+test(function() {
+  var style = create('none');
+
+  style.removeProperty('appearance');
+
+  assert_equals(style.appearance, '');
+}, 'removeProperty');
+
+test(function() {
+  var style = create('');
+
+  style['appearance'] = 'none';
+
+  assert_equals(style.appearance, 'none');
+}, 'property assignment');
+
+test(function() {
+  var style = create('none');
+
+  assert_equals(style.getPropertyValue('appearance'), 'none');
+}, 'getPropertyValue');
+
+test(function() {
+  var style = create('none');
+
+  assert_equals(style['appearance'], 'none');
+}, 'property access');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-serialization-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-serialization-expected.txt
new file mode 100644
index 0000000..edcbb8b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-serialization-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL serialization via CSSStyleDeclaration assert_equals: expected "appearance: none;" but got ""
+FAIL serialization via CSSStyleRule assert_equals: expected "#foo { appearance: none; }" but got "#foo { }"
+FAIL serialization via CSSMediaRule assert_equals: expected "@media print {\n  #foo { appearance: none; }\n}" but got "@media print {\n  #foo { }\n}"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-serialization.html b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-serialization.html
new file mode 100644
index 0000000..ed968af
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/appearance-serialization.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Serialization of `appearance`</title>
+  <link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+test(function() {
+  var input = document.createElement('input');
+  input.style.setProperty('appearance', 'none');
+
+  assert_equals(input.style.cssText, 'appearance: none;');
+}, 'serialization via CSSStyleDeclaration');
+
+test(function(t) {
+  var style = document.createElement('style');
+  document.body.appendChild(style);
+  t.add_cleanup(function() {
+    document.body.removeChild(style);
+  });
+  style.sheet.insertRule('#foo {}', 0);
+  style.sheet.cssRules[0].style.setProperty('appearance', 'none');
+
+  assert_equals(
+    style.sheet.cssRules[0].cssText, '#foo { appearance: none; }'
+  );
+}, 'serialization via CSSStyleRule');
+
+test(function(t) {
+  var style = document.createElement('style');
+  document.body.appendChild(style);
+  t.add_cleanup(function() {
+    document.body.removeChild(style);
+  });
+  style.sheet.insertRule('@media print { #foo {} }', 0);
+  style.sheet.cssRules[0].cssRules[0].style.setProperty('appearance', 'none');
+
+  assert_equals(
+    style.sheet.cssRules[0].cssText,
+    '@media print {\n  #foo { appearance: none; }\n}'
+  );
+}, 'serialization via CSSMediaRule');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-parsing-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-parsing-expected.txt
new file mode 100644
index 0000000..c748b8a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-parsing-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL parsing via attribute change steps of CSS declaration block's owner node assert_equals: expected "none" but got ""
+FAIL parsing via modification of cssText assert_equals: expected "none" but got ""
+FAIL parsing via creation of CSS declaration block assert_equals: expected "none" but got ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-parsing.html
new file mode 100644
index 0000000..0f08eab
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-parsing.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Parsing of `-webkit-appearance`</title>
+  <link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+test(function() {
+  var input = document.createElement('input');
+  input.setAttribute('style', '-webkit-appearance: none;');
+
+  assert_equals(input.style.getPropertyValue('appearance'), 'none');
+}, 'parsing via attribute change steps of CSS declaration block\'s owner node');
+
+test(function() {
+  var input = document.createElement('input');
+  input.style.cssText = '-webkit-appearance: none;';
+
+  assert_equals(input.style.getPropertyValue('appearance'), 'none');
+}, 'parsing via modification of cssText');
+
+test(function(t) {
+  var style = document.createElement('style');
+  style.appendChild(
+    document.createTextNode('#foo { -webkit-appearance: none; }')
+  );
+  document.body.appendChild(style);
+  t.add_cleanup(function() {
+    document.body.removeChild(style);
+  });
+
+  assert_equals(style.sheet.cssRules.length, 1);
+  assert_equals(
+    style.sheet.cssRules[0].style.getPropertyValue('appearance'),
+    'none'
+  );
+}, 'parsing via creation of CSS declaration block');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-property-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-property-expected.txt
new file mode 100644
index 0000000..d0e76b0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-property-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+FAIL setProperty - CSS property name assert_equals: expected (string) "none" but got (undefined) undefined
+FAIL setProperty - camel-cased property name (ignored) assert_equals: expected (string) "" but got (undefined) undefined
+FAIL setProperty - webkit-cased property name (ignored) assert_equals: expected (string) "" but got (undefined) undefined
+FAIL removeProperty - CSS property name assert_equals: expected (string) "" but got (undefined) undefined
+FAIL removeProperty - camel-cased property name (ignored) assert_equals: expected (string) "none" but got (undefined) undefined
+FAIL removeProperty - webkit-cased property name (ignored) assert_equals: expected (string) "none" but got (undefined) undefined
+FAIL property assignment - CSS property name assert_equals: expected (string) "none" but got (undefined) undefined
+FAIL property assignment - camel-cased property name assert_equals: expected (string) "none" but got (undefined) undefined
+FAIL property assignment - webkit-cased property name assert_equals: expected (string) "none" but got (undefined) undefined
+FAIL getPropertyValue - CSS property name assert_equals: expected "none" but got ""
+PASS getPropertyValue - camel-cased property name (ignored)
+PASS getPropertyValue - webkit-cased property name (ignored)
+FAIL property access - CSS property name assert_equals: expected "none" but got ""
+FAIL property access - camel-cased property name assert_equals: expected "none" but got ""
+FAIL property access - webkit-cased property name assert_equals: expected "none" but got ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-property.html b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-property.html
new file mode 100644
index 0000000..fb3f7df2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-property.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Property references to `-webkit-appearance`</title>
+  <link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+function create(initialValue) {
+  var style = document.createElement('input').style;
+
+  style.setProperty('appearance', initialValue);
+
+  return style;
+}
+
+test(function() {
+  var style = create('');
+
+  style.setProperty('-webkit-appearance', 'none');
+
+  assert_equals(style.appearance, 'none');
+}, 'setProperty - CSS property name');
+
+test(function() {
+  var style = create('');
+
+  style.setProperty('WebkitAppearance', 'none');
+
+  assert_equals(style.appearance, '');
+}, 'setProperty - camel-cased property name (ignored)');
+
+test(function() {
+  var style = create('');
+
+  style.setProperty('webkitAppearance', 'none');
+
+  assert_equals(style.appearance, '');
+}, 'setProperty - webkit-cased property name (ignored)');
+
+test(function() {
+  var style = create('none');
+
+  style.removeProperty('-webkit-appearance');
+
+  assert_equals(style.appearance, '');
+}, 'removeProperty - CSS property name');
+
+test(function() {
+  var style = create('none');
+
+  style.removeProperty('WebkitAppearance');
+
+  assert_equals(style.appearance, 'none');
+}, 'removeProperty - camel-cased property name (ignored)');
+
+test(function() {
+  var style = create('none');
+
+  style.removeProperty('webkitAppearance');
+
+  assert_equals(style.appearance, 'none');
+}, 'removeProperty - webkit-cased property name (ignored)');
+
+test(function() {
+  var style = create('');
+
+  style['-webkit-appearance'] = 'none';
+
+  assert_equals(style.appearance, 'none');
+}, 'property assignment - CSS property name');
+
+test(function() {
+  var style = create('');
+
+  style['WebkitAppearance'] = 'none';
+
+  assert_equals(style.appearance, 'none');
+}, 'property assignment - camel-cased property name');
+
+test(function() {
+  var style = create('');
+
+  style['webkitAppearance'] = 'none';
+
+  assert_equals(style.appearance, 'none');
+}, 'property assignment - webkit-cased property name');
+
+test(function() {
+  var style = create('none');
+
+  assert_equals(style.getPropertyValue('-webkit-appearance'), 'none');
+}, 'getPropertyValue - CSS property name');
+
+test(function() {
+  var style = create('none');
+
+  assert_equals(style.getPropertyValue('WebkitAppearance'), '');
+}, 'getPropertyValue - camel-cased property name (ignored)');
+
+test(function() {
+  var style = create('none');
+
+  assert_equals(style.getPropertyValue('webkitAppearance'), '');
+}, 'getPropertyValue - webkit-cased property name (ignored)');
+
+test(function() {
+  var style = create('none');
+
+  assert_equals(style['-webkit-appearance'], 'none');
+}, 'property access - CSS property name');
+
+test(function() {
+  var style = create('none');
+
+  assert_equals(style['WebkitAppearance'], 'none');
+}, 'property access - camel-cased property name');
+
+test(function() {
+  var style = create('none');
+
+  assert_equals(style['webkitAppearance'], 'none');
+}, 'property access - webkit-cased property name');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-serialization-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-serialization-expected.txt
new file mode 100644
index 0000000..3ff4fe3e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-serialization-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL serialization via CSSStyleDeclaration assert_equals: expected "appearance: none;" but got "-webkit-appearance: none;"
+FAIL serialization via CSSStyleRule assert_equals: expected "#foo { appearance: none; }" but got "#foo { -webkit-appearance: none; }"
+FAIL serialization via CSSMediaRule assert_equals: expected "@media print {\n  #foo { appearance: none; }\n}" but got "@media print {\n  #foo { -webkit-appearance: none; }\n}"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-serialization.html b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-serialization.html
new file mode 100644
index 0000000..b325fd5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/webkit-appearance-serialization.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Serialization of `-webkit-appearance`</title>
+  <link rel="help" href="https://drafts.csswg.org/css-ui-4/#appearance-switching">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+test(function() {
+  var input = document.createElement('input');
+  input.style.setProperty('-webkit-appearance', 'none');
+
+  assert_equals(input.style.cssText, 'appearance: none;');
+}, 'serialization via CSSStyleDeclaration');
+
+test(function(t) {
+  var style = document.createElement('style');
+  document.body.appendChild(style);
+  t.add_cleanup(function() {
+    document.body.removeChild(style);
+  });
+  style.sheet.insertRule('#foo {}', 0);
+  style.sheet.cssRules[0].style.setProperty('-webkit-appearance', 'none');
+
+  assert_equals(
+    style.sheet.cssRules[0].cssText, '#foo { appearance: none; }'
+  );
+}, 'serialization via CSSStyleRule');
+
+test(function(t) {
+  var style = document.createElement('style');
+  document.body.appendChild(style);
+  t.add_cleanup(function() {
+    document.body.removeChild(style);
+  });
+  style.sheet.insertRule('@media print { #foo {} }', 0);
+  style.sheet.cssRules[0].cssRules[0].style.setProperty('-webkit-appearance', 'none');
+
+  assert_equals(
+    style.sheet.cssRules[0].cssText,
+    '@media print {\n  #foo { appearance: none; }\n}'
+  );
+}, 'serialization via CSSMediaRule');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location_hash.html b/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location_hash.html
index 74d2d01..99f64dd 100644
--- a/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location_hash.html
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location_hash.html
@@ -23,6 +23,8 @@
       var frameWin = document.getElementById("srcdoc-iframe").contentWindow;
       assert_equals(frameWin.location.href, "about:srcdoc");
       assert_equals(frameWin.scrollY, 0, "Should not have scrolled yet");
+      frameWin.location.hash = "";
+      assert_equals(frameWin.location.href, "about:srcdoc#", "Setting an empty hash should result in an empty fragment, not no fragment.");
       frameWin.location.hash = "test";
       assert_equals(frameWin.location.href, "about:srcdoc#test");
       assert_true(frameWin.scrollY > frameWin.innerHeight,
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/change-payment-method.https.html b/third_party/blink/web_tests/external/wpt/payment-handler/change-payment-method-manual.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/payment-handler/change-payment-method.https.html
rename to third_party/blink/web_tests/external/wpt/payment-handler/change-payment-method-manual.https.html
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event-manual.https-expected.txt b/third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event-manual.https-expected.txt
new file mode 100644
index 0000000..b76179f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event-manual.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Tests for PaymentRequestEvent Not allowed to install this payment handler
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event.https.html b/third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event-manual.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event.https.html
rename to third_party/blink/web_tests/external/wpt/payment-handler/payment-request-event-manual.https.html
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/worker-interception-iframe.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/worker-interception-iframe.https.html
index 1ae1674..87b0ed3 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/worker-interception-iframe.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/worker-interception-iframe.https.html
@@ -2,65 +2,58 @@
 <script src="test-helpers.sub.js?pipe=sub"></script>
 <script>
 
-let worker_type;
+class TestRunner {
+  constructor(script_url, worker_type) {
+    this.script_url = script_url;
+    this.worker_type = worker_type;
+  }
 
-async function boilerplate_test(url, msg) {
-  let data;
-  if (worker_type === 'worker') {
-    const worker = new Worker(url);
-    data = await new Promise((resolve, reject) => {
+  // Tests subresource requests on a worker.
+  async run(subresource_type) {
+    let data;
+    if (this.worker_type === 'worker') {
+      data = await this.runOnDedicatedWorker(subresource_type);
+    } else if (this.worker_type === 'sharedworker') {
+      data = await this.runOnSharedWorker(subresource_type);
+    } else {
+      data = 'Unexpected worker type! ' + worker_type;
+    }
+    assert_equals(data, 'This load was successfully intercepted.');
+  }
+
+  async runOnDedicatedWorker(subresource_type) {
+    const worker = new Worker(this.script_url);
+    const data = await new Promise((resolve, reject) => {
       worker.onmessage = e => resolve(e.data);
       worker.onerror = e => reject(e);
-      worker.postMessage(msg);
+      worker.postMessage(subresource_type);
     });
-  } else if (worker_type === 'sharedworker') {
-    const worker = new SharedWorker(url);
-    data = await new Promise((resolve, reject) => {
+    worker.terminate();
+    return data;
+  }
+
+  async runOnSharedWorker(subresource_type) {
+    const worker = new SharedWorker(this.script_url);
+    return await new Promise((resolve, reject) => {
       worker.port.onmessage = e => resolve(e.data);
       worker.onerror = e => reject(e);
-      worker.port.postMessage(msg);
+      worker.port.postMessage(subresource_type);
     });
-  } else {
-    data = 'Unexpected worker type! ' + worker_type;
   }
-  assert_equals(data, 'This load was successfully intercepted.');
 }
 
-function xhr_test() {
-  return boilerplate_test('load_worker.js', 'xhr');
-}
-
-function fetch_test() {
-  return boilerplate_test('load_worker.js', 'fetch');
-}
-
-function importScripts_test() {
-  return boilerplate_test('load_worker.js', 'importScripts');
-}
-
-function nested_worker_xhr_test() {
-  return boilerplate_test('nested_load_worker.js', 'xhr');
-}
-
-function nested_worker_fetch_test() {
-  return boilerplate_test('nested_load_worker.js', 'fetch');
-}
-
-function nested_worker_importScripts_test() {
-  return boilerplate_test('nested_load_worker.js', 'importScripts');
-}
-
-window.addEventListener('message', evt => {
-  worker_type = evt.data;
+window.addEventListener('message', async evt => {
+  const test_runner = new TestRunner(evt.data.script, evt.data.type);
   const port = evt.ports[0];
-  xhr_test()
-      .then(fetch_test)
-      .then(importScripts_test)
-      .then(nested_worker_xhr_test)
-      .then(nested_worker_fetch_test)
-      .then(nested_worker_importScripts_test)
-      .then(() => port.postMessage({results: 'finish'}))
-      .catch(e => port.postMessage({results: 'failure:' + e}));
+
+  try {
+    await test_runner.run('xhr');
+    await test_runner.run('fetch');
+    await test_runner.run('importScripts');
+    port.postMessage({results: 'finish'});
+  } catch(e) {
+    port.postMessage({results: 'failure:' + e});
+  }
 });
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-interception.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-interception.https-expected.txt
index e63c027..3fb502a 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-interception.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-interception.https-expected.txt
@@ -7,7 +7,9 @@
 PASS Verify a cors worker script served by a service worker fails shared worker start.
 PASS Verify a no-cors cross-origin worker script served by a service worker fails dedicated worker start.
 PASS Verify a no-cors cross-origin worker script served by a service worker fails shared worker start.
-PASS Verify subresource requests on a dedicated worker controlled by a service worker.
-FAIL Verify subresource requests on a shared worker controlled by a service worker. assert_equals: expected "finish" but got "failure:Error: assert_equals: expected \"This load was successfully intercepted.\" but got \"Unexpected error! Worker is not defined\""
+FAIL Verify subresource requests on a dedicated worker controlled by a service worker. assert_equals: expected "finish" but got "failure:Error: assert_equals: expected \"This load was successfully intercepted.\" but got \"{\\\"error\\": {\\"message\\": \\"\\", \\"code\\": 404}}\""
+PASS Verify subresource requests on a shared worker controlled by a service worker.
+FAIL Verify subresource requests on a nested dedicated worker created from a dedicated worker and controlled by a service worker. assert_equals: expected "finish" but got "failure:Error: assert_equals: expected \"This load was successfully intercepted.\" but got \"{\\\"error\\": {\\"message\\": \\"\\", \\"code\\": 404}}\""
+FAIL Verify subresource requests on a nested dedicated worker created from a shared worker and controlled by a service worker. assert_equals: expected "finish" but got "failure:Error: assert_equals: expected \"This load was successfully intercepted.\" but got \"Unexpected error! Worker is not defined\""
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-interception.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-interception.https.html
index f30d6e9..6b66fb0f0 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-interception.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-interception.https.html
@@ -15,7 +15,7 @@
 }
 
 promise_test(async t => {
-  const worker_url = 'resources/dummy-synthesized-worker.js';
+  const worker_url = 'resources/dummy-synthesized-worker.js?dedicated';
   const service_worker = 'resources/dummy-worker-interceptor.js';
   const scope = worker_url;
 
@@ -30,7 +30,7 @@
    `document is intercepted by worker's own service worker.`);
 
 promise_test(async t => {
-  const worker_url = 'resources/dummy-synthesized-worker.js';
+  const worker_url = 'resources/dummy-synthesized-worker.js?shared';
   const service_worker = 'resources/dummy-worker-interceptor.js';
   const scope = worker_url;
 
@@ -45,7 +45,7 @@
    `document is intercepted by worker's own service worker.`);
 
 promise_test(async t => {
-  const worker_url = 'resources/dummy-same-origin-worker.js';
+  const worker_url = 'resources/dummy-same-origin-worker.js?dedicated';
   const service_worker = 'resources/dummy-worker-interceptor.js';
   const scope = worker_url;
 
@@ -60,7 +60,7 @@
    'in starting a dedicated worker.');
 
 promise_test(async t => {
-  const worker_url = 'resources/dummy-same-origin-worker.js';
+  const worker_url = 'resources/dummy-same-origin-worker.js?shared';
   const service_worker = 'resources/dummy-worker-interceptor.js';
   const scope = worker_url;
 
@@ -75,7 +75,7 @@
    'in starting a shared worker.');
 
 promise_test(async t => {
-  const worker_url = 'resources/dummy-cors-worker.js';
+  const worker_url = 'resources/dummy-cors-worker.js?dedicated';
   const service_worker = 'resources/dummy-worker-interceptor.js';
   const scope = worker_url;
 
@@ -87,7 +87,7 @@
    'worker start.');
 
 promise_test(async t => {
-  const worker_url = 'resources/dummy-cors-worker.js';
+  const worker_url = 'resources/dummy-cors-worker.js?shared';
   const service_worker = 'resources/dummy-worker-interceptor.js';
   const scope = worker_url;
 
@@ -99,7 +99,7 @@
    'worker start.');
 
 promise_test(async t => {
-  const worker_url = 'resources/dummy-no-cors-worker.js';
+  const worker_url = 'resources/dummy-no-cors-worker.js?dedicated';
   const service_worker = 'resources/dummy-worker-interceptor.js';
   const scope = worker_url;
 
@@ -111,7 +111,7 @@
    'fails dedicated worker start.');
 
 promise_test(async t => {
-  const worker_url = 'resources/dummy-no-cors-worker.js';
+  const worker_url = 'resources/dummy-no-cors-worker.js?shared';
   const service_worker = 'resources/dummy-worker-interceptor.js';
   const scope = worker_url;
 
@@ -125,7 +125,7 @@
 promise_test(async t => {
   const subdoc_url = 'resources/worker-interception-iframe.https.html';
   const service_worker = 'resources/worker-load-interceptor.js';
-  const scope = 'resources/';
+  const scope = 'resources/load_worker.js?dedicated';
 
   await setup_service_worker(t, service_worker, scope);
   const frame = await with_iframe(subdoc_url);
@@ -133,7 +133,9 @@
   const data = await new Promise((resolve, reject) => {
     const channel = new MessageChannel();
     channel.port1.onmessage = e => resolve(e.data);
-    frame.contentWindow.postMessage('worker', '*', [channel.port2]);
+    frame.contentWindow.postMessage(
+        { script: 'load_worker.js?dedicated', type: 'worker' },
+        '*', [channel.port2]);
   });
   assert_equals(data.results, 'finish');
 }, 'Verify subresource requests on a dedicated worker controlled by a ' +
@@ -142,7 +144,7 @@
 promise_test(async t => {
   const subdoc_url = 'resources/worker-interception-iframe.https.html';
   const service_worker = 'resources/worker-load-interceptor.js';
-  const scope = 'resources/';
+  const scope = 'resources/load_worker.js?shared';
 
   await setup_service_worker(t, service_worker, scope);
   const frame = await with_iframe(subdoc_url);
@@ -150,11 +152,51 @@
   const data = await new Promise((resolve, reject) => {
     const channel = new MessageChannel();
     channel.port1.onmessage = e => resolve(e.data);
-    frame.contentWindow.postMessage('sharedworker', '*', [channel.port2]);
+    frame.contentWindow.postMessage(
+        { script: 'load_worker.js?shared', type: 'sharedworker' },
+        '*', [channel.port2]);
   });
   assert_equals(data.results, 'finish');
 }, 'Verify subresource requests on a shared worker controlled by a service ' +
    'worker.');
 
+promise_test(async t => {
+  const subdoc_url = 'resources/worker-interception-iframe.https.html';
+  const service_worker = 'resources/worker-load-interceptor.js';
+  const scope = 'resources/nested_load_worker.js?dedicated';
+
+  await setup_service_worker(t, service_worker, scope);
+  const frame = await with_iframe(subdoc_url);
+  t.add_cleanup(() => frame.remove());
+  const data = await new Promise((resolve, reject) => {
+    const channel = new MessageChannel();
+    channel.port1.onmessage = e => resolve(e.data);
+    frame.contentWindow.postMessage(
+        { script: 'nested_load_worker.js?dedicated', type: 'worker' },
+        '*', [channel.port2]);
+  });
+  assert_equals(data.results, 'finish');
+}, 'Verify subresource requests on a nested dedicated worker created from a ' +
+   'dedicated worker and controlled by a service worker.');
+
+promise_test(async t => {
+  const subdoc_url = 'resources/worker-interception-iframe.https.html';
+  const service_worker = 'resources/worker-load-interceptor.js';
+  const scope = 'resources/nested_load_worker.js?shared';
+
+  await setup_service_worker(t, service_worker, scope);
+  const frame = await with_iframe(subdoc_url);
+  t.add_cleanup(() => frame.remove());
+  const data = await new Promise((resolve, reject) => {
+    const channel = new MessageChannel();
+    channel.port1.onmessage = e => resolve(e.data);
+    frame.contentWindow.postMessage(
+        { script: 'nested_load_worker.js?shared', type: 'sharedworker' },
+        '*', [channel.port2]);
+  });
+  assert_equals(data.results, 'finish');
+}, 'Verify subresource requests on a nested dedicated worker created from a ' +
+   'shared worker and controlled by a service worker.');
+
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/std-toast/options.html b/third_party/blink/web_tests/external/wpt/std-toast/options.html
index bd091a12..65d3a3a 100644
--- a/third_party/blink/web_tests/external/wpt/std-toast/options.html
+++ b/third_party/blink/web_tests/external/wpt/std-toast/options.html
@@ -15,11 +15,11 @@
 
 // message
 test(() => {
-    const toast =  new StdToastElement(false);
+    const toast =  new StdToastElement('test');
     document.body.appendChild(toast);
 
-    assert_equals(toast.textContent, 'false');
-}), 'passing false as message converts to the string `false`';
+    assert_equals(toast.textContent, 'test');
+}, 'passing test string as message sets the message properly');
 
 test(() => {
     const toast = new StdToastElement('<p>rich text</p>');
@@ -30,6 +30,34 @@
 }, 'passing markup to the constructor does not pass through the markup behaviors');
 
 test(() => {
+    const toast =  new StdToastElement(false);
+    document.body.appendChild(toast);
+
+    assert_equals(toast.textContent, 'false');
+}), 'passing false as message converts to the string `false`';
+
+test(() => {
+    const toast =  new StdToastElement();
+    document.body.appendChild(toast);
+
+    assert_equals(toast.textContent, '');
+}, 'passing nothing as message sets the message to the empty string');
+
+test(() => {
+    const toast =  new StdToastElement(undefined);
+    document.body.appendChild(toast);
+
+    assert_equals(toast.textContent, '');
+}, 'passing `undefined` as message sets the message to the empty string');
+
+test(() => {
+    const toast =  new StdToastElement('');
+    document.body.appendChild(toast);
+
+    assert_equals(toast.textContent, '');
+}, 'passing empty string as message sets the message to the empty string');
+
+test(() => {
     const toastString = '<std-toast id="test">test</std-toast>';
     document.body.innerHTML = toastString;
     const toast = document.body.querySelector('#test');
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-anon-script.js b/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-anon-script.js
deleted file mode 100644
index 8493585f1..0000000
--- a/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-anon-script.js
+++ /dev/null
@@ -1 +0,0 @@
-crossorigin_anon_script=true;
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-anon-script.js.headers b/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-anon-script.js.headers
deleted file mode 100644
index cb762eff..0000000
--- a/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-anon-script.js.headers
+++ /dev/null
@@ -1 +0,0 @@
-Access-Control-Allow-Origin: *
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-creds-script.js b/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-creds-script.js
deleted file mode 100644
index 6f39e25..0000000
--- a/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-creds-script.js
+++ /dev/null
@@ -1 +0,0 @@
-crossorigin_creds_script=true;
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-creds-script.js.sub.headers b/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-creds-script.js.sub.headers
deleted file mode 100644
index d6af1f0..0000000
--- a/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-creds-script.js.sub.headers
+++ /dev/null
@@ -1,2 +0,0 @@
-Access-Control-Allow-Origin: {{location[scheme]}}://{{domains[]}}{{GET[acao_port]}}
-Access-Control-Allow-Credentials: true
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-ineligible-script.js b/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-ineligible-script.js
deleted file mode 100644
index dd2f968..0000000
--- a/third_party/blink/web_tests/external/wpt/subresource-integrity/crossorigin-ineligible-script.js
+++ /dev/null
@@ -1 +0,0 @@
-crossorigin_ineligible_script=true;
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/matching-digest.js b/third_party/blink/web_tests/external/wpt/subresource-integrity/matching-digest.js
deleted file mode 100644
index ec41325e..0000000
--- a/third_party/blink/web_tests/external/wpt/subresource-integrity/matching-digest.js
+++ /dev/null
@@ -1 +0,0 @@
-matching_digest=true;
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/non-matching-digest.js b/third_party/blink/web_tests/external/wpt/subresource-integrity/non-matching-digest.js
deleted file mode 100644
index 1b4943e..0000000
--- a/third_party/blink/web_tests/external/wpt/subresource-integrity/non-matching-digest.js
+++ /dev/null
@@ -1 +0,0 @@
-non_matching_digest=true;
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/script.js b/third_party/blink/web_tests/external/wpt/subresource-integrity/script.js
new file mode 100644
index 0000000..bfca1efb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/subresource-integrity/script.js
@@ -0,0 +1 @@
+// nothing important.
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/sri-test-helpers.sub.js b/third_party/blink/web_tests/external/wpt/subresource-integrity/sri-test-helpers.sub.js
new file mode 100644
index 0000000..22c9e9c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/subresource-integrity/sri-test-helpers.sub.js
@@ -0,0 +1,38 @@
+// This horrible hack is needed for the 'use-credentials' tests because, on
+// response, if port 80 or 443 is the current port, it will not appear to
+// the browser as part of the origin string. Since the origin *string* is
+// used for CORS access control, instead of the origin itself, if there
+// isn't an exact string match, the check will fail. For example,
+// "http://example.com" would not match "http://example.com:80", because
+// they are not exact string matches, even though the origins are the same.
+//
+// Thus, we only want the Access-Control-Allow-Origin header to have
+// the port if it's not port 80 or 443, since the user agent will elide the
+// ports in those cases.
+const main_domain = "{{domains[]}}";
+const www_domain = "{{domains[www]}}";
+const default_port = (location.protocol === "https:") ? "{{ports[https][0]}}" :
+                                                        "{{ports[http][0]}}";
+
+const port_string = (default_port !== "80" && default_port !== "443") ?
+                      `:${default_port}` : "";
+const www_host_and_port = www_domain + port_string;
+
+// General resource prefixes.
+const same_origin_prefix = '/subresource-integrity/';
+const xorigin_prefix = `${location.protocol}//${www_host_and_port}/subresource-integrity/`;
+
+// Note that all of these style URLs have query parameters started, so any
+// additional parameters should be appended starting with '&'.
+const xorigin_anon_style = location.protocol
+  + '//' + www_host_and_port
+  + '/subresource-integrity/crossorigin-anon-style.css?';
+
+const xorigin_creds_style = location.protocol
+  + '//' + www_host_and_port
+  + '/subresource-integrity/crossorigin-creds-style.css?acao_port='
+  + port_string;
+
+const xorigin_ineligible_style = location.protocol
+  + '//' + www_host_and_port
+  + '/subresource-integrity/crossorigin-ineligible-style.css?';
diff --git a/third_party/blink/web_tests/external/wpt/subresource-integrity/subresource-integrity.sub.html b/third_party/blink/web_tests/external/wpt/subresource-integrity/subresource-integrity.html
similarity index 74%
rename from third_party/blink/web_tests/external/wpt/subresource-integrity/subresource-integrity.sub.html
rename to third_party/blink/web_tests/external/wpt/subresource-integrity/subresource-integrity.html
index 658ea6fd..f317b0e4 100644
--- a/third_party/blink/web_tests/external/wpt/subresource-integrity/subresource-integrity.sub.html
+++ b/third_party/blink/web_tests/external/wpt/subresource-integrity/subresource-integrity.html
@@ -4,64 +4,13 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/sriharness.js"></script>
+<script src="/common/utils.js"></script>
+<script src="sri-test-helpers.sub.js"></script>
 
 <div id="log"></div>
 
 <div id="container"></div>
 <script>
-    // This horrible hack is needed for the 'use-credentials' tests because, on
-    // response, if port 80 or 443 is the current port, it will not appear to
-    // the browser as part of the origin string. Since the origin *string* is
-    // used for CORS access control, instead of the origin itself, if there
-    // isn't an exact string match, the check will fail. For example,
-    // "http://example.com" would not match "http://example.com:80", because
-    // they are not exact string matches, even though the origins are the same.
-    //
-    // Thus, we only want the Access-Control-Allow-Origin header to have
-    // the port if it's not port 80 or 443, since the user agent will elide the
-    // ports in those cases.
-    var main_domain = "{{domains[]}}";
-    var www_domain = "{{domains[www]}}";
-    var default_port = "{{ports[http][0]}}";
-    if (location.protocol === "https:") {
-      default_port = "{{ports[https][0]}}";
-    }
-
-    var port_string = "";
-    if (default_port !== "80" && default_port !== "443")
-      port_string = ":" + default_port;
-
-    www_host_and_port = www_domain + port_string;
-
-    // <script> tests
-    var xorigin_anon_script = location.protocol
-      + '//' + www_host_and_port
-      + '/subresource-integrity/crossorigin-anon-script.js';
-
-    var xorigin_creds_script = location.protocol
-      + '//' + www_host_and_port
-      + '/subresource-integrity/crossorigin-creds-script.js?acao_port='
-      + port_string;
-
-    var xorigin_ineligible_script = location.protocol
-      + '//' + www_host_and_port
-      + '/subresource-integrity/crossorigin-ineligible-script.js';
-
-    // Note that all of these style URLs have query parameters started, so any
-    // additional parameters should be appended starting with '&'.
-    var xorigin_anon_style = location.protocol
-      + '//' + www_host_and_port
-      + '/subresource-integrity/crossorigin-anon-style.css?';
-
-    var xorigin_creds_style = location.protocol
-      + '//' + www_host_and_port
-      + '/subresource-integrity/crossorigin-creds-style.css?acao_port='
-      + port_string;
-
-    var xorigin_ineligible_style = location.protocol
-      + '//' + www_host_and_port
-      + '/subresource-integrity/crossorigin-ineligible-style.css?';
-
     var style_tests = [];
     style_tests.execute = function() {
         if (this.length > 0) {
@@ -78,78 +27,78 @@
     new SRIScriptTest(
         true,
         "Same-origin with correct sha256 hash.",
-        "matching-digest.js",
-        "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E="
+        `${same_origin_prefix}script.js?${token()}`,
+        "sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA="
     ).execute();
 
     new SRIScriptTest(
         true,
         "Same-origin with correct sha384 hash.",
-        "matching-digest.js",
-        "sha384-BDRTPSywZFyxfLEAzaLcL4FfERBgJgXfEkuT0r04LG93Yqn1PWNYPZMomaqEfE3H"
+        `${same_origin_prefix}script.js?${token()}`,
+        "sha384-cINXh+nCzEHPWzXS7eoT+vYMBpyqczOybRLNU3XAButFWCRhHT5hLByIbPRqIm2f"
     ).execute();
 
     new SRIScriptTest(
         true,
         "Same-origin with correct sha512 hash.",
-        "matching-digest.js",
-        "sha512-geByvIIRspbnUnwooKGNNCb39nvg+EW0O9hDScTXeo/9pVZztLSUYU3LNV6H0lZapo8bCJUpyPPLAzE9fDzpxg=="
+        `${same_origin_prefix}script.js?${token()}`,
+        "sha512-KZdenhzBd7X7Q/vmaOSyvFz1CGdoVt26xzCZjlkU9lfBEK+V/ougGys7iYDi0+tOHIQSQa87bIqx95R7GU7I9Q=="
     ).execute();
 
     new SRIScriptTest(
         true,
         "Same-origin with empty integrity.",
-        "matching-digest.js",
+        `${same_origin_prefix}script.js?${token()}`,
         ""
     ).execute();
 
     new SRIScriptTest(
         false,
         "Same-origin with incorrect hash.",
-        "non-matching-digest.js",
+        `${same_origin_prefix}script.js?${token()}`,
         "sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
     ).execute();
 
     new SRIScriptTest(
         true,
         "Same-origin with multiple sha256 hashes, including correct.",
-        "matching-digest.js",
-        "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E= sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+        `${same_origin_prefix}script.js?${token()}`,
+        "sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA= sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
     ).execute();
 
     new SRIScriptTest(
         true,
         "Same-origin with multiple sha256 hashes, including unknown algorithm.",
-        "matching-digest.js",
-        "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E= foo666-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+        `${same_origin_prefix}script.js?${token()}`,
+        "sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA= foo666-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
     ).execute();
 
     new SRIScriptTest(
         true,
         "Same-origin with sha256 mismatch, sha512 match",
-        "matching-digest.js",
-        "sha512-geByvIIRspbnUnwooKGNNCb39nvg+EW0O9hDScTXeo/9pVZztLSUYU3LNV6H0lZapo8bCJUpyPPLAzE9fDzpxg== sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
+        `${same_origin_prefix}script.js?${token()}`,
+        "sha512-KZdenhzBd7X7Q/vmaOSyvFz1CGdoVt26xzCZjlkU9lfBEK+V/ougGys7iYDi0+tOHIQSQa87bIqx95R7GU7I9Q== sha256-deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"
     ).execute();
 
     new SRIScriptTest(
         false,
         "Same-origin with sha256 match, sha512 mismatch",
-        "matching-digest.js",
-        "sha512-deadbeefspbnUnwooKGNNCb39nvg+EW0O9hDScTXeo/9pVZztLSUYU3LNV6H0lZapo8bCJUpyPPLAzE9fDzpxg== sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E="
+        `${same_origin_prefix}script.js?${token()}`,
+        "sha512-deadbeefspbnUnwooKGNNCb39nvg+EW0O9hDScTXeo/9pVZztLSUYU3LNV6H0lZapo8bCJUpyPPLAzE9fDzpxg== sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA="
     ).execute();
 
     new SRIScriptTest(
         true,
         "<crossorigin='anonymous'> with correct hash, ACAO: *",
-        xorigin_anon_script,
-        "sha256-51AjITq701Y0yKSx3/UoIKtIY2UQ9+H8WGyyMuOWOC0=",
+        `${xorigin_prefix}script.js?${token()}&pipe=header(Access-Control-Allow-Origin,*)`,
+        "sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA=",
         "anonymous"
     ).execute();
 
     new SRIScriptTest(
         false,
         "<crossorigin='anonymous'> with incorrect hash, ACAO: *",
-        xorigin_anon_script,
+        `${xorigin_prefix}script.js?${token()}&pipe=header(Access-Control-Allow-Origin,*)`,
         "sha256-deadbeefcSLlbFZCj1OACLxTxVck2TOrBTEdUbwz1yU=",
         "anonymous"
     ).execute();
@@ -157,15 +106,15 @@
     new SRIScriptTest(
         true,
         "<crossorigin='use-credentials'> with correct hash, CORS-eligible",
-        xorigin_creds_script,
-        "sha256-IaGApVboXPQxVSm2wVFmhMq1Yu37gWklajgMdxKLIvc=",
+        `${xorigin_prefix}script.js?${token()}&pipe=header(Access-Control-Allow-Credentials,true)|header(Access-Control-Allow-Origin,${location.origin})`,
+        "sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA=",
         "use-credentials"
     ).execute();
 
     new SRIScriptTest(
         false,
         "<crossorigin='use-credentials'> with incorrect hash CORS-eligible",
-        xorigin_creds_script,
+        `${xorigin_prefix}script.js?${token()}&pipe=header(Access-Control-Allow-Credentials,true)|header(Access-Control-Allow-Origin,${location.origin})`,
         "sha256-deadbeef2S+pTRZgiw3DWrhC6JLDlt2zRyGpwH7unU8=",
         "use-credentials"
     ).execute();
@@ -173,43 +122,43 @@
     new SRIScriptTest(
         false,
         "<crossorigin='anonymous'> with CORS-ineligible resource",
-        xorigin_ineligible_script,
-        "sha256-F5fXKTX7SiWjtgybxiBZIo2qhh2WiQnNx372E60XrOo=",
+        `${xorigin_prefix}script.js?${token()}`, /* no ACAO header makes this CORS-ineligible */
+        "sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA=",
         "anonymous"
     ).execute();
 
     new SRIScriptTest(
         false,
         "Cross-origin, not CORS request, with correct hash",
-        xorigin_anon_script,
-        "sha256-51AjITq701Y0yKSx3/UoIKtIY2UQ9+H8WGyyMuOWOC0="
+        `${xorigin_prefix}script.js?${token()}&pipe=header(Access-Control-Allow-Origin,*)`,
+        "sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA="
     ).execute();
 
     new SRIScriptTest(
         false,
         "Cross-origin, not CORS request, with hash mismatch",
-        xorigin_anon_script,
+        `${xorigin_prefix}script.js?${token()}&pipe=header(Access-Control-Allow-Origin,*)`,
         "sha256-deadbeef01Y0yKSx3/UoIKtIY2UQ9+H8WGyyMuOWOC0="
     ).execute();
 
     new SRIScriptTest(
         true,
         "Cross-origin, empty integrity",
-        xorigin_anon_script,
+        `${xorigin_prefix}script.js?${token()}&pipe=header(Access-Control-Allow-Origin,*)`,
         ""
     ).execute();
 
     new SRIScriptTest(
         true,
         "Same-origin with correct hash, options.",
-        "matching-digest.js",
-        "sha256-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E=?foo=bar?spam=eggs"
+        `${same_origin_prefix}script.js?${token()}`,
+        "sha256-Bu681KMnQ15RYHFvsYdWumweeFAw0hJDTFt9seErghA=?foo=bar?spam=eggs"
     ).execute();
 
     new SRIScriptTest(
         true,
         "Same-origin with unknown algorithm only.",
-        "matching-digest.js",
+        `${same_origin_prefix}script.js?${token()}`,
         "foo666-U9WYDtBWkcHx13+9UKk/3Q5eoqDc4YGxYb07EPWzb9E="
     ).execute();
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/wpt/run.py b/third_party/blink/web_tests/external/wpt/tools/wpt/run.py
index 0d7fa22..fec2afc 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wpt/run.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wpt/run.py
@@ -357,6 +357,8 @@
         if kwargs["browser_channel"] == "dev":
             logger.info("Automatically turning on experimental features for Edge Dev")
             kwargs["binary_args"].append("--enable-experimental-web-platform-features")
+        # HACK(Hexcles): work around https://github.com/web-platform-tests/wpt/issues/17403
+        kwargs["webdriver_args"].append("--disable-build-check")
 
 
 class Edge(BrowserSetup):
diff --git a/third_party/blink/web_tests/fast/loader/hashchange-event-expected.txt b/third_party/blink/web_tests/fast/loader/hashchange-event-expected.txt
index 8e69dc91..d6dd0c2 100644
--- a/third_party/blink/web_tests/fast/loader/hashchange-event-expected.txt
+++ b/third_party/blink/web_tests/fast/loader/hashchange-event-expected.txt
@@ -22,6 +22,12 @@
 ALERT: Test number 1.
 hashchange event received by body-onhashchange-attribute.
 A total of 8 events have been received.
+ALERT: Test number 1.
+hashchange event received by window-event-listener.
+A total of 9 events have been received.
+ALERT: Test number 1.
+hashchange event received by body-onhashchange-attribute.
+A total of 10 events have been received.
 ALERT: Test number 2.
 hashchange event received by window-event-listener.
 A total of 1 events have been received.
diff --git a/third_party/blink/web_tests/fast/loader/hashchange-event.html b/third_party/blink/web_tests/fast/loader/hashchange-event.html
index 44bbcba..e5535606 100644
--- a/third_party/blink/web_tests/fast/loader/hashchange-event.html
+++ b/third_party/blink/web_tests/fast/loader/hashchange-event.html
@@ -7,7 +7,7 @@
     location.href = "http://webkit.org/";
 }
 
-var targetNumberOfEventsForFirstTest = 8;
+var targetNumberOfEventsForFirstTest = 10;
 var targetNumberOfEventsForSecondTest = 10;
 
 var testNumber = 1;
diff --git a/third_party/blink/web_tests/http/tests/loading/location-hash-reload-cycle-expected.txt b/third_party/blink/web_tests/http/tests/loading/location-hash-reload-cycle-expected.txt
index 0fd9419b..b549b5aa 100644
--- a/third_party/blink/web_tests/http/tests/loading/location-hash-reload-cycle-expected.txt
+++ b/third_party/blink/web_tests/http/tests/loading/location-hash-reload-cycle-expected.txt
@@ -3,6 +3,7 @@
 main frame - didCommitLoadForFrame
 main frame - didReceiveTitle: 
 main frame - didFinishDocumentLoadForFrame
+main frame - didCommitLoadForFrame
 main frame - didHandleOnloadEventsForFrame
 main frame - didFinishLoadForFrame
 This test checks that no loader actions occur when setting window.location.hash to the empty string or "#". If this test fails when run in a browser, it will reload continuously. If this test fails when run in DumpRenderTree, the FrameLoader callback output will contain willPerformClientRedirectToURL callbacks.
diff --git a/third_party/blink/web_tests/http/tests/loading/location-hash-reload-cycle.html b/third_party/blink/web_tests/http/tests/loading/location-hash-reload-cycle.html
index 7dc51bc..768c814 100644
--- a/third_party/blink/web_tests/http/tests/loading/location-hash-reload-cycle.html
+++ b/third_party/blink/web_tests/http/tests/loading/location-hash-reload-cycle.html
@@ -5,7 +5,7 @@
 {
     if (window.testRunner)
         testRunner.dumpAsText();
-    
+
     window.location.hash = "";
     window.location.hash = "#";
 }
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 25dc01c..1ae3824 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
@@ -1504,7 +1504,6 @@
     attribute ELEMENT_ARRAY_BUFFER
     attribute ELEMENT_ARRAY_BUFFER_BINDING
     attribute EQUAL
-    attribute FALSE
     attribute FASTEST
     attribute FLOAT
     attribute FLOAT_32_UNSIGNED_INT_24_8_REV
@@ -1935,7 +1934,6 @@
     attribute TRIANGLES
     attribute TRIANGLE_FAN
     attribute TRIANGLE_STRIP
-    attribute TRUE
     attribute TYPE
     attribute UNIFORM
     attribute UNIFORM_ARRAY_STRIDE
diff --git a/third_party/blink/web_tests/media/video-played.js b/third_party/blink/web_tests/media/video-played.js
index 47793af6..0f81639 100644
--- a/third_party/blink/web_tests/media/video-played.js
+++ b/third_party/blink/web_tests/media/video-played.js
@@ -77,5 +77,5 @@
 
 function startPlayingInNewRange(t, expectedStartTimes) {
     willCreateNewRange(expectedStartTimes);
-    playForDuration(0.1, t);
+    playForDuration(0.3, t);
 }
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
new file mode 100644
index 0000000..099831a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Tests for PaymentRequestEvent.changePaymentMethod() Not allowed to install this payment handler
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
deleted file mode 100644
index 646bce6..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
deleted file mode 100644
index f68cef2..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
new file mode 100644
index 0000000..099831a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Tests for PaymentRequestEvent.changePaymentMethod() Not allowed to install this payment handler
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
new file mode 100644
index 0000000..099831a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Tests for PaymentRequestEvent.changePaymentMethod() Not allowed to install this payment handler
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
new file mode 100644
index 0000000..099831a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Tests for PaymentRequestEvent.changePaymentMethod() Not allowed to install this payment handler
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-retina/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt b/third_party/blink/web_tests/platform/mac-retina/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
new file mode 100644
index 0000000..099831a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Tests for PaymentRequestEvent.changePaymentMethod() Not allowed to install this payment handler
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
new file mode 100644
index 0000000..099831a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Tests for PaymentRequestEvent.changePaymentMethod() Not allowed to install this payment handler
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_experimental/printing/composited-thead-tfoot-repeat-expected.png b/third_party/blink/web_tests/platform/mac/virtual/layout_ng_experimental/printing/composited-thead-tfoot-repeat-expected.png
new file mode 100644
index 0000000..797ef0d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/layout_ng_experimental/printing/composited-thead-tfoot-repeat-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png b/third_party/blink/web_tests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
deleted file mode 100644
index 6259647..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png b/third_party/blink/web_tests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
deleted file mode 100644
index 3b930031..0000000
--- a/third_party/blink/web_tests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
new file mode 100644
index 0000000..099831a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Tests for PaymentRequestEvent.changePaymentMethod() Not allowed to install this payment handler
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png b/third_party/blink/web_tests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
deleted file mode 100644
index f80a414..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png b/third_party/blink/web_tests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
deleted file mode 100644
index 315db16..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt b/third_party/blink/web_tests/platform/win7/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
new file mode 100644
index 0000000..099831a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/external/wpt/payment-handler/change-payment-method-manual.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Tests for PaymentRequestEvent.changePaymentMethod() Not allowed to install this payment handler
+Harness: the test ran to completion.
+
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 254d144..cb0882f 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
@@ -1549,7 +1549,6 @@
 [Worker]     attribute ELEMENT_ARRAY_BUFFER
 [Worker]     attribute ELEMENT_ARRAY_BUFFER_BINDING
 [Worker]     attribute EQUAL
-[Worker]     attribute FALSE
 [Worker]     attribute FASTEST
 [Worker]     attribute FLOAT
 [Worker]     attribute FLOAT_32_UNSIGNED_INT_24_8_REV
@@ -1980,7 +1979,6 @@
 [Worker]     attribute TRIANGLES
 [Worker]     attribute TRIANGLE_FAN
 [Worker]     attribute TRIANGLE_STRIP
-[Worker]     attribute TRUE
 [Worker]     attribute TYPE
 [Worker]     attribute UNIFORM
 [Worker]     attribute UNIFORM_ARRAY_STRIDE
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 df0b724e..c1237a1 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
@@ -8310,7 +8310,6 @@
     attribute ELEMENT_ARRAY_BUFFER
     attribute ELEMENT_ARRAY_BUFFER_BINDING
     attribute EQUAL
-    attribute FALSE
     attribute FASTEST
     attribute FLOAT
     attribute FLOAT_32_UNSIGNED_INT_24_8_REV
@@ -8741,7 +8740,6 @@
     attribute TRIANGLES
     attribute TRIANGLE_FAN
     attribute TRIANGLE_STRIP
-    attribute TRUE
     attribute TYPE
     attribute UNIFORM
     attribute UNIFORM_ARRAY_STRIDE
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 e2386af..8575d5f 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
@@ -1413,7 +1413,6 @@
 [Worker]     attribute ELEMENT_ARRAY_BUFFER
 [Worker]     attribute ELEMENT_ARRAY_BUFFER_BINDING
 [Worker]     attribute EQUAL
-[Worker]     attribute FALSE
 [Worker]     attribute FASTEST
 [Worker]     attribute FLOAT
 [Worker]     attribute FLOAT_32_UNSIGNED_INT_24_8_REV
@@ -1844,7 +1843,6 @@
 [Worker]     attribute TRIANGLES
 [Worker]     attribute TRIANGLE_FAN
 [Worker]     attribute TRIANGLE_STRIP
-[Worker]     attribute TRUE
 [Worker]     attribute TYPE
 [Worker]     attribute UNIFORM
 [Worker]     attribute UNIFORM_ARRAY_STRIDE
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-image-doesnt-paint-contents-ref.html b/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-image-doesnt-paint-contents-ref.html
new file mode 100644
index 0000000..55a5f7c
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-image-doesnt-paint-contents-ref.html
@@ -0,0 +1,24 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Display Locking: image locking (reference)</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<style>
+div {
+  background: blue;
+}
+.myimg {
+  display: inline-block;
+  width: 400px;
+  height: 200px;
+  background: lightblue;
+  border: 1px solid black;
+}
+</style>
+
+<div>lorem ipsum</div>
+<div class="myimg"></div>
+<div class="myimg"></div>
+<div>consectetur adipiscing elit</div>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-image-doesnt-paint-contents.html b/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-image-doesnt-paint-contents.html
new file mode 100644
index 0000000..6e911a2
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-image-doesnt-paint-contents.html
@@ -0,0 +1,43 @@
+<!doctype HTML>
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>Display Locking: image locking</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="locked-image-doesnt-paint-contents-ref.html">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+div {
+  background: blue;
+}
+img {
+  contain: style layout;
+  width: 400px;
+  height: 200px;
+  background: lightblue;
+  border: 1px solid black;
+}
+</style>
+
+<div>lorem ipsum</div>
+<img id="img1" src="resources/dice.png"></img>
+<img id="img2" src="resources/circles.svg"></img>
+<div>consectetur adipiscing elit</div>
+
+<script>
+async function runTest() {
+  const promises = [];
+  promises.push(document.getElementById("img1").displayLock.acquire({ timeout: Infinity }));
+  promises.push(document.getElementById("img2").displayLock.acquire({ timeout: Infinity }));
+  await Promise.all(promises);
+  takeScreenshot();
+}
+
+window.onload = () => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(runTest);
+  });
+};
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-svg-doesnt-paint-contents-ref.html b/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-svg-doesnt-paint-contents-ref.html
new file mode 100644
index 0000000..5b5aa52
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-svg-doesnt-paint-contents-ref.html
@@ -0,0 +1,23 @@
+<!doctype HTML>
+<html>
+<meta charset="utf8">
+<title>Display Locking: image locking (reference)</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+
+<style>
+div {
+  background: blue;
+}
+.mysvg {
+  display: inline-block;
+  width: 400px;
+  height: 300px;
+  background: lightblue;
+  border: 1px solid black;
+}
+</style>
+
+<div>lorem ipsum</div>
+<div class="mysvg"></div>
+<div>consectetur adipiscing elit</div>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-svg-doesnt-paint-contents.html b/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-svg-doesnt-paint-contents.html
new file mode 100644
index 0000000..6bf5801
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/paint/locked-svg-doesnt-paint-contents.html
@@ -0,0 +1,49 @@
+<!doctype HTML>
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>Display Locking: svg locking</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://github.com/WICG/display-locking">
+<link rel="match" href="locked-svg-doesnt-paint-contents-ref.html">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+div {
+  background: blue;
+}
+svg {
+  contain: style layout;
+  border: 1px solid black;
+  background: lightblue;
+}
+</style>
+
+<div>lorem ipsum</div>
+<svg xmlns="http://www.w3.org/2000/svg" width="400" height="300" viewBox="0 0 400 300" id="svg">
+  <g stroke-width="10" transform="translate(-30)">
+      <circle cx="80" cy="50" r="35" fill="#084" stroke="none"/>
+      <circle cx="80" cy="50" r="20" fill="#080" stroke="#FF0"/>
+      <circle cx="80" cy="120" r="35" fill="#004" stroke="none"/>
+      <circle cx="80" cy="120" r="20" fill="#080" stroke="#FF0"/>
+      <circle cx="80" cy="190" r="35" fill="#088" stroke="none"/>
+      <circle cx="80" cy="190" r="20" fill="#080" stroke="#FF0"/>
+      <circle cx="80" cy="260" r="35" fill="#008" stroke="none"/>
+      <circle cx="80" cy="260" r="20" fill="#080" stroke="#FF0"/>
+  </g>
+</svg>
+<div>consectetur adipiscing elit</div>
+
+<script>
+async function runTest() {
+  const container = document.getElementById("svg");
+  await container.displayLock.acquire({ timeout: Infinity });
+  takeScreenshot();
+}
+
+window.onload = () => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(runTest);
+  });
+};
+</script>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/paint/resources/circles.svg b/third_party/blink/web_tests/wpt_internal/display-lock/paint/resources/circles.svg
new file mode 100644
index 0000000..1a9ad75
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/paint/resources/circles.svg
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<svg viewBox="0 0 500 300" xmlns="http://www.w3.org/2000/svg">
+<rect x="0" y="0" width="500" height="300" fill="#fff" />
+
+<g stroke-width="10" transform="translate(-30)">
+    <circle cx="80" cy="50" r="35" fill="#084" stroke="none"/>
+    <circle cx="80" cy="50" r="20" fill="#080" stroke="#FF0"/>
+    <circle cx="80" cy="120" r="35" fill="#004" stroke="none"/>
+    <circle cx="80" cy="120" r="20" fill="#080" stroke="#FF0"/>
+    <circle cx="80" cy="190" r="35" fill="#088" stroke="none"/>
+    <circle cx="80" cy="190" r="20" fill="#080" stroke="#FF0"/>
+    <circle cx="80" cy="260" r="35" fill="#008" stroke="none"/>
+    <circle cx="80" cy="260" r="20" fill="#080" stroke="#FF0"/>
+</g>
+
+<g stroke-width="10" transform="translate(170)">
+    <circle cx="80" cy="50" r="35" fill="#085" stroke="none"/>
+    <circle cx="80" cy="50" r="20" fill="#080" stroke="#FF0"/>
+    <circle cx="80" cy="120" r="35" fill="#005" stroke="none"/>
+    <circle cx="80" cy="120" r="20" fill="#080" stroke="#FF0"/>
+    <circle cx="80" cy="190" r="35" fill="#689" stroke="none"/>
+    <circle cx="80" cy="190" r="20" fill="#080" stroke="#FF0"/>
+    <circle cx="80" cy="260" r="35" fill="#609" stroke="none"/>
+    <circle cx="80" cy="260" r="20" fill="#080" stroke="#FF0"/>
+</g>
+
+<g stroke-width="10" transform="translate(370)">
+    <circle cx="80" cy="50" r="35" fill="#084" stroke="none"/>
+    <circle cx="80" cy="50" r="20" fill="#080" stroke="#FF0"/>
+    <circle cx="80" cy="120" r="35" fill="#084" stroke="none"/>
+    <circle cx="80" cy="120" r="20" fill="#080" stroke="#FF0"/>
+    <circle cx="80" cy="190" r="35" fill="#088" stroke="none"/>
+    <circle cx="80" cy="190" r="20" fill="#080" stroke="#FF0"/>
+    <circle cx="80" cy="260" r="35" fill="#088" stroke="none"/>
+    <circle cx="80" cy="260" r="20" fill="#080" stroke="#FF0"/>
+</g>
+</svg>
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/paint/resources/dice.png b/third_party/blink/web_tests/wpt_internal/display-lock/paint/resources/dice.png
new file mode 100644
index 0000000..f18b8141
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/paint/resources/dice.png
Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/sizing/layout-replaced-ref.html b/third_party/blink/web_tests/wpt_internal/display-lock/sizing/layout-replaced-ref.html
index 1ac35c6..581031b 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/sizing/layout-replaced-ref.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/sizing/layout-replaced-ref.html
@@ -14,6 +14,7 @@
   contain: style layout;
   width: 12px;
   height: 34px;
+  visibility: hidden;
 }
 </style>
 
diff --git a/third_party/blink/web_tests/wpt_internal/std-switch/README.md b/third_party/blink/web_tests/wpt_internal/std-switch/README.md
new file mode 100644
index 0000000..e8a6d127
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/std-switch/README.md
@@ -0,0 +1,5 @@
+# Tests for std-switch
+
+https://github.com/tkent-google/std-switch/blob/master/README.md
+
+Tests in tentative/ should be upstreamed to WPT in the future.
diff --git a/third_party/blink/web_tests/wpt_internal/std-switch/tentative/form-associated-basic.js b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/form-associated-basic.js
new file mode 100644
index 0000000..988a71f
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/form-associated-basic.js
@@ -0,0 +1,97 @@
+const HTML_DOM = '/html/dom/';
+
+function loadScript(src) {
+  return new Promise((resolve, reject) => {
+    let script = document.createElement('script');
+    script.src = src;
+    script.addEventListener('load', e => resolve(), {once: true});
+    script.addEventListener('error', e => reject(), {once: true});
+    document.body.appendChild(script);
+  });
+}
+
+function formAssociatedTests(tag, reflectionDict) {
+  test(() => {
+    let control = document.createElement(tag);
+    assert_equals(control.type, tag);
+  }, `${tag} supports type property`);
+
+  test(() => {
+    let control = document.createElement(tag);
+    assert_equals(control.form, null);
+
+    let form1 = document.createElement('form');
+    form1.appendChild(control);
+    assert_equals(control.form, form1);
+
+    let form2 = document.createElement('form');
+    form2.id = 'connected-form';
+    document.body.appendChild(form2);
+    control.setAttribute('form', 'connected-form');
+    control.remove();
+    assert_equals(control.form, null);
+    document.body.appendChild(control);
+    assert_equals(control.form, document.getElementById('connected-form'));
+  }, `${tag} supports form property`);
+
+  test(() => {
+    let control = document.createElement(tag);
+    assert_true(control.willValidate);
+    control.setAttribute('disabled', '');
+    assert_false(control.willValidate);
+    control.removeAttribute('disabled');
+    let datalist = document.createElement('datalist');
+    datalist.appendChild(control);
+    assert_false(control.willValidate);
+  }, `${tag} supports willValidate property`);
+
+  test(() => {
+    let control = document.createElement(tag);
+    assert_true(control.validity.valid);
+    assert_false(control.validity.customError);
+    assert_equals(control.validationMessage, '');
+    assert_true(control.checkValidity(), '1: ' + control);
+    assert_true(control.reportValidity());
+
+    control.setCustomValidity('Invalid!');
+    assert_false(control.validity.valid);
+    assert_true(control.validity.customError);
+    assert_equals(control.validationMessage, 'Invalid!');
+    assert_false(control.checkValidity(), '2: ' + control);
+    assert_false(control.reportValidity());
+  }, `${tag} supports form validation`);
+
+  test(() => {
+    let control = document.createElement(tag);
+    assert_equals(control.labels.length, 0);
+
+    let label = document.createElement('label');
+    document.body.appendChild(label);
+    label.appendChild(control);
+    assert_array_equals(control.labels, [label], 'descendant association');
+    assert_equals(label.control, control);
+
+    document.body.appendChild(control);
+    document.body.appendChild(label);
+    assert_equals(control.labels.length, 0);
+    control.id = 'control';
+    label.htmlFor = 'control';
+    assert_array_equals(control.labels, [label], 'for= association');
+    assert_equals(label.control, control);
+  }, `${tag} supports labels property`);
+
+  promise_test(async () => {
+    await loadScript(HTML_DOM + 'original-harness.js');
+    await loadScript(HTML_DOM + 'new-harness.js');
+    let targetElements = {};
+    targetElements[tag] = {
+      disabled: 'boolean',
+      name: 'string',
+    };
+    for (let [key, value] of Object.entries(reflectionDict)) {
+      targetElements[tag][key] = value;
+    }
+    mergeElements(targetElements);
+    await loadScript(HTML_DOM + 'reflection.js');
+  }, 'Setup reflection tests');
+}
diff --git a/third_party/blink/web_tests/wpt_internal/std-switch/tentative/import.html b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/import.html
new file mode 100644
index 0000000..33feb40e
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/import.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+test(() => {
+  assert_not_own_property(window, 'StdSwitchElement');
+  assert_true(document.createElement('std-switch') instanceof HTMLElement);
+}, 'StdSwitchElement should not be available before import');
+
+let t = async_test('StdSwitchElement should be available after import');
+</script>
+<script type="module">
+import { StdSwitchElement } from 'std:elements/switch';
+
+t.step(() => {
+  assert_not_own_property(window, 'StdSwitchElement');
+  assert_true(document.createElement('std-switch') instanceof StdSwitchElement);
+  t.done();
+});
+</script>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/std-switch/tentative/interface.html b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/interface.html
new file mode 100644
index 0000000..d0aabcd5
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/interface.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/webidl2/lib/webidl2.js"></script>
+<script src="/resources/idlharness.js"></script>
+<script type="module">
+import { StdSwitchElement } from 'std:elements/switch';
+
+const DEPS = ['html', 'SVG', 'cssom', 'touch-events', 'uievents', 'dom', 'xhr'];
+
+// WebIDL and idlharness.js are not capable of interfaces in a module.
+const SWITCH_IDL = `
+[Exposed=Window,
+ Constructor()]
+interface StdSwitchElement : HTMLElement {
+  [CEReactions] attribute boolean defaultOn;
+  [CEReactions] attribute boolean on;
+
+  [CEReactions] attribute boolean disabled;
+  readonly attribute HTMLFormElement? form;
+  [CEReactions] attribute DOMString name;
+  readonly attribute DOMString type;
+
+  readonly attribute boolean willValidate;
+  readonly attribute ValidityState validity;
+  readonly attribute DOMString validationMessage;
+  boolean checkValidity();
+  boolean reportValidity();
+  void setCustomValidity(DOMString error);
+
+  readonly attribute NodeList labels;
+};`;
+
+Object.defineProperty(window, 'StdSwitchElement', {
+  configurable: true,
+  enumerable: false,
+  value: StdSwitchElement,
+  writable: true,
+});
+
+promise_test(async () => {
+  let idlArray = new IdlArray();
+  let dependentIdls = await Promise.all(DEPS.map(spec => { return fetch_spec(spec); }));
+  idlArray.add_idls(SWITCH_IDL);
+  for (let dep of dependentIdls) {
+    idlArray.add_dependency_idls(dep);
+  }
+  idlArray.add_objects({StdSwitchElement: ['document.createElement("std-switch")']});
+  idlArray.test();
+});
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/std-switch/tentative/properties-functions.html b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/properties-functions.html
new file mode 100644
index 0000000..4f07751803
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/std-switch/tentative/properties-functions.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script src="form-associated-basic.js"></script>
+<script type=module>
+import 'std:elements/switch';
+
+formAssociatedTests('std-switch', {
+    defaultOn: {type: 'boolean', domAttrName: 'defaulton'},
+    on: 'boolean'
+});
+</script>
+</body>
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 12831cfa..fd0dd559 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -37,8 +37,8 @@
 # Do NOT CHANGE this if you don't know what you're doing -- see
 # https://chromium.googlesource.com/chromium/src/+/master/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
-CLANG_REVISION = '49b965079b18f8aa485dd1156dd088d40b7ee465'
-CLANG_SVN_REVISION = '363450'
+CLANG_REVISION = 'd874c057bc2361da5157553e1e2178f43c3ade1a'
+CLANG_SVN_REVISION = '363790'
 CLANG_SUB_REVISION = 1
 
 PACKAGE_VERSION = '%s-%s-%s' % (CLANG_SVN_REVISION, CLANG_REVISION[:8],
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index eebe8680..d5a3ceb 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3382,6 +3382,7 @@
   <int value="17" label="Get scripts failed"/>
   <int value="18" label="Get scripts unparsable"/>
   <int value="19" label="No initial scripts"/>
+  <int value="20" label="Installing the feature module failed"/>
 </enum>
 
 <enum name="AutofillAssistantOnBoarding">
@@ -33922,7 +33923,6 @@
   <int value="-1308600417" label="NewNetErrorPageUI:disabled"/>
   <int value="-1304957199" label="OfflinePagesShowAlternateDinoPage:enabled"/>
   <int value="-1304758527" label="SyncSendTabToSelf:disabled"/>
-  <int value="-1303995589" label="shelf-new-ui"/>
   <int value="-1302904242" label="enable-navigation-tracing"/>
   <int value="-1302859198" label="enable-stylus-virtual-keyboard:disabled"/>
   <int value="-1294050129" label="ContentFullscreen:disabled"/>
@@ -35168,6 +35168,7 @@
   <int value="567368307" label="enable-experimental-canvas-features"/>
   <int value="575380532" label="ExperimentalAccessibilityLabels:disabled"/>
   <int value="575394365" label="AndroidPaymentApps:disabled"/>
+  <int value="575829120" label="shelf-hotseat"/>
   <int value="576701073" label="WebPaymentsJustInTimePaymentApp:disabled"/>
   <int value="576878329" label="enable-background-blur"/>
   <int value="581118445" label="enable-eol-notification"/>
@@ -57433,6 +57434,9 @@
 </enum>
 
 <enum name="UncacheableReason">
+  <obsolete>
+    Deprecated 06/2019 in issue 975278.
+  </obsolete>
   <int value="0" label="kNoData"/>
   <int value="1" label="kPre11PartialResponse"/>
   <int value="2" label="kNoStrongValidatorOnPartialResponse"/>
@@ -60847,6 +60851,11 @@
   <int value="1" label="onReceivedClientCertRequest"/>
   <int value="2" label="onReceivedHttpAuthRequest"/>
   <int value="3" label="onDownloadStart"/>
+  <int value="4" label="onPageStarted"/>
+  <int value="5" label="onPageFinished"/>
+  <int value="6" label="onLoadResource"/>
+  <int value="7" label="onPageCommitVisible"/>
+  <int value="8" label="shouldOverrideUrlLoading"/>
 </enum>
 
 <enum name="WebViewClientErrorCode">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 65ffe90..e36ece1d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -11286,7 +11286,7 @@
 </histogram>
 
 <histogram name="BackgroundMode.BackgroundApplicationsCount"
-    expires_after="M77">
+    expires_after="2020-02-01">
   <owner>atwilson@chromium.org</owner>
   <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
@@ -11296,7 +11296,7 @@
 </histogram>
 
 <histogram name="BackgroundMode.BackgroundModeEnabledPrefChanged"
-    enum="BooleanEnabled" expires_after="M77">
+    enum="BooleanEnabled" expires_after="2020-02-01">
   <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
     Logged if BackgroundModeManager is running and listening for pref changes,
@@ -11305,7 +11305,7 @@
 </histogram>
 
 <histogram name="BackgroundMode.MenuItemClick" enum="BackgroundModeMenuItem"
-    expires_after="M77">
+    expires_after="2020-02-01">
   <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
     Logged when an item in the system tray icon menu is clicked.
@@ -11313,8 +11313,9 @@
 </histogram>
 
 <histogram name="BackgroundMode.OnStartup.AutoLaunchState"
-    enum="AutoLaunchState" expires_after="2018-08-30">
+    enum="AutoLaunchState" expires_after="2020-02-01">
   <owner>gab@chromium.org</owner>
+  <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
     Logged during BackgroundModeManager's initialization. Indicates the
     AutoLaunchState the current browser process was launched in.
@@ -11322,16 +11323,19 @@
 </histogram>
 
 <histogram name="BackgroundMode.OnStartup.IsBackgroundModePrefEnabled"
-    enum="BooleanEnabled" expires_after="2018-08-30">
+    enum="BooleanEnabled" expires_after="2020-02-01">
   <owner>gab@chromium.org</owner>
+  <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
     Logged during BackgroundModeManager's initialization. Indicates the state of
     the kBackgroundModeEnabled pref on startup.
   </summary>
 </histogram>
 
-<histogram name="BackgroundMode.TimeBeforeOptimizedRestart" units="ms">
+<histogram name="BackgroundMode.TimeBeforeOptimizedRestart" units="ms"
+    expires_after="2020-02-01">
   <owner>peter@chromium.org</owner>
+  <owner>mvanouwerkerk@chromium.org</owner>
   <summary>
     The duration of a session before the browser got restarted in the background
     by the BackgroundOptimizer to purge the memory.
@@ -39840,6 +39844,9 @@
 </histogram>
 
 <histogram base="true" name="Extensions.LongInjectionTaskTime" units="ms">
+  <obsolete>
+    Removed June 2019.
+  </obsolete>
   <owner>ksakamoto@chromium.org</owner>
   <summary>
     The amount of time taken to inject content scripts. If multiple scripts are
@@ -40965,6 +40972,9 @@
 
 <histogram base="true" name="Extensions.TimeYieldedBetweenContentScriptRuns"
     units="ms">
+  <obsolete>
+    Removed June 2019.
+  </obsolete>
   <owner>ksakamoto@chromium.org</owner>
   <summary>
     Time elapsed between two asynchronously-injected content script runs.
@@ -54765,6 +54775,9 @@
 </histogram>
 
 <histogram name="Media.CacheUseful" enum="BooleanSuccess" expires_after="M77">
+  <obsolete>
+    Deprecated 06/2019 in issue 975278.
+  </obsolete>
   <owner>dalecurtis@chromium.org</owner>
   <summary>
     Whether a media response might be used to satisfy a future request.
@@ -57271,6 +57284,9 @@
 
 <histogram name="Media.UncacheableReason" enum="UncacheableReason"
     expires_after="M77">
+  <obsolete>
+    Deprecated 06/2019 in issue 975278.
+  </obsolete>
   <owner>dalecurtis@chromium.org</owner>
   <summary>
     Reasons a media response won't be used to satisfy a future request.
@@ -69863,6 +69879,9 @@
 
 <histogram name="Net.HttpTimeToFirstByte.LargeUpload" units="ms"
     expires_after="M77">
+  <obsolete>
+    Deprecated 06/2019.
+  </obsolete>
   <owner>mmenke@chromium.org</owner>
   <summary>
     Time from when an HTTP request is issued to when the first byte is
@@ -116528,7 +116547,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.ActivateEvent.Time" units="ms"
-    expires_after="M77">
+    expires_after="2020-06-18">
   <owner>shimazu@chromium.org</owner>
   <summary>
     Execution time of ServiceWorkerGlobalScope.onactivate. Includes the time for
@@ -116549,7 +116568,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.ActivateEventStatus"
-    enum="ServiceWorkerStatusCode" expires_after="M77">
+    enum="ServiceWorkerStatusCode" expires_after="2020-06-18">
   <owner>falken@chromium.org</owner>
   <summary>
     The result of dispatching the activate event to the worker. This is recorded
@@ -132130,6 +132149,9 @@
 
 <histogram name="TabManager.Discarding.LogMemoryTime" units="ms"
     expires_after="M77">
+  <obsolete>
+    Removed 06/2019, check Arc.LowMemoryKiller.FirstKillLatency instead.
+  </obsolete>
   <owner>cywang@chromium.org</owner>
   <owner>georgesak@chromium.org</owner>
   <summary>
@@ -143676,6 +143698,9 @@
 </histogram>
 
 <histogram name="WebFont.CORSSuccess" enum="BooleanSuccess" expires_after="M77">
+  <obsolete>
+    Deprecated June 2019.
+  </obsolete>
   <owner>bashi@chromium.org</owner>
   <owner>kenjibaheux@chromium.org</owner>
   <summary>The success or failure of web fonts CORS-enabled fetching.</summary>
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index faff790..86b8519 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -199,17 +199,6 @@
   return false;
 }
 
-AtkObject* FindFirstAncestorThatEmitsAtkTextEvents(AtkObject* atk_object) {
-  if (!atk_object)
-    return nullptr;
-
-  if (EmitsAtkTextEvents(atk_object))
-    return atk_object;
-
-  return FindFirstAncestorThatEmitsAtkTextEvents(
-      atk_object_get_parent(atk_object));
-}
-
 bool IsFrameAncestorOfAtkObject(AtkObject* frame, AtkObject* atk_object) {
   AtkObject* current_frame = FindAtkObjectParentFrame(atk_object);
   while (current_frame) {
@@ -341,6 +330,10 @@
 
   return nullptr;
 }
+bool SelectionOffsetsIndicateSelection(const std::pair<int, int>& offsets) {
+  return offsets.first >= 0 && offsets.second >= 0 &&
+         offsets.first != offsets.second;
+}
 
 namespace atk_component {
 
@@ -3016,16 +3009,34 @@
 }
 
 void AXPlatformNodeAuraLinux::OnTextSelectionChanged() {
-  AtkObject* object = FindFirstAncestorThatEmitsAtkTextEvents(atk_object_);
-  if (!object)
+  DCHECK(atk_object_);
+  if (!EmitsAtkTextEvents(atk_object_)) {
+    if (auto* parent = AtkObjectToAXPlatformNodeAuraLinux(GetParent()))
+      parent->OnTextSelectionChanged();
     return;
-
-  DCHECK(ATK_IS_TEXT(object));
-  if (HasCaret()) {
-    g_signal_emit_by_name(object, "text-caret-moved",
-                          atk_text_get_caret_offset(ATK_TEXT(object)));
   }
-  g_signal_emit_by_name(object, "text-selection-changed");
+
+  DCHECK(ATK_IS_TEXT(atk_object_));
+
+  std::pair<int, int> new_selection;
+  GetSelectionOffsets(&new_selection.first, &new_selection.second);
+  std::pair<int, int> old_selection = text_selection_;
+  text_selection_ = new_selection;
+
+  // ATK does not consider a collapsed selection a selection, so
+  // when the collapsed selection changes (caret movement), we should
+  // avoid sending text-selection-changed events.
+  bool has_selection = SelectionOffsetsIndicateSelection(new_selection);
+  bool had_selection = SelectionOffsetsIndicateSelection(old_selection);
+  if (has_selection != had_selection ||
+      (has_selection && new_selection != old_selection)) {
+    g_signal_emit_by_name(atk_object_, "text-selection-changed");
+  }
+
+  if (HasCaret() && new_selection.second != old_selection.second) {
+    g_signal_emit_by_name(atk_object_, "text-caret-moved",
+                          UTF16ToUnicodeOffsetInText(new_selection.second));
+  }
 }
 
 bool AXPlatformNodeAuraLinux::SupportsSelectionWithAtkSelection() {
@@ -3636,10 +3647,9 @@
 }
 
 bool AXPlatformNodeAuraLinux::HasSelection() {
-  int selection_start, selection_end;
-  GetSelectionOffsets(&selection_start, &selection_end);
-  return selection_start >= 0 && selection_end >= 0 &&
-         selection_start != selection_end;
+  std::pair<int, int> selection;
+  GetSelectionOffsets(&selection.first, &selection.second);
+  return SelectionOffsetsIndicateSelection(selection);
 }
 
 void AXPlatformNodeAuraLinux::GetSelectionExtents(int* start_offset,
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.h b/ui/accessibility/platform/ax_platform_node_auralinux.h
index 243de66..1f36fcb9 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.h
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.h
@@ -8,6 +8,7 @@
 #include <atk/atk.h>
 
 #include <string>
+#include <utility>
 
 #include "base/macros.h"
 #include "base/optional.h"
@@ -230,6 +231,11 @@
   // minimized the last time it's visibility changed.
   bool was_minimized_ = false;
 
+  // The previously observed text selection for this node. We store
+  // this in order to avoid sending duplicate text-selection-changed
+  // and text-caret-moved events.
+  std::pair<int, int> text_selection_ = std::make_pair(-1, -1);
+
   DISALLOW_COPY_AND_ASSIGN(AXPlatformNodeAuraLinux);
 };
 
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 59beb86..087e27c 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -57,7 +57,6 @@
   flags = [
     "ENABLE_HIDPI=$enable_hidpi",
     "ENABLE_MESSAGE_CENTER=$enable_message_center",
-    "ENABLE_MUS=$enable_mus",
     "USE_ATK=$use_atk",
     "USE_XKBCOMMON=$use_xkbcommon",
     "HAS_NATIVE_ACCESSIBILITY=$has_native_accessibility",
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 396d1cc..0853c00 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -120,11 +120,6 @@
 };
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
 
-const base::Feature kMash = {"Mash", base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kSingleProcessMash = {"SingleProcessMash",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
-
 const base::Feature kFormControlsRefresh = {"FormControlsRefresh",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -132,19 +127,6 @@
   return base::FeatureList::IsEnabled(features::kFormControlsRefresh);
 }
 
-bool IsUsingWindowService() {
-  return IsSingleProcessMash() || IsMultiProcessMash();
-}
-
-bool IsMultiProcessMash() {
-  return base::FeatureList::IsEnabled(features::kMash);
-}
-
-bool IsSingleProcessMash() {
-  return base::FeatureList::IsEnabled(features::kSingleProcessMash) &&
-         !base::FeatureList::IsEnabled(features::kMash);
-}
-
 bool IsAutomaticUiAdjustmentsForTouchEnabled() {
 #if defined(OS_WIN) || defined(OS_CHROMEOS)
   return base::FeatureList::IsEnabled(
@@ -169,8 +151,7 @@
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
 bool IsOzoneDrmMojo() {
-  return base::FeatureList::IsEnabled(kEnableOzoneDrmMojo) ||
-         IsMultiProcessMash();
+  return base::FeatureList::IsEnabled(kEnableOzoneDrmMojo);
 }
 
 #if defined(OS_CHROMEOS)
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index 332d7c4..4f07ffb1 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -64,35 +64,11 @@
 extern const base::Feature kDirectManipulationStylus;
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
 
-// Used to have ash (Chrome OS system UI) run in its own process.
-// TODO(jamescook): Make flag only available in Chrome OS.
-COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kMash;
-
-// NOTE: Do not access directly outside of tests. Use IsSingleProcessMash()
-// to avoid problems when Mash and SingleProcessMash are both enabled.
-COMPONENT_EXPORT(UI_BASE_FEATURES)
-extern const base::Feature kSingleProcessMash;
-
 // Used to enable the new controls UI.
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kFormControlsRefresh;
 COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsFormControlsRefreshEnabled();
 
-// Returns true if Chrome's aura usage is backed by the WindowService.
-COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsUsingWindowService();
-
-// Returns true if ash in running in a separate process (and is hosting the UI
-// service and Viz graphics). See //ash/README.md.
-COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsMultiProcessMash();
-
-// Returns true if code outside of ash is using the WindowService. In this mode
-// there are two aura::Envs. Ash uses one with Env::Mode::LOCAL. Non-ash code
-// uses an aura::Env with a mode of MUS. The non-ash code using mus targets the
-// WindowService that ash is running. This exercises the WindowService mojo APIs
-// similar to kMash, but leaves ash and browser running in the same process.
-// See //ash/README.md.
-COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsSingleProcessMash();
-
 // Whether the UI may accommodate touch input in response to hardware changes.
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 bool IsAutomaticUiAdjustmentsForTouchEnabled();
diff --git a/ui/base/ui_features.gni b/ui/base/ui_features.gni
index 6ecd398d..88b0226 100644
--- a/ui/base/ui_features.gni
+++ b/ui/base/ui_features.gni
@@ -13,9 +13,6 @@
 
   # Whether the message center should be included for displaying notifications.
   enable_message_center = is_win || is_mac || is_linux || is_chromeos
-
-  # Set to true to if mus (aka the window service) is enabled.
-  enable_mus = is_chromeos
 }
 
 enable_hidpi = is_mac || is_win || is_linux
diff --git a/ui/chromeos/ime/candidate_window_view.cc b/ui/chromeos/ime/candidate_window_view.cc
index 97d801f36..c945c12 100644
--- a/ui/chromeos/ime/candidate_window_view.cc
+++ b/ui/chromeos/ime/candidate_window_view.cc
@@ -10,7 +10,6 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/chromeos/ime/candidate_view.h"
 #include "ui/chromeos/ime/candidate_window_constants.h"
 #include "ui/display/display.h"
@@ -153,7 +152,7 @@
       was_candidate_window_open_(false),
       window_shell_id_(window_shell_id) {
   SetCanActivate(false);
-  DCHECK(parent || features::IsUsingWindowService());
+  DCHECK(parent);
   set_parent_window(parent);
   set_margins(gfx::Insets());
 
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 316f96f..3b95dd1a 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -400,6 +400,10 @@
   return cc_layer_->masks_to_bounds();
 }
 
+void Layer::SetClipRect(const gfx::Rect& clip_rect) {
+  cc_layer_->SetClipRect(clip_rect);
+}
+
 void Layer::SetOpacity(float opacity) {
   GetAnimator()->SetOpacity(opacity);
 }
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index 0850558..9ffad84 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -184,6 +184,12 @@
   void SetMasksToBounds(bool masks_to_bounds);
   bool GetMasksToBounds() const;
 
+  // Sets/gets the clip rect for the layer. |clip_rect| is in layer space and
+  // relative to |this| layer. Prefer SetMasksToBounds() to set the clip to the
+  // bounds of |this| layer. This clips the subtree rooted at |this| layer.
+  void SetClipRect(const gfx::Rect& clip_rect);
+  const gfx::Rect& clip_rect() const { return cc_layer_->clip_rect(); }
+
   // The opacity of the layer. The opacity is applied to each pixel of the
   // texture (resulting alpha = opacity * alpha).
   float opacity() const;
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc
index 9dc0d242..eb6047d 100644
--- a/ui/events/blink/input_handler_proxy_unittest.cc
+++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -10,9 +10,9 @@
 #include "base/bind_helpers.h"
 #include "base/containers/circular_deque.h"
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/test/trace_event_analyzer.h"
 #include "cc/input/main_thread_scrolling_reason.h"
@@ -497,7 +497,7 @@
   std::vector<InputHandlerProxy::EventDisposition> event_disposition_recorder_;
   std::vector<ui::LatencyInfo> latency_info_recorder_;
 
-  base::MessageLoop loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   base::WeakPtrFactory<InputHandlerProxyEventQueueTest> weak_ptr_factory_;
 };
 
diff --git a/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js b/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
index e773622..a0e6fc23 100644
--- a/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
+++ b/ui/file_manager/file_manager/background/js/runtime_loaded_test_util.js
@@ -440,18 +440,28 @@
       if (opt_button !== undefined) {
         props.button = opt_button;
       }
+      if (!targetQuery) {
+        return false;
+      }
+      if (typeof targetQuery === 'string') {
+        targetQuery = [targetQuery];
+      }
+      const elems = test.util.sync.deepQuerySelectorAll_(
+          contentWindow.document, /** @type !Array<string> */ (targetQuery));
+      if (elems.length === 0) {
+        return false;
+      }
+      // Only sends the event to the first matched element.
+      const target = elems[0];
+
       const mouseOverEvent = new MouseEvent('mouseover', props);
-      const resultMouseOver =
-          test.util.sync.sendEvent(contentWindow, targetQuery, mouseOverEvent);
+      const resultMouseOver = target.dispatchEvent(mouseOverEvent);
       const mouseDownEvent = new MouseEvent('mousedown', props);
-      const resultMouseDown =
-          test.util.sync.sendEvent(contentWindow, targetQuery, mouseDownEvent);
+      const resultMouseDown = target.dispatchEvent(mouseDownEvent);
       const mouseUpEvent = new MouseEvent('mouseup', props);
-      const resultMouseUp =
-          test.util.sync.sendEvent(contentWindow, targetQuery, mouseUpEvent);
+      const resultMouseUp = target.dispatchEvent(mouseUpEvent);
       const clickEvent = new MouseEvent('click', props);
-      const resultClick =
-          test.util.sync.sendEvent(contentWindow, targetQuery, clickEvent);
+      const resultClick = target.dispatchEvent(clickEvent);
       return resultMouseOver && resultMouseDown && resultMouseUp && resultClick;
     };
 
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index fa80b5d..59bb9e3 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -1462,8 +1462,8 @@
 
 @keyframes acceptsBlink {
   0% {
-    color: rgb(90, 90, 90);
     background-color: transparent;
+    color: rgb(90, 90, 90);
   }
 }
 
@@ -2149,6 +2149,19 @@
   flex: 1 0 auto;
 }
 
+/* Feedback panels */
+
+.feedback-panels {
+  bottom: 24px;
+  position: absolute;
+  right: 24px;
+  z-index: 502; /* Above splitter, but below quickview. */
+}
+
+#completed-panel {
+  padding-bottom: 8px;
+}
+
 /* Progress center */
 
 @keyframes progress-center-toggle {
diff --git a/ui/file_manager/file_manager/foreground/elements/BUILD.gn b/ui/file_manager/file_manager/foreground/elements/BUILD.gn
index 81c6d0a..6af4b055 100644
--- a/ui/file_manager/file_manager/foreground/elements/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/elements/BUILD.gn
@@ -94,3 +94,18 @@
     ":files_toast_unittest",
   ]
 }
+
+js_library("xf_activity_complete") {
+}
+
+js_library("xf_button") {
+}
+
+js_library("xf_circular_progress") {
+}
+
+js_library("xf_display_panel") {
+}
+
+js_library("xf_panel_item") {
+}
diff --git a/ui/file_manager/file_manager/foreground/elements/elements_bundle.html b/ui/file_manager/file_manager/foreground/elements/elements_bundle.html
index e45b8e3..f34d700 100644
--- a/ui/file_manager/file_manager/foreground/elements/elements_bundle.html
+++ b/ui/file_manager/file_manager/foreground/elements/elements_bundle.html
@@ -13,3 +13,4 @@
 <link rel="import" href="files_toggle_ripple.html">
 <link rel="import" href="files_tooltip.html">
 <link rel="import" href="icons.html">
+<link rel="import" href="files_xf_elements.css">
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_button.js b/ui/file_manager/file_manager/foreground/elements/xf_button.js
index f56f218b..d4818d7 100644
--- a/ui/file_manager/file_manager/foreground/elements/xf_button.js
+++ b/ui/file_manager/file_manager/foreground/elements/xf_button.js
@@ -11,9 +11,8 @@
   }
 
   /**
-   * Creates an instance of Panelitem, attaching the template clone.
+   * Creates a PanelButton.
    * @private
-   * @return {PanelButton} Custom element instance.
    */
   static createElement_() {
     const template = document.createElement('template');
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_display_panel.js b/ui/file_manager/file_manager/foreground/elements/xf_display_panel.js
index 26dc5cb..219d52ac 100644
--- a/ui/file_manager/file_manager/foreground/elements/xf_display_panel.js
+++ b/ui/file_manager/file_manager/foreground/elements/xf_display_panel.js
@@ -9,13 +9,13 @@
   constructor() {
     DisplayPanel.createElement_.call(super());
 
-    /** @private {!HTMLElement} */
+    /** @private {?Element} */
     this.summary_ = this.shadowRoot.querySelector('#summary');
 
-    /** @private {!HTMLElement} */
+    /** @private {?Element} */
     this.separator_ = this.shadowRoot.querySelector('#separator');
 
-    /** @private {!HTMLElement} */
+    /** @private {?Element} */
     this.panels_ = this.shadowRoot.querySelector('#panels');
 
     /**
@@ -54,6 +54,7 @@
   /**
    * Get the custom element template string.
    * @private
+   * @return {string}
    */
   static html_() {
     return `<style>
@@ -109,7 +110,7 @@
 
   /**
    * Update the summary panel item progress indicator.
-   * @private
+   * @public
    */
   updateProgress() {
     let total = 0;
@@ -131,7 +132,7 @@
 
   /**
    * Update the summary panel.
-   * @private
+   * @public
    */
   updateSummaryPanel() {
     let summaryHost = this.shadowRoot.querySelector('#summary');
@@ -208,7 +209,7 @@
 
   /**
    * Find a panel with given 'id'.
-   * @private
+   * @public
    */
   findPanelItemById(id) {
     const panel = this.shadowRoot.querySelector('xf-panel-item[id=' + id + ']');
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
index f8a63027..57c6c9e7 100644
--- a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
+++ b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
@@ -28,6 +28,9 @@
 
     /** @private {number} */
     this.panelType_ = this.panelTypeDefault;
+
+    /** @public {?DisplayPanel} */
+    this.parent = null;
   }
 
   /**
@@ -91,24 +94,28 @@
 
     // Setup the panel configuration for the panel type.
     // TOOD(crbug.com/947388) Simplify this switch breaking out common cases.
+    /** @type {?Element} */
+    let primaryButton = null;
+    /** @type {?Element} */
+    let secondaryButton = null;
     switch (type) {
       case this.panelTypeProgress:
         this.setAttribute('indicator', 'progress');
-        let primaryButton = document.createElement('xf-button');
+        primaryButton = document.createElement('xf-button');
         primaryButton.id = 'primary-action';
-        primaryButton.setAttribute('category', 'pause');
+        primaryButton.dataset.category = 'pause';
         buttonSpacer.insertAdjacentElement('beforebegin', primaryButton);
 
-        let secondaryButton = document.createElement('xf-button');
+        secondaryButton = document.createElement('xf-button');
         secondaryButton.id = 'secondary-action';
-        secondaryButton.setAttribute('category', 'cancel');
+        secondaryButton.dataset.category = 'cancel';
         buttonSpacer.insertAdjacentElement('afterend', secondaryButton);
         break;
       case this.panelTypeSummary:
         this.setAttribute('indicator', 'largeprogress');
         primaryButton = document.createElement('xf-button');
         primaryButton.id = 'primary-action';
-        primaryButton.setAttribute('category', 'expand');
+        primaryButton.dataset.category = 'expand';
         buttonSpacer.insertAdjacentElement('afterend', primaryButton);
         break;
       case this.panelTypeDone:
@@ -116,12 +123,12 @@
         this.setAttribute('status', 'success');
         primaryButton = document.createElement('xf-button');
         primaryButton.id = 'primary-action';
-        primaryButton.setAttribute('category', 'dismiss');
+        primaryButton.dataset.category = 'dismiss';
         buttonSpacer.insertAdjacentElement('beforebegin', primaryButton);
 
         secondaryButton = document.createElement('xf-button');
         secondaryButton.id = 'secondary-action';
-        secondaryButton.setAttribute('category', 'undo');
+        secondaryButton.dataset.category = 'undo';
         buttonSpacer.insertAdjacentElement('afterend', secondaryButton);
         break;
       case this.panelTypeError:
@@ -129,7 +136,7 @@
         this.setAttribute('status', 'failure');
         secondaryButton = document.createElement('xf-button');
         secondaryButton.id = 'secondary-action';
-        secondaryButton.setAttribute('category', 'retry');
+        secondaryButton.dataset.category = 'retry';
         buttonSpacer.insertAdjacentElement('afterend', secondaryButton);
         break;
       case this.panelTypeInfo:
@@ -163,9 +170,9 @@
    * @private
    */
   attributeChangedCallback(name, oldValue, newValue) {
-    /** @type {HTMLElement} */
+    /** @type {Element} */
     let indicator = null;
-    /** @type {HTMLSpanElement} */
+    /** @type {Element} */
     let textNode;
     if (oldValue === newValue) {
       return;
@@ -174,7 +181,7 @@
     switch (name) {
       case 'count':
         if (this.indicator_) {
-          this.indicator_.setAttribute('label', newValue);
+          this.indicator_.setAttribute('label', newValue || '');
         }
         break;
       case 'indicator':
diff --git a/ui/file_manager/file_manager/foreground/js/main_scripts.js b/ui/file_manager/file_manager/foreground/js/main_scripts.js
index f33d0df..3206805 100644
--- a/ui/file_manager/file_manager/foreground/js/main_scripts.js
+++ b/ui/file_manager/file_manager/foreground/js/main_scripts.js
@@ -181,6 +181,11 @@
 // <include src="main_window_component.js">
 // <include src="../../../base/js/filtered_volume_manager.js">
 // <include src="webui_command_extender.js">
+// <include src="../elements/xf_activity_complete.js">
+// <include src="../elements/xf_button.js">
+// <include src="../elements/xf_circular_progress.js">
+// <include src="../elements/xf_display_panel.js">
+// <include src="../elements/xf_panel_item.js">
 
 // // For accurate load performance tracking place main.js should be
 // // the last include to include.
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
index 03b0eff..f4e2079 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -262,6 +262,11 @@
     "//ui/file_manager/file_manager/foreground/elements:files_toast",
     "//ui/file_manager/file_manager/foreground/elements:files_toggle_ripple",
     "//ui/file_manager/file_manager/foreground/elements:files_tooltip",
+    "//ui/file_manager/file_manager/foreground/elements:xf_activity_complete",
+    "//ui/file_manager/file_manager/foreground/elements:xf_button",
+    "//ui/file_manager/file_manager/foreground/elements:xf_circular_progress",
+    "//ui/file_manager/file_manager/foreground/elements:xf_display_panel",
+    "//ui/file_manager/file_manager/foreground/elements:xf_panel_item",
     "//ui/file_manager/file_manager/foreground/js:launch_param",
     "//ui/file_manager/file_manager/foreground/js:providers_model",
     "//ui/webui/resources/js:util",
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
index fa2e69d..739d1f4 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
@@ -241,6 +241,22 @@
         queryRequiredElement('#progress-center', this.element));
 
     /**
+     * Activity complete feedback panel.
+     * @type {!HTMLElement}
+     * @const
+     */
+    this.activityCompletePanel =
+        queryRequiredElement('#completed-panel', this.element);
+
+    /**
+     * Activity feedback panel.
+     * @type {!HTMLElement}
+     * @const
+     */
+    this.activityProgressPanel =
+        queryRequiredElement('#progress-panel', this.element);
+
+    /**
      * List container.
      * @type {ListContainer}
      */
diff --git a/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js b/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js
index 1c230ba5..4db9a6b 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js
@@ -211,6 +211,12 @@
         HTMLDivElement);
 
     /**
+     * Reference to the feedback panel host.
+     * TODO(crbug.com/947388) Add closure annotation here.
+     */
+    this.feedbackHost_ = document.querySelector('#progress-panel');
+
+    /**
      * Close view that is a summarized progress item.
      * @type {ProgressCenterItemElement}
      * @private
@@ -312,6 +318,8 @@
   /**
    * Updates an item to the progress center panel.
    * @param {!ProgressCenterItem} item Item including new contents.
+   * @suppress {checkTypes}
+   * TODO(crbug.com/947388) Remove the suppress, and fix closure compile.
    */
   updateItem(item) {
     const targetGroup = this.getGroupForItem_(item);
@@ -321,30 +329,51 @@
 
     // Update an open view item.
     const newItem = targetGroup.getItem(item.id);
-    let itemElement = this.getItemElement_(item.id);
-    if (newItem) {
-      if (!itemElement) {
-        itemElement =
-            new ProgressCenterItemElement(this.element_.ownerDocument);
-        // Find quiet node and insert the item before the quiet node.
-        this.openView_.insertBefore(
-            itemElement, this.openView_.querySelector('.quiet'));
+    if (util.isFeedbackPanelEnabled()) {
+      let panelItem = this.feedbackHost_.findPanelItemById(item.id);
+      if (newItem) {
+        if (!panelItem) {
+          panelItem = this.feedbackHost_.addPanelItem(item.id);
+          panelItem.panelType = panelItem.panelTypeProgress;
+          panelItem.setAttribute('primary-text', item.message);
+        }
+        panelItem.progress = item.progressRateInPercent;
+        // Remove the feedback panel when complete, and create
+        // an activity complete panel.
+        if (item.state == 'completed') {
+          this.feedbackHost_.removePanelItem(panelItem);
+        }
+      } else if (panelItem) {
+        this.feedbackHost_.removePanelItem(panelItem);
       }
-      itemElement.update(newItem, targetGroup.isAnimated(item.id));
     } else {
-      if (itemElement) {
-        itemElement.parentNode.removeChild(itemElement);
+      let itemElement = this.getItemElement_(item.id);
+      if (newItem) {
+        if (!itemElement) {
+          itemElement =
+              new ProgressCenterItemElement(this.element_.ownerDocument);
+          // Find quiet node and insert the item before the quiet node.
+          this.openView_.insertBefore(
+              itemElement, this.openView_.querySelector('.quiet'));
+        }
+        itemElement.update(newItem, targetGroup.isAnimated(item.id));
+      } else {
+        if (itemElement) {
+          itemElement.parentNode.removeChild(itemElement);
+        }
       }
-    }
 
-    // Update the close view.
-    this.updateCloseView_();
+      // Update the close view.
+      this.updateCloseView_();
+    }
   }
 
   /**
    * Handles the item animation end.
    * @param {Event} event Item animation end event.
    * @private
+   * @suppress {checkTypes}
+   * TODO(crbug.com/947388) Remove the suppress, and fix closure compile.
    */
   onItemAnimationEnd_(event) {
     const targetGroup = event.target.classList.contains('quiet') ?
@@ -355,10 +384,17 @@
     } else {
       const itemId = event.target.getAttribute('data-progress-id');
       targetGroup.completeItemAnimation(itemId);
-      const newItem = targetGroup.getItem(itemId);
-      const itemElement = this.getItemElement_(itemId);
-      if (!newItem && itemElement) {
-        itemElement.parentNode.removeChild(itemElement);
+      if (util.isFeedbackPanelEnabled()) {
+        const panelItem = this.feedbackHost_.findPanelItemById(itemId);
+        if (panelItem) {
+          this.feedbackHost_.removePanelItem(panelItem);
+        }
+      } else {
+        const newItem = targetGroup.getItem(itemId);
+        const itemElement = this.getItemElement_(itemId);
+        if (!newItem && itemElement) {
+          itemElement.parentNode.removeChild(itemElement);
+        }
       }
     }
     this.updateCloseView_();
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index a96ee05..e99192250 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -24,6 +24,7 @@
     <link rel="stylesheet" href="foreground/css/file_status.css">
     <link rel="stylesheet" href="foreground/css/file_types.css">
     <link rel="stylesheet" href="foreground/css/common.css">
+    <link rel="stylesheet" href="foreground/elements/files_xf_elements.css">
     <link rel="stylesheet" href="cws_widget/cws_widget_container.css">
 
     <script src="foreground/js/elements_importer.js"></script>
@@ -77,6 +78,7 @@
             background-color: black;
           };
           --files-toggle-ripple-activated: {
+            background-color: black;
             opacity: 0.08;
           };
         }
@@ -551,6 +553,12 @@
         </div>
       </div>
     </div>
+    <div class="feedback-panels">
+      <xf-display-panel id="completed-panel">
+      </xf-display-panel>
+      <xf-display-panel id="progress-panel">
+      </xf-display-panel>
+    </div>
     <div class="dialog-footer progressable" tabindex="-1"
          visibleif="saveas-file open-file open-multi-file folder upload-folder">
       <div class="left">
diff --git a/ui/file_manager/file_manager/test/js/strings.js b/ui/file_manager/file_manager/test/js/strings.js
index 2660169..b50b3172 100644
--- a/ui/file_manager/file_manager/test/js/strings.js
+++ b/ui/file_manager/file_manager/test/js/strings.js
@@ -11,6 +11,7 @@
 loadTimeData.overrideValues({
   'CROSTINI_ENABLED': true,
   'DRIVE_FS_ENABLED': true,
+  'FEEDBACK_PANEL_ENABLED': false,
   'GOOGLE_DRIVE_REDEEM_URL': 'http://www.google.com/intl/en/chrome/devices' +
       '/goodies.html?utm_source=filesapp&utm_medium=banner&utm_campaign=gsg',
   'GOOGLE_DRIVE_OVERVIEW_URL':
diff --git a/ui/file_manager/file_manager/test/scripts/create_test_main.py b/ui/file_manager/file_manager/test/scripts/create_test_main.py
index e89604f..5f66f6bb 100755
--- a/ui/file_manager/file_manager/test/scripts/create_test_main.py
+++ b/ui/file_manager/file_manager/test/scripts/create_test_main.py
@@ -236,6 +236,7 @@
     ('foreground/elements/files_toast.html', ()),
     ('foreground/elements/files_toggle_ripple.html', ()),
     ('foreground/elements/files_tooltip.html', ()),
+    ('foreground/elements/files_xf_elements.css', ()),
     ('foreground/elements/icons.html', ()),
     ):
   buf = i18n(read('ui/file_manager/file_manager/' + filename))
diff --git a/ui/file_manager/file_manager_resources.grd b/ui/file_manager/file_manager_resources.grd
index 09f2ab7..9f93a3d7 100644
--- a/ui/file_manager/file_manager_resources.grd
+++ b/ui/file_manager/file_manager_resources.grd
@@ -133,6 +133,7 @@
       <include name="IDR_FILE_MANAGER_IMG_UI_2X_DRIVE_WELCOME_LOGO" file="file_manager/foreground/images/files/ui/2x/drive_logo.png" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_IMG_UI_DRIVE_WELCOME_PEOPLE" file="file_manager/foreground/images/files/ui/drive_banner_people.png" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_IMG_UI_2X_DRIVE_WELCOME_PEOPLE" file="file_manager/foreground/images/files/ui/2x/drive_banner_people.png" type="BINDATA" />
+      <include name="IDR_FILE_MANAGER_ELEMENTS_FILES_XF_ELEMENTS_CSS" file="file_manager/foreground/elements/files_xf_elements.css" type="BINDATA" />
 
       <!-- The VideoPlayer app pages and scripts. -->
       <include name="IDR_VIDEO_PLAYER_MANIFEST" file="video_player/manifest.json" type="BINDATA" />
diff --git a/ui/file_manager/gallery/gallery.html b/ui/file_manager/gallery/gallery.html
index bb38438..6d8f2a6a 100644
--- a/ui/file_manager/gallery/gallery.html
+++ b/ui/file_manager/gallery/gallery.html
@@ -36,9 +36,7 @@
       cr-checkbox {
         --cr-checkbox-checked-box-color: white;
         --cr-checkbox-label-color: white;
-        --cr-checkbox-label-container: {
-          -webkit-padding-start: 6px;
-        };
+        --cr-checkbox-label-padding-start: 6px;
         --cr-checkbox-mark-color: black;
         --cr-checkbox-ripple-checked-color: white;
         --cr-checkbox-ripple-size: 38px;
diff --git a/ui/file_manager/integration_tests/file_manager/copy_between_windows.js b/ui/file_manager/integration_tests/file_manager/copy_between_windows.js
index c211e9a..d7cba90 100644
--- a/ui/file_manager/integration_tests/file_manager/copy_between_windows.js
+++ b/ui/file_manager/integration_tests/file_manager/copy_between_windows.js
@@ -43,16 +43,12 @@
 
   await remoteCall.waitForFiles(window1, [file.getExpectedRow()]);
 
-  await remoteCall.callRemoteTestUtil('fakeMouseClick', window1, []);
-
   chrome.test.assertTrue(
       !!await remoteCall.callRemoteTestUtil('selectFile', window1, [name]),
       'Failed: selectFile ' + name);
 
   await remoteCall.callRemoteTestUtil('execCommand', window1, ['copy']);
 
-  await remoteCall.callRemoteTestUtil('fakeMouseClick', window2, []);
-
   await remoteCall.callRemoteTestUtil('execCommand', window2, ['paste']);
 
   const expectedFiles = [file.getExpectedRow()];
diff --git a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
index 408a5cd..3e1f67ab 100644
--- a/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
+++ b/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc
@@ -11,7 +11,6 @@
 #include "base/command_line.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
 #include "ui/ozone/common/gpu/ozone_gpu_messages.h"
@@ -93,11 +92,8 @@
                      : nullptr),
       cursor_(cursor),
       weak_ptr_factory_(this) {
-  if (ui_runner_) {
+  if (ui_runner_)
     weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
-  } else {
-    DCHECK(!features::IsMultiProcessMash());
-  }
 }
 
 DrmGpuPlatformSupportHost::~DrmGpuPlatformSupportHost() {}
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index be9e3026..3175143 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -1898,14 +1898,7 @@
 }
 
 // Tests that menus without parent widgets do not crash in MenuPreTargetHandler.
-// This is generally true, except on Chrome OS running with the window service.
-// In that case, a DCHECK fires to ensure menus can consume parents' key events.
 TEST_F(MenuControllerTest, RunWithoutWidgetDoesntCrash) {
-#if defined(OS_CHROMEOS)
-  if (::features::IsUsingWindowService())
-    return;
-#endif  // OS_CHROMEOS
-
   ExitMenuRun();
   MenuController* controller = menu_controller();
   controller->Run(nullptr, nullptr, menu_item(), gfx::Rect(),
diff --git a/ui/views/controls/menu/menu_pre_target_handler_aura.cc b/ui/views/controls/menu/menu_pre_target_handler_aura.cc
index 2bfd294..94976c2 100644
--- a/ui/views/controls/menu/menu_pre_target_handler_aura.cc
+++ b/ui/views/controls/menu/menu_pre_target_handler_aura.cc
@@ -6,7 +6,6 @@
 
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/views/controls/menu/menu_controller.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/public/activation_client.h"
@@ -29,11 +28,7 @@
     root_->AddObserver(this);
   } else {
     // This should only happen in cases like when context menus are shown for
-    // Windows OS system tray items and there is no parent window. This should
-    // not be hit on Chrome OS, where Window Service clients need to install a
-    // pre-target handler on the aura::Env associated with their app window.
-    DCHECK(!::features::IsUsingWindowService())
-        << "MenuPreTargetHandlerAura may not work correctly without an owner.";
+    // Windows OS system tray items and there is no parent window.
   }
   aura::Env::GetInstance()->AddPreTargetHandler(
       this, ui::EventTarget::Priority::kSystem);
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
index bd6c454..ba013a9 100644
--- a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
@@ -10,7 +10,7 @@
   --cr-checkbox-border-size
   --cr-checkbox-checked-box-color
   --cr-checkbox-label-color
-  --cr-checkbox-label-container (CSS mixin)
+  --cr-checkbox-label-padding-start
   --cr-checkbox-mark-color
   --cr-checkbox-ripple-checked-color
   --cr-checkbox-ripple-size
@@ -137,10 +137,8 @@
 
       #label-container {
         color: var(--cr-checkbox-label-color, var(--cr-primary-text-color));
-        padding-inline-start: 20px;
+        padding-inline-start: var(--cr-checkbox-label-padding-start, 20px);
         white-space: normal;
-
-        @apply --cr-checkbox-label-container;
       }
 
       :host(.no-label) #label-container {
@@ -160,7 +158,9 @@
         aria-describedby="ariaDescription">
       <span id="checkmark"></span>
     </div>
-    <div id="label-container" aria-hidden="true"><slot></slot></div>
+    <div id="label-container" aria-hidden="true" part="label-container">
+      <slot></slot>
+    </div>
     <div id="ariaDescription">[[ariaDescription]]</div>
   </template>
   <script src="cr_checkbox.js"></script>
diff --git a/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_style_css.html b/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_style_css.html
index f5c9390a..5d797a2 100644
--- a/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_style_css.html
+++ b/ui/webui/resources/cr_elements/cr_radio_button/cr_radio_button_style_css.html
@@ -50,18 +50,12 @@
       #labelWrapper {
         flex: 1;
         margin-inline-start: var(--cr-radio-button-label-spacing, 20px);
-
-        @apply --cr-radio-button-label;
       }
 
       #label {
         color: inherit;
       }
 
-      #slotted-content {
-        @apply --cr-radio-button-slot;
-      }
-
       .disc-border,
       .disc,
       .disc-wrapper,
@@ -71,10 +65,9 @@
 
       .disc-wrapper {
         height: var(--cr-radio-button-size);
+        margin-block-start: var(--cr-radio-button-disc-margin-block-start, 0);
         position: relative;
         width: var(--cr-radio-button-size);
-
-        @apply --cr-radio-button-disc;
       }
 
       .disc-border,