diff --git a/DEPS b/DEPS
index d4ab8188..3a1b2d7 100644
--- a/DEPS
+++ b/DEPS
@@ -171,7 +171,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '32803ff7444854c109d1a751a2f240812aca0d29',
+  'skia_revision': 'e9462dd2dac4dac9b3e17dd962066db34cfaf39c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -183,7 +183,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': 'a9f11bf14bcebf84788914012da0c3525d4b5e94',
+  'angle_revision': '08af1c80188f9797d72b0a16749cad9f764df4b7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -191,7 +191,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': 'e792f87ded42c8ba18496c8e2769319d31eeb615',
+  'pdfium_revision': '794ed818b8937a727759a340d513a25d12c067cb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -234,7 +234,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': '61a1e81e89bd875be5eb5d3d11b798e09ba35ec6',
+  'catapult_revision': '577977ad233c62f9150425ec63fc02de4d598b9d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -310,11 +310,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '1f6c8c4d549574542a93ec7649e2b56a09696e1b',
+  'dawn_revision': '402fbcca936b3ecc8ede079f5cb1f87c818811b4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': 'b5a8b9ebfa6e9fa50120c847d22e3b703a08174a',
+  'quiche_revision': '4cc508c720aca5e15d1e5d89b970f58c57414764',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -874,7 +874,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1f4478ca783974c3cb4ea3d3200bc993fab75aff',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '75647d9e1ae7c9f50014993944a608f08a157a46',
       'condition': 'checkout_linux',
   },
 
@@ -984,7 +984,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'a3f0da56e4c9cfe22f6be29ff8dd8b9d2c059016',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '341701573a5db1153bb5ea48b88e8fd6c5a590c2',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1308,7 +1308,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '3c660b0e0c31b497ca1021b09b869eb54070498c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '9fb9a722fb283f7473684415dd793df77c2a6716',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1509,7 +1509,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '2701c130839edbeb226735b0775966b6423d9e83',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '21bfa401ddea0d119b515c4806a36c0b00aa03ae',
+    Var('webrtc_git') + '/src.git' + '@' + '6adb0a26846c0a6ddf7d7e72ebf80b829c34d693',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1576,7 +1576,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ea58518db3b859ac016ae1712e90cfc582b4c2b6',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4900f195bb83089d469c39206af022edf8bddef6',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index 2393e2c3..be29bd3 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -59,6 +59,8 @@
   "+printing",
 
   "+services/network/public",
+  # This is needed for the CookieManager to configure the CookieStore directly.
+  "+services/network/cookie_access_delegate_impl.h",
   "+services/resource_coordinator/public/cpp",
   "+services/service_manager/public/cpp",
 
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index 8b476e0..f800e23 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -454,6 +454,9 @@
       network::mojom::CookieManagerParams::New();
   context_params->cookie_manager_params->allow_file_scheme_cookies =
       GetCookieManager()->AllowFileSchemeCookies();
+  // Set all cookies to Legacy on Android WebView, for compatibility reasons.
+  context_params->cookie_manager_params->cookie_access_delegate_type =
+      network::mojom::CookieAccessDelegateType::ALWAYS_LEGACY;
 
   context_params->initial_ssl_config = network::mojom::SSLConfig::New();
   // Allow SHA-1 to be used for locally-installed trust anchors, as WebView
diff --git a/android_webview/browser/cookie_manager.cc b/android_webview/browser/cookie_manager.cc
index e5757ef3..8850c74 100644
--- a/android_webview/browser/cookie_manager.cc
+++ b/android_webview/browser/cookie_manager.cc
@@ -44,6 +44,7 @@
 #include "net/cookies/cookie_util.h"
 #include "net/cookies/parsed_cookie.h"
 #include "net/url_request/url_request_context.h"
+#include "services/network/cookie_access_delegate_impl.h"
 #include "services/network/network_service.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "url/url_constants.h"
@@ -324,6 +325,11 @@
     }
 
     cookie_store_ = content::CreateCookieStore(cookie_config, nullptr);
+    // Use a CookieAccessDelegate that always returns Legacy mode, for
+    // compatibility reasons.
+    cookie_store_->SetCookieAccessDelegate(
+        std::make_unique<network::CookieAccessDelegateImpl>(
+            network::mojom::CookieAccessDelegateType::ALWAYS_LEGACY));
   }
 
   return cookie_store_.get();
diff --git a/android_webview/browser/network_service/android_stream_reader_url_loader.cc b/android_webview/browser/network_service/android_stream_reader_url_loader.cc
index e5164d64..9402725 100644
--- a/android_webview/browser/network_service/android_stream_reader_url_loader.cc
+++ b/android_webview/browser/network_service/android_stream_reader_url_loader.cc
@@ -100,8 +100,7 @@
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
     std::unique_ptr<ResponseDelegate> response_delegate)
     : resource_request_(resource_request),
-      resource_response_head_(
-          std::make_unique<network::ResourceResponseHead>()),
+      response_head_(network::mojom::URLResponseHead::New()),
       client_(std::move(client)),
       traffic_annotation_(traffic_annotation),
       response_delegate_(std::move(response_delegate)),
@@ -218,7 +217,7 @@
   // HttpResponseHeaders expects its input string to be terminated by two NULs.
   status.append("\0\0", 2);
 
-  network::ResourceResponseHead& head = *resource_response_head_;
+  network::mojom::URLResponseHead& head = *response_head_;
   head.request_start = base::TimeTicks::Now();
   head.response_start = base::TimeTicks::Now();
   head.headers = new net::HttpResponseHeaders(status);
@@ -286,7 +285,7 @@
   //    needs the ability to break the stream after getting the headers but
   //    before finishing the read.
   if (!base::FeatureList::IsEnabled(features::kWebViewSniffMimeType) ||
-      !resource_response_head_->mime_type.empty()) {
+      !response_head_->mime_type.empty()) {
     SendResponseToClient();
   }
   ReadMore();
@@ -295,8 +294,7 @@
 void AndroidStreamReaderURLLoader::SendResponseToClient() {
   DCHECK(consumer_handle_.is_valid());
   DCHECK(client_.is_bound());
-  client_->OnReceiveResponse(*resource_response_head_);
-  resource_response_head_ = nullptr;
+  client_->OnReceiveResponse(std::move(response_head_));
   client_->OnStartLoadingResponseBody(std::move(consumer_handle_));
 }
 
@@ -360,7 +358,7 @@
     // We only hit this on for the first buffer read, which we expect to be
     // enough to determine the MIME type.
     DCHECK(base::FeatureList::IsEnabled(features::kWebViewSniffMimeType));
-    if (resource_response_head_->mime_type.empty()) {
+    if (response_head_->mime_type.empty()) {
       // Limit sniffing to the first net::kMaxBytesToSniff.
       size_t data_length = result;
       if (data_length > net::kMaxBytesToSniff)
@@ -373,8 +371,8 @@
       // SniffMimeType() returns false if there is not enough data to
       // determine the mime type. However, even if it returns false, it
       // returns a new type that is probably better than the current one.
-      resource_response_head_->mime_type.assign(new_type);
-      resource_response_head_->did_mime_sniff = true;
+      response_head_->mime_type.assign(new_type);
+      response_head_->did_mime_sniff = true;
     }
 
     SendResponseToClient();
diff --git a/android_webview/browser/network_service/android_stream_reader_url_loader.h b/android_webview/browser/network_service/android_stream_reader_url_loader.h
index cd77346d..856535f5 100644
--- a/android_webview/browser/network_service/android_stream_reader_url_loader.h
+++ b/android_webview/browser/network_service/android_stream_reader_url_loader.h
@@ -9,8 +9,8 @@
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "net/http/http_byte_range.h"
 #include "services/network/public/cpp/net_adapters.h"
-#include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 
 namespace android_webview {
 
@@ -107,7 +107,7 @@
 
   net::HttpByteRange byte_range_;
   network::ResourceRequest resource_request_;
-  std::unique_ptr<network::ResourceResponseHead> resource_response_head_;
+  network::mojom::URLResponseHeadPtr response_head_;
   network::mojom::URLLoaderClientPtr client_;
   const net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
   std::unique_ptr<ResponseDelegate> response_delegate_;
diff --git a/android_webview/browser/network_service/aw_url_loader_throttle.cc b/android_webview/browser/network_service/aw_url_loader_throttle.cc
index 94d2e9c1..93fa212 100644
--- a/android_webview/browser/network_service/aw_url_loader_throttle.cc
+++ b/android_webview/browser/network_service/aw_url_loader_throttle.cc
@@ -21,7 +21,7 @@
 
 void AwURLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& response_head,
+    const network::mojom::URLResponseHead& response_head,
     bool* defer,
     std::vector<std::string>* to_be_removed_request_headers,
     net::HttpRequestHeaders* modified_request_headers) {
diff --git a/android_webview/browser/network_service/aw_url_loader_throttle.h b/android_webview/browser/network_service/aw_url_loader_throttle.h
index 6a58de8..2164172 100644
--- a/android_webview/browser/network_service/aw_url_loader_throttle.h
+++ b/android_webview/browser/network_service/aw_url_loader_throttle.h
@@ -27,7 +27,7 @@
                         bool* defer) override;
   void WillRedirectRequest(
       net::RedirectInfo* redirect_info,
-      const network::ResourceResponseHead& response_head,
+      const network::mojom::URLResponseHead& response_head,
       bool* defer,
       std::vector<std::string>* to_be_removed_request_headers,
       net::HttpRequestHeaders* modified_request_headers) override;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwTracingController.java b/android_webview/java/src/org/chromium/android_webview/AwTracingController.java
index 19d5607..4dca8e42 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwTracingController.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwTracingController.java
@@ -26,7 +26,7 @@
  */
 @JNINamespace("android_webview")
 public class AwTracingController {
-    private static final String TAG = "cr.AwTracingController";
+    private static final String TAG = "AwTracingController";
 
     public static final int RESULT_SUCCESS = 0;
     public static final int RESULT_ALREADY_TRACING = 1;
diff --git a/android_webview/java/src/org/chromium/android_webview/permission/AwGeolocationCallback.java b/android_webview/java/src/org/chromium/android_webview/permission/AwGeolocationCallback.java
index 9f1b04f..2435b337 100644
--- a/android_webview/java/src/org/chromium/android_webview/permission/AwGeolocationCallback.java
+++ b/android_webview/java/src/org/chromium/android_webview/permission/AwGeolocationCallback.java
@@ -17,7 +17,7 @@
  * WebView applications through WebChromeClient.onGeolocationPermissionsShowPrompt().
  */
 public class AwGeolocationCallback implements AwGeolocationPermissions.Callback {
-    private static final String TAG = "cr.Geolocation";
+    private static final String TAG = "Geolocation";
 
     private CleanupRunable mCleanupRunable;
     private CleanupReference mCleanupReference;
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwDebugTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwDebugTest.java
index abbd14e..fdcffd4b 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwDebugTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwDebugTest.java
@@ -33,7 +33,7 @@
     @Rule
     public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
 
-    private static final String TAG = "cr_AwDebugTest";
+    private static final String TAG = "AwDebugTest";
 
     // These constants must match android_webview/browser/aw_debug.cc.
     private static final String WHITELISTED_DEBUG_KEY = "AW_WHITELISTED_DEBUG_KEY";
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
index 2adc7c5..f6184729 100644
--- a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
+++ b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellActivity.java
@@ -51,7 +51,7 @@
  * This is a lightweight activity for tests that only require WebView functionality.
  */
 public class AwShellActivity extends Activity {
-    private static final String TAG = "cr.AwShellActivity";
+    private static final String TAG = "AwShellActivity";
     private static final String PREFERENCES_NAME = "AwShellPrefs";
     private static final String INITIAL_URL = ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL;
     private AwBrowserContext mBrowserContext;
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index e26d377b..eb62562 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1352,6 +1352,7 @@
     "//components/discardable_memory/public/mojom",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/system",
+    "//services/data_decoder/public/mojom",
     "//services/device/public/cpp/bluetooth",
     "//services/device/public/mojom",
     "//services/media_session/public/cpp",
diff --git a/ash/DEPS b/ash/DEPS
index 3b13cbc..b8b0a23 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -27,6 +27,7 @@
   "+media",
   "+mojo/public",
   "+services/content/public",
+  "+services/data_decoder/public",
   "+services/media_session/public",
   "+services/preferences/public",
   "+services/service_manager/public",
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc
index cf9177d..a0cf6e5 100644
--- a/ash/display/mirror_window_controller.cc
+++ b/ash/display/mirror_window_controller.cc
@@ -30,7 +30,6 @@
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/compositor/reflector.h"
 #include "ui/display/display_layout.h"
-#include "ui/display/manager/display_layout_store.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
 #include "ui/display/screen.h"
@@ -250,32 +249,15 @@
     }
 
     if (features::IsVizDisplayCompositorEnabled()) {
-      // |mirror_size| is the size of the compositor of the mirror source in
-      // physical pixels. The RootWindowTransformer corrects the scale of the
-      // mirrored display and the location of input events.
-      ui::Compositor* source_compositor =
-          Shell::GetRootWindowForDisplayId(reflecting_source_id_)
-              ->GetHost()
-              ->compositor();
-      gfx::Size mirror_size = source_compositor->size();
-
-      auto* mirroring_host_info = mirroring_host_info_map_[display_info.id()];
-
-      // The rotation of the source display (internal display) should be undone
-      // in the destination display (external display) if mirror mode is enabled
-      // in tablet mode. This allows the destination display to show in an
-      // orientation independent of the source display.
-      // See https://crbug.com/824417
-      const bool should_undo_rotation = Shell::Get()
-                                            ->display_manager()
-                                            ->layout_store()
-                                            ->forced_mirror_mode_for_tablet();
-      if (!should_undo_rotation) {
-        mirroring_host_info->ash_host->AsWindowTreeHost()
-            ->SetDisplayTransformHint(source_compositor->display_transform());
-      }
-
-      aura::Window* mirror_window = mirroring_host_info->mirror_window;
+      // |mirror_size| is the size of the mirror source in physical pixels.
+      // The RootWindowTransformer corrects the scale of the mirrored display
+      // and the location of input events.
+      gfx::Size mirror_size =
+          display_manager->GetDisplayInfo(reflecting_source_id_)
+              .bounds_in_native()
+              .size();
+      aura::Window* mirror_window =
+          mirroring_host_info_map_[display_info.id()]->mirror_window;
       mirror_window->SetBounds(gfx::Rect(mirror_size));
       mirror_window->Show();
       mirror_window->layer()->SetShowReflectedSurface(reflecting_surface_id,
diff --git a/ash/display/root_window_transformers.cc b/ash/display/root_window_transformers.cc
index b9a8ffb..6fbf5e06 100644
--- a/ash/display/root_window_transformers.cc
+++ b/ash/display/root_window_transformers.cc
@@ -12,7 +12,6 @@
 #include "ash/shell.h"
 #include "ash/utility/transformer_util.h"
 #include "base/command_line.h"
-#include "components/viz/common/features.h"
 #include "ui/compositor/dip_util.h"
 #include "ui/display/display.h"
 #include "ui/display/manager/display_layout_store.h"
@@ -57,44 +56,6 @@
   return transform;
 }
 
-// Returns a transform with rotation adjusted |insets_in_pixel|. The transform
-// is applied to the root window so that |insets_in_pixel| looks correct after
-// the rotation applied at the output.
-gfx::Transform CreateReverseRotatedInsetsTransform(
-    display::Display::Rotation rotation,
-    const gfx::Insets& insets_in_pixel,
-    float device_scale_factor) {
-  float x_offset = 0;
-  float y_offset = 0;
-
-  switch (rotation) {
-    case display::Display::ROTATE_0:
-      x_offset = insets_in_pixel.left();
-      y_offset = insets_in_pixel.top();
-      break;
-    case display::Display::ROTATE_90:
-      x_offset = insets_in_pixel.top();
-      y_offset = insets_in_pixel.right();
-      break;
-    case display::Display::ROTATE_180:
-      x_offset = insets_in_pixel.right();
-      y_offset = insets_in_pixel.bottom();
-      break;
-    case display::Display::ROTATE_270:
-      x_offset = insets_in_pixel.bottom();
-      y_offset = insets_in_pixel.left();
-      break;
-  }
-
-  gfx::Transform transform;
-  if (x_offset != 0 || y_offset != 0) {
-    x_offset /= device_scale_factor;
-    y_offset /= device_scale_factor;
-    transform.Translate(x_offset, y_offset);
-  }
-  return transform;
-}
-
 // RootWindowTransformer for ash environment.
 class AshRootWindowTransformer : public RootWindowTransformer {
  public:
@@ -109,16 +70,10 @@
                                       display.device_scale_factor()) *
         CreateRootWindowRotationTransform(root, display);
     transform_ = root_window_bounds_transform_;
-    insets_and_scale_transform_ = CreateReverseRotatedInsetsTransform(
-        info.GetLogicalActiveRotation(), host_insets_,
-        display.device_scale_factor());
     MagnificationController* magnifier =
         Shell::Get()->magnification_controller();
-    if (magnifier) {
-      gfx::Transform magnifier_scale = magnifier->GetMagnifierTransform();
-      transform_ *= magnifier_scale;
-      insets_and_scale_transform_ *= magnifier_scale;
-    }
+    if (magnifier)
+      transform_ *= magnifier->GetMagnifierTransform();
 
     CHECK(transform_.GetInverse(&invert_transform_));
   }
@@ -143,9 +98,6 @@
   }
 
   gfx::Insets GetHostInsets() const override { return host_insets_; }
-  gfx::Transform GetInsetsAndScaleTransform() const override {
-    return insets_and_scale_transform_;
-  }
 
  private:
   ~AshRootWindowTransformer() override = default;
@@ -163,7 +115,6 @@
   gfx::Transform root_window_bounds_transform_;
 
   gfx::Insets host_insets_;
-  gfx::Transform insets_and_scale_transform_;
 
   DISALLOW_COPY_AND_ASSIGN(AshRootWindowTransformer);
 };
@@ -229,12 +180,8 @@
       transform_.Scale(inverted_scale, inverted_scale);
     }
 
-    // Apply rotation only when reflector is used for mirroring (non viz display
-    // compositor).
-    if (!features::IsVizDisplayCompositorEnabled()) {
-      // Make sure the rotation transform is applied in the beginning.
-      transform_.PreconcatTransform(rotation_transform);
-    }
+    // Make sure the rotation transform is applied in the beginning.
+    transform_.PreconcatTransform(rotation_transform);
   }
 
   // aura::RootWindowTransformer overrides:
@@ -248,9 +195,6 @@
     return root_bounds_;
   }
   gfx::Insets GetHostInsets() const override { return insets_; }
-  gfx::Transform GetInsetsAndScaleTransform() const override {
-    return transform_;
-  }
 
  private:
   ~MirrorRootWindowTransformer() override = default;
@@ -309,9 +253,6 @@
     return root_bounds_;
   }
   gfx::Insets GetHostInsets() const override { return gfx::Insets(); }
-  gfx::Transform GetInsetsAndScaleTransform() const override {
-    return transform_;
-  }
 
  private:
   gfx::Transform transform_;
diff --git a/ash/display/root_window_transformers_unittest.cc b/ash/display/root_window_transformers_unittest.cc
index 2ce189e..d4fd8e7 100644
--- a/ash/display/root_window_transformers_unittest.cc
+++ b/ash/display/root_window_transformers_unittest.cc
@@ -310,8 +310,7 @@
 // on 2.25 scale factor device so that HW overlay kicks in.
 // https://crbug.com/869090.
 TEST_F(RootWindowTransformersTest, OriginAlignmentWithFractionalScale) {
-  auto* host = Shell::GetPrimaryRootWindow()->GetHost();
-  auto* host_window = host->window();
+  auto* host_window = Shell::GetPrimaryRootWindow()->GetHost()->window();
   EXPECT_EQ(Shell::GetPrimaryRootWindow(), host_window);
 
   float device_scale_factor = 2.25f;
@@ -330,7 +329,7 @@
     gfx::RectF tmp(1998, 2999);
     // Creates a transform that can be applied to already scaled layer.
     gfx::Transform transform(invert_transform);
-    transform.ConcatTransform(host->GetRootTransform() * invert_transform);
+    transform.ConcatTransform(host_window->layer()->transform());
     transform.ConcatTransform(scale_transform);
     transform.TransformRect(&tmp);
     EXPECT_EQ(gfx::SizeF(2999, 1998), tmp.size());
@@ -343,7 +342,7 @@
 
     gfx::RectF tmp(2999, 1998);
     gfx::Transform transform(invert_transform);
-    transform.ConcatTransform(host->GetRootTransform() * invert_transform);
+    transform.ConcatTransform(host_window->layer()->transform());
     transform.ConcatTransform(scale_transform);
     transform.TransformRect(&tmp);
     EXPECT_EQ(gfx::SizeF(2999, 1998), tmp.size());
@@ -356,7 +355,7 @@
 
     gfx::RectF tmp(1998, 2999);
     gfx::Transform transform(invert_transform);
-    transform.ConcatTransform(host->GetRootTransform() * invert_transform);
+    transform.ConcatTransform(host_window->layer()->transform());
     transform.ConcatTransform(scale_transform);
     transform.TransformRect(&tmp);
     EXPECT_EQ(gfx::SizeF(2999, 1998), tmp.size());
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc
index 747c1eb6..f309c77b 100644
--- a/ash/display/window_tree_host_manager.cc
+++ b/ash/display/window_tree_host_manager.cc
@@ -24,7 +24,6 @@
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/unified/unified_system_tray.h"
-#include "ash/utility/transformer_util.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
 #include "base/metrics/histogram.h"
@@ -76,17 +75,12 @@
                                 const display::Display& display) {
   display::ManagedDisplayInfo info =
       GetDisplayManager()->GetDisplayInfo(display.id());
-  const display::Display::Rotation effective_rotation =
-      info.GetLogicalActiveRotation();
   aura::WindowTreeHost* host = ash_host->AsWindowTreeHost();
-  ash_host->SetCursorConfig(display, effective_rotation);
+  ash_host->SetCursorConfig(display, info.GetLogicalActiveRotation());
   std::unique_ptr<RootWindowTransformer> transformer(
       CreateRootWindowTransformerForDisplay(host->window(), display));
   ash_host->SetRootWindowTransformer(std::move(transformer));
 
-  host->SetDisplayTransformHint(
-      DisplayRotationToOverlayTransform(effective_rotation));
-
   // Just moving the display requires the full redraw.
   // chrome-os-partner:33558.
   host->compositor()->ScheduleFullRedraw();
diff --git a/ash/host/root_window_transformer.h b/ash/host/root_window_transformer.h
index e7c73a1..66ebd0bae 100644
--- a/ash/host/root_window_transformer.h
+++ b/ash/host/root_window_transformer.h
@@ -38,10 +38,6 @@
   // Returns the insets that specifies the effective area of
   // the host window.
   virtual gfx::Insets GetHostInsets() const = 0;
-
-  // Returns the transform for applying host insets and magnifier scale. It is
-  // similar to GetTransform() but without the screen rotation.
-  virtual gfx::Transform GetInsetsAndScaleTransform() const = 0;
 };
 
 }  // namespace ash
diff --git a/ash/host/transformer_helper.cc b/ash/host/transformer_helper.cc
index 0d9afdb3..e0256cbd 100644
--- a/ash/host/transformer_helper.cc
+++ b/ash/host/transformer_helper.cc
@@ -49,9 +49,6 @@
   }
 
   gfx::Insets GetHostInsets() const override { return gfx::Insets(); }
-  gfx::Transform GetInsetsAndScaleTransform() const override {
-    return transform_;
-  }
 
  private:
   ~SimpleRootWindowTransformer() override = default;
@@ -89,7 +86,7 @@
   transformer_ = std::move(transformer);
   aura::WindowTreeHost* host = ash_host_->AsWindowTreeHost();
   aura::Window* window = host->window();
-  window->SetTransform(transformer_->GetInsetsAndScaleTransform());
+  window->SetTransform(transformer_->GetTransform());
   // If the layer is not animating with a transform animation, then we need to
   // update the root window size immediately.
   if (!window->layer()->GetAnimator()->IsAnimatingProperty(
diff --git a/ash/login/ui/image_parser.cc b/ash/login/ui/image_parser.cc
index 2449629c..451c274 100644
--- a/ash/login/ui/image_parser.cc
+++ b/ash/login/ui/image_parser.cc
@@ -7,8 +7,10 @@
 #include <utility>
 
 #include "ash/shell.h"
+#include "ash/shell_delegate.h"
 #include "base/bind.h"
 #include "ipc/ipc_channel.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "services/data_decoder/public/cpp/decode_image.h"
 
 namespace ash {
@@ -40,9 +42,9 @@
   // also goes through libpng, but APNG support is handled specifically by
   // blink's PNGImageReader.cpp.
   data_decoder::DecodeAnimation(
-      Shell::Get()->connector(), image_data, true /*shrink_to_fit*/,
-      kMaxImageSizeInBytes,
-      base::Bind(&ConvertToAnimationFrame, Passed(&on_decoded)));
+      Shell::Get()->shell_delegate()->LaunchDataDecoder(), image_data,
+      true /*shrink_to_fit*/, kMaxImageSizeInBytes,
+      base::BindOnce(&ConvertToAnimationFrame, std::move(on_decoded)));
 }
 
 }  // namespace ash
diff --git a/ash/magnifier/docked_magnifier_controller_impl.cc b/ash/magnifier/docked_magnifier_controller_impl.cc
index a0da631..cfd610c4 100644
--- a/ash/magnifier/docked_magnifier_controller_impl.cc
+++ b/ash/magnifier/docked_magnifier_controller_impl.cc
@@ -713,8 +713,6 @@
       GetViewportParentContainerForRoot(current_source_root_window_);
   params.name = kDockedMagnifierViewportWindowName;
   viewport_widget_->Init(std::move(params));
-  viewport_widget_->Show();
-  viewport_widget_->AddObserver(this);
 
   // 2- Create the separator layer right below the viwport widget, parented to
   //    the layer of the root window.
@@ -760,6 +758,11 @@
 
   // 6- Confine the mouse cursor within the remaining part of the display.
   ConfineMouseCursorOutsideViewport();
+
+  // 7- Show the widget, which can trigger events to request movement of the
+  // viewport now that all internal state has been created.
+  viewport_widget_->AddObserver(this);
+  viewport_widget_->Show();
 }
 
 void DockedMagnifierControllerImpl::MaybeCachePointOfInterestMinimumHeight(
diff --git a/ash/public/cpp/manifest.cc b/ash/public/cpp/manifest.cc
index bdbfc658..e0b4338 100644
--- a/ash/public/cpp/manifest.cc
+++ b/ash/public/cpp/manifest.cc
@@ -13,7 +13,6 @@
 #include "base/no_destructor.h"
 #include "chromeos/services/multidevice_setup/public/mojom/constants.mojom.h"
 #include "services/content/public/mojom/constants.mojom.h"
-#include "services/data_decoder/public/mojom/constants.mojom.h"
 #include "services/device/public/mojom/constants.mojom.h"
 #include "services/preferences/public/mojom/preferences.mojom.h"
 #include "services/service_manager/public/cpp/manifest_builder.h"
@@ -43,7 +42,6 @@
           .RequireCapability("*", "accessibility")
           .RequireCapability("*", "app")
           .RequireCapability(content::mojom::kServiceName, "navigation")
-          .RequireCapability(data_decoder::mojom::kServiceName, "image_decoder")
           .RequireCapability(device::mojom::kServiceName,
                              "device:bluetooth_system")
           .RequireCapability(device::mojom::kServiceName, "device:fingerprint")
diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc
index 979c038..603dcddd 100644
--- a/ash/shell/shell_delegate_impl.cc
+++ b/ash/shell/shell_delegate_impl.cc
@@ -33,5 +33,12 @@
   return true;
 }
 
+mojo::Remote<data_decoder::mojom::DataDecoderService>
+ShellDelegateImpl::LaunchDataDecoder() {
+  mojo::Remote<data_decoder::mojom::DataDecoderService> remote;
+  ignore_result(remote.BindNewPipeAndPassReceiver());
+  return remote;
+}
+
 }  // namespace shell
 }  // namespace ash
diff --git a/ash/shell/shell_delegate_impl.h b/ash/shell/shell_delegate_impl.h
index 7e5f5e1f..6d39484 100644
--- a/ash/shell/shell_delegate_impl.h
+++ b/ash/shell/shell_delegate_impl.h
@@ -24,6 +24,8 @@
   std::unique_ptr<ScreenshotDelegate> CreateScreenshotDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   bool CanGoBack(gfx::NativeWindow window) const override;
+  mojo::Remote<data_decoder::mojom::DataDecoderService> LaunchDataDecoder()
+      override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ShellDelegateImpl);
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index d50a7969..261c609 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -11,6 +11,8 @@
 #include "ash/ash_export.h"
 #include "base/callback.h"
 #include "base/strings/string16.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/data_decoder/public/mojom/data_decoder_service.mojom.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace aura {
@@ -42,6 +44,12 @@
   virtual bool CanGoBack(gfx::NativeWindow window) const = 0;
 
   virtual void OpenKeyboardShortcutHelpPage() const {}
+
+  // Launches an instance of the Data Decoder service and returns a remote
+  // endpoint to communicate with it. In testing environments, this may return
+  // a disconnected Remote.
+  virtual mojo::Remote<data_decoder::mojom::DataDecoderService>
+  LaunchDataDecoder() = 0;
 };
 
 }  // namespace ash
diff --git a/ash/test_shell_delegate.cc b/ash/test_shell_delegate.cc
index b421491..bed376a 100644
--- a/ash/test_shell_delegate.cc
+++ b/ash/test_shell_delegate.cc
@@ -33,4 +33,11 @@
   return true;
 }
 
+mojo::Remote<data_decoder::mojom::DataDecoderService>
+TestShellDelegate::LaunchDataDecoder() {
+  mojo::Remote<data_decoder::mojom::DataDecoderService> remote;
+  ignore_result(remote.BindNewPipeAndPassReceiver());
+  return remote;
+}
+
 }  // namespace ash
diff --git a/ash/test_shell_delegate.h b/ash/test_shell_delegate.h
index 151361c5..a9bac40 100644
--- a/ash/test_shell_delegate.h
+++ b/ash/test_shell_delegate.h
@@ -22,6 +22,8 @@
   std::unique_ptr<ScreenshotDelegate> CreateScreenshotDelegate() override;
   AccessibilityDelegate* CreateAccessibilityDelegate() override;
   bool CanGoBack(gfx::NativeWindow window) const override;
+  mojo::Remote<data_decoder::mojom::DataDecoderService> LaunchDataDecoder()
+      override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestShellDelegate);
diff --git a/ash/utility/transformer_util.cc b/ash/utility/transformer_util.cc
index 4e4e6c3..fcb5ef3 100644
--- a/ash/utility/transformer_util.cc
+++ b/ash/utility/transformer_util.cc
@@ -52,20 +52,4 @@
   return rotate;
 }
 
-gfx::OverlayTransform DisplayRotationToOverlayTransform(
-    display::Display::Rotation rotation) {
-  switch (rotation) {
-    case display::Display::ROTATE_0:
-      return gfx::OVERLAY_TRANSFORM_NONE;
-    case display::Display::ROTATE_90:
-      return gfx::OVERLAY_TRANSFORM_ROTATE_90;
-    case display::Display::ROTATE_180:
-      return gfx::OVERLAY_TRANSFORM_ROTATE_180;
-    case display::Display::ROTATE_270:
-      return gfx::OVERLAY_TRANSFORM_ROTATE_270;
-  }
-  NOTREACHED();
-  return gfx::OVERLAY_TRANSFORM_NONE;
-}
-
 }  // namespace ash
diff --git a/ash/utility/transformer_util.h b/ash/utility/transformer_util.h
index 3059aec3..456e6c6b 100644
--- a/ash/utility/transformer_util.h
+++ b/ash/utility/transformer_util.h
@@ -7,7 +7,6 @@
 
 #include "ash/ash_export.h"
 #include "ui/display/display.h"
-#include "ui/gfx/overlay_transform.h"
 
 namespace gfx {
 class SizeF;
@@ -23,10 +22,6 @@
     display::Display::Rotation new_rotation,
     const gfx::SizeF& size_to_rotate);
 
-// Maps display::Display::Rotation to gfx::OverlayTransform.
-ASH_EXPORT gfx::OverlayTransform DisplayRotationToOverlayTransform(
-    display::Display::Rotation rotation);
-
 }  // namespace ash
 
 #endif  // ASH_TRANSFORMER_UTIL_H_
diff --git a/ash/wallpaper/wallpaper_utils/wallpaper_decoder.cc b/ash/wallpaper/wallpaper_utils/wallpaper_decoder.cc
index 92b1a73..4811d78 100644
--- a/ash/wallpaper/wallpaper_utils/wallpaper_decoder.cc
+++ b/ash/wallpaper/wallpaper_utils/wallpaper_decoder.cc
@@ -5,6 +5,7 @@
 #include "ash/wallpaper/wallpaper_utils/wallpaper_decoder.h"
 
 #include "ash/shell.h"
+#include "ash/shell_delegate.h"
 #include "base/bind.h"
 #include "ipc/ipc_channel.h"
 
@@ -32,16 +33,11 @@
 void DecodeWallpaper(const std::string& image_data,
                      const data_decoder::mojom::ImageCodec& image_codec,
                      OnWallpaperDecoded callback) {
-  // The connector for the mojo service manager is null in unit tests.
-  if (!Shell::Get()->connector()) {
-    std::move(callback).Run(gfx::ImageSkia());
-    return;
-  }
   std::vector<uint8_t> image_bytes(image_data.begin(), image_data.end());
   data_decoder::DecodeImage(
-      Shell::Get()->connector(), std::move(image_bytes), image_codec,
-      /*shrink_to_fit=*/true, kMaxImageSizeInBytes,
-      /*desired_image_frame_size=*/gfx::Size(),
+      Shell::Get()->shell_delegate()->LaunchDataDecoder(),
+      std::move(image_bytes), image_codec, /*shrink_to_fit=*/true,
+      kMaxImageSizeInBytes, /*desired_image_frame_size=*/gfx::Size(),
       base::BindOnce(&ConvertToImageSkia, std::move(callback)));
 }
 
diff --git a/ash/wallpaper/wallpaper_utils/wallpaper_decoder.h b/ash/wallpaper/wallpaper_utils/wallpaper_decoder.h
index 33690a1..a85ff24 100644
--- a/ash/wallpaper/wallpaper_utils/wallpaper_decoder.h
+++ b/ash/wallpaper/wallpaper_utils/wallpaper_decoder.h
@@ -26,4 +26,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_WALLPAPER_WALLPAPER_UTILS_WALLPAPER_DECODER_H_
\ No newline at end of file
+#endif  // ASH_WALLPAPER_WALLPAPER_UTILS_WALLPAPER_DECODER_H_
diff --git a/base/android/java/src/org/chromium/base/TraceEvent.java b/base/android/java/src/org/chromium/base/TraceEvent.java
index 49c2c39a..b1e08b4 100644
--- a/base/android/java/src/org/chromium/base/TraceEvent.java
+++ b/base/android/java/src/org/chromium/base/TraceEvent.java
@@ -134,7 +134,7 @@
     private static final class IdleTracingLooperMonitor extends BasicLooperMonitor
             implements MessageQueue.IdleHandler {
         // Tags for dumping to logcat or TraceEvent
-        private static final String TAG = "TraceEvent.LooperMonitor";
+        private static final String TAG = "TraceEvent_LooperMonitor";
         private static final String IDLE_EVENT_NAME = "Looper.queueIdle";
 
         // Calculation constants
diff --git a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
index 711b1ef..a4ce9b4 100644
--- a/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
+++ b/base/test/android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java
@@ -36,7 +36,7 @@
  */
 @JNINamespace("base::android")
 public final class MultiprocessTestClientLauncher {
-    private static final String TAG = "cr_MProcTCLauncher";
+    private static final String TAG = "MProcTCLauncher";
 
     private static final int CONNECTION_TIMEOUT_MS = 10 * 1000;
 
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java b/base/test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java
index e46b9799..a28e85ec 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java
@@ -18,8 +18,7 @@
  * Currently, this only includes checks against a few {@link android.os.Build} values.
  */
 public class DisableIfSkipCheck extends SkipCheck {
-
-    private static final String TAG = "cr_base_test";
+    private static final String TAG = "base_test";
 
     @Override
     public boolean shouldSkip(FrameworkMethod method) {
diff --git a/build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java b/build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java
index 801a4126..228d60c9 100644
--- a/build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java
+++ b/build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java
@@ -30,7 +30,7 @@
  * JellyBean through Marshmallow.
  */
 public final class BootstrapApplication extends Application {
-    private static final String TAG = "cr.incrementalinstall";
+    private static final String TAG = "incrementalinstall";
     private static final String MANAGED_DIR_PREFIX = "/data/local/tmp/incremental-app-";
     private static final String REAL_APP_META_DATA_NAME = "incremental-install-real-app";
     private static final String REAL_INSTRUMENTATION_META_DATA_NAME0 =
diff --git a/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java b/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java
index 3b619ed0..9c172f01 100644
--- a/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java
+++ b/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java
@@ -25,7 +25,7 @@
  * Tested with Jellybean MR2 - Marshmellow.
  */
 final class ClassLoaderPatcher {
-    private static final String TAG = "cr.incrementalinstall";
+    private static final String TAG = "incrementalinstall";
     private final File mAppFilesSubDir;
     private final ClassLoader mClassLoader;
     private final Object mLibcoreOs;
diff --git a/build/android/incremental_install/java/org/chromium/incrementalinstall/LockFile.java b/build/android/incremental_install/java/org/chromium/incrementalinstall/LockFile.java
index 6e48f3b..19d1f762 100644
--- a/build/android/incremental_install/java/org/chromium/incrementalinstall/LockFile.java
+++ b/build/android/incremental_install/java/org/chromium/incrementalinstall/LockFile.java
@@ -16,7 +16,7 @@
  * Helpers for dealing with .lock files used during install / first run.
  */
 final class LockFile {
-    private static final String TAG = "cr.incrementalinstall";
+    private static final String TAG = "incrementalinstall";
 
     private final File mFile;
     private final FileOutputStream mOutputStream;
diff --git a/build/fuchsia/OWNERS b/build/fuchsia/OWNERS
index 22e1b69..407e79e 100644
--- a/build/fuchsia/OWNERS
+++ b/build/fuchsia/OWNERS
@@ -1,9 +1,7 @@
-jamesr@chromium.org
 kmarshall@chromium.org
-scottmg@chromium.org
+fdegans@chromium.org
 sergeyu@chromium.org
-thakis@chromium.org
 wez@chromium.org
 
 # TEAM: cr-fuchsia@chromium.org
-# COMPONENT: Internals>PlatformIntegration
+# COMPONENT: Fuchsia
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index ae05436e..1fef6af 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8898829176655968080
\ No newline at end of file
+8898798323855543984
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 23e44f66..1f4f6413 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8898827944168201792
\ No newline at end of file
+8898803709896757552
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 4041cc6..d21d3f7 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -83,6 +83,8 @@
     "java/src/org/chromium/chrome/browser/tasks/TasksSurfaceCoordinator.java",
     "java/src/org/chromium/chrome/browser/tasks/TasksView.java",
     "java/src/org/chromium/chrome/browser/tasks/TasksViewBinder.java",
+    "java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java",
+    "java/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCache.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtils.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/ClosableTabGridView.java",
     "java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java
new file mode 100644
index 0000000..cf4a25b1
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java
@@ -0,0 +1,238 @@
+// 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.tasks.pseudotab;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabList;
+import org.chromium.chrome.browser.tabmodel.TabModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
+import org.chromium.chrome.browser.util.FeatureUtilities;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.WeakHashMap;
+
+/**
+ * Representation of a Tab-like card in the Grid Tab Switcher.
+ */
+public class PseudoTab {
+    private final Integer mTabId;
+    private final WeakReference<Tab> mTab;
+    private static final WeakHashMap<Integer, PseudoTab> sAllTabs = new WeakHashMap<>();
+
+    /**
+     * An interface to get the title to be used for a tab.
+     */
+    public interface TitleProvider { String getTitle(PseudoTab tab); }
+
+    /**
+     * Construct from a tab ID. An earlier instance with the same ID can be returned.
+     */
+    public static PseudoTab fromTabId(int tabId) {
+        PseudoTab cached = sAllTabs.get(tabId);
+        if (cached != null) return cached;
+        return new PseudoTab(tabId);
+    }
+
+    private PseudoTab(int tabId) {
+        mTabId = tabId;
+        mTab = null;
+        sAllTabs.put(getId(), this);
+    }
+
+    /**
+     * Construct from a {@link Tab}. An earlier instance with the same {@link Tab} can be returned.
+     */
+    public static PseudoTab fromTab(@NonNull Tab tab) {
+        PseudoTab cached = sAllTabs.get(tab.getId());
+        if (cached != null && cached.hasRealTab()) {
+            assert cached.getTab() == tab;
+            return cached;
+        }
+        // We need to upgrade a pre-native Tab to a post-native Tab.
+        return new PseudoTab(tab);
+    }
+
+    private PseudoTab(@NonNull Tab tab) {
+        mTabId = tab.getId();
+        mTab = new WeakReference<>(tab);
+        sAllTabs.put(getId(), this);
+    }
+
+    /**
+     * Convert a list of {@link Tab} to a list of {@link PseudoTab}.
+     * @param tabs A list of {@link Tab}
+     * @return A list of {@link PseudoTab}
+     */
+    public static List<PseudoTab> getListOfPseudoTab(@Nullable List<Tab> tabs) {
+        List<PseudoTab> pseudoTabs = null;
+        if (tabs != null) {
+            pseudoTabs = new ArrayList<>();
+            for (Tab tab : tabs) {
+                pseudoTabs.add(fromTab(tab));
+            }
+        }
+        return pseudoTabs;
+    }
+
+    /**
+     * Convert a {@link TabList} to a list of {@link PseudoTab}.
+     * @param tabList A {@link TabList}
+     * @return A list of {@link PseudoTab}
+     */
+    public static List<PseudoTab> getListOfPseudoTab(@Nullable TabList tabList) {
+        List<PseudoTab> pseudoTabs = null;
+        if (tabList != null) {
+            pseudoTabs = new ArrayList<>();
+            for (int i = 0; i < tabList.getCount(); i++) {
+                pseudoTabs.add(fromTab(tabList.getTabAt(i)));
+            }
+        }
+        return pseudoTabs;
+    }
+
+    @Override
+    public String toString() {
+        assert mTabId != null;
+        return "Tab " + mTabId;
+    }
+
+    /**
+     * @return The ID of the {@link PseudoTab}
+     */
+    public int getId() {
+        return mTabId;
+    }
+
+    /**
+     * Get the title of the {@link PseudoTab} through a {@link TitleProvider}.
+     *
+     * If the {@link TitleProvider} is {@code null}, fall back to {@link #getTitle()}.
+     * @param titleProvider The {@link TitleProvider} to provide the title.
+     * @return The title
+     */
+    public String getTitle(@Nullable TitleProvider titleProvider) {
+        if (titleProvider != null) return titleProvider.getTitle(this);
+        return getTitle();
+    }
+
+    /**
+     * Get the title of the {@link PseudoTab}.
+     * @return The title
+     */
+    public String getTitle() {
+        if (mTab != null && mTab.get() != null) {
+            return mTab.get().getTitle();
+        }
+        assert mTabId != null;
+        return TabAttributeCache.getTitle(mTabId);
+    }
+
+    /**
+     * Get the URL of the {@link PseudoTab}.
+     * @return The URL
+     */
+    public String getUrl() {
+        if (mTab != null && mTab.get() != null) {
+            return mTab.get().getUrl();
+        }
+        assert mTabId != null;
+        return TabAttributeCache.getUrl(mTabId);
+    }
+
+    /**
+     * Get the root ID of the {@link PseudoTab}.
+     * @return The root ID
+     */
+    public int getRootId() {
+        if (mTab != null && mTab.get() != null) {
+            return mTab.get().getRootId();
+        }
+        assert mTabId != null;
+        return TabAttributeCache.getRootId(mTabId);
+    }
+
+    /**
+     * @return Whether the {@link PseudoTab} is in the Incognito mode.
+     */
+    public boolean isIncognito() {
+        if (mTab != null && mTab.get() != null) return mTab.get().isIncognito();
+        assert mTabId != null;
+        return false;
+    }
+
+    /**
+     * @return {@link Tab#getTimestampMillis()} of the underlying real {@link Tab}
+     */
+    public long getTimestampMillis() {
+        assert mTab != null
+                && mTab.get() != null : "getTimestampMillis can only be used with real tabs";
+        return mTab.get().getTimestampMillis();
+    }
+
+    /**
+     * @return Whether an underlying real {@link Tab} is available.
+     */
+    public boolean hasRealTab() {
+        return getTab() != null;
+    }
+
+    /**
+     * Get the underlying real {@link Tab}. We should avoid using this.
+     * @return The underlying real {@link Tab}.
+     */
+    @Deprecated
+    public @Nullable Tab getTab() {
+        if (mTab != null) return mTab.get();
+        return null;
+    }
+
+    /**
+     * Get related tabs of a certain {@link PseudoTab}, through {@link TabModelFilter}s if
+     * available.
+     * @param member The {@link PseudoTab} related to
+     * @param provider The {@link TabModelFilterProvider} to query the tab relation
+     * @return Related {@link PseudoTab}s
+     */
+    public static @NonNull List<PseudoTab> getRelatedTabs(
+            PseudoTab member, @NonNull TabModelFilterProvider provider) {
+        List<Tab> relatedTabs = getRelatedTabList(provider, member.getId());
+        if (relatedTabs != null) return getListOfPseudoTab(relatedTabs);
+
+        List<PseudoTab> related = new ArrayList<>();
+        int rootId = member.getRootId();
+        if (rootId == Tab.INVALID_TAB_ID || !FeatureUtilities.isTabGroupsAndroidEnabled()) {
+            related.add(member);
+            return related;
+        }
+        for (Integer key : sAllTabs.keySet()) {
+            PseudoTab tab = sAllTabs.get(key);
+            assert tab != null;
+            if (tab.getRootId() == Tab.INVALID_TAB_ID) continue;
+            if (tab.getRootId() != rootId) continue;
+            related.add(tab);
+        }
+        assert related.size() > 0;
+        return related;
+    }
+
+    private static @Nullable List<Tab> getRelatedTabList(
+            @NonNull TabModelFilterProvider provider, int tabId) {
+        if (provider.getTabModelFilter(false) != null) {
+            List<Tab> related = provider.getTabModelFilter(false).getRelatedTabList(tabId);
+            if (related.size() > 0) return related;
+        }
+        if (provider.getTabModelFilter(true) != null) {
+            List<Tab> related = provider.getTabModelFilter(true).getRelatedTabList(tabId);
+            assert related.size() > 0;
+            return related;
+        }
+        return null;
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCache.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCache.java
new file mode 100644
index 0000000..761a5ac9
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCache.java
@@ -0,0 +1,199 @@
+// 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.tasks.pseudotab;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.LifetimeAssert;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
+
+/**
+ * Cache for attributes of {@link PseudoTab} to be available before native is ready.
+ */
+public class TabAttributeCache {
+    private static final String PREFERENCES_NAME = "tab_attribute_cache";
+    private static SharedPreferences sPref;
+    private final TabModelSelector mTabModelSelector;
+    private final TabModelObserver mTabModelObserver;
+    private final TabModelSelectorTabObserver mTabModelSelectorTabObserver;
+    private final TabModelSelectorObserver mTabModelSelectorObserver;
+    private final LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this);
+
+    private static SharedPreferences getSharedPreferences() {
+        if (sPref == null) {
+            sPref = ContextUtils.getApplicationContext().getSharedPreferences(
+                    PREFERENCES_NAME, Context.MODE_PRIVATE);
+        }
+        return sPref;
+    }
+
+    /**
+     * Create a TabAttributeCache instance to observe tab attribute changes.
+     *
+     * Note that querying tab attributes doesn't rely on having an instance.
+     * @param tabModelSelector The {@link TabModelSelector} to observe.
+     */
+    public TabAttributeCache(TabModelSelector tabModelSelector) {
+        mTabModelSelector = tabModelSelector;
+        mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(mTabModelSelector) {
+            @Override
+            public void onUrlUpdated(Tab tab) {
+                if (tab.isIncognito()) return;
+                String url = tab.getUrl();
+                cacheUrl(tab.getId(), url);
+            }
+
+            @Override
+            public void onTitleUpdated(Tab tab) {
+                if (tab.isIncognito()) return;
+                String title = tab.getTitle();
+                cacheTitle(tab.getId(), title);
+            }
+
+            @Override
+            public void onRootIdChanged(Tab tab, int newRootId) {
+                if (tab.isIncognito()) return;
+                assert newRootId == tab.getRootId();
+                cacheRootId(tab.getId(), newRootId);
+            }
+        };
+
+        mTabModelObserver = new EmptyTabModelObserver() {
+            @Override
+            public void tabClosureCommitted(Tab tab) {
+                int id = tab.getId();
+                getSharedPreferences()
+                        .edit()
+                        .remove(getUrlKey(id))
+                        .remove(getTitleKey(id))
+                        .remove(getRootIdKey(id))
+                        .apply();
+            }
+        };
+
+        mTabModelSelectorObserver = new EmptyTabModelSelectorObserver() {
+            @Override
+            public void onTabStateInitialized() {
+                // TODO(wychen): after this cache is enabled by default, we only need to populate it
+                // once.
+                TabModelFilter filter =
+                        mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(false);
+                for (int i = 0; i < filter.getCount(); i++) {
+                    Tab tab = filter.getTabAt(i);
+                    cacheUrl(tab.getId(), tab.getUrl());
+                    cacheTitle(tab.getId(), tab.getTitle());
+                    cacheRootId(tab.getId(), tab.getRootId());
+                }
+                filter.addObserver(mTabModelObserver);
+            }
+        };
+        mTabModelSelector.addObserver(mTabModelSelectorObserver);
+    }
+
+    private static String getTitleKey(int id) {
+        return id + "_title";
+    }
+
+    /**
+     * Get the title of a {@link PseudoTab}.
+     * @param id The ID of the {@link PseudoTab}.
+     * @return The title
+     */
+    public static String getTitle(int id) {
+        return getSharedPreferences().getString(getTitleKey(id), "");
+    }
+
+    private static void cacheTitle(int id, String title) {
+        getSharedPreferences().edit().putString(getTitleKey(id), title).apply();
+    }
+
+    /**
+     * Set the title of a {@link PseudoTab}. Only for testing.
+     * @param id The ID of the {@link PseudoTab}.
+     * @param title The title
+     */
+    static void setTitleForTesting(int id, String title) {
+        cacheTitle(id, title);
+    }
+
+    private static String getUrlKey(int id) {
+        return id + "_url";
+    }
+
+    /**
+     * Get the URL of a {@link PseudoTab}.
+     * @param id The ID of the {@link PseudoTab}.
+     * @return The URL
+     */
+    public static String getUrl(int id) {
+        return getSharedPreferences().getString(getUrlKey(id), "");
+    }
+
+    private static void cacheUrl(int id, String url) {
+        getSharedPreferences().edit().putString(getUrlKey(id), url).apply();
+    }
+
+    /**
+     * Set the URL of a {@link PseudoTab}.
+     * @param id The ID of the {@link PseudoTab}.
+     * @param url The URL
+     */
+    static void setUrlForTesting(int id, String url) {
+        cacheUrl(id, url);
+    }
+
+    private static String getRootIdKey(int id) {
+        return id + "_rootID";
+    }
+
+    /**
+     * Get the root ID of a {@link PseudoTab}.
+     * @param id The ID of the {@link PseudoTab}.
+     * @return The root ID
+     */
+    public static int getRootId(int id) {
+        return getSharedPreferences().getInt(getRootIdKey(id), Tab.INVALID_TAB_ID);
+    }
+
+    private static void cacheRootId(int id, int rootId) {
+        getSharedPreferences().edit().putInt(getRootIdKey(id), rootId).apply();
+    }
+
+    /**
+     * Set the root ID for a {@link PseudoTab}.
+     * @param id The ID of the {@link PseudoTab}.
+     * @param rootId The root ID
+     */
+    static void setRootIdForTesting(int id, int rootId) {
+        cacheRootId(id, rootId);
+    }
+
+    /**
+     * Clear everything in the storage.
+     */
+    static void clearAllForTesting() {
+        getSharedPreferences().edit().clear().apply();
+    }
+
+    /**
+     * Remove all the observers.
+     */
+    public void destroy() {
+        mTabModelSelectorTabObserver.destroy();
+        mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(false).removeObserver(
+                mTabModelObserver);
+        mTabModelSelector.removeObserver(mTabModelSelectorObserver);
+        LifetimeAssert.setSafeToGc(mLifetimeAssert, true);
+    }
+}
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTabUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTabUnitTest.java
new file mode 100644
index 0000000..c135d5b
--- /dev/null
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTabUnitTest.java
@@ -0,0 +1,401 @@
+// 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.tasks.pseudotab;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabList;
+import org.chromium.chrome.browser.tabmodel.TabModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
+import org.chromium.chrome.browser.util.FeatureUtilities;
+import org.chromium.chrome.test.util.browser.Features;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link PseudoTab}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+public class PseudoTabUnitTest {
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+
+    private static final int TAB1_ID = 456;
+    private static final int TAB2_ID = 789;
+    private static final int TAB3_ID = 123;
+
+    @Mock
+    TabModelFilter mTabModelFilter;
+    @Mock
+    TabModelFilter mTabModelFilter2;
+    @Mock
+    TabModelFilterProvider mTabModelFilterProvider;
+
+    private Tab mTab1;
+    private Tab mTab2;
+    private Tab mTab3;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mTab1 = prepareTab(TAB1_ID);
+        mTab2 = prepareTab(TAB2_ID);
+        mTab3 = prepareTab(TAB3_ID);
+    }
+
+    @After
+    public void tearDown() {
+        TabAttributeCache.clearAllForTesting();
+
+        // This is necessary to get the cache behavior correct.
+        Runtime runtime = Runtime.getRuntime();
+        runtime.runFinalization();
+        runtime.gc();
+    }
+
+    @Test
+    public void fromTabId() {
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals(TAB1_ID, tab.getId());
+        Assert.assertFalse(tab.hasRealTab());
+        Assert.assertNull(tab.getTab());
+    }
+
+    @Test
+    public void fromTabId_cached() {
+        PseudoTab tab1 = PseudoTab.fromTabId(TAB1_ID);
+        PseudoTab tab2 = PseudoTab.fromTabId(TAB2_ID);
+        PseudoTab tab1prime = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertNotEquals(tab1, tab2);
+        Assert.assertEquals(tab1, tab1prime);
+    }
+
+    @Test
+    public void fromTab() {
+        PseudoTab tab = PseudoTab.fromTab(mTab1);
+        Assert.assertEquals(TAB1_ID, tab.getId());
+        Assert.assertTrue(tab.hasRealTab());
+        Assert.assertEquals(mTab1, tab.getTab());
+    }
+
+    @Test
+    public void fromTab_cached() {
+        PseudoTab tab1 = PseudoTab.fromTab(mTab1);
+        PseudoTab tab2 = PseudoTab.fromTab(mTab2);
+        PseudoTab tab1prime = PseudoTab.fromTab(mTab1);
+        Assert.assertNotEquals(tab1, tab2);
+        Assert.assertEquals(tab1, tab1prime);
+    }
+
+    @Test
+    public void fromTab_cached_upgrade() {
+        PseudoTab tab1 = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertFalse(tab1.hasRealTab());
+
+        PseudoTab tab1upgraded = PseudoTab.fromTab(mTab1);
+        Assert.assertTrue(tab1upgraded.hasRealTab());
+
+        Assert.assertNotEquals(tab1, tab1upgraded);
+
+        PseudoTab tab1prime = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals(tab1upgraded, tab1prime);
+    }
+
+    @Test
+    public void getListOfPseudoTab_listOfTab() {
+        List<PseudoTab> list = PseudoTab.getListOfPseudoTab(Arrays.asList(mTab1, mTab2));
+        Assert.assertEquals(2, list.size());
+        Assert.assertEquals(TAB1_ID, list.get(0).getId());
+        Assert.assertEquals(TAB2_ID, list.get(1).getId());
+    }
+
+    @Test
+    public void getListOfPseudoTab_listOfTab_null() {
+        List<Tab> tabs = null;
+        List<PseudoTab> list = PseudoTab.getListOfPseudoTab(tabs);
+        Assert.assertNull(list);
+    }
+
+    @Test
+    public void getListOfPseudoTab_TabList() {
+        doReturn(mTab1).when(mTabModelFilter).getTabAt(0);
+        doReturn(mTab2).when(mTabModelFilter).getTabAt(1);
+        doReturn(mTab3).when(mTabModelFilter).getTabAt(2);
+        doReturn(3).when(mTabModelFilter).getCount();
+
+        List<PseudoTab> list = PseudoTab.getListOfPseudoTab(mTabModelFilter);
+        Assert.assertEquals(3, list.size());
+        Assert.assertEquals(TAB1_ID, list.get(0).getId());
+        Assert.assertEquals(TAB2_ID, list.get(1).getId());
+        Assert.assertEquals(TAB3_ID, list.get(2).getId());
+    }
+
+    @Test
+    public void getListOfPseudoTab_TabList_null() {
+        TabList tabs = null;
+        List<PseudoTab> list = PseudoTab.getListOfPseudoTab(tabs);
+        Assert.assertNull(list);
+    }
+
+    @Test
+    public void testToString() {
+        Assert.assertEquals("Tab 456", PseudoTab.fromTabId(TAB1_ID).toString());
+    }
+
+    @Test
+    public void getTitle_provider() {
+        String title = "title provider";
+        PseudoTab.TitleProvider provider = (tab) -> title;
+
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals(title, tab.getTitle(provider));
+
+        PseudoTab realTab = PseudoTab.fromTab(mTab1);
+        Assert.assertNotEquals(tab, realTab);
+        Assert.assertEquals(title, realTab.getTitle(provider));
+    }
+
+    @Test
+    public void getTitle_nullProvider() {
+        PseudoTab.TitleProvider provider = null;
+
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals(tab.getTitle(), tab.getTitle(provider));
+    }
+
+    @Test
+    public void getTitle_realTab() {
+        String title = "title 1 real";
+        doReturn(title).when(mTab1).getTitle();
+
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals("", tab.getTitle());
+
+        PseudoTab realTab = PseudoTab.fromTab(mTab1);
+        Assert.assertNotEquals(tab, realTab);
+        Assert.assertEquals(title, realTab.getTitle());
+    }
+
+    @Test
+    public void getTitle_cache() {
+        String title = "title 1";
+        TabAttributeCache.setTitleForTesting(TAB1_ID, title);
+
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals(title, tab.getTitle());
+
+        PseudoTab realTab = PseudoTab.fromTab(mTab1);
+        Assert.assertNotEquals(tab, realTab);
+        Assert.assertNull(realTab.getTitle());
+    }
+
+    @Test
+    public void getUrl_real() {
+        String url = "url 1 real";
+        doReturn(url).when(mTab1).getUrl();
+
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals("", tab.getUrl());
+
+        PseudoTab realTab = PseudoTab.fromTab(mTab1);
+        Assert.assertNotEquals(tab, realTab);
+        Assert.assertEquals(url, realTab.getUrl());
+    }
+
+    @Test
+    public void getUrl_cache() {
+        String url = "url 1";
+        TabAttributeCache.setUrlForTesting(TAB1_ID, url);
+
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals(url, tab.getUrl());
+
+        PseudoTab realTab = PseudoTab.fromTab(mTab1);
+        Assert.assertNotEquals(tab, realTab);
+        Assert.assertNull(realTab.getUrl());
+    }
+
+    @Test
+    public void getRootId_real() {
+        int rootId = 1337;
+        doReturn(rootId).when(mTab1).getRootId();
+
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals(Tab.INVALID_TAB_ID, tab.getRootId());
+
+        PseudoTab realTab = PseudoTab.fromTab(mTab1);
+        Assert.assertNotEquals(tab, realTab);
+        Assert.assertEquals(rootId, realTab.getRootId());
+    }
+
+    @Test
+    public void getRootId_cache() {
+        int rootId = 42;
+        TabAttributeCache.setRootIdForTesting(TAB1_ID, rootId);
+
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals(rootId, tab.getRootId());
+
+        PseudoTab realTab = PseudoTab.fromTab(mTab1);
+        Assert.assertNotEquals(tab, realTab);
+        Assert.assertNotEquals(rootId, realTab.getRootId());
+    }
+
+    @Test
+    public void isIncognito() {
+        doReturn(true).when(mTab1).isIncognito();
+
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertFalse(tab.isIncognito());
+
+        PseudoTab realTab = PseudoTab.fromTab(mTab1);
+        Assert.assertNotEquals(tab, realTab);
+        Assert.assertTrue(realTab.isIncognito());
+
+        doReturn(false).when(mTab1).isIncognito();
+        Assert.assertFalse(realTab.isIncognito());
+    }
+
+    @Test
+    public void getTimestampMillis_realTab() {
+        long timestamp = 12345;
+        doReturn(timestamp).when(mTab1).getTimestampMillis();
+
+        PseudoTab tab = PseudoTab.fromTab(mTab1);
+        Assert.assertEquals(timestamp, tab.getTimestampMillis());
+    }
+
+    @Test(expected = AssertionError.class)
+    public void getTimestampMillis_notRealTab() {
+        PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
+        tab.getTimestampMillis();
+    }
+
+    @Test
+    public void getRelatedTabs_noProvider_groupDisabled_single() {
+        doReturn(null).when(mTabModelFilterProvider).getTabModelFilter(anyBoolean());
+        FeatureUtilities.setTabGroupsAndroidEnabledForTesting(false);
+
+        PseudoTab tab1 = PseudoTab.fromTabId(TAB1_ID);
+        List<PseudoTab> related = PseudoTab.getRelatedTabs(tab1, mTabModelFilterProvider);
+        Assert.assertEquals(1, related.size());
+        Assert.assertEquals(TAB1_ID, related.get(0).getId());
+    }
+
+    @Test
+    public void getRelatedTabs_noProvider_groupDisabled_group() {
+        doReturn(null).when(mTabModelFilterProvider).getTabModelFilter(anyBoolean());
+        FeatureUtilities.setTabGroupsAndroidEnabledForTesting(false);
+
+        TabAttributeCache.setRootIdForTesting(TAB1_ID, TAB1_ID);
+        TabAttributeCache.setRootIdForTesting(TAB2_ID, TAB1_ID);
+        PseudoTab tab1 = PseudoTab.fromTabId(TAB1_ID);
+        Assert.assertEquals(TAB1_ID, tab1.getRootId());
+        PseudoTab tab2 = PseudoTab.fromTabId(TAB2_ID);
+        Assert.assertEquals(TAB1_ID, tab2.getRootId());
+
+        List<PseudoTab> related = PseudoTab.getRelatedTabs(tab1, mTabModelFilterProvider);
+        Assert.assertEquals(1, related.size());
+        Assert.assertEquals(TAB1_ID, related.get(0).getId());
+    }
+
+    @Test
+    public void getRelatedTabs_noProvider_single() {
+        doReturn(null).when(mTabModelFilterProvider).getTabModelFilter(anyBoolean());
+        FeatureUtilities.setTabGroupsAndroidEnabledForTesting(true);
+
+        PseudoTab tab1 = PseudoTab.fromTabId(TAB1_ID);
+        List<PseudoTab> related = PseudoTab.getRelatedTabs(tab1, mTabModelFilterProvider);
+        Assert.assertEquals(1, related.size());
+        Assert.assertEquals(TAB1_ID, related.get(0).getId());
+    }
+
+    @Test
+    public void getRelatedTabs_noProvider_group() {
+        doReturn(null).when(mTabModelFilterProvider).getTabModelFilter(anyBoolean());
+        FeatureUtilities.setTabGroupsAndroidEnabledForTesting(true);
+
+        TabAttributeCache.setRootIdForTesting(TAB1_ID, TAB1_ID);
+        TabAttributeCache.setRootIdForTesting(TAB2_ID, TAB1_ID);
+        PseudoTab tab1 = PseudoTab.fromTabId(TAB1_ID);
+        PseudoTab tab2 = PseudoTab.fromTabId(TAB2_ID);
+
+        List<PseudoTab> related = PseudoTab.getRelatedTabs(tab1, mTabModelFilterProvider);
+        Assert.assertEquals(2, related.size());
+        Assert.assertEquals(TAB1_ID, related.get(0).getId());
+        Assert.assertEquals(TAB2_ID, related.get(1).getId());
+    }
+
+    @Test
+    public void getRelatedTabs_noProvider_badGroup() {
+        doReturn(null).when(mTabModelFilterProvider).getTabModelFilter(anyBoolean());
+        FeatureUtilities.setTabGroupsAndroidEnabledForTesting(true);
+
+        TabAttributeCache.setRootIdForTesting(TAB1_ID, TAB1_ID);
+        TabAttributeCache.setRootIdForTesting(TAB2_ID, Tab.INVALID_TAB_ID);
+        TabAttributeCache.setRootIdForTesting(TAB3_ID, TAB3_ID);
+        PseudoTab tab1 = PseudoTab.fromTabId(TAB1_ID);
+        PseudoTab tab2 = PseudoTab.fromTabId(TAB2_ID);
+        PseudoTab tab3 = PseudoTab.fromTabId(TAB3_ID);
+
+        List<PseudoTab> related = PseudoTab.getRelatedTabs(tab1, mTabModelFilterProvider);
+        Assert.assertEquals(1, related.size());
+        Assert.assertEquals(TAB1_ID, related.get(0).getId());
+    }
+
+    @Test
+    public void getRelatedTabs_provider_normal() {
+        doReturn(mTabModelFilter).when(mTabModelFilterProvider).getTabModelFilter(eq(false));
+        List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2, mTab3));
+        doReturn(tabs).when(mTabModelFilter).getRelatedTabList(TAB1_ID);
+
+        PseudoTab tab1 = PseudoTab.fromTabId(TAB1_ID);
+        List<PseudoTab> related = PseudoTab.getRelatedTabs(tab1, mTabModelFilterProvider);
+        Assert.assertEquals(3, related.size());
+        Assert.assertEquals(TAB1_ID, related.get(0).getId());
+        Assert.assertEquals(TAB2_ID, related.get(1).getId());
+        Assert.assertEquals(TAB3_ID, related.get(2).getId());
+    }
+
+    @Test
+    public void getRelatedTabs_provider_incognito() {
+        doReturn(mTabModelFilter).when(mTabModelFilterProvider).getTabModelFilter(eq(false));
+        List<Tab> empty = new ArrayList<>();
+        doReturn(empty).when(mTabModelFilter).getRelatedTabList(TAB1_ID);
+        doReturn(mTabModelFilter2).when(mTabModelFilterProvider).getTabModelFilter(eq(true));
+        List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2));
+        doReturn(tabs).when(mTabModelFilter2).getRelatedTabList(TAB1_ID);
+
+        PseudoTab tab1 = PseudoTab.fromTabId(TAB1_ID);
+        List<PseudoTab> related = PseudoTab.getRelatedTabs(tab1, mTabModelFilterProvider);
+        Assert.assertEquals(2, related.size());
+        Assert.assertEquals(TAB1_ID, related.get(0).getId());
+        Assert.assertEquals(TAB2_ID, related.get(1).getId());
+    }
+
+    private Tab prepareTab(int id) {
+        Tab tab = mock(Tab.class);
+        doReturn(id).when(tab).getId();
+        return tab;
+    }
+}
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCacheUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCacheUnitTest.java
new file mode 100644
index 0000000..d4f9aca
--- /dev/null
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCacheUnitTest.java
@@ -0,0 +1,235 @@
+// 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.tasks.pseudotab;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabObserver;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
+import org.chromium.chrome.test.util.browser.Features;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link TabAttributeCache}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class TabAttributeCacheUnitTest {
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+
+    private static final int TAB1_ID = 456;
+    private static final int TAB2_ID = 789;
+    private static final int POSITION1 = 0;
+    private static final int POSITION2 = 1;
+
+    @Mock
+    TabModelSelectorImpl mTabModelSelector;
+    @Mock
+    TabModelFilterProvider mTabModelFilterProvider;
+    @Mock
+    TabModelFilter mTabModelFilter;
+    @Mock
+    TabModel mTabModel;
+    @Captor
+    ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor;
+    @Captor
+    ArgumentCaptor<TabModelSelectorObserver> mTabModelSelectorObserverCaptor;
+    @Captor
+    ArgumentCaptor<TabObserver> mTabObserverCaptor;
+
+    private Tab mTab1;
+    private Tab mTab2;
+    private TabAttributeCache mCache;
+
+    @Before
+    public void setUp() {
+        RecordUserAction.setDisabledForTests(true);
+        RecordHistogram.setDisabledForTests(true);
+
+        MockitoAnnotations.initMocks(this);
+
+        mTab1 = prepareTab(TAB1_ID);
+        mTab2 = prepareTab(TAB2_ID);
+
+        List<TabModel> tabModelList = new ArrayList<>();
+        tabModelList.add(mTabModel);
+
+        doReturn(tabModelList).when(mTabModelSelector).getModels();
+        doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider();
+        doReturn(mTabModelFilter).when(mTabModelFilterProvider).getTabModelFilter(eq(false));
+
+        doNothing().when(mTabModelFilter).addObserver(mTabModelObserverCaptor.capture());
+
+        doNothing().when(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
+
+        doReturn(mTab1).when(mTabModel).getTabAt(POSITION1);
+        doReturn(mTab2).when(mTabModel).getTabAt(POSITION2);
+        doReturn(POSITION1).when(mTabModel).indexOf(mTab1);
+        doReturn(POSITION2).when(mTabModel).indexOf(mTab2);
+        doNothing().when(mTab1).addObserver(mTabObserverCaptor.capture());
+        doNothing().when(mTab2).addObserver(mTabObserverCaptor.capture());
+        doReturn(0).when(mTabModel).index();
+        doReturn(2).when(mTabModel).getCount();
+        doReturn(mTabModel).when(mTabModel).getComprehensiveModel();
+
+        mCache = new TabAttributeCache(mTabModelSelector);
+    }
+
+    @After
+    public void tearDown() {
+        RecordUserAction.setDisabledForTests(false);
+        RecordHistogram.setDisabledForTests(false);
+        mCache.destroy();
+        TabAttributeCache.clearAllForTesting();
+    }
+
+    @Test
+    public void updateUrl() {
+        String url = "url 1";
+        doReturn(url).when(mTab1).getUrl();
+
+        Assert.assertNotEquals(url, TabAttributeCache.getUrl(TAB1_ID));
+
+        mTabObserverCaptor.getValue().onUrlUpdated(mTab1);
+        Assert.assertEquals(url, TabAttributeCache.getUrl(TAB1_ID));
+
+        mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
+        mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
+        Assert.assertNotEquals(url, TabAttributeCache.getUrl(TAB1_ID));
+    }
+
+    @Test
+    public void updateUrl_incognito() {
+        String url = "url 1";
+        doReturn(url).when(mTab1).getUrl();
+        doReturn(true).when(mTab1).isIncognito();
+
+        mTabObserverCaptor.getValue().onUrlUpdated(mTab1);
+        Assert.assertNotEquals(url, TabAttributeCache.getUrl(TAB1_ID));
+    }
+
+    @Test
+    public void updateTitle() {
+        String title = "title 1";
+        doReturn(title).when(mTab1).getTitle();
+
+        Assert.assertNotEquals(title, TabAttributeCache.getTitle(TAB1_ID));
+
+        mTabObserverCaptor.getValue().onTitleUpdated(mTab1);
+        Assert.assertEquals(title, TabAttributeCache.getTitle(TAB1_ID));
+
+        mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
+        mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
+        Assert.assertNotEquals(title, TabAttributeCache.getTitle(TAB1_ID));
+    }
+
+    @Test
+    public void updateTitle_incognito() {
+        String title = "title 1";
+        doReturn(title).when(mTab1).getTitle();
+        doReturn(true).when(mTab1).isIncognito();
+
+        mTabObserverCaptor.getValue().onTitleUpdated(mTab1);
+        Assert.assertNotEquals(title, TabAttributeCache.getTitle(TAB1_ID));
+    }
+
+    @Test
+    public void updateRootId() {
+        int rootId = 1337;
+        doReturn(rootId).when(mTab1).getRootId();
+
+        Assert.assertNotEquals(rootId, TabAttributeCache.getRootId(TAB1_ID));
+
+        mTabObserverCaptor.getValue().onRootIdChanged(mTab1, rootId);
+        Assert.assertEquals(rootId, TabAttributeCache.getRootId(TAB1_ID));
+
+        mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
+        mTabModelObserverCaptor.getValue().tabClosureCommitted(mTab1);
+        Assert.assertNotEquals(rootId, TabAttributeCache.getRootId(TAB1_ID));
+    }
+
+    @Test
+    public void updateRootId_incognito() {
+        int rootId = 1337;
+        doReturn(rootId).when(mTab1).getRootId();
+        doReturn(true).when(mTab1).isIncognito();
+
+        mTabObserverCaptor.getValue().onRootIdChanged(mTab1, rootId);
+        Assert.assertNotEquals(rootId, TabAttributeCache.getRootId(TAB1_ID));
+    }
+
+    @Test
+    public void onTabStateInitialized() {
+        String url1 = "url 1";
+        doReturn(url1).when(mTab1).getUrl();
+        String title1 = "title 1";
+        doReturn(title1).when(mTab1).getTitle();
+        int rootId1 = 1337;
+        doReturn(rootId1).when(mTab1).getRootId();
+
+        String url2 = "url 2";
+        doReturn(url2).when(mTab2).getUrl();
+        String title2 = "title 2";
+        doReturn(title2).when(mTab2).getTitle();
+        int rootId2 = 42;
+        doReturn(rootId2).when(mTab2).getRootId();
+
+        doReturn(mTab1).when(mTabModelFilter).getTabAt(0);
+        doReturn(mTab2).when(mTabModelFilter).getTabAt(1);
+        doReturn(2).when(mTabModelFilter).getCount();
+
+        Assert.assertNotEquals(url1, TabAttributeCache.getUrl(TAB1_ID));
+        Assert.assertNotEquals(title1, TabAttributeCache.getTitle(TAB1_ID));
+        Assert.assertNotEquals(rootId1, TabAttributeCache.getRootId(TAB1_ID));
+
+        Assert.assertNotEquals(url2, TabAttributeCache.getUrl(TAB2_ID));
+        Assert.assertNotEquals(title2, TabAttributeCache.getTitle(TAB2_ID));
+        Assert.assertNotEquals(rootId2, TabAttributeCache.getRootId(TAB2_ID));
+
+        mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
+
+        Assert.assertEquals(url1, TabAttributeCache.getUrl(TAB1_ID));
+        Assert.assertEquals(title1, TabAttributeCache.getTitle(TAB1_ID));
+        Assert.assertEquals(rootId1, TabAttributeCache.getRootId(TAB1_ID));
+
+        Assert.assertEquals(url2, TabAttributeCache.getUrl(TAB2_ID));
+        Assert.assertEquals(title2, TabAttributeCache.getTitle(TAB2_ID));
+        Assert.assertEquals(rootId2, TabAttributeCache.getRootId(TAB2_ID));
+    }
+
+    private Tab prepareTab(int id) {
+        Tab tab = mock(Tab.class);
+        doReturn(id).when(tab).getId();
+        return tab;
+    }
+}
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index ed30730..10fc3cba 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -35,6 +35,8 @@
 
 tab_management_junit_java_sources = [
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/MostVisitedListViewBinderUnitTest.java",
+  "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTabUnitTest.java",
+  "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCacheUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupModelFilterUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtilsUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java",
diff --git a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrInputConnection.java b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrInputConnection.java
index 2bb45c9f..c742183 100644
--- a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrInputConnection.java
+++ b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrInputConnection.java
@@ -22,7 +22,7 @@
  */
 @JNINamespace("vr")
 public class VrInputConnection {
-    private static final String TAG = "cr_VrIC";
+    private static final String TAG = "VrIC";
     private static final boolean DEBUG_LOGS = false;
     private static final int CHARS_AROUND_CURSOR = 100;
 
diff --git a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/keyboard/VrInputMethodManagerWrapper.java b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/keyboard/VrInputMethodManagerWrapper.java
index 5f84034..ffb7d65 100644
--- a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/keyboard/VrInputMethodManagerWrapper.java
+++ b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/keyboard/VrInputMethodManagerWrapper.java
@@ -21,7 +21,7 @@
  * InputMethodManager and instead talks to the Daydream keyboard.
  */
 public class VrInputMethodManagerWrapper implements InputMethodManagerWrapper {
-    private static final String TAG = "cr_VrIme";
+    private static final String TAG = "VrIme";
     private static final boolean DEBUG_LOGS = false;
 
     private final Context mContext;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/help/HelpAndFeedback.java b/chrome/android/java/src/org/chromium/chrome/browser/help/HelpAndFeedback.java
index 348ead6..9f884f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/help/HelpAndFeedback.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/help/HelpAndFeedback.java
@@ -31,7 +31,7 @@
 public class HelpAndFeedback {
     protected static final String FALLBACK_SUPPORT_URL =
             "https://support.google.com/chrome/topic/6069782";
-    private static final String TAG = "cr_HelpAndFeedback";
+    private static final String TAG = "HelpAndFeedback";
 
     private static HelpAndFeedback sInstance;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditText.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditText.java
index 51b88df..4bfff9d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditText.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditText.java
@@ -30,7 +30,7 @@
  */
 public class AutocompleteEditText
         extends VerticallyFixedEditText implements AutocompleteEditTextModelBase.Delegate {
-    private static final String TAG = "cr_AutocompleteEdit";
+    private static final String TAG = "AutocompleteEdit";
 
     private static final boolean DEBUG = false;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModel.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModel.java
index 22b51b0e..bcb1f16 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextModel.java
@@ -20,7 +20,7 @@
  * An autocomplete model that appends autocomplete text at the end of query/URL text and selects it.
  */
 public class AutocompleteEditTextModel implements AutocompleteEditTextModelBase {
-    private static final String TAG = "cr_AutocompleteModel";
+    private static final String TAG = "AutocompleteModel";
 
     private static final boolean DEBUG = false;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java
index b79aed2..1a2b093 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/SpannableAutocompleteEditTextModel.java
@@ -33,7 +33,7 @@
  * effectively hide the existence of autocomplete text from keyboard.
  */
 public class SpannableAutocompleteEditTextModel implements AutocompleteEditTextModelBase {
-    private static final String TAG = "cr_SpanAutocomplete";
+    private static final String TAG = "SpanAutocomplete";
 
     private static final boolean DEBUG = false;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index 4d2bda3..0b0d391b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -51,7 +51,7 @@
  * The URL text entry view for the Omnibox.
  */
 public abstract class UrlBar extends AutocompleteEditText {
-    private static final String TAG = "cr_UrlBar";
+    private static final String TAG = "UrlBar";
 
     private static final boolean DEBUG = false;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
index d21c62d..6ce6515 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
@@ -28,7 +28,7 @@
  * Bridge to the native AutocompleteControllerAndroid.
  */
 public class AutocompleteController {
-    private static final String TAG = "cr_Autocomplete";
+    private static final String TAG = "Autocomplete";
 
     // Maximum number of voice suggestions to show.
     private static final int MAX_VOICE_SUGGESTION_COUNT = 3;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index dd27c7d..0a9daf0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -82,7 +82,7 @@
         }
     }
 
-    private static final String TAG = "cr_Autocomplete";
+    private static final String TAG = "Autocomplete";
     private static final int SUGGESTION_NOT_FOUND = -1;
 
     // Delay triggering the omnibox results upon key press to allow the location bar to repaint
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
index a244cbcc..fc94295 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
@@ -57,10 +57,15 @@
             final int paddingStart = res.getDimensionPixelSize(sds.isLarge
                             ? R.dimen.omnibox_suggestion_36dp_icon_margin_start
                             : R.dimen.omnibox_suggestion_24dp_icon_margin_start);
-
             final int paddingEnd = res.getDimensionPixelSize(sds.isLarge
                             ? R.dimen.omnibox_suggestion_36dp_icon_margin_end
                             : R.dimen.omnibox_suggestion_24dp_icon_margin_end);
+            final int edgeSize = res.getDimensionPixelSize(sds.isLarge
+                            ? R.dimen.omnibox_suggestion_36dp_icon_size
+                            : R.dimen.omnibox_suggestion_24dp_icon_size);
+
+            view.setPadding(paddingStart, 0, paddingEnd, 0);
+            view.setMinimumHeight(edgeSize);
 
             // TODO(ender): move logic applying corner rounding to updateIcon when action images use
             // RoundedCornerImageView too.
@@ -69,8 +74,6 @@
                     ? res.getDimensionPixelSize(R.dimen.default_rounded_corner_radius)
                     : 0;
             rciv.setRoundedCorners(radius, radius, radius, radius);
-
-            view.setPadding(paddingStart, 0, paddingEnd, 0);
         }
 
         updateIcon(view, sds, isDarkMode(model));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java
index 40ea71f..094a40c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DecoratedSuggestionView.java
@@ -8,6 +8,7 @@
 import android.support.annotation.DrawableRes;
 import android.support.annotation.IdRes;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 
 import org.chromium.chrome.R;
@@ -19,7 +20,6 @@
 class DecoratedSuggestionView extends SimpleHorizontalLayoutView {
     private final RoundedCornerImageView mSuggestionIcon;
     private View mContentView;
-    private boolean mUseDarkIconTint;
 
     /**
      * Constructs a new suggestion view.
@@ -39,7 +39,7 @@
 
         mSuggestionIcon.setLayoutParams(new LayoutParams(
                 getResources().getDimensionPixelSize(R.dimen.omnibox_suggestion_icon_area_size),
-                LayoutParams.MATCH_PARENT));
+                ViewGroup.LayoutParams.WRAP_CONTENT));
         addView(mSuggestionIcon);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleHorizontalLayoutView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleHorizontalLayoutView.java
index a65c309..acac0d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleHorizontalLayoutView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleHorizontalLayoutView.java
@@ -53,23 +53,29 @@
         // Note: We layout children in the following order:
         // - first-to-last in LTR orientation and
         // - last-to-first in RTL orientation.
-        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        final int increment = isRtl ? -1 : 1;
+        final int height = getMeasuredHeight();
         int index = isRtl ? getChildCount() - 1 : 0;
-        int increment = isRtl ? -1 : 1;
 
         left = 0;
 
         for (; index >= 0 && index < getChildCount(); index += increment) {
             View v = getChildAt(index);
             if (v.getVisibility() == GONE) continue;
-            v.layout(left, 0, left + v.getMeasuredWidth(), bottom - top);
+
+            int verticalMargin = (height - v.getMeasuredHeight()) / 2;
+            if (verticalMargin < 0) verticalMargin = 0;
+
+            v.layout(left, verticalMargin, left + v.getMeasuredWidth(), height - verticalMargin);
             left += v.getMeasuredWidth();
         }
     }
 
     @Override
     protected void onMeasure(int widthSpec, int heightSpec) {
-        int contentViewWidth = MeasureSpec.getSize(widthSpec);
+        final int widthPx = MeasureSpec.getSize(widthSpec);
+        int contentViewWidth = widthPx;
         View dynamicView = null;
 
         // Compute and apply space we can offer to content view.
@@ -96,7 +102,7 @@
         dynamicView.measure(MeasureSpec.makeMeasureSpec(contentViewWidth, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
         final int heightPx = dynamicView.getMeasuredHeight();
-        heightSpec = MeasureSpec.makeMeasureSpec(heightPx, MeasureSpec.EXACTLY);
+        heightSpec = MeasureSpec.makeMeasureSpec(heightPx, MeasureSpec.AT_MOST);
 
         // Apply measured dimensions to all children.
         for (int index = 0; index < getChildCount(); ++index) {
@@ -106,9 +112,9 @@
             if (v == dynamicView) continue;
 
             v.measure(MeasureSpec.makeMeasureSpec(v.getLayoutParams().width, MeasureSpec.EXACTLY),
-                    heightSpec);
+                    getChildMeasureSpec(heightSpec, 0, v.getLayoutParams().height));
         }
 
-        setMeasuredDimension(MeasureSpec.getSize(widthSpec), MeasureSpec.getSize(heightSpec));
+        setMeasuredDimension(widthPx, heightPx);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksProviderIterator.java b/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksProviderIterator.java
index b37a345..62077e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksProviderIterator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/partnerbookmarks/PartnerBookmarksProviderIterator.java
@@ -18,7 +18,7 @@
  * Imports bookmarks from partner content provider using the private provider API.
  */
 public class PartnerBookmarksProviderIterator implements PartnerBookmark.BookmarkIterator {
-    private static final String TAG = "cr_PartnerBookmarks";
+    private static final String TAG = "PartnerBookmarks";
     private static final String PROVIDER_AUTHORITY = "com.android.partnerbookmarks";
     private static final Uri CONTENT_URI = new Uri.Builder()
                                                    .scheme(UrlConstants.CONTENT_SCHEME)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index 23ba4e0e..4897225 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -225,7 +225,7 @@
     /** Limit in the number of suggested items in a section. */
     public static final int SUGGESTIONS_LIMIT = 4;
 
-    private static final String TAG = "cr_PaymentRequest";
+    private static final String TAG = "PaymentRequest";
     private static final String ANDROID_PAY_METHOD_NAME = "https://android.com/pay";
     private static final String PAY_WITH_GOOGLE_METHOD_NAME = "https://google.com/pay";
     // Reverse order of the comparator to sort in descending order of completeness scores.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
index 3e7ab804..68975fda 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SearchEngineAdapter.java
@@ -56,7 +56,7 @@
 public class SearchEngineAdapter extends BaseAdapter
         implements TemplateUrlService.LoadListener, TemplateUrlService.TemplateUrlServiceObserver,
                 OnClickListener {
-    private static final String TAG = "cr_SearchEngines";
+    private static final String TAG = "SearchEngines";
 
     private static final int VIEW_TYPE_ITEM = 0;
     private static final int VIEW_TYPE_DIVIDER = 1;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreShimImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreShimImpl.java
index 1b6696f..e8a4517 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreShimImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreShimImpl.java
@@ -9,6 +9,7 @@
 
 import com.google.ar.core.ArCoreApk;
 
+import org.chromium.base.StrictModeContext;
 import org.chromium.base.annotations.UsedByReflection;
 import org.chromium.chrome.browser.vr.ArCoreShim.Availability;
 import org.chromium.chrome.browser.vr.ArCoreShim.InstallStatus;
@@ -35,9 +36,13 @@
 
     @Override
     public @Availability int checkAvailability(Context applicationContext) {
-        ArCoreApk.Availability availability =
-                ArCoreApk.getInstance().checkAvailability(applicationContext);
-        return mapArCoreApkAvailability(availability);
+        // ARCore's checkAvailability reads shared preferences via ArCoreContentProvider, need to
+        // turn off strict mode to allow that.
+        try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
+            ArCoreApk.Availability availability =
+                    ArCoreApk.getInstance().checkAvailability(applicationContext);
+            return mapArCoreApkAvailability(availability);
+        }
     }
 
     private @InstallStatus int mapArCoreApkInstallStatus(ArCoreApk.InstallStatus installStatus) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
index 6e9ee06..22794bd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivity.java
@@ -29,7 +29,7 @@
     /** The start time that the activity becomes focused in milliseconds since boot. */
     private long mStartTime;
 
-    private static final String TAG = "cr_WebApkActivity";
+    private static final String TAG = "WebApkActivity";
 
     @VisibleForTesting
     public static final String STARTUP_UMA_HISTOGRAM_SUFFIX = ".WebApk";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java
index 3062db1..77bd730 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java
@@ -59,7 +59,7 @@
     public static final String CHANNEL_ID_WEBAPKS = "default_channel_id";
 
     private static final String CATEGORY_WEBAPK_API = "android.intent.category.WEBAPK_API";
-    private static final String TAG = "cr_WebApk";
+    private static final String TAG = "WebApk";
 
     private static WebApkServiceClient sInstance;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
index 332d9ca..237a3b5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadTest.java
@@ -69,7 +69,7 @@
     @Rule
     public DownloadTestRule mDownloadTestRule = new DownloadTestRule(this);
 
-    private static final String TAG = "cr_DownloadTest";
+    private static final String TAG = "DownloadTest";
     private static final String SUPERBO_CONTENTS =
             "plain text response from a POST";
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java
index 78bfa4cd..cda1a5d6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java
@@ -409,7 +409,11 @@
         View bigView = notification.bigContentView.apply(context, new LinearLayout(context));
         Drawable bigViewIcon = ((ImageView) bigView.findViewById(R.id.icon)).getDrawable();
         Assert.assertNotNull(bigViewIcon);
-        Assert.assertTrue(expectedIcon.sameAs(((BitmapDrawable) bigViewIcon).getBitmap()));
+
+        // Starts from Android O MR1, large icon can be downscaled by Android platform code.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
+            Assert.assertTrue(expectedIcon.sameAs(((BitmapDrawable) bigViewIcon).getBitmap()));
+        }
     }
 
     private static void assertSmallNotificationIconAsExpected(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
index 07a930e..261e685 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
@@ -30,7 +30,6 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.RetryOnFailure;
@@ -610,7 +609,6 @@
     @MediumTest
     @Feature({"Browser", "Notifications"})
     @RetryOnFailure
-    @DisableIf.Build(sdk_is_greater_than = 25, message = "https://crbug.com/999357")
     public void testShowNotificationWithoutIcon() throws Exception {
         mNotificationTestRule.setNotificationContentSettingForOrigin(
                 ContentSettingValues.ALLOW, mPermissionTestRule.getOrigin());
@@ -634,8 +632,11 @@
 
         Bitmap generatedIcon = generator.generateIconForUrl(mPermissionTestRule.getOrigin());
         Assert.assertNotNull(generatedIcon);
-        Assert.assertTrue(generatedIcon.sameAs(
-                NotificationTestUtil.getLargeIconFromNotification(context, notification)));
+        // Starts from Android O MR1, large icon can be downscaled by Android platform code.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
+            Assert.assertTrue(generatedIcon.sameAs(
+                    NotificationTestUtil.getLargeIconFromNotification(context, notification)));
+        }
     }
 
     /*
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
index f4a1b2a..96b4d531 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/AutocompleteEditTextTest.java
@@ -53,7 +53,7 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class AutocompleteEditTextTest {
-    private static final String TAG = "cr_AutocompleteTest";
+    private static final String TAG = "AutocompleteTest";
 
     private static final boolean DEBUG = false;
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java
index 6f52440..7f4610e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewTest.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions.base;
 
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+
 import android.app.Activity;
 import android.view.View;
 import android.view.View.MeasureSpec;
@@ -124,7 +126,7 @@
         Assert.assertEquals("right view edge", right, v.getRight());
         Assert.assertEquals("bottom view edge", bottom, v.getBottom());
         Assert.assertEquals("view width", right - left, v.getMeasuredWidth());
-        Assert.assertEquals("view height", bottom - top, v.getMeasuredHeight());
+        Assert.assertThat("view height", v.getMeasuredHeight(), lessThanOrEqualTo(bottom - top));
     }
 
     @Test
diff --git a/chrome/android/public/crypto/java/src/org/chromium/chrome/browser/crypto/CipherFactory.java b/chrome/android/public/crypto/java/src/org/chromium/chrome/browser/crypto/CipherFactory.java
index 7d0e501..ee06252 100644
--- a/chrome/android/public/crypto/java/src/org/chromium/chrome/browser/crypto/CipherFactory.java
+++ b/chrome/android/public/crypto/java/src/org/chromium/chrome/browser/crypto/CipherFactory.java
@@ -51,7 +51,7 @@
  */
 @ThreadSafe
 public class CipherFactory {
-    private static final String TAG = "cr.CipherFactory";
+    private static final String TAG = "CipherFactory";
     static final int NUM_BYTES = 16;
 
     static final String BUNDLE_IV = "org.chromium.content.browser.crypto.CipherFactory.IV";
diff --git a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java
index d184825c..b5eea2c 100644
--- a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java
+++ b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java
@@ -21,7 +21,7 @@
  * Note: This class is copied (mostly) verbatim from DexOptUtils in GMSCore.
  */
 public class DexOptimizer {
-    private static final String TAG = "cr_DexOptimzer";
+    private static final String TAG = "DexOptimzer";
 
     private static final String DEX_SUFFIX = ".dex";
     private static final String ODEX_SUFFIX = ".odex";
diff --git a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkIdentityServiceClient.java b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkIdentityServiceClient.java
index b900054..abfb0f2 100644
--- a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkIdentityServiceClient.java
+++ b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkIdentityServiceClient.java
@@ -45,7 +45,7 @@
     public static final int SHELL_APK_VERSION_SUPPORTING_SWITCH_RUNTIME_HOST = 6;
 
     public static final String ACTION_WEBAPK_IDENTITY_SERVICE = "org.webapk.IDENTITY_SERVICE_API";
-    private static final String TAG = "cr_WebApkIdentityService";
+    private static final String TAG = "WebApkIdentityService";
 
     private static WebApkIdentityServiceClient sInstance;
 
diff --git a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkServiceConnectionManager.java b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkServiceConnectionManager.java
index 4591e830..21cfe2f7 100644
--- a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkServiceConnectionManager.java
+++ b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkServiceConnectionManager.java
@@ -82,7 +82,7 @@
         }
     }
 
-    private static final String TAG = "cr_WebApkService";
+    private static final String TAG = "WebApkService";
 
     /** The category of the service to connect to. */
     private String mCategory;
diff --git a/chrome/android/webapk/libs/runtime_library/src/org/chromium/webapk/lib/runtime_library/WebApkServiceImpl.java b/chrome/android/webapk/libs/runtime_library/src/org/chromium/webapk/lib/runtime_library/WebApkServiceImpl.java
index 49130a3aa..6f65612 100644
--- a/chrome/android/webapk/libs/runtime_library/src/org/chromium/webapk/lib/runtime_library/WebApkServiceImpl.java
+++ b/chrome/android/webapk/libs/runtime_library/src/org/chromium/webapk/lib/runtime_library/WebApkServiceImpl.java
@@ -28,7 +28,7 @@
     public static final String KEY_SMALL_ICON_ID = "small_icon_id";
     public static final String KEY_HOST_BROWSER_UID = "host_browser_uid";
 
-    private static final String TAG = "cr_WebApkServiceImpl";
+    private static final String TAG = "WebApkServiceImpl";
 
     private final Context mContext;
 
diff --git a/chrome/app/chrome_content_renderer_overlay_manifest.cc b/chrome/app/chrome_content_renderer_overlay_manifest.cc
index dd9f94b..ec8c42c8 100644
--- a/chrome/app/chrome_content_renderer_overlay_manifest.cc
+++ b/chrome/app/chrome_content_renderer_overlay_manifest.cc
@@ -8,7 +8,7 @@
 #include "base/no_destructor.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
-#include "chrome/common/content_settings_renderer.mojom.h"
+#include "chrome/common/content_settings_agent.mojom.h"
 #include "chrome/common/prerender.mojom.h"
 #include "chrome/common/search.mojom.h"
 #include "components/autofill/content/common/mojom/autofill_agent.mojom.h"
@@ -63,7 +63,7 @@
                 blink::mojom::PauseSubresourceLoadingHandle,
                 blink::mojom::PreviewsResourceLoadingHintsReceiver,
                 chrome::mojom::ChromeRenderFrame,
-                chrome::mojom::ContentSettingsRenderer,
+                chrome::mojom::ContentSettingsAgent,
                 chrome::mojom::PrerenderDispatcher,
                 dom_distiller::mojom::DistillerPageNotifierService,
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4e47f04..71ae526 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -341,6 +341,8 @@
     "content_index/content_index_provider_impl.h",
     "content_settings/chrome_content_settings_utils.cc",
     "content_settings/chrome_content_settings_utils.h",
+    "content_settings/content_settings_manager_impl.cc",
+    "content_settings/content_settings_manager_impl.h",
     "content_settings/cookie_settings_factory.cc",
     "content_settings/cookie_settings_factory.h",
     "content_settings/host_content_settings_map_factory.cc",
@@ -5360,6 +5362,7 @@
   if (is_chromeos) {
     deps += [
       "//chrome/browser/resources/chromeos/crostini_installer:polymer3_elements",
+      "//chrome/browser/resources/chromeos/emulator:polymer3_elements",
       "//chrome/browser/resources/chromeos/set_time_dialog:polymer3_elements",
       "//chrome/browser/ui/webui/chromeos/add_supervision:mojo_bindings_js",
       "//chrome/browser/ui/webui/chromeos/crostini_installer:mojo_bindings_js",
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index f2cd5f3..cd1c743 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -71,22 +71,22 @@
         </if>
       </if>
       <if expr="chromeos">
-        <structure name="IDR_FIRST_RUN_HTML" file="resources\chromeos\first_run\first_run.html" flattenhtml="true" type="chrome_html"/>
-        <structure name="IDR_FIRST_RUN_JS" file="resources\chromeos\first_run\first_run.js" flattenhtml="true" type="chrome_html" />
-        <structure name="IDR_MD_LOGIN_HTML" file="resources\chromeos\login\md_login.html" flattenhtml="true" type="chrome_html" variables="OOBE=md_login" expand_variables="true"/>
-        <structure name="IDR_MD_LOGIN_JS" file="resources\chromeos\login\md_login.js" flattenhtml="true" type="chrome_html" />
-        <structure name="IDR_OOBE_HTML" file="resources\chromeos\login\oobe.html" flattenhtml="true" type="chrome_html" variables="OOBE=oobe" expand_variables="true"/>
-        <structure name="IDR_OOBE_JS" file="resources\chromeos\login\oobe.js" flattenhtml="true" type="chrome_html" />
-        <structure name="IDR_KEYBOARD_UTILS_JS" file="resources\chromeos\keyboard\keyboard_utils.js" flattenhtml="true" type="chrome_html" compress="gzip"/>
-        <structure name="IDR_CHROMEOS_DISCOVER_APP_HTML" file="resources\chromeos\login\discover\discover_app.html" flattenhtml="true" type="chrome_html"/>
-        <structure name="IDR_CHROMEOS_DISCOVER_APP_JS" file="resources\chromeos\login\discover\discover_app.js" flattenhtml="true" type="chrome_html"/>
-        <structure name="IDR_CHROMEOS_DISCOVER_MANIFEST" file="resources\chromeos\login\discover\manifest.json" flattenhtml="true" type="chrome_html"/>
-        <structure name="IDR_CUSTOM_ELEMENTS_OOBE_HTML" file="resources\chromeos\login\custom_elements_oobe.html" flattenhtml="true" type="chrome_html" />
-        <structure name="IDR_CUSTOM_ELEMENTS_OOBE_JS" file="resources\chromeos\login\custom_elements_oobe.js" flattenhtml="true" type="chrome_html" />
-        <structure name="IDR_CUSTOM_ELEMENTS_LOGIN_HTML" file="resources\chromeos\login\custom_elements_login.html" flattenhtml="true" type="chrome_html" />
-        <structure name="IDR_CUSTOM_ELEMENTS_LOGIN_JS" file="resources\chromeos\login\custom_elements_login.js" flattenhtml="true" type="chrome_html" />
-        <structure name="IDR_ASSISTANT_OPTIN_HTML" file="resources\chromeos\assistant_optin\assistant_optin.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
-        <structure name="IDR_ASSISTANT_OPTIN_JS" file="resources\chromeos\assistant_optin\assistant_optin.js" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
+        <structure name="IDR_FIRST_RUN_HTML" file="resources\chromeos\first_run\first_run.html" compress="gzip" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_FIRST_RUN_JS" file="resources\chromeos\first_run\first_run.js" compress="gzip" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_MD_LOGIN_HTML" file="resources\chromeos\login\md_login.html" compress="gzip" flattenhtml="true" type="chrome_html" variables="OOBE=md_login" expand_variables="true" />
+        <structure name="IDR_MD_LOGIN_JS" file="resources\chromeos\login\md_login.js" compress="gzip" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_OOBE_HTML" file="resources\chromeos\login\oobe.html" compress="gzip" flattenhtml="true" type="chrome_html" variables="OOBE=oobe" expand_variables="true" />
+        <structure name="IDR_OOBE_JS" file="resources\chromeos\login\oobe.js" compress="gzip" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_KEYBOARD_UTILS_JS" file="resources\chromeos\keyboard\keyboard_utils.js" flattenhtml="true" type="chrome_html" compress="gzip" />
+        <structure name="IDR_CHROMEOS_DISCOVER_APP_HTML" file="resources\chromeos\login\discover\discover_app.html" compress="gzip" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_CHROMEOS_DISCOVER_APP_JS" file="resources\chromeos\login\discover\discover_app.js" compress="gzip" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_CHROMEOS_DISCOVER_MANIFEST" file="resources\chromeos\login\discover\manifest.json" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_CUSTOM_ELEMENTS_OOBE_HTML" file="resources\chromeos\login\custom_elements_oobe.html" compress="gzip" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_CUSTOM_ELEMENTS_OOBE_JS" file="resources\chromeos\login\custom_elements_oobe.js" compress="gzip" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_CUSTOM_ELEMENTS_LOGIN_HTML" file="resources\chromeos\login\custom_elements_login.html" compress="gzip" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_CUSTOM_ELEMENTS_LOGIN_JS" file="resources\chromeos\login\custom_elements_login.js" compress="gzip" flattenhtml="true" type="chrome_html" />
+        <structure name="IDR_ASSISTANT_OPTIN_HTML" file="resources\chromeos\assistant_optin\assistant_optin.html" compress="gzip" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
+        <structure name="IDR_ASSISTANT_OPTIN_JS" file="resources\chromeos\assistant_optin\assistant_optin.js" compress="gzip" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
       </if>
     </structures>
     <includes>
@@ -199,18 +199,18 @@
       </if>
 
       <!-- App Management. -->
-      <if expr="not is_android">
-        <include name="IDR_APP_MANAGEMENT_BITMAP_MOJO_LITE_JS" file="${root_gen_dir}\skia\public\mojom\bitmap.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_APP_MANAGEMENT_IMAGE_INFO_MOJO_LITE_JS" file="${root_gen_dir}\skia\public\mojom\image_info.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_APP_MANAGEMENT_IMAGE_MOJO_LITE_JS" file="${root_gen_dir}\ui\gfx\image\mojom\image.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_APP_MANAGEMENT_MOJO_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\app_management\app_management.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_APP_MANAGEMENT_TYPES_MOJO_LITE_JS" file="${root_gen_dir}\chrome\services\app_service\public\mojom\types.mojom-lite.js" use_base_dir="false" type="BINDATA" />
+      <if expr="chromeos">
+        <include name="IDR_APP_MANAGEMENT_BITMAP_MOJO_LITE_JS" file="${root_gen_dir}\skia\public\mojom\bitmap.mojom-lite.js" compress="gzip" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_IMAGE_INFO_MOJO_LITE_JS" file="${root_gen_dir}\skia\public\mojom\image_info.mojom-lite.js" compress="gzip" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_IMAGE_MOJO_LITE_JS" file="${root_gen_dir}\ui\gfx\image\mojom\image.mojom-lite.js" compress="gzip" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_MOJO_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\app_management\app_management.mojom-lite.js" compress="gzip" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_TYPES_MOJO_LITE_JS" file="${root_gen_dir}\chrome\services\app_service\public\mojom\types.mojom-lite.js" compress="gzip" use_base_dir="false" type="BINDATA" />
 
-        <include name="IDR_APP_MANAGEMENT_APP_HTML" file="resources\app_management\app.html" type="BINDATA" />
-        <include name="IDR_APP_MANAGEMENT_APP_JS" file="resources\app_management\app.js" type="BINDATA" />
-        <include name="IDR_APP_MANAGEMENT_EXPANDABLE_APP_LIST_HTML" file="resources\app_management\expandable_app_list.html" type="BINDATA"/>
-        <include name="IDR_APP_MANAGEMENT_EXPANDABLE_APP_LIST_JS" file="resources\app_management\expandable_app_list.js" type="BINDATA"/>
-        <include name="IDR_APP_MANAGEMENT_INDEX_HTML" file="resources\app_management\index.html" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_APP_HTML" file="resources\app_management\app.html" compress="gzip" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_APP_JS" file="resources\app_management\app.js" compress="gzip" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_EXPANDABLE_APP_LIST_HTML" file="resources\app_management\expandable_app_list.html" compress="gzip" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_EXPANDABLE_APP_LIST_JS" file="resources\app_management\expandable_app_list.js" compress="gzip" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_INDEX_HTML" file="resources\app_management\index.html" compress="gzip" type="BINDATA" />
       </if>
 
       <if expr="not is_android">
@@ -381,23 +381,30 @@
         <include name="IDR_URGENT_PASSWORD_EXPIRY_NOTIFICATION_JS" file="resources\chromeos\password_change\urgent_password_expiry_notification.js" type="chrome_html" />
 
         <include name="IDR_CROSH_BUILTIN_MANIFEST" file="resources\chromeos\crosh_builtin\manifest.json" type="BINDATA" />
-        <include name="IDR_CROSTINI_INSTALLER_INDEX_HTML" file="resources\chromeos\crostini_installer\index.html" type="BINDATA" />
-        <include name="IDR_CROSTINI_INSTALLER_APP_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\crostini_installer\app.js" type="BINDATA" use_base_dir="false" />
-        <include name="IDR_CROSTINI_INSTALLER_BROWSER_PROXY_JS" file="resources\chromeos\crostini_installer\browser_proxy.js" type="BINDATA" />
-        <include name="IDR_CROSTINI_INSTALLER_MOJO_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\chromeos\crostini_installer\crostini_installer.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_CROSTINI_INSTALLER_TYPES_MOJO_LITE_JS" file="${root_gen_dir}\chrome\browser\chromeos\crostini\crostini_installer_types.mojom-lite.js" use_base_dir="false" type="BINDATA" />
-        <include name="IDR_CRYPTOHOME_HTML" file="resources\chromeos\cryptohome.html" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_CRYPTOHOME_JS" file="resources\chromeos\cryptohome.js" type="BINDATA" />
+
+        <include name="IDR_CROSTINI_INSTALLER_INDEX_HTML" file="resources\chromeos\crostini_installer\index.html" compress="gzip" type="BINDATA" />
+        <include name="IDR_CROSTINI_INSTALLER_APP_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\crostini_installer\app.js" compress="gzip" type="BINDATA" use_base_dir="false" />
+        <include name="IDR_CROSTINI_INSTALLER_BROWSER_PROXY_JS" file="resources\chromeos\crostini_installer\browser_proxy.js" compress="gzip" type="BINDATA" />
+        <include name="IDR_CROSTINI_INSTALLER_MOJO_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\chromeos\crostini_installer\crostini_installer.mojom-lite.js" use_base_dir="false" compress="gzip" type="BINDATA" />
+        <include name="IDR_CROSTINI_INSTALLER_TYPES_MOJO_LITE_JS" file="${root_gen_dir}\chrome\browser\chromeos\crostini\crostini_installer_types.mojom-lite.js" use_base_dir="false" compress="gzip" type="BINDATA" />
+
+        <include name="IDR_CRYPTOHOME_HTML" file="resources\chromeos\cryptohome.html" flattenhtml="true" compress="gzip" type="BINDATA" />
+        <include name="IDR_CRYPTOHOME_JS" file="resources\chromeos\cryptohome.js" compress="gzip" type="BINDATA" />
+
         <!-- manifest file of Connectivity Diagnostics app -->
         <include name="IDR_CONNECTIVITY_DIAGNOSTICS_MANIFEST" file="resources\chromeos\connectivity_diagnostics\manifest.json" type="BINDATA" />
         <include name="IDR_CONNECTIVITY_DIAGNOSTICS_LAUNCHER_MANIFEST" file="resources\chromeos\connectivity_diagnostics_launcher\manifest.json" type="BINDATA" />
-        <include name="IDR_DRIVE_INTERNALS_CSS" file="resources\chromeos\drive_internals.css" type="BINDATA" />
-        <include name="IDR_DRIVE_INTERNALS_HTML" file="resources\chromeos\drive_internals.html" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_DRIVE_INTERNALS_JS" file="resources\chromeos\drive_internals.js" type="BINDATA" />
-         <include name="IDR_GUEST_SESSION_TAB_HTML" file="resources\chromeos\guest_session_tab.html" flattenhtml="true" type="BINDATA" />
+
+        <include name="IDR_DRIVE_INTERNALS_CSS" file="resources\chromeos\drive_internals.css" type="BINDATA" compress="gzip" />
+        <include name="IDR_DRIVE_INTERNALS_HTML" file="resources\chromeos\drive_internals.html" flattenhtml="true" type="BINDATA" compress="gzip" />
+        <include name="IDR_DRIVE_INTERNALS_JS" file="resources\chromeos\drive_internals.js" type="BINDATA" compress="gzip" />
+
+        <include name="IDR_GUEST_SESSION_TAB_HTML" file="resources\chromeos\guest_session_tab.html" flattenhtml="true" type="BINDATA" compress="gzip" />
+
         <include name="IDR_MOBILE_MANIFEST" file="resources\chromeos\mobile_app\manifest.json" type="BINDATA" />
-        <include name="IDR_MOBILE_SETUP_PAGE_HTML" file="resources\chromeos\mobile_setup.html" flattenhtml="true" type="BINDATA" />
-        <include name="IDR_MOBILE_SETUP_PORTAL_PAGE_HTML" file="resources\chromeos\mobile_setup_portal.html" flattenhtml="true" type="BINDATA" />
+        <include name="IDR_MOBILE_SETUP_PAGE_HTML" file="resources\chromeos\mobile_setup.html" flattenhtml="true" type="BINDATA" compress="gzip" />
+        <include name="IDR_MOBILE_SETUP_PORTAL_PAGE_HTML" file="resources\chromeos\mobile_setup_portal.html" flattenhtml="true" type="BINDATA" compress="gzip" />
+
         <include name="IDR_ECHO_MANIFEST" file="resources\chromeos\echo\manifest.json" type="BINDATA" />
         <include name="IDR_OS_CREDITS_HTML" file="resources\chromeos\about_os_credits.html" flattenhtml="true" type="BINDATA" />
         <if expr="optimize_webui">
@@ -497,18 +504,14 @@
         <include name="IDR_ABOUT_POWER_JS" file="resources\chromeos\power.js" compress="gzip" type="BINDATA" />
         <include name="IDR_ABOUT_POWER_CSS" file="resources\chromeos\power.css" compress="gzip" type="BINDATA" />
         <include name="IDR_DEVICE_EMULATOR_HTML" file="resources\chromeos\emulator\device_emulator.html" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_AUDIO_SETTINGS_HTML" file="resources\chromeos\emulator\audio_settings.html" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_AUDIO_SETTINGS_JS" file="resources\chromeos\emulator\audio_settings.js" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_BATTERY_SETTINGS_HTML" file="resources\chromeos\emulator\battery_settings.html" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_BATTERY_SETTINGS_JS" file="resources\chromeos\emulator\battery_settings.js" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_BLUETOOTH_SETTINGS_HTML" file="resources\chromeos\emulator\bluetooth_settings.html" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_BLUETOOTH_SETTINGS_JS" file="resources\chromeos\emulator\bluetooth_settings.js" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_ICONS_HTML" file="resources\chromeos\emulator\icons.html" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_INPUT_DEVICE_SETTINGS_HTML" file="resources\chromeos\emulator\input_device_settings.html" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_INPUT_DEVICE_SETTINGS_JS" file="resources\chromeos\emulator\input_device_settings.js" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_PAGES_HTML" file="resources\chromeos\emulator\device_emulator_pages.html" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_PAGES_JS" file="resources\chromeos\emulator\device_emulator_pages.js" compress="gzip" type="BINDATA" />
-        <include name="IDR_DEVICE_EMULATOR_SHARED_STYLES_HTML" file="resources\chromeos\emulator\shared_styles.html" compress="gzip" type="BINDATA" />
+        <include name="IDR_DEVICE_EMULATOR_CSS" file="resources\chromeos\emulator\device_emulator.css" compress="gzip" type="BINDATA" />
+        <include name="IDR_DEVICE_EMULATOR_AUDIO_SETTINGS_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\emulator\audio_settings.js" use_base_dir="false" compress="gzip" type="BINDATA" />
+        <include name="IDR_DEVICE_EMULATOR_BATTERY_SETTINGS_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\emulator\battery_settings.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_DEVICE_EMULATOR_BLUETOOTH_SETTINGS_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\emulator\bluetooth_settings.js" use_base_dir="false" compress="gzip" type="BINDATA" />
+        <include name="IDR_DEVICE_EMULATOR_ICONS_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\emulator\icons.js" use_base_dir="false"  compress="gzip" type="BINDATA" />
+        <include name="IDR_DEVICE_EMULATOR_INPUT_DEVICE_SETTINGS_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\emulator\input_device_settings.js" use_base_dir="false"  compress="gzip" type="BINDATA" />
+        <include name="IDR_DEVICE_EMULATOR_PAGES_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\emulator\device_emulator_pages.js" use_base_dir="false"  compress="gzip" type="BINDATA" />
+        <include name="IDR_DEVICE_EMULATOR_SHARED_STYLES_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\emulator\shared_styles.js" use_base_dir="false"  compress="gzip" type="BINDATA" />
       </if>
       <if expr="chromeos">
         <include name="IDR_SET_TIME_HTML" file="resources\chromeos\set_time_dialog\set_time.html" type="BINDATA" compress="gzip" />
@@ -560,12 +563,12 @@
       <include name="IDR_MEDIA_ENGAGEMENT_SCORE_DETAILS_MOJOM_LITE_JS" file="${root_gen_dir}\chrome\browser\media\media_engagement_score_details.mojom-lite.js" use_base_dir="false" type="BINDATA" compress="gzip" />
       <include name="IDR_PWA_HTML" file="resources\pwa.html" type="BINDATA" />
       <if expr="chromeos">
-        <include name="IDR_SMB_SHARES_DIALOG_CONTAINER_HTML" file="resources\chromeos\smb_shares\smb_share_dialog_container.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
-        <include name="IDR_SMB_SHARES_DIALOG_HTML" file="resources\chromeos\smb_shares\smb_share_dialog.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
-        <include name="IDR_SMB_SHARES_DIALOG_JS" file="resources\chromeos\smb_shares\smb_share_dialog.js" type="chrome_html" />
-        <include name="IDR_SMB_CREDENTIALS_DIALOG_CONTAINER_HTML" file="resources\chromeos\smb_shares\smb_credentials_dialog_container.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
-        <include name="IDR_SMB_CREDENTIALS_DIALOG_HTML" file="resources\chromeos\smb_shares\smb_credentials_dialog.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
-        <include name="IDR_SMB_CREDENTIALS_DIALOG_JS" file="resources\chromeos\smb_shares\smb_credentials_dialog.js" type="chrome_html" />
+        <include name="IDR_SMB_SHARES_DIALOG_CONTAINER_HTML" file="resources\chromeos\smb_shares\smb_share_dialog_container.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="chrome_html" />
+        <include name="IDR_SMB_SHARES_DIALOG_HTML" file="resources\chromeos\smb_shares\smb_share_dialog.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="chrome_html" />
+        <include name="IDR_SMB_SHARES_DIALOG_JS" file="resources\chromeos\smb_shares\smb_share_dialog.js" compress="gzip" type="chrome_html" />
+        <include name="IDR_SMB_CREDENTIALS_DIALOG_CONTAINER_HTML" file="resources\chromeos\smb_shares\smb_credentials_dialog_container.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="chrome_html" />
+        <include name="IDR_SMB_CREDENTIALS_DIALOG_HTML" file="resources\chromeos\smb_shares\smb_credentials_dialog.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="chrome_html" />
+        <include name="IDR_SMB_CREDENTIALS_DIALOG_JS" file="resources\chromeos\smb_shares\smb_credentials_dialog.js" compress="gzip" type="chrome_html" />
         <include name="IDR_SYS_INTERNALS_HTML" file="resources\chromeos\sys_internals\index.html" compress="gzip" type="BINDATA" />
         <include name="IDR_SYS_INTERNALS_CSS" file="resources\chromeos\sys_internals\index.css" compress="gzip" type="BINDATA" />
         <include name="IDR_SYS_INTERNALS_JS" file="resources\chromeos\sys_internals\index.js" compress="gzip" type="BINDATA" />
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index eccc37c..a8bc3fb 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -6,6 +6,7 @@
 
 #include "base/feature_list.h"
 #include "build/build_config.h"
+#include "chrome/browser/content_settings/content_settings_manager_impl.h"
 #include "chrome/browser/navigation_predictor/navigation_predictor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ssl/insecure_sensitive_input_driver_factory.h"
@@ -63,6 +64,9 @@
 
 void PopulateChromeFrameBinders(
     service_manager::BinderMapWithContext<content::RenderFrameHost*>* map) {
+  map->Add<mojom::ContentSettingsManager>(
+      base::BindRepeating(&ContentSettingsManagerImpl::Create));
+
   map->Add<image_annotation::mojom::Annotator>(
       base::BindRepeating(&BindImageAnnotator));
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 5ee71fb..5233f8f 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4152,7 +4152,7 @@
   }
 
   void WillRedirectRequest(net::RedirectInfo* redirect_info,
-                           const network::ResourceResponseHead& response_head,
+                           const network::mojom::URLResponseHead& response_head,
                            bool* defer,
                            std::vector<std::string>* to_be_removed_headers,
                            net::HttpRequestHeaders* modified_headers) override {
diff --git a/chrome/browser/chromeos/accessibility/magnification_manager.cc b/chrome/browser/chromeos/accessibility/magnification_manager.cc
index f95dcbd0..d7cbd5c 100644
--- a/chrome/browser/chromeos/accessibility/magnification_manager.cc
+++ b/chrome/browser/chromeos/accessibility/magnification_manager.cc
@@ -25,6 +25,9 @@
 #include "content/public/browser/notification_source.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "ui/accessibility/ax_role_properties.h"
+#include "ui/views/accessibility/ax_event_manager.h"
+#include "ui/views/accessibility/view_accessibility.h"
 
 namespace chromeos {
 
@@ -98,6 +101,25 @@
   SetProfile(nullptr);
 }
 
+void MagnificationManager::OnViewEvent(views::View* view,
+                                       ax::mojom::Event event_type) {
+  if (!fullscreen_magnifier_enabled_ && !IsDockedMagnifierEnabled())
+    return;
+
+  if (event_type != ax::mojom::Event::kFocus &&
+      event_type != ax::mojom::Event::kSelection) {
+    return;
+  }
+
+  ui::AXNodeData data;
+  view->GetViewAccessibility().GetAccessibleNodeData(&data);
+
+  // Disallow focus on large containers, which probably should not move the
+  // magnified viewport to the center of the view.
+  if (ui::IsControl(data.role))
+    HandleFocusChanged(view->GetBoundsInScreen(), false);
+}
+
 void MagnificationManager::SetProfileForTest(Profile* profile) {
   SetProfile(profile);
 }
@@ -110,10 +132,12 @@
   registrar_.Add(this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
                  content::NotificationService::AllSources());
   user_manager::UserManager::Get()->AddSessionStateObserver(this);
+  views::AXEventManager::Get()->AddObserver(this);
 }
 
 MagnificationManager::~MagnificationManager() {
   CHECK(this == g_magnification_manager);
+  views::AXEventManager::Get()->RemoveObserver(this);
   user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
 }
 
@@ -281,17 +305,22 @@
   if (node_details->is_editable_node)
     return;
 
-  const gfx::Rect& bounds_in_screen = node_details->node_bounds_in_screen;
+  HandleFocusChanged(node_details->node_bounds_in_screen,
+                     node_details->is_editable_node);
+}
+
+void MagnificationManager::HandleFocusChanged(const gfx::Rect& bounds_in_screen,
+                                              bool is_editable) {
   if (bounds_in_screen.IsEmpty())
     return;
 
   // Fullscreen magnifier and docked magnifier are mutually exclusive.
   if (fullscreen_magnifier_enabled_) {
     ash::Shell::Get()->magnification_controller()->HandleFocusedNodeChanged(
-        node_details->is_editable_node, node_details->node_bounds_in_screen);
+        is_editable, bounds_in_screen);
     return;
   }
-  DCHECK(docked_magnifier_enabled);
+  DCHECK(IsDockedMagnifierEnabled());
   ash::DockedMagnifierController::Get()->CenterOnPoint(
       bounds_in_screen.CenterPoint());
 }
diff --git a/chrome/browser/chromeos/accessibility/magnification_manager.h b/chrome/browser/chromeos/accessibility/magnification_manager.h
index 233a5e7..36508be2 100644
--- a/chrome/browser/chromeos/accessibility/magnification_manager.h
+++ b/chrome/browser/chromeos/accessibility/magnification_manager.h
@@ -13,6 +13,7 @@
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "ui/views/accessibility/ax_event_observer.h"
 
 class PrefChangeRegistrar;
 
@@ -33,7 +34,8 @@
 class MagnificationManager
     : public content::NotificationObserver,
       public user_manager::UserManager::UserSessionStateObserver,
-      public ProfileObserver {
+      public ProfileObserver,
+      public views::AXEventObserver {
  public:
   // Creates an instance of MagnificationManager. This should be called once.
   static void Initialize();
@@ -65,6 +67,9 @@
   // ProfileObserver:
   void OnProfileWillBeDestroyed(Profile* profile) override;
 
+  // views::AXEventObserver:
+  void OnViewEvent(views::View* view, ax::mojom::Event event_type) override;
+
   void SetProfileForTest(Profile* profile);
 
  private:
@@ -91,6 +96,9 @@
   // Called when received content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE.
   void HandleFocusChangedInPage(const content::NotificationDetails& details);
 
+  // Called in response to AXEventObserver.
+  void HandleFocusChanged(const gfx::Rect& bounds_in_screen, bool is_editable);
+
   Profile* profile_ = nullptr;
   ScopedObserver<Profile, ProfileObserver> profile_observer_{this};
 
diff --git a/chrome/browser/content_settings/content_settings_manager_impl.cc b/chrome/browser/content_settings/content_settings_manager_impl.cc
new file mode 100644
index 0000000..06565b02
--- /dev/null
+++ b/chrome/browser/content_settings/content_settings_manager_impl.cc
@@ -0,0 +1,167 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/content_settings/content_settings_manager_impl.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/content_settings/cookie_settings_factory.h"
+#include "chrome/browser/content_settings/tab_specific_content_settings.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
+#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "extensions/buildflags/buildflags.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
+#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
+#endif
+
+namespace chrome {
+namespace {
+
+void OnDomStorageAccessed(int process_id,
+                          int frame_id,
+                          const GURL& origin_url,
+                          const GURL& top_origin_url,
+                          bool local,
+                          bool blocked_by_policy) {
+  content::RenderFrameHost* frame =
+      content::RenderFrameHost::FromID(process_id, frame_id);
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(frame);
+  if (!web_contents)
+    return;
+
+  TabSpecificContentSettings* tab_settings =
+      TabSpecificContentSettings::FromWebContents(web_contents);
+  if (tab_settings)
+    tab_settings->OnDomStorageAccessed(origin_url, local, blocked_by_policy);
+
+  page_load_metrics::MetricsWebContentsObserver* metrics_observer =
+      page_load_metrics::MetricsWebContentsObserver::FromWebContents(
+          web_contents);
+  if (metrics_observer)
+    metrics_observer->OnDomStorageAccessed(origin_url, top_origin_url, local,
+                                           blocked_by_policy);
+}
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+void OnFileSystemAccessedInGuestViewContinuation(
+    int render_process_id,
+    int render_frame_id,
+    const GURL& url,
+    base::OnceCallback<void(bool)> callback,
+    bool allowed) {
+  TabSpecificContentSettings::FileSystemAccessed(
+      render_process_id, render_frame_id, url, !allowed);
+  std::move(callback).Run(allowed);
+}
+
+void OnFileSystemAccessedInGuestView(int render_process_id,
+                                     int render_frame_id,
+                                     const GURL& url,
+                                     bool allowed,
+                                     base::OnceCallback<void(bool)> callback) {
+  extensions::WebViewPermissionHelper* web_view_permission_helper =
+      extensions::WebViewPermissionHelper::FromFrameID(render_process_id,
+                                                       render_frame_id);
+  auto continuation = base::BindOnce(
+      &OnFileSystemAccessedInGuestViewContinuation, render_process_id,
+      render_frame_id, url, std::move(callback));
+  if (!web_view_permission_helper) {
+    std::move(continuation).Run(allowed);
+    return;
+  }
+  web_view_permission_helper->RequestFileSystemPermission(
+      url, allowed, std::move(continuation));
+}
+#endif
+
+}  // namespace
+
+ContentSettingsManagerImpl::~ContentSettingsManagerImpl() = default;
+
+// static
+void ContentSettingsManagerImpl::Create(
+    content::RenderFrameHost* render_frame_host,
+    mojo::PendingReceiver<mojom::ContentSettingsManager> receiver) {
+  mojo::MakeSelfOwnedReceiver(
+      base::WrapUnique(new ContentSettingsManagerImpl(render_frame_host)),
+      std::move(receiver));
+}
+
+void ContentSettingsManagerImpl::Clone(
+    mojo::PendingReceiver<mojom::ContentSettingsManager> receiver) {
+  mojo::MakeSelfOwnedReceiver(
+      base::WrapUnique(new ContentSettingsManagerImpl(*this)),
+      std::move(receiver));
+}
+
+void ContentSettingsManagerImpl::AllowStorageAccess(
+    StorageType storage_type,
+    const url::Origin& origin,
+    const GURL& site_for_cookies,
+    const url::Origin& top_frame_origin,
+    base::OnceCallback<void(bool)> callback) {
+  GURL url = origin.GetURL();
+
+  bool allowed = cookie_settings_->IsCookieAccessAllowed(url, site_for_cookies,
+                                                         top_frame_origin);
+
+  switch (storage_type) {
+    case StorageType::DATABASE:
+      TabSpecificContentSettings::WebDatabaseAccessed(
+          render_process_id_, render_frame_id_, url, !allowed);
+      break;
+    case StorageType::LOCAL_STORAGE:
+      OnDomStorageAccessed(render_process_id_, render_frame_id_, url,
+                           top_frame_origin.GetURL(), true, !allowed);
+      break;
+    case StorageType::SESSION_STORAGE:
+      OnDomStorageAccessed(render_process_id_, render_frame_id_, url,
+                           top_frame_origin.GetURL(), false, !allowed);
+      break;
+    case StorageType::FILE_SYSTEM:
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+      if (extensions::WebViewRendererState::GetInstance()->IsGuest(
+              render_process_id_)) {
+        OnFileSystemAccessedInGuestView(render_process_id_, render_frame_id_,
+                                        url, allowed, std::move(callback));
+        return;
+      }
+#endif
+      TabSpecificContentSettings::FileSystemAccessed(
+          render_process_id_, render_frame_id_, url, !allowed);
+      break;
+    case StorageType::INDEXED_DB:
+      TabSpecificContentSettings::IndexedDBAccessed(
+          render_process_id_, render_frame_id_, url, !allowed);
+      break;
+    case StorageType::CACHE:
+      TabSpecificContentSettings::CacheStorageAccessed(
+          render_process_id_, render_frame_id_, url, !allowed);
+      break;
+  }
+
+  std::move(callback).Run(allowed);
+}
+
+ContentSettingsManagerImpl::ContentSettingsManagerImpl(
+    content::RenderFrameHost* render_frame_host)
+    : render_process_id_(render_frame_host->GetProcess()->GetID()),
+      render_frame_id_(render_frame_host->GetRoutingID()),
+      cookie_settings_(
+          CookieSettingsFactory::GetForProfile(Profile::FromBrowserContext(
+              render_frame_host->GetProcess()->GetBrowserContext()))) {}
+
+ContentSettingsManagerImpl::ContentSettingsManagerImpl(
+    const ContentSettingsManagerImpl& other)
+    : render_process_id_(other.render_process_id_),
+      render_frame_id_(other.render_frame_id_),
+      cookie_settings_(other.cookie_settings_) {}
+
+}  // namespace chrome
diff --git a/chrome/browser/content_settings/content_settings_manager_impl.h b/chrome/browser/content_settings/content_settings_manager_impl.h
new file mode 100644
index 0000000..955dc8c
--- /dev/null
+++ b/chrome/browser/content_settings/content_settings_manager_impl.h
@@ -0,0 +1,53 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CONTENT_SETTINGS_CONTENT_SETTINGS_MANAGER_IMPL_H_
+#define CHROME_BROWSER_CONTENT_SETTINGS_CONTENT_SETTINGS_MANAGER_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "chrome/common/content_settings_manager.mojom.h"
+
+namespace content {
+class RenderFrameHost;
+}  // namespace content
+
+namespace content_settings {
+class CookieSettings;
+}  // namespace content_settings
+
+namespace chrome {
+
+class ContentSettingsManagerImpl : public mojom::ContentSettingsManager {
+ public:
+  ~ContentSettingsManagerImpl() override;
+
+  static void Create(
+      content::RenderFrameHost* render_frame_host,
+      mojo::PendingReceiver<mojom::ContentSettingsManager> receiver);
+
+  // mojom::ContentSettingsManager methods:
+  void Clone(
+      mojo::PendingReceiver<mojom::ContentSettingsManager> receiver) override;
+  void AllowStorageAccess(StorageType storage_type,
+                          const url::Origin& origin,
+                          const GURL& site_for_cookies,
+                          const url::Origin& top_frame_origin,
+                          base::OnceCallback<void(bool)> callback) override;
+
+ private:
+  explicit ContentSettingsManagerImpl(
+      content::RenderFrameHost* render_frame_host);
+  explicit ContentSettingsManagerImpl(const ContentSettingsManagerImpl& other);
+
+  // Use these IDs to hold a weak reference back to the RenderFrameHost.
+  int render_process_id_;
+  int render_frame_id_;
+
+  // Used to look up storage permissions.
+  scoped_refptr<content_settings::CookieSettings> cookie_settings_;
+};
+
+}  // namespace chrome
+
+#endif  // CHROME_BROWSER_CONTENT_SETTINGS_CONTENT_SETTINGS_MANAGER_IMPL_H_
diff --git a/chrome/browser/content_settings/mixed_content_settings_tab_helper.cc b/chrome/browser/content_settings/mixed_content_settings_tab_helper.cc
index deea331..6c76ccc 100644
--- a/chrome/browser/content_settings/mixed_content_settings_tab_helper.cc
+++ b/chrome/browser/content_settings/mixed_content_settings_tab_helper.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/content_settings/mixed_content_settings_tab_helper.h"
 
-#include "chrome/common/content_settings_renderer.mojom.h"
+#include "chrome/common/content_settings_agent.mojom.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
@@ -49,9 +49,9 @@
   if (!is_running_insecure_content_allowed_)
     return;
 
-  mojo::AssociatedRemote<chrome::mojom::ContentSettingsRenderer> renderer;
-  render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(&renderer);
-  renderer->SetAllowRunningInsecureContent();
+  mojo::AssociatedRemote<chrome::mojom::ContentSettingsAgent> agent;
+  render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(&agent);
+  agent->SetAllowRunningInsecureContent();
 }
 
 void MixedContentSettingsTabHelper::DidFinishNavigation(
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.cc b/chrome/browser/content_settings/tab_specific_content_settings.cc
index d2e6db2..d54ea80 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings.cc
@@ -26,7 +26,7 @@
 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/content_settings_renderer.mojom.h"
+#include "chrome/common/content_settings_agent.mojom.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/common/renderer_configuration.mojom.h"
@@ -747,11 +747,11 @@
     content::RenderFrameHost* render_frame_host) {
   // We want to tell the renderer-side code to ignore content settings for this
   // page.
-  mojo::AssociatedRemote<chrome::mojom::ContentSettingsRenderer>
-      content_settings_renderer;
+  mojo::AssociatedRemote<chrome::mojom::ContentSettingsAgent>
+      content_settings_agent;
   render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
-      &content_settings_renderer);
-  content_settings_renderer->SetAsInterstitial();
+      &content_settings_agent);
+  content_settings_agent->SetAsInterstitial();
 }
 
 bool TabSpecificContentSettings::OnMessageReceived(
diff --git a/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
index ab981ff..bdd99dc5 100644
--- a/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
@@ -24,6 +24,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/extension_util.h"
+#include "extensions/common/api/declarative_net_request.h"
 #include "extensions/common/api/declarative_net_request/constants.h"
 #include "extensions/common/api/declarative_net_request/test_utils.h"
 #include "extensions/common/file_util.h"
@@ -37,6 +38,8 @@
 namespace extensions {
 namespace declarative_net_request {
 
+namespace dnr_api = api::declarative_net_request;
+
 namespace {
 
 constexpr char kJSONRulesFilename[] = "rules_file.json";
@@ -153,12 +156,13 @@
   TestRule rule_two = CreateGenericRule();
   rule_two.condition->url_filter = std::string("two.com");
 
-  auto should_block_request = [this](const WebRequestInfo& request,
+  auto should_block_request = [this](const WebRequestInfo& request, int rule_id,
                                      const ExtensionId& extension_id) {
     manager()->EvaluateRequest(request, false /*is_incognito_context*/);
     return !request.dnr_actions->empty() &&
            ((*request.dnr_actions)[0] ==
-            RequestAction(RequestActionType::BLOCK, extension_id));
+            RequestAction(RequestActionType::BLOCK, rule_id,
+                          dnr_api::SOURCE_TYPE_MANIFEST, extension_id));
   };
 
   for (int mask = 0; mask < 4; mask++) {
@@ -196,12 +200,14 @@
     WebRequestInfo request_three_info(
         GetRequestParamsForURL("http://three.com"));
 
-    EXPECT_EQ((mask & kEnableRulesetOne) != 0,
-              should_block_request(request_one_info, extension_id_one));
-    EXPECT_EQ((mask & kEnableRulesetTwo) != 0,
-              should_block_request(request_two_info, extension_id_two));
-    EXPECT_FALSE(
-        should_block_request(request_three_info, "" /* extension_id */));
+    EXPECT_EQ(
+        (mask & kEnableRulesetOne) != 0,
+        should_block_request(request_one_info, *rule_one.id, extension_id_one));
+    EXPECT_EQ(
+        (mask & kEnableRulesetTwo) != 0,
+        should_block_request(request_two_info, *rule_two.id, extension_id_two));
+    EXPECT_FALSE(should_block_request(request_three_info, 0 /* rule_id */,
+                                      "" /* extension_id */));
 
     // Remove the rulesets.
     if (mask & kEnableRulesetOne)
@@ -236,9 +242,10 @@
 
   manager()->EvaluateRequest(request_info, false /*is_incognito_context*/);
   ASSERT_EQ(1u, request_info.dnr_actions->size());
-  EXPECT_EQ(
-      RequestAction(RequestActionType::BLOCK, last_loaded_extension()->id()),
-      (*request_info.dnr_actions)[0]);
+  EXPECT_EQ(RequestAction(RequestActionType::BLOCK, *rule_one.id,
+                          dnr_api::SOURCE_TYPE_MANIFEST,
+                          last_loaded_extension()->id()),
+            (*request_info.dnr_actions)[0]);
   request_info.dnr_actions.reset();
 
   // Enabling the extension in incognito mode, should cause requests from
@@ -249,16 +256,18 @@
 
   manager()->EvaluateRequest(request_info, true /*is_incognito_context*/);
   ASSERT_EQ(1u, request_info.dnr_actions->size());
-  EXPECT_EQ(
-      RequestAction(RequestActionType::BLOCK, last_loaded_extension()->id()),
-      (*request_info.dnr_actions)[0]);
+  EXPECT_EQ(RequestAction(RequestActionType::BLOCK, *rule_one.id,
+                          dnr_api::SOURCE_TYPE_MANIFEST,
+                          last_loaded_extension()->id()),
+            (*request_info.dnr_actions)[0]);
   request_info.dnr_actions.reset();
 
   manager()->EvaluateRequest(request_info, false /*is_incognito_context*/);
   ASSERT_EQ(1u, request_info.dnr_actions->size());
-  EXPECT_EQ(
-      RequestAction(RequestActionType::BLOCK, last_loaded_extension()->id()),
-      (*request_info.dnr_actions)[0]);
+  EXPECT_EQ(RequestAction(RequestActionType::BLOCK, *rule_one.id,
+                          dnr_api::SOURCE_TYPE_MANIFEST,
+                          last_loaded_extension()->id()),
+            (*request_info.dnr_actions)[0]);
   request_info.dnr_actions.reset();
 }
 
@@ -301,9 +310,10 @@
 
     manager()->EvaluateRequest(example_com_request, is_incognito_context);
     ASSERT_EQ(1u, example_com_request.dnr_actions->size());
-    EXPECT_EQ(
-        RequestAction(RequestActionType::BLOCK, last_loaded_extension()->id()),
-        (*example_com_request.dnr_actions)[0]);
+    EXPECT_EQ(RequestAction(RequestActionType::BLOCK, *rule.id,
+                            dnr_api::SOURCE_TYPE_MANIFEST,
+                            last_loaded_extension()->id()),
+              (*example_com_request.dnr_actions)[0]);
 
     tester.ExpectTotalCount(kHistogramName, 1);
 
@@ -336,7 +346,8 @@
   // redirected to "google.com".
   const bool is_incognito_context = false;
   const char* kExampleURL = "http://example.com";
-  RequestAction expected_redirect_action(RequestActionType::REDIRECT,
+  RequestAction expected_redirect_action(RequestActionType::REDIRECT, *rule.id,
+                                         dnr_api::SOURCE_TYPE_MANIFEST,
                                          last_loaded_extension()->id());
   expected_redirect_action.redirect_url = GURL("http://google.com");
   WebRequestInfo request_1(GetRequestParamsForURL(kExampleURL, base::nullopt));
@@ -411,7 +422,8 @@
   WebRequestInfo request_1(GetRequestParamsForURL("http://example.com"));
   manager()->EvaluateRequest(request_1, false /*is_incognito_context*/);
   ASSERT_EQ(1u, request_1.dnr_actions->size());
-  EXPECT_EQ(RequestAction(RequestActionType::BLOCK, extension_1->id()),
+  EXPECT_EQ(RequestAction(RequestActionType::BLOCK, kMinValidID,
+                          dnr_api::SOURCE_TYPE_MANIFEST, extension_1->id()),
             (*request_1.dnr_actions)[0]);
 
   // Ensure that the background page for |extension_1| won't be blocked or
@@ -497,6 +509,7 @@
   // Removal of the cookie header should be attributed to |extension_2| because
   // it was installed later than |extension_1| and thus has more priority.
   RequestAction expected_action_1(RequestActionType::REMOVE_HEADERS,
+                                  kMinValidID, dnr_api::SOURCE_TYPE_MANIFEST,
                                   extension_2->id());
   expected_action_1.request_headers_to_remove.push_back(
       net::HttpRequestHeaders::kCookie);
@@ -506,6 +519,7 @@
       net::HttpRequestHeaders::kReferer);
 
   RequestAction expected_action_2(RequestActionType::REMOVE_HEADERS,
+                                  kMinValidID, dnr_api::SOURCE_TYPE_MANIFEST,
                                   extension_1->id());
   expected_action_2.response_headers_to_remove.push_back("set-cookie");
 
@@ -541,6 +555,9 @@
                           std::move(pattern_set));
   }
 
+  int rule_1_id = kMinValidID;
+  int rule_2_id = kMinValidID + 1;
+
   constexpr int kDummyFrameRoutingId = 2;
   constexpr int kDummyFrameId = 3;
   constexpr int kDummyParentFrameId = 1;
@@ -561,6 +578,7 @@
     int frame_routing_id;
     base::Optional<FrameDataParams> frame_data_params;
     bool expect_blocked_with_allowed_pages;
+    base::Optional<int> matched_rule_id;
   } test_cases[] = {
       // Main frame requests. Allowed based on request url.
       {kAllowedPageURL, content::ResourceType::kMainFrame, base::nullopt,
@@ -568,18 +586,18 @@
        FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
                         ExtensionApiFrameIdMap::kInvalidFrameId,
                         "http://google.com/xyz", base::nullopt}),
-       false},
+       false, base::nullopt},
       {"http://google.com/xyz", content::ResourceType::kMainFrame,
        base::nullopt, MSG_ROUTING_NONE,
        FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
                         ExtensionApiFrameIdMap::kInvalidFrameId,
                         kAllowedPageURL, base::nullopt}),
-       true},
+       true, rule_2_id},
 
       // Non-navigation browser or service worker request. Not allowed,
       // since the request doesn't correspond to a frame.
       {"http://google.com/xyz", content::ResourceType::kScript, base::nullopt,
-       MSG_ROUTING_NONE, base::nullopt, true},
+       MSG_ROUTING_NONE, base::nullopt, true, rule_1_id},
 
       // Renderer requests - with no |pending_main_frame_url|. Allowed based
       // on the |last_committed_main_frame_url|.
@@ -587,12 +605,12 @@
        kDummyFrameRoutingId,
        FrameDataParams({kDummyFrameId, kDummyParentFrameId,
                         "http://google.com/xyz", base::nullopt}),
-       true},
+       true, rule_1_id},
       {"http://google.com/xyz", content::ResourceType::kScript,
        "http://google.com", kDummyFrameRoutingId,
        FrameDataParams({kDummyFrameId, kDummyParentFrameId, kAllowedPageURL,
                         base::nullopt}),
-       false},
+       false, base::nullopt},
 
       // Renderer requests with |pending_main_frame_url|. This only happens for
       // main frame subresource requests.
@@ -604,7 +622,7 @@
        FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
                         ExtensionApiFrameIdMap::kInvalidFrameId,
                         kAllowedPageURL, GURL("http://example.com/xyz")}),
-       true},
+       true, rule_1_id},
 
       // Here we'll determine |kAllowedPageURL| to be the main
       // frame url due to the origin.
@@ -613,7 +631,7 @@
        FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
                         ExtensionApiFrameIdMap::kInvalidFrameId,
                         kAllowedPageURL, GURL("http://yahoo.com/xyz")}),
-       false},
+       false, base::nullopt},
 
       // In these cases both |pending_main_frame_url| and
       // |last_committed_main_frame_url| will be tested since we won't be able
@@ -623,20 +641,20 @@
        FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
                         ExtensionApiFrameIdMap::kInvalidFrameId,
                         "http://google.com/abc", GURL(kAllowedPageURL)}),
-       false},
+       false, base::nullopt},
       {"http://example.com/script.js", content::ResourceType::kScript,
        base::nullopt, kDummyFrameRoutingId,
        FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
                         ExtensionApiFrameIdMap::kInvalidFrameId,
                         kAllowedPageURL, GURL("http://google.com/abc")}),
-       false},
+       false, base::nullopt},
       {"http://example.com/script.js", content::ResourceType::kScript,
        base::nullopt, kDummyFrameRoutingId,
        FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
                         ExtensionApiFrameIdMap::kInvalidFrameId,
                         "http://yahoo.com/abc",
                         GURL("http://yahoo.com/allow123")}),
-       true},
+       true, rule_1_id},
   };
 
   for (size_t i = 0; i < base::size(test_cases); ++i) {
@@ -667,9 +685,11 @@
 
     if (test_case.expect_blocked_with_allowed_pages) {
       ASSERT_EQ(1u, actions.size());
-      EXPECT_EQ(RequestAction(RequestActionType::BLOCK,
-                              last_loaded_extension()->id()),
-                actions[0]);
+      EXPECT_EQ(
+          RequestAction(RequestActionType::BLOCK, *test_case.matched_rule_id,
+                        dnr_api::SOURCE_TYPE_MANIFEST,
+                        last_loaded_extension()->id()),
+          actions[0]);
     } else {
       EXPECT_TRUE(actions.empty());
     }
@@ -759,7 +779,8 @@
     manager()->AddRuleset(redirect_extension_id, std::move(redirect_matcher),
                           URLPatternSet());
     for (const auto& test : cases) {
-      RequestAction redirect_action(RequestActionType::REDIRECT,
+      RequestAction redirect_action(RequestActionType::REDIRECT, kMinValidID,
+                                    dnr_api::SOURCE_TYPE_MANIFEST,
                                     redirect_extension_id);
       redirect_action.redirect_url = GURL("https://foo.com/");
 
@@ -776,7 +797,8 @@
     manager()->AddRuleset(blocking_extension_id, std::move(blocking_matcher),
                           URLPatternSet());
     for (const auto& test : cases) {
-      RequestAction block_action(RequestActionType::BLOCK,
+      RequestAction block_action(RequestActionType::BLOCK, kMinValidID,
+                                 dnr_api::SOURCE_TYPE_MANIFEST,
                                  blocking_extension_id);
 
       verify_test_case(
diff --git a/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc b/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc
index 4041ed42..f07480c 100644
--- a/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc
+++ b/chrome/browser/extensions/api/page_capture/page_capture_apitest.cc
@@ -5,6 +5,7 @@
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
 #include "chrome/browser/extensions/active_tab_permission_granter.h"
 #include "chrome/browser/extensions/api/page_capture/page_capture_api.h"
 #include "chrome/browser/extensions/extension_action_runner.h"
@@ -67,15 +68,21 @@
 
 // TODO(crbug.com/961017): Fix memory leaks in tests and re-enable on LSAN.
 #ifdef LEAK_SANITIZER
-#define MAYBE_SaveAsMHTML DISABLED_SaveAsMHTML
 #define MAYBE_SaveAsMHTMLWithActiveTabWithFileAccess \
   DISABLED_SaveAsMHTMLWithActiveTabWithFileAccess
 #else
-#define MAYBE_SaveAsMHTML SaveAsMHTML
 #define MAYBE_SaveAsMHTMLWithActiveTabWithFileAccess \
   SaveAsMHTMLWithActiveTabWithFileAccess
 #endif
 
+// TODO(crbug.com/961017): Fix memory leaks in tests and re-enable on LSAN.
+// Also flaky-failing on Linux Tests (dbg): https://crbug.com/1017305
+#if defined(LEAK_SANITIZER) || (defined(OS_LINUX) && !defined(NDEBUG))
+#define MAYBE_SaveAsMHTML DISABLED_SaveAsMHTML
+#else
+#define MAYBE_SaveAsMHTML SaveAsMHTML
+#endif
+
 IN_PROC_BROWSER_TEST_F(ExtensionPageCaptureApiTest, MAYBE_SaveAsMHTML) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   PageCaptureSaveAsMHTMLDelegate delegate;
diff --git a/chrome/browser/global_keyboard_shortcuts_mac.h b/chrome/browser/global_keyboard_shortcuts_mac.h
index 65ed981..084f41e4 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac.h
+++ b/chrome/browser/global_keyboard_shortcuts_mac.h
@@ -35,9 +35,6 @@
   // The command to execute. -1 if none was found.
   int chrome_command;
 
-  // The action, if any, which the key event would trigger.
-  SEL action;
-
   // Whether the command was from a mapping in the main menu. Only relevant if
   // command != -1.
   bool from_main_menu;
diff --git a/chrome/browser/global_keyboard_shortcuts_mac.mm b/chrome/browser/global_keyboard_shortcuts_mac.mm
index d52cf50b..ecbc85f 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac.mm
@@ -59,7 +59,7 @@
   return result;
 }
 
-int MenuCommandForKeyEvent(NSEvent* event, SEL* action) {
+int MenuCommandForKeyEvent(NSEvent* event) {
   if ([event type] != NSKeyDown)
     return -1;
 
@@ -78,10 +78,8 @@
   if (!item)
     return -1;
 
-  if ([item action] == @selector(commandDispatch:) && [item tag] > 0) {
-    *action = [item action];
+  if ([item action] == @selector(commandDispatch:) && [item tag] > 0)
     return [item tag];
-  }
 
   // "Close window" doesn't use the |commandDispatch:| mechanism. Menu items
   // that do not correspond to IDC_ constants need no special treatment however,
@@ -120,15 +118,15 @@
 }
 
 CommandForKeyEventResult NoCommand() {
-  return {-1, nil, /*from_main_menu=*/false};
+  return {-1, /*from_main_menu=*/false};
 }
 
-CommandForKeyEventResult MainMenuCommand(int cmd, SEL action) {
-  return {cmd, action, /*from_main_menu=*/true};
+CommandForKeyEventResult MainMenuCommand(int cmd) {
+  return {cmd, /*from_main_menu=*/true};
 }
 
 CommandForKeyEventResult ShortcutCommand(int cmd) {
-  return {cmd, nil, /*from_main_menu=*/false};
+  return {cmd, /*from_main_menu=*/false};
 }
 
 }  // namespace
@@ -205,10 +203,9 @@
   if ([event type] != NSKeyDown)
     return NoCommand();
 
-  SEL action;
-  int cmdNum = MenuCommandForKeyEvent(event, &action);
+  int cmdNum = MenuCommandForKeyEvent(event);
   if (cmdNum != -1)
-    return MainMenuCommand(cmdNum, action);
+    return MainMenuCommand(cmdNum);
 
   // Scan through keycodes and see if it corresponds to one of the non-menu
   // shortcuts.
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
index 699043a..d50993a1 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.cc
@@ -240,34 +240,4 @@
   std::move(callback).Run(allow && web_view_guest()->attached());
 }
 
-void ChromeWebViewPermissionHelperDelegate::FileSystemAccessedAsync(
-    int render_process_id,
-    int render_frame_id,
-    int request_id,
-    const GURL& url,
-    bool blocked_by_policy) {
-  RequestFileSystemPermission(
-      url, !blocked_by_policy,
-      base::BindOnce(&ChromeWebViewPermissionHelperDelegate::
-                         FileSystemAccessedAsyncResponse,
-                     weak_factory_.GetWeakPtr(), render_process_id,
-                     render_frame_id, request_id, url));
-}
-
-void ChromeWebViewPermissionHelperDelegate::FileSystemAccessedAsyncResponse(
-    int render_process_id,
-    int render_frame_id,
-    int request_id,
-    const GURL& url,
-    bool allowed) {
-  TabSpecificContentSettings::FileSystemAccessed(
-      render_process_id, render_frame_id, url, !allowed);
-  content::RenderFrameHost* rfh =
-      content::RenderFrameHost::FromID(render_process_id, render_frame_id);
-  if (rfh) {
-    rfh->Send(new ChromeViewMsg_RequestFileSystemAccessAsyncResponse(
-        render_frame_id, request_id, allowed));
-  }
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
index 72c8659..29360150 100644
--- a/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
+++ b/chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h
@@ -44,11 +44,6 @@
       const GURL& url,
       bool allowed_by_default,
       base::OnceCallback<void(bool)> callback) override;
-  void FileSystemAccessedAsync(int render_process_id,
-                               int render_frame_id,
-                               int request_id,
-                               const GURL& url,
-                               bool blocked_by_policy) override;
 #if BUILDFLAG(ENABLE_PLUGINS)
   // content::WebContentsObserver implementation.
   bool OnMessageReceived(const IPC::Message& message,
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index 2813526..517a40d 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -417,6 +417,8 @@
       &settings_for_legacy_cookie_access);
   out->settings_for_legacy_cookie_access =
       std::move(settings_for_legacy_cookie_access);
+  out->cookie_access_delegate_type =
+      network::mojom::CookieAccessDelegateType::USE_CONTENT_SETTINGS;
   return out;
 }
 
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index cb0bf49..db52048d 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -676,9 +676,8 @@
   // The OSCrypt keys are process bound, so if network service is out of
   // process, send it the required key.
   if (content::IsOutOfProcessNetworkService()) {
-    std::string key = OSCrypt::GetRawEncryptionKey();
-    DCHECK(!key.empty());
-    content::GetNetworkService()->SetEncryptionKey(key);
+    content::GetNetworkService()->SetEncryptionKey(
+        OSCrypt::GetRawEncryptionKey());
   }
 #endif
 
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 835535e..d070b49 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -155,8 +155,8 @@
   // added, the observer is removed and added again, to ensure that it is added
   // only once.
 #if defined(OS_ANDROID)
-  widget_host->RemoveImeTextCommittedEventObserver(observer);
-  widget_host->AddImeTextCommittedEventObserver(observer);
+  widget_host->RemoveImeInputEventObserver(observer);
+  widget_host->AddImeInputEventObserver(observer);
 #endif
   widget_host->RemoveInputEventObserver(observer);
   widget_host->AddInputEventObserver(observer);
@@ -662,7 +662,23 @@
 void ChromePasswordManagerClient::OnImeTextCommittedEvent(
     const base::string16& text_str) {
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-  password_reuse_detection_manager_.OnKeyPressed(text_str);
+  password_reuse_detection_manager_.OnKeyPressedCommitted(text_str);
+#endif  // defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+}
+
+void ChromePasswordManagerClient::OnImeSetComposingTextEvent(
+    const base::string16& text_str) {
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+  last_composing_text_ = text_str;
+  password_reuse_detection_manager_.OnKeyPressedUncommitted(
+      last_composing_text_);
+#endif  // defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+}
+
+void ChromePasswordManagerClient::OnImeFinishComposingTextEvent() {
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+  password_reuse_detection_manager_.OnKeyPressedCommitted(last_composing_text_);
+  last_composing_text_.clear();
 #endif  // defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 }
 #endif  // defined(OS_ANDROID)
@@ -679,10 +695,10 @@
     return;
   const blink::WebKeyboardEvent& key_event =
       static_cast<const blink::WebKeyboardEvent&>(event);
-  password_reuse_detection_manager_.OnKeyPressed(key_event.text);
+  password_reuse_detection_manager_.OnKeyPressedCommitted(key_event.text);
 #endif  // defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
 
-#else   // defined(OS_ANDROID)
+#else   // !defined(OS_ANDROID)
   if (event.GetType() != blink::WebInputEvent::kChar)
     return;
   const blink::WebKeyboardEvent& key_event =
@@ -692,7 +708,7 @@
   if (key_event.windows_key_code == (ui::VKEY_V & 0x1f)) {
     OnPaste();
   } else {
-    password_reuse_detection_manager_.OnKeyPressed(key_event.text);
+    password_reuse_detection_manager_.OnKeyPressedCommitted(key_event.text);
   }
 #endif  // defined(OS_ANDROID)
 }
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.h b/chrome/browser/password_manager/chrome_password_manager_client.h
index 5ecfd06..5b51527 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.h
+++ b/chrome/browser/password_manager/chrome_password_manager_client.h
@@ -179,6 +179,8 @@
           saving_flow_recorder);
 
   void OnImeTextCommittedEvent(const base::string16& text_str) override;
+  void OnImeSetComposingTextEvent(const base::string16& text_str) override;
+  void OnImeFinishComposingTextEvent() override;
 #endif  // defined(OS_ANDROID)
 
 #if defined(ON_FOCUS_PING_ENABLED)
@@ -309,6 +311,11 @@
   // Controller for the Touch To Fill sheet. Created on demand during the first
   // call to GetOrCreateTouchToFillController().
   std::unique_ptr<TouchToFillController> touch_to_fill_controller_;
+
+  // Last composing text from ime, this is updated when ime set composing text
+  // event is triggered. It is sent to password reuse detection manager and
+  // reset when ime finish composing text event is triggered.
+  base::string16 last_composing_text_;
 #endif
 
   password_manager::ContentPasswordManagerDriverFactory* driver_factory_;
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 48ff19e..3614088 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -226,7 +226,8 @@
 
 // Generate HTML for a simple password form with the specified action URL.
 std::string GeneratePasswordFormForAction(const GURL& action_url) {
-  return "<form method='POST' action='" + action_url.spec() + "'"
+  return "<form method='POST' action='" + action_url.spec() +
+         "'"
          "      onsubmit='return true;' id='testform'>"
          "  <input type='password' id='password_field'>"
          "</form>";
@@ -241,7 +242,8 @@
       "var frame = document.createElement('iframe');"
       "frame.id = 'iframe';"
       "document.body.appendChild(frame);"
-      "frame.contentDocument.body.innerHTML = \"" + form_html + "\"";
+      "frame.contentDocument.body.innerHTML = \"" +
+      form_html + "\"";
   ASSERT_TRUE(content::ExecuteScript(web_contents,
                                      inject_blank_frame_with_password_form));
 }
@@ -291,6 +293,22 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
+                       PromptIfChangePasswordFormReappearedEmpty) {
+  NavigateToFile("/password/update_form_empty_fields.html");
+  // Fill a form and submit through a <input type="submit"> button. Nothing
+  // special.
+  NavigationObserver observer(WebContents());
+  std::string fill_and_submit =
+      "document.getElementById('password').value = 'old_pass';"
+      "document.getElementById('new_password_1').value = 'new_pass';"
+      "document.getElementById('new_password_2').value = 'new_pass';"
+      "document.getElementById('chg_submit_wo_username_button').click()";
+  ASSERT_TRUE(content::ExecuteScript(WebContents(), fill_and_submit));
+  observer.Wait();
+  EXPECT_TRUE(BubbleObserver(WebContents()).IsSavePromptShownAutomatically());
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
                        NoPromptIfFormReappearedWithPartsHidden) {
   NavigateToFile("/password/failed_partly_visible.html");
   TestPromptNotShown("partly visible form", WebContents());
@@ -304,25 +322,25 @@
 
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
                        NoPromptAfterCredentialsAPIPasswordStore) {
-    NavigateToFile("/password/password_form.html");
-    // Simulate the Credential Manager API function store() is called and
-    // PasswordManager instance is notified about that.
-    ChromePasswordManagerClient::FromWebContents(WebContents())
-        ->NotifyStorePasswordCalled();
+  NavigateToFile("/password/password_form.html");
+  // Simulate the Credential Manager API function store() is called and
+  // PasswordManager instance is notified about that.
+  ChromePasswordManagerClient::FromWebContents(WebContents())
+      ->NotifyStorePasswordCalled();
 
-    // Fill a form and submit through a <input type="submit"> button. The
-    // renderer should not send "PasswordFormsParsed" messages after the page
-    // was loaded.
-    NavigationObserver observer(WebContents());
-    std::string fill_and_submit =
-        "document.getElementById('username_field').value = 'temp';"
-        "document.getElementById('password_field').value = 'random';"
-        "document.getElementById('input_submit_button').click()";
-    ASSERT_TRUE(content::ExecuteScript(WebContents(), fill_and_submit));
-    observer.Wait();
-    std::unique_ptr<BubbleObserver> prompt_observer(
-        new BubbleObserver(WebContents()));
-    EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically());
+  // Fill a form and submit through a <input type="submit"> button. The
+  // renderer should not send "PasswordFormsParsed" messages after the page
+  // was loaded.
+  NavigationObserver observer(WebContents());
+  std::string fill_and_submit =
+      "document.getElementById('username_field').value = 'temp';"
+      "document.getElementById('password_field').value = 'random';"
+      "document.getElementById('input_submit_button').click()";
+  ASSERT_TRUE(content::ExecuteScript(WebContents(), fill_and_submit));
+  observer.Wait();
+  std::unique_ptr<BubbleObserver> prompt_observer(
+      new BubbleObserver(WebContents()));
+  EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically());
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
@@ -1485,71 +1503,70 @@
 IN_PROC_BROWSER_TEST_F(
     PasswordManagerBrowserTest,
     NoPromptForSeperateLoginFormWhenSwitchingFromHttpsToHttp) {
-    std::string path = "/password/password_form.html";
-    GURL https_url(https_test_server().GetURL(path));
-    ASSERT_TRUE(https_url.SchemeIs(url::kHttpsScheme));
+  std::string path = "/password/password_form.html";
+  GURL https_url(https_test_server().GetURL(path));
+  ASSERT_TRUE(https_url.SchemeIs(url::kHttpsScheme));
 
-    NavigationObserver form_observer(WebContents());
-    ui_test_utils::NavigateToURL(browser(), https_url);
-    form_observer.Wait();
+  NavigationObserver form_observer(WebContents());
+  ui_test_utils::NavigateToURL(browser(), https_url);
+  form_observer.Wait();
 
-    std::string fill_and_submit_redirect =
-        "document.getElementById('username_redirect').value = 'user';"
-        "document.getElementById('password_redirect').value = 'password';"
-        "document.getElementById('submit_redirect').click()";
-    ASSERT_TRUE(
-        content::ExecuteScript(WebContents(), fill_and_submit_redirect));
+  std::string fill_and_submit_redirect =
+      "document.getElementById('username_redirect').value = 'user';"
+      "document.getElementById('password_redirect').value = 'password';"
+      "document.getElementById('submit_redirect').click()";
+  ASSERT_TRUE(content::ExecuteScript(WebContents(), fill_and_submit_redirect));
 
-    NavigationObserver redirect_observer(WebContents());
-    redirect_observer.SetPathToWaitFor("/password/redirect.html");
-    redirect_observer.Wait();
+  NavigationObserver redirect_observer(WebContents());
+  redirect_observer.SetPathToWaitFor("/password/redirect.html");
+  redirect_observer.Wait();
 
-    WaitForPasswordStore();
-    BubbleObserver prompt_observer(WebContents());
-    EXPECT_TRUE(prompt_observer.IsSavePromptShownAutomatically());
+  WaitForPasswordStore();
+  BubbleObserver prompt_observer(WebContents());
+  EXPECT_TRUE(prompt_observer.IsSavePromptShownAutomatically());
 
-    // Normally the redirect happens to done.html. Here an attack is simulated
-    // that hijacks the redirect to a attacker controlled page.
-    GURL http_url(
-        embedded_test_server()->GetURL("/password/simple_password.html"));
-    std::string attacker_redirect =
-        "window.location.href = '" + http_url.spec() + "';";
-    ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture(RenderFrameHost(),
-                                                         attacker_redirect));
+  // Normally the redirect happens to done.html. Here an attack is simulated
+  // that hijacks the redirect to a attacker controlled page.
+  GURL http_url(
+      embedded_test_server()->GetURL("/password/simple_password.html"));
+  std::string attacker_redirect =
+      "window.location.href = '" + http_url.spec() + "';";
+  ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture(RenderFrameHost(),
+                                                       attacker_redirect));
 
-    NavigationObserver attacker_observer(WebContents());
-    attacker_observer.SetPathToWaitFor("/password/simple_password.html");
-    attacker_observer.Wait();
+  NavigationObserver attacker_observer(WebContents());
+  attacker_observer.SetPathToWaitFor("/password/simple_password.html");
+  attacker_observer.Wait();
 
-    EXPECT_TRUE(prompt_observer.IsSavePromptShownAutomatically());
+  EXPECT_TRUE(prompt_observer.IsSavePromptShownAutomatically());
 
-    std::string fill_and_submit_attacker_form =
-        "document.getElementById('username_field').value = 'attacker_username';"
-        "document.getElementById('password_field').value = 'attacker_password';"
-        "document.getElementById('input_submit_button').click()";
-    ASSERT_TRUE(
-        content::ExecuteScript(WebContents(), fill_and_submit_attacker_form));
+  std::string fill_and_submit_attacker_form =
+      "document.getElementById('username_field').value = 'attacker_username';"
+      "document.getElementById('password_field').value = 'attacker_password';"
+      "document.getElementById('input_submit_button').click()";
+  ASSERT_TRUE(
+      content::ExecuteScript(WebContents(), fill_and_submit_attacker_form));
 
-    NavigationObserver done_observer(WebContents());
-    done_observer.SetPathToWaitFor("/password/done.html");
-    done_observer.Wait();
+  NavigationObserver done_observer(WebContents());
+  done_observer.SetPathToWaitFor("/password/done.html");
+  done_observer.Wait();
 
-    EXPECT_TRUE(prompt_observer.IsSavePromptShownAutomatically());
-    prompt_observer.AcceptSavePrompt();
+  EXPECT_TRUE(prompt_observer.IsSavePromptShownAutomatically());
+  prompt_observer.AcceptSavePrompt();
 
-    // Wait for password store and check that credentials are stored.
-    WaitForPasswordStore();
-    CheckThatCredentialsStored("user", "password");
+  // Wait for password store and check that credentials are stored.
+  WaitForPasswordStore();
+  CheckThatCredentialsStored("user", "password");
 
-    // Password store clearing is required because there are 2 test iterations.
-    // TODO(https://crbug.com/831123): Remove store clearing when the old parser
-    // is gone and there is only one iteration in this test.
-    scoped_refptr<password_manager::TestPasswordStore> password_store =
-        static_cast<password_manager::TestPasswordStore*>(
-            PasswordStoreFactory::GetForProfile(
-                browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS)
-                .get());
-    password_store->Clear();
+  // Password store clearing is required because there are 2 test iterations.
+  // TODO(https://crbug.com/831123): Remove store clearing when the old parser
+  // is gone and there is only one iteration in this test.
+  scoped_refptr<password_manager::TestPasswordStore> password_store =
+      static_cast<password_manager::TestPasswordStore*>(
+          PasswordStoreFactory::GetForProfile(
+              browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS)
+              .get());
+  password_store->Clear();
 }
 
 // Tests that after HTTP -> HTTPS migration the credential is autofilled.
@@ -2005,34 +2022,34 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptOnBack) {
-    // Go to a successful landing page through submitting first, so that it is
-    // reachable through going back, and the remembered page transition is form
-    // submit. There is no need to submit non-empty strings.
-    NavigateToFile("/password/password_form.html");
+  // Go to a successful landing page through submitting first, so that it is
+  // reachable through going back, and the remembered page transition is form
+  // submit. There is no need to submit non-empty strings.
+  NavigateToFile("/password/password_form.html");
 
-    NavigationObserver dummy_submit_observer(WebContents());
-    std::string just_submit =
-        "document.getElementById('input_submit_button').click()";
-    ASSERT_TRUE(content::ExecuteScript(WebContents(), just_submit));
-    dummy_submit_observer.Wait();
+  NavigationObserver dummy_submit_observer(WebContents());
+  std::string just_submit =
+      "document.getElementById('input_submit_button').click()";
+  ASSERT_TRUE(content::ExecuteScript(WebContents(), just_submit));
+  dummy_submit_observer.Wait();
 
-    // Now go to a page with a form again, fill the form, and go back instead of
-    // submitting it.
-    NavigateToFile("/password/dummy_submit.html");
+  // Now go to a page with a form again, fill the form, and go back instead of
+  // submitting it.
+  NavigateToFile("/password/dummy_submit.html");
 
-    NavigationObserver observer(WebContents());
-    BubbleObserver prompt_observer(WebContents());
-    // The (dummy) submit is necessary to provisionally save the typed password.
-    // A user typing in the password field would not need to submit to
-    // provisionally save it, but the script cannot trigger that just by
-    // assigning to the field's value.
-    std::string fill_and_back =
-        "document.getElementById('password_field').value = 'random';"
-        "document.getElementById('input_submit_button').click();"
-        "window.history.back();";
-    ASSERT_TRUE(content::ExecuteScript(WebContents(), fill_and_back));
-    observer.Wait();
-    EXPECT_FALSE(prompt_observer.IsSavePromptShownAutomatically());
+  NavigationObserver observer(WebContents());
+  BubbleObserver prompt_observer(WebContents());
+  // The (dummy) submit is necessary to provisionally save the typed password.
+  // A user typing in the password field would not need to submit to
+  // provisionally save it, but the script cannot trigger that just by
+  // assigning to the field's value.
+  std::string fill_and_back =
+      "document.getElementById('password_field').value = 'random';"
+      "document.getElementById('input_submit_button').click();"
+      "window.history.back();";
+  ASSERT_TRUE(content::ExecuteScript(WebContents(), fill_and_back));
+  observer.Wait();
+  EXPECT_FALSE(prompt_observer.IsSavePromptShownAutomatically());
 }
 
 // Regression test for http://crbug.com/452306
@@ -3408,7 +3425,8 @@
   std::string form_html = GeneratePasswordFormForAction(submit_url);
   std::string open_blank_popup_with_password_form =
       "var w = window.open('about:blank');"
-      "w.document.body.innerHTML = \"" + form_html + "\";";
+      "w.document.body.innerHTML = \"" +
+      form_html + "\";";
   ASSERT_TRUE(content::ExecuteScript(WebContents(),
                                      open_blank_popup_with_password_form));
   tab_add.Wait();
@@ -3504,7 +3522,9 @@
   std::string form_html = GeneratePasswordFormForAction(submit_url);
   std::string inject_data_frame_with_password_form =
       "var frame = document.createElement('iframe');\n"
-      "frame.src = \"data:text/html," + form_html + "\";\n"
+      "frame.src = \"data:text/html," +
+      form_html +
+      "\";\n"
       "document.body.appendChild(frame);\n";
   ASSERT_TRUE(content::ExecuteScript(WebContents(),
                                      inject_data_frame_with_password_form));
@@ -3552,48 +3572,48 @@
 // password manager works even though it should be disabled on the previous
 // page.
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, CorrectEntryForHttpAuth) {
-    // The embedded_test_server() is already started at this point and adding
-    // the request handler to it would not be thread safe. Therefore, use a new
-    // server.
-    net::EmbeddedTestServer http_test_server;
+  // The embedded_test_server() is already started at this point and adding
+  // the request handler to it would not be thread safe. Therefore, use a new
+  // server.
+  net::EmbeddedTestServer http_test_server;
 
-    // Teach the embedded server to handle requests by issuing the basic auth
-    // challenge.
-    http_test_server.RegisterRequestHandler(base::Bind(&HandleTestAuthRequest));
-    ASSERT_TRUE(http_test_server.Start());
+  // Teach the embedded server to handle requests by issuing the basic auth
+  // challenge.
+  http_test_server.RegisterRequestHandler(base::Bind(&HandleTestAuthRequest));
+  ASSERT_TRUE(http_test_server.Start());
 
-    LoginPromptBrowserTestObserver login_observer;
-    login_observer.Register(content::Source<content::NavigationController>(
-        &WebContents()->GetController()));
+  LoginPromptBrowserTestObserver login_observer;
+  login_observer.Register(content::Source<content::NavigationController>(
+      &WebContents()->GetController()));
 
-    // Navigate to about:blank first. This is a page where password manager
-    // should not work.
-    ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+  // Navigate to about:blank first. This is a page where password manager
+  // should not work.
+  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
 
-    content::NavigationController* nav_controller =
-        &WebContents()->GetController();
-    WindowedAuthNeededObserver auth_needed_observer(nav_controller);
-    // Navigate to a page requiring HTTP auth
-    ui_test_utils::NavigateToURL(browser(),
-                                 http_test_server.GetURL("/basic_auth"));
+  content::NavigationController* nav_controller =
+      &WebContents()->GetController();
+  WindowedAuthNeededObserver auth_needed_observer(nav_controller);
+  // Navigate to a page requiring HTTP auth
+  ui_test_utils::NavigateToURL(browser(),
+                               http_test_server.GetURL("/basic_auth"));
 
-    auth_needed_observer.Wait();
+  auth_needed_observer.Wait();
 
-    NavigationObserver nav_observer(WebContents());
-    WindowedAuthSuppliedObserver auth_supplied_observer(nav_controller);
-    // Offer valid credentials on the auth challenge.
-    ASSERT_EQ(1u, login_observer.handlers().size());
-    LoginHandler* handler = *login_observer.handlers().begin();
-    ASSERT_TRUE(handler);
-    // Any username/password will work.
-    handler->SetAuth(base::UTF8ToUTF16("user"), base::UTF8ToUTF16("pwd"));
-    auth_supplied_observer.Wait();
+  NavigationObserver nav_observer(WebContents());
+  WindowedAuthSuppliedObserver auth_supplied_observer(nav_controller);
+  // Offer valid credentials on the auth challenge.
+  ASSERT_EQ(1u, login_observer.handlers().size());
+  LoginHandler* handler = *login_observer.handlers().begin();
+  ASSERT_TRUE(handler);
+  // Any username/password will work.
+  handler->SetAuth(base::UTF8ToUTF16("user"), base::UTF8ToUTF16("pwd"));
+  auth_supplied_observer.Wait();
 
-    // The password manager should be working correctly.
-    nav_observer.Wait();
-    WaitForPasswordStore();
-    BubbleObserver bubble_observer(WebContents());
-    EXPECT_TRUE(bubble_observer.IsSavePromptShownAutomatically());
+  // The password manager should be working correctly.
+  nav_observer.Wait();
+  WaitForPasswordStore();
+  BubbleObserver bubble_observer(WebContents());
+  EXPECT_TRUE(bubble_observer.IsSavePromptShownAutomatically());
 }
 
 // Test that if HTTP auth login (i.e., credentials not put through web forms)
diff --git a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
index b893e48..145cb43 100644
--- a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
+++ b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc
@@ -19,7 +19,7 @@
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h"
 #include "extensions/common/extension.h"
 #include "mojo/public/cpp/system/data_pipe.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 
 PluginResponseInterceptorURLLoaderThrottle::
     PluginResponseInterceptorURLLoaderThrottle(int resource_type,
@@ -31,7 +31,7 @@
 
 void PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (content::download_utils::MustDownload(response_url,
@@ -90,10 +90,13 @@
                                std::move(new_client_request), &original_loader,
                                &original_client);
 
-  // Make a deep copy of ResourceResponseHead before passing it cross-thread.
-  auto resource_response = base::MakeRefCounted<network::ResourceResponse>();
-  resource_response->head = *response_head;
-  auto deep_copied_response = resource_response->DeepCopy();
+  // Make a deep copy of URLResponseHead before passing it cross-thread.
+  auto deep_copied_response = response_head->Clone();
+  if (response_head->headers) {
+    deep_copied_response->headers =
+        base::MakeRefCounted<net::HttpResponseHeaders>(
+            response_head->headers->raw_headers());
+  }
 
   auto transferrable_loader = content::mojom::TransferrableURLLoader::New();
   transferrable_loader->url = GURL(
@@ -101,7 +104,7 @@
       base::GenerateGUID());
   transferrable_loader->url_loader = original_loader.PassInterface();
   transferrable_loader->url_loader_client = std::move(original_client);
-  transferrable_loader->head = std::move(deep_copied_response->head);
+  transferrable_loader->head = std::move(deep_copied_response);
   transferrable_loader->head->intercepted_by_plugin = true;
 
   bool embedded =
diff --git a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h
index 235518e0..f495a44e 100644
--- a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h
+++ b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h
@@ -30,7 +30,7 @@
  private:
   // blink::URLLoaderThrottle overrides;
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override;
   // Resumes loading for an intercepted response. This would give the extension
   // layer chance to initialize its browser side state.
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index d24d8e2..ccebf05 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1218,6 +1218,11 @@
   { key::kCorsLegacyModeEnabled,
     prefs::kCorsLegacyModeEnabled,
     base::Value::Type::BOOLEAN },
+#if !defined(OS_CHROMEOS) && BUILDFLAG(ENABLE_EXTENSIONS)
+  { key::kBlockExternalExtensions,
+    extensions::pref_names::kBlockExternalExtensions,
+    base::Value::Type::BOOLEAN },
+#endif
 };
 // clang-format on
 
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index 547fbec..a1652a2 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -13,69 +13,25 @@
 #include "base/logging.h"
 #include "base/stl_util.h"
 #include "base/task/post_task.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/content_settings/cookie_settings_factory.h"
-#include "chrome/browser/content_settings/tab_specific_content_settings.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/predictors/loading_predictor.h"
 #include "chrome/browser/predictors/loading_predictor_factory.h"
 #include "chrome/browser/predictors/preconnect_manager.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/render_messages.h"
-#include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/network_hints/common/network_hints_common.h"
 #include "components/network_hints/common/network_hints_messages.h"
-#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
-#include "components/web_cache/browser/web_cache_manager.h"
 #include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/buildflags/buildflags.h"
 #include "net/base/network_isolation_key.h"
 #include "ppapi/buildflags/buildflags.h"
-#include "url/origin.h"
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "extensions/browser/guest_view/web_view/web_view_permission_helper.h"
-#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
-#include "extensions/common/manifest_handlers/default_locale_handler.h"
-#endif
 
 using content::BrowserThread;
 
 namespace {
 
-void OnDomStorageAccessedUI(int process_id,
-                            int routing_id,
-                            const GURL& origin_url,
-                            const GURL& top_origin_url,
-                            bool local,
-                            bool blocked_by_policy) {
-  content::RenderFrameHost* frame =
-      content::RenderFrameHost::FromID(process_id, routing_id);
-  content::WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(frame);
-
-  if (!web_contents)
-    return;
-
-  TabSpecificContentSettings* tab_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
-  if (tab_settings)
-    tab_settings->OnDomStorageAccessed(origin_url, local, blocked_by_policy);
-
-  page_load_metrics::MetricsWebContentsObserver* metrics_observer =
-      page_load_metrics::MetricsWebContentsObserver::FromWebContents(
-          web_contents);
-  if (metrics_observer)
-    metrics_observer->OnDomStorageAccessed(origin_url, top_origin_url, local,
-                                           blocked_by_policy);
-}
-
 const uint32_t kRenderFilteredMessageClasses[] = {
     ChromeMsgStart, NetworkHintsMsgStart,
 };
@@ -115,8 +71,7 @@
     : BrowserMessageFilter(kRenderFilteredMessageClasses,
                            base::size(kRenderFilteredMessageClasses)),
       render_process_id_(render_process_id),
-      preconnect_manager_initialized_(false),
-      cookie_settings_(CookieSettingsFactory::GetForProfile(profile)) {
+      preconnect_manager_initialized_(false) {
   auto* loading_predictor =
       predictors::LoadingPredictorFactory::GetForProfile(profile);
   if (loading_predictor && loading_predictor->preconnect_manager()) {
@@ -133,16 +88,6 @@
   IPC_BEGIN_MESSAGE_MAP(ChromeRenderMessageFilter, message)
     IPC_MESSAGE_HANDLER(NetworkHintsMsg_DNSPrefetch, OnDnsPrefetch)
     IPC_MESSAGE_HANDLER(NetworkHintsMsg_Preconnect, OnPreconnect)
-    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_AllowDatabase, OnAllowDatabase)
-    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_AllowDOMStorage, OnAllowDOMStorage)
-    IPC_MESSAGE_HANDLER_DELAY_REPLY(
-        ChromeViewHostMsg_RequestFileSystemAccessSync,
-        OnRequestFileSystemAccessSync)
-    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RequestFileSystemAccessAsync,
-                        OnRequestFileSystemAccessAsync)
-    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_AllowIndexedDB, OnAllowIndexedDB)
-    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_AllowCacheStorage,
-                        OnAllowCacheStorage)
 #if BUILDFLAG(ENABLE_PLUGINS)
     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_IsCrashReportingEnabled,
                         OnIsCrashReportingEnabled)
@@ -202,176 +147,6 @@
                      render_frame_id, url, allow_credentials));
 }
 
-void ChromeRenderMessageFilter::OnAllowDatabase(
-    int render_frame_id,
-    const url::Origin& origin,
-    const GURL& site_for_cookies,
-    const url::Origin& top_frame_origin,
-    bool* allowed) {
-  *allowed = cookie_settings_->IsCookieAccessAllowed(
-      origin.GetURL(), site_for_cookies, top_frame_origin);
-  base::PostTask(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&TabSpecificContentSettings::WebDatabaseAccessed,
-                     render_process_id_, render_frame_id, origin.GetURL(),
-                     !*allowed));
-}
-
-void ChromeRenderMessageFilter::OnAllowDOMStorage(
-    int render_frame_id,
-    const url::Origin& origin,
-    const GURL& site_for_cookies,
-    const url::Origin& top_frame_origin,
-    bool local,
-    bool* allowed) {
-  GURL url = origin.GetURL();
-  *allowed = cookie_settings_->IsCookieAccessAllowed(url, site_for_cookies,
-                                                     top_frame_origin);
-  // Record access to DOM storage for potential display in UI.
-  base::PostTask(FROM_HERE, {BrowserThread::UI},
-                 base::BindOnce(&OnDomStorageAccessedUI, render_process_id_,
-                                render_frame_id, url, top_frame_origin.GetURL(),
-                                local, !*allowed));
-}
-
-void ChromeRenderMessageFilter::OnRequestFileSystemAccessSync(
-    int render_frame_id,
-    const url::Origin& origin,
-    const GURL& site_for_cookies,
-    const url::Origin& top_frame_origin,
-    IPC::Message* reply_msg) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  base::Callback<void(bool)> callback = base::Bind(
-      &ChromeRenderMessageFilter::OnRequestFileSystemAccessSyncResponse,
-      base::WrapRefCounted(this), reply_msg);
-  OnRequestFileSystemAccess(render_frame_id, origin, site_for_cookies,
-                            top_frame_origin, callback);
-}
-
-void ChromeRenderMessageFilter::OnRequestFileSystemAccessSyncResponse(
-    IPC::Message* reply_msg,
-    bool allowed) {
-  ChromeViewHostMsg_RequestFileSystemAccessSync::WriteReplyParams(reply_msg,
-                                                                  allowed);
-  Send(reply_msg);
-}
-
-void ChromeRenderMessageFilter::OnRequestFileSystemAccessAsync(
-    int render_frame_id,
-    int request_id,
-    const url::Origin& origin,
-    const GURL& site_for_cookies,
-    const url::Origin& top_frame_origin) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  base::Callback<void(bool)> callback = base::Bind(
-      &ChromeRenderMessageFilter::OnRequestFileSystemAccessAsyncResponse,
-      base::WrapRefCounted(this), render_frame_id, request_id);
-  OnRequestFileSystemAccess(render_frame_id, origin, site_for_cookies,
-                            top_frame_origin, callback);
-}
-
-void ChromeRenderMessageFilter::OnRequestFileSystemAccessAsyncResponse(
-    int render_frame_id,
-    int request_id,
-    bool allowed) {
-  Send(new ChromeViewMsg_RequestFileSystemAccessAsyncResponse(
-      render_frame_id, request_id, allowed));
-}
-
-void ChromeRenderMessageFilter::OnRequestFileSystemAccess(
-    int render_frame_id,
-    const url::Origin& origin,
-    const GURL& site_for_cookies,
-    const url::Origin& top_frame_origin,
-    base::Callback<void(bool)> callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  bool allowed = cookie_settings_->IsCookieAccessAllowed(
-      origin.GetURL(), site_for_cookies, top_frame_origin);
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  bool is_web_view_guest = extensions::WebViewRendererState::GetInstance()
-      ->IsGuest(render_process_id_);
-  if (is_web_view_guest) {
-    // Record access to file system for potential display in UI.
-    base::PostTask(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&ChromeRenderMessageFilter::FileSystemAccessedOnUIThread,
-                       render_process_id_, render_frame_id, origin.GetURL(),
-                       allowed, callback));
-    return;
-  }
-#endif
-  callback.Run(allowed);
-  // Record access to file system for potential display in UI.
-  base::PostTask(FROM_HERE, {BrowserThread::UI},
-                 base::BindOnce(&TabSpecificContentSettings::FileSystemAccessed,
-                                render_process_id_, render_frame_id,
-                                origin.GetURL(), !allowed));
-}
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-void ChromeRenderMessageFilter::FileSystemAccessedOnUIThread(
-    int render_process_id,
-    int render_frame_id,
-    const GURL& url,
-    bool allowed,
-    base::Callback<void(bool)> callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  extensions::WebViewPermissionHelper* web_view_permission_helper =
-      extensions::WebViewPermissionHelper::FromFrameID(
-          render_process_id, render_frame_id);
-  // Between the time the permission request is made and the time it is handled
-  // by the UI thread, the extensions::WebViewPermissionHelper might be gone.
-  if (!web_view_permission_helper)
-    return;
-  web_view_permission_helper->RequestFileSystemPermission(
-      url, allowed,
-      base::BindOnce(&ChromeRenderMessageFilter::FileSystemAccessedResponse,
-                     render_process_id, render_frame_id, url, callback));
-}
-
-void ChromeRenderMessageFilter::FileSystemAccessedResponse(
-    int render_process_id,
-    int render_frame_id,
-    const GURL& url,
-    base::Callback<void(bool)> callback,
-    bool allowed) {
-  TabSpecificContentSettings::FileSystemAccessed(
-      render_process_id, render_frame_id, url, !allowed);
-  callback.Run(allowed);
-}
-#endif
-
-void ChromeRenderMessageFilter::OnAllowIndexedDB(
-    int render_frame_id,
-    const url::Origin& origin,
-    const GURL& site_for_cookies,
-    const url::Origin& top_frame_origin,
-    bool* allowed) {
-  *allowed = cookie_settings_->IsCookieAccessAllowed(
-      origin.GetURL(), site_for_cookies, top_frame_origin);
-  base::PostTask(FROM_HERE, {BrowserThread::UI},
-                 base::BindOnce(&TabSpecificContentSettings::IndexedDBAccessed,
-                                render_process_id_, render_frame_id,
-                                origin.GetURL(), !*allowed));
-}
-
-void ChromeRenderMessageFilter::OnAllowCacheStorage(
-    int render_frame_id,
-    const url::Origin& origin,
-    const GURL& site_for_cookies,
-    const url::Origin& top_frame_origin,
-    bool* allowed) {
-  GURL url = origin.GetURL();
-  *allowed = cookie_settings_->IsCookieAccessAllowed(url, site_for_cookies,
-                                                     top_frame_origin);
-  base::PostTask(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&TabSpecificContentSettings::CacheStorageAccessed,
-                     render_process_id_, render_frame_id, url, !*allowed));
-}
-
 #if BUILDFLAG(ENABLE_PLUGINS)
 void ChromeRenderMessageFilter::OnIsCrashReportingEnabled(bool* enabled) {
   *enabled = ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.h b/chrome/browser/renderer_host/chrome_render_message_filter.h
index bc8a292..567ee38 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.h
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.h
@@ -5,32 +5,20 @@
 #ifndef CHROME_BROWSER_RENDERER_HOST_CHROME_RENDER_MESSAGE_FILTER_H_
 #define CHROME_BROWSER_RENDERER_HOST_CHROME_RENDER_MESSAGE_FILTER_H_
 
-#include <string>
-#include <vector>
-
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/sequenced_task_runner_helpers.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/browser_thread.h"
-#include "extensions/buildflags/buildflags.h"
 #include "ppapi/buildflags/buildflags.h"
 
 class GURL;
 class Profile;
 
-namespace url {
-class Origin;
-}
-
 namespace predictors {
 class PreconnectManager;
 }
 
-namespace content_settings {
-class CookieSettings;
-}
-
 namespace network_hints {
 struct LookupRequest;
 }
@@ -58,59 +46,6 @@
                     bool allow_credentials,
                     int count);
 
-  void OnAllowDatabase(int render_frame_id,
-                       const url::Origin& origin,
-                       const GURL& site_for_cookies,
-                       const url::Origin& top_frame_origin,
-                       bool* allowed);
-  void OnAllowDOMStorage(int render_frame_id,
-                         const url::Origin& origin,
-                         const GURL& site_for_cookies,
-                         const url::Origin& top_frame_origin,
-                         bool local,
-                         bool* allowed);
-  void OnRequestFileSystemAccessSync(int render_frame_id,
-                                     const url::Origin& origin,
-                                     const GURL& site_for_cookies,
-                                     const url::Origin& top_frame_origin,
-                                     IPC::Message* message);
-  void OnRequestFileSystemAccessAsync(int render_frame_id,
-                                      int request_id,
-                                      const url::Origin& origin,
-                                      const GURL& site_for_cookies,
-                                      const url::Origin& top_frame_origin);
-  void OnRequestFileSystemAccessSyncResponse(IPC::Message* reply_msg,
-                                             bool allowed);
-  void OnRequestFileSystemAccessAsyncResponse(int render_frame_id,
-                                              int request_id,
-                                              bool allowed);
-  void OnRequestFileSystemAccess(int render_frame_id,
-                                 const url::Origin& origin,
-                                 const GURL& site_for_cookies,
-                                 const url::Origin& top_frame_origin,
-                                 base::Callback<void(bool)> callback);
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  static void FileSystemAccessedOnUIThread(int render_process_id,
-                                           int render_frame_id,
-                                           const GURL& url,
-                                           bool allowed,
-                                           base::Callback<void(bool)> callback);
-  static void FileSystemAccessedResponse(int render_process_id,
-                                         int render_frame_id,
-                                         const GURL& url,
-                                         base::Callback<void(bool)> callback,
-                                         bool allowed);
-#endif
-  void OnAllowIndexedDB(int render_frame_id,
-                        const url::Origin& origin,
-                        const GURL& site_for_cookies,
-                        const url::Origin& top_frame_origin,
-                        bool* allowed);
-  void OnAllowCacheStorage(int render_frame_id,
-                           const url::Origin& origin,
-                           const GURL& site_for_cookies,
-                           const url::Origin& top_frame_origin,
-                           bool* allowed);
 #if BUILDFLAG(ENABLE_PLUGINS)
   void OnIsCrashReportingEnabled(bool* enabled);
 #endif
@@ -124,9 +59,6 @@
   // initialized.
   bool preconnect_manager_initialized_;
 
-  // Used to look up permissions at database creation time.
-  scoped_refptr<content_settings::CookieSettings> cookie_settings_;
-
   DISALLOW_COPY_AND_ASSIGN(ChromeRenderMessageFilter);
 };
 
diff --git a/chrome/browser/resources/chromeos/emulator/BUILD.gn b/chrome/browser/resources/chromeos/emulator/BUILD.gn
index a300af9..248fc9c 100644
--- a/chrome/browser/resources/chromeos/emulator/BUILD.gn
+++ b/chrome/browser/resources/chromeos/emulator/BUILD.gn
@@ -3,8 +3,10 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/polymer.gni")
 
 js_type_check("closure_compile") {
+  is_polymer3 = true
   deps = [
     ":audio_settings",
     ":battery_settings",
@@ -16,32 +18,93 @@
 
 js_library("audio_settings") {
   deps = [
-    "//ui/webui/resources/js:cr",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:cr.m",
   ]
   externs_list = [ "$externs_path/chrome_send.js" ]
 }
 
 js_library("bluetooth_settings") {
   deps = [
-    "//ui/webui/resources/js:web_ui_listener_behavior",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
   externs_list = [ "$externs_path/chrome_send.js" ]
 }
 
 js_library("battery_settings") {
   deps = [
-    "//ui/webui/resources/js:web_ui_listener_behavior",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
   externs_list = [ "$externs_path/chrome_send.js" ]
 }
 
 js_library("input_device_settings") {
   deps = [
-    "//ui/webui/resources/js:web_ui_listener_behavior",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
   externs_list = [ "$externs_path/chrome_send.js" ]
 }
 
 js_library("device_emulator_pages") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
   externs_list = [ "$externs_path/chrome_send.js" ]
 }
+
+polymer_modulizer("audio_settings") {
+  js_file = "audio_settings.js"
+  html_file = "audio_settings.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("battery_settings") {
+  js_file = "battery_settings.js"
+  html_file = "battery_settings.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("bluetooth_settings") {
+  js_file = "bluetooth_settings.js"
+  html_file = "bluetooth_settings.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("input_device_settings") {
+  js_file = "input_device_settings.js"
+  html_file = "input_device_settings.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("device_emulator_pages") {
+  js_file = "device_emulator_pages.js"
+  html_file = "device_emulator_pages.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("icons") {
+  js_file = "icons.js"
+  html_file = "icons.html"
+  html_type = "v3-ready"
+}
+
+polymer_modulizer("shared_styles") {
+  js_file = "shared_styles.js"
+  html_file = "shared_styles.html"
+  html_type = "v3-ready"
+}
+
+group("polymer3_elements") {
+  deps = [
+    ":audio_settings_module",
+    ":battery_settings_module",
+    ":bluetooth_settings_module",
+    ":device_emulator_pages_module",
+    ":icons_module",
+    ":input_device_settings_module",
+    ":shared_styles_module",
+  ]
+}
diff --git a/chrome/browser/resources/chromeos/emulator/audio_settings.html b/chrome/browser/resources/chromeos/emulator/audio_settings.html
index f141d03..17bfd18 100644
--- a/chrome/browser/resources/chromeos/emulator/audio_settings.html
+++ b/chrome/browser/resources/chromeos/emulator/audio_settings.html
@@ -1,20 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_radio_group/cr_radio_group.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
-<link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-<link rel="import" href="icons.html">
-<link rel="import" href="shared_styles.html">
-
-<dom-module id="audio-settings">
-  <template>
     <style include="device-emulator-shared-styles cr-shared-style iron-flex
         iron-flex-alignment iron-positioning">
     </style>
@@ -103,6 +87,3 @@
         <cr-button on-click="appendNewNode">Add Node</cr-button>
       </div>
     </div>
-  </template>
-  <script src="audio_settings.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/chromeos/emulator/audio_settings.js b/chrome/browser/resources/chromeos/emulator/audio_settings.js
index 24912bd..d0d5498f 100644
--- a/chrome/browser/resources/chromeos/emulator/audio_settings.js
+++ b/chrome/browser/resources/chromeos/emulator/audio_settings.js
@@ -2,6 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.m.js';
+import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.m.js';
+import 'chrome://resources/cr_elements/shared_style_css.m.js';
+import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import './icons.js';
+import './shared_styles.js';
+
+import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 /** @enum {string} */ var AudioNodeType = {
   HEADPHONE: 'HEADPHONE',
   MIC: 'MIC',
@@ -50,6 +65,8 @@
 Polymer({
   is: 'audio-settings',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     /**
      * An AudioNode which is currently being edited.
@@ -122,7 +139,7 @@
   },
 
   ready: function() {
-    cr.sendWithPromise('requestAudioNodes').then(
+    sendWithPromise('requestAudioNodes').then(
         this.updateAudioNodes_.bind(this));
   },
 
diff --git a/chrome/browser/resources/chromeos/emulator/battery_settings.html b/chrome/browser/resources/chromeos/emulator/battery_settings.html
index 4438768..8f8c18dc 100644
--- a/chrome/browser/resources/chromeos/emulator/battery_settings.html
+++ b/chrome/browser/resources/chromeos/emulator/battery_settings.html
@@ -1,23 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_radio_group/cr_radio_group.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-<link rel="import" href="icons.html">
-<link rel="import" href="shared_styles.html">
-
-<dom-module id="battery-settings">
-  <template>
-    <!-- TODO(michaelpg): Wrap the line below to fit within the 80-char limit.
-         See https://github.com/Polymer/polymer/pull/3668. -->
     <style include="device-emulator-shared-styles iron-flex iron-flex-alignment
         iron-positioning md-select">
       cr-input {
@@ -89,6 +69,3 @@
         </tbody>
       </table>
     </div>
-  </template>
-  <script src="battery_settings.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/chromeos/emulator/battery_settings.js b/chrome/browser/resources/chromeos/emulator/battery_settings.js
index 76daa756..39cd342 100644
--- a/chrome/browser/resources/chromeos/emulator/battery_settings.js
+++ b/chrome/browser/resources/chromeos/emulator/battery_settings.js
@@ -2,9 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.m.js';
+import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.m.js';
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+import 'chrome://resources/cr_elements/md_select_css.m.js';
+import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
+import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import './icons.js';
+import './shared_styles.js';
+
+import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 Polymer({
   is: 'battery-settings',
 
+  _template: html`{__html_template__}`,
+
   behaviors: [WebUIListenerBehavior],
 
   properties: {
diff --git a/chrome/browser/resources/chromeos/emulator/bluetooth_settings.html b/chrome/browser/resources/chromeos/emulator/bluetooth_settings.html
index a1eb364ee..782f5f3 100644
--- a/chrome/browser/resources/chromeos/emulator/bluetooth_settings.html
+++ b/chrome/browser/resources/chromeos/emulator/bluetooth_settings.html
@@ -1,20 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_radio_group/cr_radio_group.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-<link rel="import" href="icons.html">
-<link rel="import" href="shared_styles.html">
-
-<dom-module id="bluetooth-settings">
-  <template>
     <style include="device-emulator-shared-styles cr-shared-style iron-flex
         iron-flex-alignment iron-positioning">
     </style>
@@ -173,6 +156,3 @@
         </cr-button>
       </div>
     </div>
-  </template>
-  <script src="bluetooth_settings.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/chromeos/emulator/bluetooth_settings.js b/chrome/browser/resources/chromeos/emulator/bluetooth_settings.js
index 1e4c0f04..0b3147b 100644
--- a/chrome/browser/resources/chromeos/emulator/bluetooth_settings.js
+++ b/chrome/browser/resources/chromeos/emulator/bluetooth_settings.js
@@ -2,6 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.m.js';
+import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.m.js';
+import 'chrome://resources/cr_elements/shared_style_css.m.js';
+import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import './icons.js';
+import './shared_styles.js';
+
+import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+
 /**
  * A bluetooth device.
  * @constructor
@@ -54,6 +70,8 @@
 Polymer({
   is: 'bluetooth-settings',
 
+  _template: html`{__html_template__}`,
+
   behaviors: [WebUIListenerBehavior],
 
   properties: {
diff --git a/chrome/browser/resources/chromeos/emulator/device_emulator.css b/chrome/browser/resources/chromeos/emulator/device_emulator.css
index 5ba0039..0553f30 100644
--- a/chrome/browser/resources/chromeos/emulator/device_emulator.css
+++ b/chrome/browser/resources/chromeos/emulator/device_emulator.css
@@ -2,6 +2,13 @@
    Use of this source code is governed by a BSD-style license that can be
    found in the LICENSE file. */
 
+html,
+body {
+  height: 100%;
+  width: 100%;
+}
+
 body {
   font-family: 'Roboto', Arial, Verdana, Helvetica;
+  margin: 0;
 }
diff --git a/chrome/browser/resources/chromeos/emulator/device_emulator.html b/chrome/browser/resources/chromeos/emulator/device_emulator.html
index ae8d768d..26c632b 100644
--- a/chrome/browser/resources/chromeos/emulator/device_emulator.html
+++ b/chrome/browser/resources/chromeos/emulator/device_emulator.html
@@ -2,29 +2,9 @@
 <head>
   <meta charset="utf-8">
   <title>Device Emulator</title>
-  <link rel="import" href="chrome://resources/html/polymer.html">
-
-  <link rel="import" href="chrome://resources/html/util.html">
-  <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-  <link rel="import" href="device_emulator_pages.html">
-
-  <link rel="stylesheet" href="chrome://resources/css/roboto.css">
   <link rel="stylesheet" href="device_emulator.css">
-  <custom-style>
-    <style include="iron-flex iron-flex-alignment">
-      html,
-      body {
-        height: 100%;
-        width: 100%;
-      }
-
-      body {
-        font-family: Roboto;
-        margin: 0;
-      }
-    </style>
-  </custom-style>
 </head>
-<body unresolved class="fullbleed layout vertical">
-  <device-emulator-pages class="flex layout vertical"></device-emulator-pages>
+<body>
+  <device-emulator-pages></device-emulator-pages>
+  <script type="module" src="device_emulator_pages.js"></script>
 </body>
diff --git a/chrome/browser/resources/chromeos/emulator/device_emulator_pages.html b/chrome/browser/resources/chromeos/emulator/device_emulator_pages.html
index 760abcb..dbb86ea 100644
--- a/chrome/browser/resources/chromeos/emulator/device_emulator_pages.html
+++ b/chrome/browser/resources/chromeos/emulator/device_emulator_pages.html
@@ -1,26 +1,9 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_drawer/cr_drawer.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-selector/iron-selector.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/shadow.html">
-<link rel="import" href="audio_settings.html">
-<link rel="import" href="battery_settings.html">
-<link rel="import" href="bluetooth_settings.html">
-<link rel="import" href="icons.html">
-<link rel="import" href="input_device_settings.html">
-<link rel="import" href="shared_styles.html">
-
-<dom-module id="device-emulator-pages">
-  <template>
     <style include="device-emulator-shared-styles iron-flex iron-flex-alignment
         iron-positioning">
       :host {
         display: flex;
         flex-direction: column;
+        height: 100%;
       }
 
       iron-pages {
@@ -105,6 +88,3 @@
         <input-device-settings id="inputDeviceSettings"></input-device-settings>
       </iron-pages>
     </div>
-  </template>
-  <script src="device_emulator_pages.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/chromeos/emulator/device_emulator_pages.js b/chrome/browser/resources/chromeos/emulator/device_emulator_pages.js
index 6c7a518..1b487a8 100644
--- a/chrome/browser/resources/chromeos/emulator/device_emulator_pages.js
+++ b/chrome/browser/resources/chromeos/emulator/device_emulator_pages.js
@@ -2,9 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_drawer/cr_drawer.m.js';
+import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.m.js';
+import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+import 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
+import 'chrome://resources/polymer/v3_0/iron-pages/iron-pages.js';
+import 'chrome://resources/polymer/v3_0/paper-styles/shadow.js';
+import './audio_settings.js';
+import './battery_settings.js';
+import './bluetooth_settings.js';
+import './icons.js';
+import './input_device_settings.js';
+import './shared_styles.js';
+
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 Polymer({
   is: 'device-emulator-pages',
 
+  _template: html`{__html_template__}`,
+
   properties: {
     selectedPage: {
       type: Number,
diff --git a/chrome/browser/resources/chromeos/emulator/icons.html b/chrome/browser/resources/chromeos/emulator/icons.html
index 80bc4ba0..1ec0036 100644
--- a/chrome/browser/resources/chromeos/emulator/icons.html
+++ b/chrome/browser/resources/chromeos/emulator/icons.html
@@ -1,5 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 
 <iron-iconset-svg name="device-emulator" size="24">
   <svg>
diff --git a/chrome/browser/resources/chromeos/emulator/icons.js b/chrome/browser/resources/chromeos/emulator/icons.js
new file mode 100644
index 0000000..500ccf6
--- /dev/null
+++ b/chrome/browser/resources/chromeos/emulator/icons.js
@@ -0,0 +1,10 @@
+// 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 'chrome://resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
+
+import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+const template = html`{__html_template__}`;
+document.head.appendChild(template.content);
diff --git a/chrome/browser/resources/chromeos/emulator/input_device_settings.html b/chrome/browser/resources/chromeos/emulator/input_device_settings.html
index 9b29a95..a10c75b1 100644
--- a/chrome/browser/resources/chromeos/emulator/input_device_settings.html
+++ b/chrome/browser/resources/chromeos/emulator/input_device_settings.html
@@ -1,15 +1,3 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-<link rel="import" href="icons.html">
-<link rel="import" href="shared_styles.html">
-
-<dom-module id="input-device-settings">
-  <template>
     <style include="device-emulator-shared-styles iron-flex iron-flex-alignment
         iron-positioning">
       .content > *:not(:first-child) {
@@ -41,6 +29,3 @@
         </iron-collapse>
       </div>
     </div>
-  </template>
-  <script src="input_device_settings.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/chromeos/emulator/input_device_settings.js b/chrome/browser/resources/chromeos/emulator/input_device_settings.js
index cb8cb643..c6398a8 100644
--- a/chrome/browser/resources/chromeos/emulator/input_device_settings.js
+++ b/chrome/browser/resources/chromeos/emulator/input_device_settings.js
@@ -2,9 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.m.js';
+import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
+import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import './icons.js';
+import './shared_styles.js';
+
+import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 Polymer({
   is: 'input-device-settings',
 
+  _template: html`{__html_template__}`,
+
   behaviors: [WebUIListenerBehavior],
 
   ready: function() {
diff --git a/chrome/browser/resources/chromeos/emulator/shared_styles.html b/chrome/browser/resources/chromeos/emulator/shared_styles.html
index e49712f..35225d96 100644
--- a/chrome/browser/resources/chromeos/emulator/shared_styles.html
+++ b/chrome/browser/resources/chromeos/emulator/shared_styles.html
@@ -1,6 +1,4 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
 
-<dom-module id="device-emulator-shared-styles">
   <template>
     <style>
       cr-icon-button {
@@ -112,4 +110,3 @@
       }
     </style>
   </template>
-</dom-module>
diff --git a/chrome/browser/resources/chromeos/emulator/shared_styles.js b/chrome/browser/resources/chromeos/emulator/shared_styles.js
new file mode 100644
index 0000000..a41cbb60
--- /dev/null
+++ b/chrome/browser/resources/chromeos/emulator/shared_styles.js
@@ -0,0 +1,10 @@
+// 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 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+const styleElement = document.createElement('dom-module');
+styleElement.setAttribute('assetpath', 'chrome://resources/');
+styleElement.innerHTML = `{__html_template__}`;
+styleElement.register('device-emulator-shared-styles');
diff --git a/chrome/browser/resources/ntp4/incognito_tab.html b/chrome/browser/resources/ntp4/incognito_tab.html
index e82b93c..48631cb0 100644
--- a/chrome/browser/resources/ntp4/incognito_tab.html
+++ b/chrome/browser/resources/ntp4/incognito_tab.html
@@ -36,6 +36,7 @@
       $i18n{cookieControlsDescription}
     </div>
     <cr-toggle id="cookie-controls-toggle"
+               aria-label="$i18n{cookieControlsTitle}"
                $i18n{cookieControlsToggleChecked} dark></cr-toggle>
   </div>
   <a class="learn-more-button" href="$i18n{learnMoreLink}">$i18n{learnMore}</a>
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_add_languages_dialog.js b/chrome/browser/resources/settings/chromeos/os_languages_page/os_add_languages_dialog.js
index 3eaa9945..fd01d930 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_add_languages_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_add_languages_dialog.js
@@ -166,6 +166,8 @@
     // Close dialog if 'esc' is pressed and the search box is already empty.
     if (e.key == 'Escape' && !this.$.search.getValue().trim()) {
       this.$.dialog.close();
+    } else {
+      this.$.search.scrollIntoViewIfNeeded();
     }
   },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
index f7a77c9..baf0083 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
@@ -122,7 +122,7 @@
               settings.routes.SYNC.path,
               loadTimeData.getBoolean('unifiedConsentEnabled') ?
                   '#sync-setup' :
-                  '#sync-status .subpage-arrow');
+                  '#sync-status');
         }
         if (settings.routes.LOCK_SCREEN) {
           map.set(
diff --git a/chrome/browser/resources/settings/languages_page/add_languages_dialog.js b/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
index 9d498bb4..61f8b10 100644
--- a/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
+++ b/chrome/browser/resources/settings/languages_page/add_languages_dialog.js
@@ -166,6 +166,8 @@
     // Close dialog if 'esc' is pressed and the search box is already empty.
     if (e.key == 'Escape' && !this.$.search.getValue().trim()) {
       this.$.dialog.close();
+    } else {
+      this.$.search.scrollIntoViewIfNeeded();
     }
   },
 });
diff --git a/chrome/browser/resources/settings/people_page/people_page.js b/chrome/browser/resources/settings/people_page/people_page.js
index ec7591d..4cd42ea 100644
--- a/chrome/browser/resources/settings/people_page/people_page.js
+++ b/chrome/browser/resources/settings/people_page/people_page.js
@@ -204,7 +204,7 @@
               settings.routes.SYNC.path,
               loadTimeData.getBoolean('unifiedConsentEnabled') ?
                   '#sync-setup' :
-                  '#sync-status .subpage-arrow');
+                  '#sync-status');
         }
         // <if expr="not chromeos">
         if (settings.routes.MANAGE_PROFILE) {
diff --git a/chrome/browser/signin/chrome_signin_url_loader_throttle.cc b/chrome/browser/signin/chrome_signin_url_loader_throttle.cc
index 225f323..397c71c 100644
--- a/chrome/browser/signin/chrome_signin_url_loader_throttle.cc
+++ b/chrome/browser/signin/chrome_signin_url_loader_throttle.cc
@@ -7,7 +7,7 @@
 #include "chrome/browser/signin/chrome_signin_helper.h"
 #include "chrome/browser/signin/header_modification_delegate.h"
 #include "components/signin/core/browser/signin_header_helper.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 
 namespace signin {
 
@@ -166,7 +166,7 @@
 
 void URLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& response_head,
+    const network::mojom::URLResponseHead& response_head,
     bool* /* defer */,
     std::vector<std::string>* to_be_removed_request_headers,
     net::HttpRequestHeaders* modified_request_headers) {
@@ -190,7 +190,7 @@
 
 void URLLoaderThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {
   ThrottleResponseAdapter adapter(this, response_head->headers.get());
   delegate_->ProcessResponse(&adapter, GURL() /* redirect_url */);
diff --git a/chrome/browser/signin/chrome_signin_url_loader_throttle.h b/chrome/browser/signin/chrome_signin_url_loader_throttle.h
index 6b4ebbbf..dfe7aef 100644
--- a/chrome/browser/signin/chrome_signin_url_loader_throttle.h
+++ b/chrome/browser/signin/chrome_signin_url_loader_throttle.h
@@ -37,12 +37,12 @@
   void WillStartRequest(network::ResourceRequest* request,
                         bool* defer) override;
   void WillRedirectRequest(net::RedirectInfo* redirect_info,
-                           const network::ResourceResponseHead& response_head,
+                           const network::mojom::URLResponseHead& response_head,
                            bool* defer,
                            std::vector<std::string>* headers_to_remove,
                            net::HttpRequestHeaders* modified_headers) override;
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override;
 
  private:
diff --git a/chrome/browser/signin/chrome_signin_url_loader_throttle_unittest.cc b/chrome/browser/signin/chrome_signin_url_loader_throttle_unittest.cc
index 19038d36..a8692e0b 100644
--- a/chrome/browser/signin/chrome_signin_url_loader_throttle_unittest.cc
+++ b/chrome/browser/signin/chrome_signin_url_loader_throttle_unittest.cc
@@ -9,7 +9,7 @@
 #include "base/test/mock_callback.h"
 #include "chrome/browser/signin/chrome_signin_helper.h"
 #include "chrome/browser/signin/header_modification_delegate.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -165,7 +165,7 @@
   // referrer but we do for testing purposes.
   redirect_info.new_referrer = kTestURL.spec();
 
-  auto response_head = std::make_unique<network::ResourceResponseHead>();
+  auto response_head = network::mojom::URLResponseHead::New();
   response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
   response_head->headers->AddHeader("X-Response-1: Foo");
   response_head->headers->AddHeader("X-Response-2: Bar");
@@ -209,7 +209,7 @@
         EXPECT_EQ(GURL(), redirect_url);
       }));
 
-  response_head = std::make_unique<network::ResourceResponseHead>();
+  response_head = network::mojom::URLResponseHead::New();
   response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
   response_head->headers->AddHeader("X-Response-3: Foo");
   response_head->headers->AddHeader("X-Response-4: Bar");
@@ -256,19 +256,19 @@
 
   net::RedirectInfo redirect_info;
   redirect_info.new_url = GURL("https://youtube.com");
-  network::ResourceResponseHead response_head;
+  auto response_head = network::mojom::URLResponseHead::New();
 
   std::vector<std::string> request_headers_to_remove;
   net::HttpRequestHeaders modified_request_headers;
-  throttle->WillRedirectRequest(&redirect_info, response_head, &defer,
+  throttle->WillRedirectRequest(&redirect_info, *response_head, &defer,
                                 &request_headers_to_remove,
                                 &modified_request_headers);
   EXPECT_FALSE(defer);
   EXPECT_TRUE(request_headers_to_remove.empty());
   EXPECT_TRUE(modified_request_headers.IsEmpty());
 
-  throttle->WillProcessResponse(GURL("https://youtube.com"), &response_head,
-                                &defer);
+  throttle->WillProcessResponse(GURL("https://youtube.com"),
+                                response_head.get(), &defer);
   EXPECT_FALSE(defer);
 }
 
diff --git a/chrome/browser/speech/extension_api/tts_extension_apitest.cc b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
index d43c578..e427c0a9 100644
--- a/chrome/browser/speech/extension_api/tts_extension_apitest.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
@@ -27,11 +27,9 @@
 #include "extensions/browser/notification_types.h"
 #include "net/base/network_change_notifier.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gmock_mutant.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::AnyNumber;
-using ::testing::CreateFunctor;
 using ::testing::DoAll;
 using ::testing::Invoke;
 using ::testing::InSequence;
@@ -395,10 +393,10 @@
   EXPECT_CALL(mock_platform_impl_, StopSpeaking())
       .WillOnce(Return(true));
   EXPECT_CALL(mock_platform_impl_, DoSpeak(_, "first try", _, _, _))
-      .WillOnce(DoAll(InvokeWithoutArgs(CreateFunctor(
-                          &MockTtsPlatformImpl::SetErrorToEpicFail,
-                          base::Unretained(&mock_platform_impl_))),
-                      Return()));
+      .WillOnce(
+          DoAll(InvokeWithoutArgs(&mock_platform_impl_,
+                                  &MockTtsPlatformImpl::SetErrorToEpicFail),
+                Return()));
   EXPECT_CALL(mock_platform_impl_, StopSpeaking())
       .WillOnce(Return(true));
 
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 57dbe8f..1cb672b3 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -73,7 +73,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/content_settings_renderer.mojom.h"
+#include "chrome/common/content_settings_agent.mojom.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/web_application_info.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -3601,9 +3601,9 @@
   void SetAllowRunningInsecureContent() {
     content::RenderFrameHost* render_frame_host =
         browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
-    mojo::AssociatedRemote<chrome::mojom::ContentSettingsRenderer> renderer;
-    render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(&renderer);
-    renderer->SetAllowRunningInsecureContent();
+    mojo::AssociatedRemote<chrome::mojom::ContentSettingsAgent> agent;
+    render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(&agent);
+    agent->SetAllowRunningInsecureContent();
   }
 
   void CheckErrorStateIsCleared() {
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index 0b8e358..372cae28 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -216,6 +216,23 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientNigoriSyncTestWithUssTests);
 };
 
+class SingleClientNigoriSyncTestWithNotAwaitQuiescence
+    : public SingleClientNigoriSyncTestWithUssTests {
+ public:
+  SingleClientNigoriSyncTestWithNotAwaitQuiescence() = default;
+  ~SingleClientNigoriSyncTestWithNotAwaitQuiescence() = default;
+
+  bool TestUsesSelfNotifications() override {
+    // This test fixture is used with tests, which expect SetupSync() to be
+    // waiting for completion, but not for quiescense, because it can't be
+    // achieved and isn't needed.
+    return false;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SingleClientNigoriSyncTestWithNotAwaitQuiescence);
+};
+
 IN_PROC_BROWSER_TEST_P(SingleClientNigoriSyncTestWithUssTests,
                        ShouldCommitKeystoreNigoriWhenReceivedDefault) {
   // SetupSync() should make FakeServer send default NigoriSpecifics.
@@ -351,6 +368,31 @@
                          SingleClientNigoriSyncTestWithUssTests,
                          ::testing::Values(false, true));
 
+// Performs initial sync for Nigori, but doesn't allow initialized Nigori to be
+// commited.
+IN_PROC_BROWSER_TEST_P(SingleClientNigoriSyncTestWithNotAwaitQuiescence,
+                       PRE_ShouldCompleteKeystoreInitializationAfterRestart) {
+  GetFakeServer()->TriggerCommitError(sync_pb::SyncEnums::THROTTLED);
+  ASSERT_TRUE(SetupSync());
+}
+
+// After browser restart the client should commit initialized Nigori.
+IN_PROC_BROWSER_TEST_P(SingleClientNigoriSyncTestWithNotAwaitQuiescence,
+                       ShouldCompleteKeystoreInitializationAfterRestart) {
+  ASSERT_TRUE(SetupClients());
+  ASSERT_TRUE(ServerNigoriChecker(GetSyncService(0), GetFakeServer(),
+                                  syncer::PassphraseType::kImplicitPassphrase)
+                  .Wait());
+  GetFakeServer()->TriggerCommitError(sync_pb::SyncEnums::SUCCESS);
+  EXPECT_TRUE(ServerNigoriChecker(GetSyncService(0), GetFakeServer(),
+                                  syncer::PassphraseType::kKeystorePassphrase)
+                  .Wait());
+}
+
+INSTANTIATE_TEST_SUITE_P(USS,
+                         SingleClientNigoriSyncTestWithNotAwaitQuiescence,
+                         ::testing::Values(false, true));
+
 class SingleClientNigoriWithWebApiTest : public SyncTest {
  public:
   SingleClientNigoriWithWebApiTest() : SyncTest(SINGLE_CLIENT) {
diff --git a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
index 2a21ed3..ddbd00e 100644
--- a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
@@ -190,9 +190,9 @@
   // involves clearing the server data so that the birthday gets incremented,
   // and also sending an appropriate error.
   GetFakeServer()->ClearServerData();
-  ASSERT_TRUE(GetFakeServer()->TriggerActionableError(
+  GetFakeServer()->TriggerActionableError(
       sync_pb::SyncEnums::NOT_MY_BIRTHDAY, "Reset Sync from Dashboard",
-      "https://chrome.google.com/sync", sync_pb::SyncEnums::UNKNOWN_ACTION));
+      "https://chrome.google.com/sync", sync_pb::SyncEnums::UNKNOWN_ACTION);
   EXPECT_TRUE(SyncDisabledByUserChecker(GetSyncService(0)).Wait());
   GetFakeServer()->ClearActionableError();
 
diff --git a/chrome/browser/sync/test/integration/sync_errors_test.cc b/chrome/browser/sync/test/integration/sync_errors_test.cc
index 5b5ebc8c..abc365e 100644
--- a/chrome/browser/sync/test/integration/sync_errors_test.cc
+++ b/chrome/browser/sync/test/integration/sync_errors_test.cc
@@ -139,11 +139,9 @@
 
   std::string description = "Not My Fault";
   std::string url = "www.google.com";
-  EXPECT_TRUE(GetFakeServer()->TriggerActionableError(
-      sync_pb::SyncEnums::TRANSIENT_ERROR,
-      description,
-      url,
-      sync_pb::SyncEnums::UPGRADE_CLIENT));
+  GetFakeServer()->TriggerActionableError(sync_pb::SyncEnums::TRANSIENT_ERROR,
+                                          description, url,
+                                          sync_pb::SyncEnums::UPGRADE_CLIENT);
 
   // Now make one more change so we will do another sync.
   const BookmarkNode* node2 = AddFolder(0, 0, "title2");
@@ -181,8 +179,7 @@
       GetClient(0)->DisableSyncForType(syncer::UserSelectableType::kAutofill));
 #endif
 
-  EXPECT_TRUE(GetFakeServer()->TriggerError(
-      sync_pb::SyncEnums::TRANSIENT_ERROR));
+  GetFakeServer()->TriggerError(sync_pb::SyncEnums::TRANSIENT_ERROR);
   EXPECT_TRUE(GetFakeServer()->EnableAlternatingTriggeredErrors());
 
 #if defined(OS_CHROMEOS)
@@ -206,9 +203,9 @@
   // Clear the server data so that the birthday gets incremented, and also send
   // an appropriate error.
   GetFakeServer()->ClearServerData();
-  ASSERT_TRUE(GetFakeServer()->TriggerActionableError(
-      sync_pb::SyncEnums::NOT_MY_BIRTHDAY, "Not My Fault", "www.google.com",
-      sync_pb::SyncEnums::UNKNOWN_ACTION));
+  GetFakeServer()->TriggerActionableError(sync_pb::SyncEnums::NOT_MY_BIRTHDAY,
+                                          "Not My Fault", "www.google.com",
+                                          sync_pb::SyncEnums::UNKNOWN_ACTION);
 
   // Now make one more change so we will do another sync.
   const BookmarkNode* node2 = AddFolder(0, 0, "title2");
@@ -246,8 +243,7 @@
   GetSyncService(0)->QueryDetailedSyncStatusForDebugging(&status);
   std::string old_cache_guid = status.sync_id;
 
-  EXPECT_TRUE(
-      GetFakeServer()->TriggerError(sync_pb::SyncEnums::CLIENT_DATA_OBSOLETE));
+  GetFakeServer()->TriggerError(sync_pb::SyncEnums::CLIENT_DATA_OBSOLETE);
 
   // Trigger sync by making one more change.
   const BookmarkNode* node2 = AddFolder(0, 0, "title2");
@@ -256,7 +252,7 @@
   ASSERT_TRUE(SyncEngineStoppedChecker(GetSyncService(0)).Wait());
 
   // Make server return SUCCESS so that sync can initialize.
-  EXPECT_TRUE(GetFakeServer()->TriggerError(sync_pb::SyncEnums::SUCCESS));
+  GetFakeServer()->TriggerError(sync_pb::SyncEnums::SUCCESS);
 
   ASSERT_TRUE(GetClient(0)->AwaitEngineInitialization());
 
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 8b96433..3c39acb 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "content/public/browser/data_decoder_service.h"
 #include "ui/aura/window.h"
 #include "url/gurl.h"
 
@@ -74,3 +75,8 @@
 ChromeShellDelegate::CreateScreenshotDelegate() {
   return std::make_unique<ChromeScreenshotGrabber>();
 }
+
+mojo::Remote<data_decoder::mojom::DataDecoderService>
+ChromeShellDelegate::LaunchDataDecoder() {
+  return content::LaunchDataDecoder();
+}
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index e20d66f8..2d7311c 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -19,6 +19,8 @@
   ash::AccessibilityDelegate* CreateAccessibilityDelegate() override;
   void OpenKeyboardShortcutHelpPage() const override;
   bool CanGoBack(gfx::NativeWindow window) const override;
+  mojo::Remote<data_decoder::mojom::DataDecoderService> LaunchDataDecoder()
+      override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ChromeShellDelegate);
diff --git a/chrome/browser/ui/ash/screen_rotation_interactive_uitest.cc b/chrome/browser/ui/ash/screen_rotation_interactive_uitest.cc
index 6cdac1d..e971527 100644
--- a/chrome/browser/ui/ash/screen_rotation_interactive_uitest.cc
+++ b/chrome/browser/ui/ash/screen_rotation_interactive_uitest.cc
@@ -130,7 +130,15 @@
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_P(ScreenRotationTest, RotateInTablet) {
+// Failing flakily on ChromeOS debug, ASAN, and MSAN.
+// https://crbug.com/1017206
+#if defined(OS_CHROMEOS) && (!defined(NDEBUG) || defined(ADDRESS_SANITIZER) || \
+                             defined(MEMORY_SANITIZER))
+#define MAYBE_RotateInTablet DISABLED_RotateInTablet
+#else
+#define MAYBE_RotateInTablet RotateInTablet
+#endif
+IN_PROC_BROWSER_TEST_P(ScreenRotationTest, MAYBE_RotateInTablet) {
   // Browser window is used just to identify display.
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
   gfx::NativeWindow browser_window =
diff --git a/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller.h b/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller.h
index 68cb9c05..580856fe 100644
--- a/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller.h
+++ b/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller.h
@@ -18,7 +18,6 @@
   virtual ~VerifyPendingDialogController() = default;
 
   virtual base::string16 GetDialogTitle() const = 0;
-  virtual base::string16 GetCancelButtonLabel() const = 0;
 
   virtual void OnCancel() = 0;
   virtual void OnDialogClosed() = 0;
diff --git a/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller_impl.cc b/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller_impl.cc
index a6fec50d..26e707d 100644
--- a/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller_impl.cc
@@ -44,11 +44,6 @@
   return l10n_util::GetStringUTF16(IDS_AUTOFILL_VERIFY_PENDING_DIALOG_TITLE);
 }
 
-base::string16 VerifyPendingDialogControllerImpl::GetCancelButtonLabel() const {
-  return l10n_util::GetStringUTF16(
-      IDS_AUTOFILL_VERIFY_PENDING_DIALOG_CANCEL_BUTTON_LABEL);
-}
-
 void VerifyPendingDialogControllerImpl::OnCancel() {
   if (cancel_card_verification_callback_)
     std::move(cancel_card_verification_callback_).Run();
diff --git a/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller_impl.h b/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller_impl.h
index 4bb3bdf..2b652b3 100644
--- a/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller_impl.h
+++ b/chrome/browser/ui/autofill/payments/verify_pending_dialog_controller_impl.h
@@ -30,7 +30,6 @@
 
   // VerifyPendingDialogController:
   base::string16 GetDialogTitle() const override;
-  base::string16 GetCancelButtonLabel() const override;
   void OnCancel() override;
   void OnDialogClosed() override;
 
diff --git a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
index 38cb375..e9e0d77 100644
--- a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
+++ b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
@@ -94,24 +94,17 @@
   // By not passing the event to AppKit, we do lose out on the brief
   // highlighting of the NSMenu.
   CommandForKeyEventResult result = CommandForKeyEvent(event);
-
-  if (!result.found())
-    return ui::PerformKeyEquivalentResult::kUnhandled;
-
-  // If the menu item will dispatch to a different window (real-world example:
-  // the dictionary definition popover), don't handle the event here.
-  if (result.action && [NSApp targetForAction:result.action] != window)
-    return ui::PerformKeyEquivalentResult::kUnhandled;
-
-  auto* bridge =
-      remote_cocoa::NativeWidgetNSWindowBridge::GetFromNativeWindow(window);
-  if (bridge) {
-    bool was_executed = false;
-    bridge->host()->ExecuteCommand(
-        result.chrome_command, WindowOpenDisposition::CURRENT_TAB,
-        true /* is_before_first_responder */, &was_executed);
-    if (was_executed)
-      return ui::PerformKeyEquivalentResult::kHandled;
+  if (result.found()) {
+    auto* bridge =
+        remote_cocoa::NativeWidgetNSWindowBridge::GetFromNativeWindow(window);
+    if (bridge) {
+      bool was_executed = false;
+      bridge->host()->ExecuteCommand(
+          result.chrome_command, WindowOpenDisposition::CURRENT_TAB,
+          true /* is_before_first_responder */, &was_executed);
+      if (was_executed)
+        return ui::PerformKeyEquivalentResult::kHandled;
+    }
   }
 
   return ui::PerformKeyEquivalentResult::kUnhandled;
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index 454017a..c31fcfd0 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -42,7 +42,7 @@
 #include "chrome/browser/ui/content_settings/content_setting_bubble_model_delegate.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/content_settings_renderer.mojom.h"
+#include "chrome/common/content_settings_agent.mojom.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/grit/generated_resources.h"
@@ -157,9 +157,9 @@
 }
 
 void SetAllowRunningInsecureContent(content::RenderFrameHost* frame) {
-  mojo::AssociatedRemote<chrome::mojom::ContentSettingsRenderer> renderer;
-  frame->GetRemoteAssociatedInterfaces()->GetInterface(&renderer);
-  renderer->SetAllowRunningInsecureContent();
+  mojo::AssociatedRemote<chrome::mojom::ContentSettingsAgent> agent;
+  frame->GetRemoteAssociatedInterfaces()->GetInterface(&agent);
+  agent->SetAllowRunningInsecureContent();
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/autofill/payments/verify_pending_dialog_view_impl.cc b/chrome/browser/ui/views/autofill/payments/verify_pending_dialog_view_impl.cc
index 4523e9f3..68346f6 100644
--- a/chrome/browser/ui/views/autofill/payments/verify_pending_dialog_view_impl.cc
+++ b/chrome/browser/ui/views/autofill/payments/verify_pending_dialog_view_impl.cc
@@ -7,6 +7,8 @@
 #include "chrome/browser/ui/autofill/payments/verify_pending_dialog_controller.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "components/constrained_window/constrained_window_views.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/window/dialog_client_view.h"
 
@@ -15,6 +17,10 @@
 VerifyPendingDialogViewImpl::VerifyPendingDialogViewImpl(
     VerifyPendingDialogController* controller)
     : controller_(controller) {
+  DialogDelegate::set_button_label(
+      ui::DIALOG_BUTTON_CANCEL,
+      l10n_util::GetStringUTF16(
+          IDS_AUTOFILL_VERIFY_PENDING_DIALOG_CANCEL_BUTTON_LABEL));
   // TODO(crbug.com/1014278): Should get correct width automatically when
   // snapping.
   const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
@@ -61,12 +67,6 @@
   return ui::DIALOG_BUTTON_CANCEL;
 }
 
-base::string16 VerifyPendingDialogViewImpl::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  DCHECK_EQ(button, ui::DIALOG_BUTTON_CANCEL);
-  return controller_->GetCancelButtonLabel();
-}
-
 ui::ModalType VerifyPendingDialogViewImpl::GetModalType() const {
   return ui::MODAL_TYPE_CHILD;
 }
diff --git a/chrome/browser/ui/views/autofill/payments/verify_pending_dialog_view_impl.h b/chrome/browser/ui/views/autofill/payments/verify_pending_dialog_view_impl.h
index b09b8b6..b70dfab 100644
--- a/chrome/browser/ui/views/autofill/payments/verify_pending_dialog_view_impl.h
+++ b/chrome/browser/ui/views/autofill/payments/verify_pending_dialog_view_impl.h
@@ -30,7 +30,6 @@
   void AddedToWidget() override;
   bool Cancel() override;
   int GetDialogButtons() const override;
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   ui::ModalType GetModalType() const override;
   base::string16 GetWindowTitle() const override;
   bool ShouldShowCloseButton() const override;
diff --git a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.cc b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.cc
index 7c45f984..ae817d9e 100644
--- a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_ui.cc
@@ -23,31 +23,20 @@
       content::WebUIDataSource::Create(chrome::kChromeUIDeviceEmulatorHost);
 
   // Add resources.
-  html->AddResourcePath("audio_settings.html",
-                        IDR_DEVICE_EMULATOR_AUDIO_SETTINGS_HTML);
   html->AddResourcePath("audio_settings.js",
                         IDR_DEVICE_EMULATOR_AUDIO_SETTINGS_JS);
-  html->AddResourcePath("battery_settings.html",
-                        IDR_DEVICE_EMULATOR_BATTERY_SETTINGS_HTML);
   html->AddResourcePath("battery_settings.js",
                         IDR_DEVICE_EMULATOR_BATTERY_SETTINGS_JS);
-  html->AddResourcePath("bluetooth_settings.html",
-                        IDR_DEVICE_EMULATOR_BLUETOOTH_SETTINGS_HTML);
   html->AddResourcePath("bluetooth_settings.js",
                         IDR_DEVICE_EMULATOR_BLUETOOTH_SETTINGS_JS);
-  html->AddResourcePath("icons.html", IDR_DEVICE_EMULATOR_ICONS_HTML);
-  html->AddResourcePath("input_device_settings.html",
-                        IDR_DEVICE_EMULATOR_INPUT_DEVICE_SETTINGS_HTML);
+  html->AddResourcePath("icons.js", IDR_DEVICE_EMULATOR_ICONS_JS);
   html->AddResourcePath("input_device_settings.js",
                         IDR_DEVICE_EMULATOR_INPUT_DEVICE_SETTINGS_JS);
-  html->AddResourcePath("device_emulator_pages.html",
-                        IDR_DEVICE_EMULATOR_PAGES_HTML);
   html->AddResourcePath("device_emulator_pages.js",
                         IDR_DEVICE_EMULATOR_PAGES_JS);
-
-  html->AddResourcePath("shared_styles.html",
-                        IDR_DEVICE_EMULATOR_SHARED_STYLES_HTML);
-
+  html->AddResourcePath("shared_styles.js",
+                        IDR_DEVICE_EMULATOR_SHARED_STYLES_JS);
+  html->AddResourcePath("device_emulator.css", IDR_DEVICE_EMULATOR_CSS);
   html->SetDefaultResource(IDR_DEVICE_EMULATOR_HTML);
 
   return html;
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 23a41d5..a3cf3e7 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -737,7 +737,8 @@
   sources = [
     "cache_stats_recorder.mojom",
     "chrome_render_frame.mojom",
-    "content_settings_renderer.mojom",
+    "content_settings_agent.mojom",
+    "content_settings_manager.mojom",
     "media/webrtc_logging.mojom",
     "navigation_corrector.mojom",
     "net_benchmarking.mojom",
diff --git a/chrome/common/content_settings_renderer.mojom b/chrome/common/content_settings_agent.mojom
similarity index 63%
rename from chrome/common/content_settings_renderer.mojom
rename to chrome/common/content_settings_agent.mojom
index a8f7704..a93f0a5 100644
--- a/chrome/common/content_settings_renderer.mojom
+++ b/chrome/common/content_settings_agent.mojom
@@ -4,13 +4,12 @@
 
 module chrome.mojom;
 
-// Tells the renderer that it is displaying an interstitial page.
-interface ContentSettingsRenderer {
+// An interface to the content settings agent running in the renderer process.
+interface ContentSettingsAgent {
   // Sent to allow the running of insecure mixed-content. If received by the
   // main frame, it will also reload the frame afterwards.
   SetAllowRunningInsecureContent();
 
-  // Message sent from the browser to the render to inform the renderer
-  // that it is displaying interstitial page.
+  // Sent to inform the renderer that it is displaying interstitial page.
   SetAsInterstitial();
 };
diff --git a/chrome/common/content_settings_manager.mojom b/chrome/common/content_settings_manager.mojom
new file mode 100644
index 0000000..7442d729
--- /dev/null
+++ b/chrome/common/content_settings_manager.mojom
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chrome.mojom;
+
+import "url/mojom/origin.mojom";
+import "url/mojom/url.mojom";
+
+// An interface to the content settings manager running in the browser process
+// associated with a RenderFrameHost instance.
+interface ContentSettingsManager {
+  // Enable cloning the connection, which helps support worker threads created
+  // from other worker threads.
+  Clone(pending_receiver<ContentSettingsManager> clone);
+
+  enum StorageType {
+    DATABASE,
+    LOCAL_STORAGE,
+    SESSION_STORAGE,
+    FILE_SYSTEM,
+    INDEXED_DB,
+    CACHE
+  };
+
+  // Sent by the renderer process to check whether access to a particular
+  // storage system is granted by content settings. Sync support is optional
+  // for when needed; prefer async whenever possible!
+  // TODO(darin): Ideally this interface would be attached to a document-
+  // specific browser-side object that would already know the origin info.
+  // Then these parameters would not need to be passed here.
+  [Sync]
+  AllowStorageAccess(
+      StorageType storage_type,
+      url.mojom.Origin origin,
+      url.mojom.Url site_for_cookies,
+      url.mojom.Origin top_frame_origin) => (bool allowed);
+};
diff --git a/chrome/common/google_url_loader_throttle.cc b/chrome/common/google_url_loader_throttle.cc
index 0a565575..69539de 100644
--- a/chrome/common/google_url_loader_throttle.cc
+++ b/chrome/common/google_url_loader_throttle.cc
@@ -60,12 +60,11 @@
 
 void GoogleURLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& response_head,
+    const network::mojom::URLResponseHead& response_head,
     bool* /* defer */,
     std::vector<std::string>* to_be_removed_headers,
     net::HttpRequestHeaders* modified_headers) {
-  network::mojom::URLResponseHeadPtr response_head_ptr = response_head;
-  variations::RemoveVariationsHeaderIfNeeded(*redirect_info, *response_head_ptr,
+  variations::RemoveVariationsHeaderIfNeeded(*redirect_info, response_head,
                                              to_be_removed_headers);
 
   // URLLoaderThrottles can only change the redirect URL when the network
@@ -96,7 +95,7 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 void GoogleURLLoaderThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {
   // Built-in additional protection for the chrome web store origin.
   GURL webstore_url(extension_urls::GetWebstoreLaunchURL());
diff --git a/chrome/common/google_url_loader_throttle.h b/chrome/common/google_url_loader_throttle.h
index 2bc4b57..fc30054 100644
--- a/chrome/common/google_url_loader_throttle.h
+++ b/chrome/common/google_url_loader_throttle.h
@@ -26,13 +26,13 @@
   void WillStartRequest(network::ResourceRequest* request,
                         bool* defer) override;
   void WillRedirectRequest(net::RedirectInfo* redirect_info,
-                           const network::ResourceResponseHead& response_head,
+                           const network::mojom::URLResponseHead& response_head,
                            bool* defer,
                            std::vector<std::string>* to_be_removed_headers,
                            net::HttpRequestHeaders* modified_headers) override;
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override;
 #endif
 
diff --git a/chrome/common/prerender_url_loader_throttle.cc b/chrome/common/prerender_url_loader_throttle.cc
index 70c8c83..df102b8 100644
--- a/chrome/common/prerender_url_loader_throttle.cc
+++ b/chrome/common/prerender_url_loader_throttle.cc
@@ -11,7 +11,7 @@
 #include "net/base/load_flags.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 
 namespace prerender {
 
@@ -43,7 +43,7 @@
 }
 
 // Returns true if the response has a "no-store" cache control header.
-bool IsNoStoreResponse(const network::ResourceResponseHead& response_head) {
+bool IsNoStoreResponse(const network::mojom::URLResponseHead& response_head) {
   return response_head.headers &&
          response_head.headers->HasHeaderValue("cache-control", "no-store");
 }
@@ -152,7 +152,7 @@
 
 void PrerenderURLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& response_head,
+    const network::mojom::URLResponseHead& response_head,
     bool* defer,
     std::vector<std::string>* /* to_be_removed_headers */,
     net::HttpRequestHeaders* /* modified_headers */) {
@@ -197,7 +197,7 @@
 
 void PrerenderURLLoaderThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {
   if (mode_ != PREFETCH_ONLY)
     return;
diff --git a/chrome/common/prerender_url_loader_throttle.h b/chrome/common/prerender_url_loader_throttle.h
index bf8d9a90..13dd4e1 100644
--- a/chrome/common/prerender_url_loader_throttle.h
+++ b/chrome/common/prerender_url_loader_throttle.h
@@ -47,12 +47,12 @@
   void WillStartRequest(network::ResourceRequest* request,
                         bool* defer) override;
   void WillRedirectRequest(net::RedirectInfo* redirect_info,
-                           const network::ResourceResponseHead& response_head,
+                           const network::mojom::URLResponseHead& response_head,
                            bool* defer,
                            std::vector<std::string>* to_be_removed_headers,
                            net::HttpRequestHeaders* modified_headers) override;
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override;
 
   void OnTimedOut();
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index 97bb5f3e..5d3f34e 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -46,11 +46,6 @@
 IPC_MESSAGE_ROUTED1(ChromeViewMsg_LoadBlockedPlugins,
                     std::string /* identifier */)
 
-// Tells the renderer whether or not a file system access has been allowed.
-IPC_MESSAGE_ROUTED2(ChromeViewMsg_RequestFileSystemAccessAsyncResponse,
-                    int  /* request_id */,
-                    bool /* allowed */)
-
 // JavaScript related messages -----------------------------------------------
 
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
@@ -74,61 +69,6 @@
                     ContentSettingsType /* type of blocked content */,
                     base::string16 /* details on blocked content */)
 
-// Sent by the renderer process to check whether access to web databases is
-// granted by content settings.
-IPC_SYNC_MESSAGE_CONTROL4_1(ChromeViewHostMsg_AllowDatabase,
-                            int /* render_frame_id */,
-                            url::Origin /* origin */,
-                            GURL /* site_for_cookies */,
-                            url::Origin /* top frame_origin */,
-                            bool /* allowed */)
-
-// Sent by the renderer process to check whether access to DOM Storage is
-// granted by content settings.
-IPC_SYNC_MESSAGE_CONTROL5_1(ChromeViewHostMsg_AllowDOMStorage,
-                            int /* render_frame_id */,
-                            url::Origin /* origin */,
-                            GURL /* site_for_cookies */,
-                            url::Origin /* top frame_origin */,
-                            bool /* if true local storage, otherwise session */,
-                            bool /* allowed */)
-
-// Sent by the renderer process to check whether access to FileSystem is
-// granted by content settings.
-IPC_SYNC_MESSAGE_CONTROL4_1(ChromeViewHostMsg_RequestFileSystemAccessSync,
-                            int /* render_frame_id */,
-                            url::Origin /* origin */,
-                            GURL /* site_for_cookies */,
-                            url::Origin /* top frame_origin */,
-                            bool /* allowed */)
-
-// Sent by the renderer process to check whether access to FileSystem is
-// granted by content settings.
-IPC_MESSAGE_CONTROL5(ChromeViewHostMsg_RequestFileSystemAccessAsync,
-                     int /* render_frame_id */,
-                     int /* request_id */,
-                     url::Origin /* origin */,
-                     GURL /* site_for_cookies */,
-                     url::Origin /* top frame_origin */)
-
-// Sent by the renderer process to check whether access to Indexed DB is
-// granted by content settings.
-IPC_SYNC_MESSAGE_CONTROL4_1(ChromeViewHostMsg_AllowIndexedDB,
-                            int /* render_frame_id */,
-                            url::Origin /* origin */,
-                            GURL /* site_for_cookies */,
-                            url::Origin /* top frame_origin */,
-                            bool /* allowed */)
-
-// Sent by the renderer process to check whether access to CacheStorage is
-// granted by content settings.
-IPC_SYNC_MESSAGE_CONTROL4_1(ChromeViewHostMsg_AllowCacheStorage,
-                            int /* render_frame_id */,
-                            url::Origin /* origin */,
-                            GURL /* site_for_cookies */,
-                            url::Origin /* top frame_origin */,
-                            bool /* allowed */)
-
 #if BUILDFLAG(ENABLE_PLUGINS)
 // Sent by the renderer to check if crash reporting is enabled.
 IPC_SYNC_MESSAGE_CONTROL0_1(ChromeViewHostMsg_IsCrashReportingEnabled,
diff --git a/chrome/install_static/product_install_details.cc b/chrome/install_static/product_install_details.cc
index 2d14cbc1..58ff7f5 100644
--- a/chrome/install_static/product_install_details.cc
+++ b/chrome/install_static/product_install_details.cc
@@ -17,6 +17,8 @@
 
 namespace {
 
+enum class InstallChildDir { kApplication, kUserData };
+
 // Returns the executable path for the current process.
 std::wstring GetCurrentProcessExePath() {
   std::wstring exe_path(MAX_PATH, L'\0');
@@ -27,6 +29,63 @@
   return exe_path;
 }
 
+// Returns the install suffix embedded in |exe_path| or an empty string if none
+// is found. |install_child_dir| is the directory directly below the one
+// containing the install suffix. |exe_path| is expected be something similar to
+// "...\[kProductPathName][suffix]\[install_child_dir]".
+std::wstring GetInstallSuffixFromChildDir(const std::wstring& exe_path,
+                                          InstallChildDir install_child_dir) {
+  static constexpr wchar_t kApplicationDirName[] = L"\\Application";
+  static constexpr wchar_t kUserDataDirName[] = L"\\User Data";
+  const bool in_application_dir =
+      install_child_dir == InstallChildDir::kApplication;
+  const wchar_t* const install_child_dir_name =
+      in_application_dir ? kApplicationDirName : kUserDataDirName;
+
+  // Get length of |install_child_dir_name| as signed integer to allow
+  // comparison with the distance between iterators (which is signed).
+  const int install_child_dir_length =
+      in_application_dir ? static_cast<int>(_countof(kApplicationDirName) - 1)
+                         : static_cast<int>(_countof(kUserDataDirName) - 1);
+
+  // Search backwards from the end of the path for |install_child_dir_name|,
+  // using a manual search for the sake of case-insensitivity.
+  if (exe_path.size() < kProductPathNameLength + install_child_dir_length)
+    return std::wstring();
+  std::wstring::const_reverse_iterator scan =
+      exe_path.crbegin() + (install_child_dir_length - 1);
+  while (_wcsnicmp(&*scan, install_child_dir_name, install_child_dir_length) &&
+         ++scan != exe_path.crend()) {
+  }
+  if (scan == exe_path.crend())
+    return std::wstring();
+
+  // Ensure that the dir is followed by a separator or is at the end of the
+  // path.
+  if (scan - exe_path.crbegin() != install_child_dir_length - 1 &&
+      *(scan - install_child_dir_length) != L'\\') {
+    return std::wstring();
+  }
+
+  // Scan backwards to the next separator or the beginning of the path.
+  std::wstring::const_reverse_iterator name =
+      std::find(scan + 1, exe_path.crend(), L'\\');
+  // Back up one character to ignore the separator/end of iteration.
+  if (name == exe_path.crend())
+    name = exe_path.crbegin() + exe_path.size() - 1;
+  else
+    --name;
+
+  // Check for a match of the product directory name.
+  if (_wcsnicmp(&*name, kProductPathName, kProductPathNameLength))
+    return std::wstring();
+
+  // Return the (possibly empty) suffix betwixt the product name and
+  // |install_child_dir|.
+  return std::wstring(&*(name - kProductPathNameLength),
+                      (name - scan) - kProductPathNameLength);
+}
+
 const InstallConstants* FindInstallMode(const std::wstring& suffix) {
   // Search for a mode with the matching suffix.
   for (int i = 0; i < NUM_INSTALL_MODES; ++i) {
@@ -78,44 +137,12 @@
 }
 
 std::wstring GetInstallSuffix(const std::wstring& exe_path) {
-  // Search backwards from the end of the path for "\Application", using a
-  // manual search for the sake of case-insensitivity.
-  static constexpr wchar_t kInstallBinaryDir[] = L"\\Application";
-  constexpr size_t kInstallBinaryDirLength = _countof(kInstallBinaryDir) - 1;
-  if (exe_path.size() < kProductPathNameLength + kInstallBinaryDirLength)
-    return std::wstring();
-  std::wstring::const_reverse_iterator scan =
-      exe_path.crbegin() + (kInstallBinaryDirLength - 1);
-  while (_wcsnicmp(&*scan, kInstallBinaryDir, kInstallBinaryDirLength) &&
-         ++scan != exe_path.crend()) {
-  }
-  if (scan == exe_path.crend())
-    return std::wstring();
+  return GetInstallSuffixFromChildDir(exe_path, InstallChildDir::kApplication);
+}
 
-  // Ensure that the dir is followed by a separator or is at the end of the
-  // path.
-  if (scan - exe_path.crbegin() != kInstallBinaryDirLength - 1 &&
-      *(scan - kInstallBinaryDirLength) != L'\\') {
-    return std::wstring();
-  }
-
-  // Scan backwards to the next separator or the beginning of the path.
-  std::wstring::const_reverse_iterator name =
-      std::find(scan + 1, exe_path.crend(), L'\\');
-  // Back up one character to ignore the separator/end of iteration.
-  if (name == exe_path.crend())
-    name = exe_path.crbegin() + exe_path.size() - 1;
-  else
-    --name;
-
-  // Check for a match of the product directory name.
-  if (_wcsnicmp(&*name, kProductPathName, kProductPathNameLength))
-    return std::wstring();
-
-  // Return the (possibly empty) suffix betwixt the product name and install
-  // binary dir.
-  return std::wstring(&*(name - kProductPathNameLength),
-                      (name - scan) - kProductPathNameLength);
+std::wstring GetUserDataSuffix(const std::wstring& user_data_path) {
+  return GetInstallSuffixFromChildDir(user_data_path,
+                                      InstallChildDir::kUserData);
 }
 
 std::unique_ptr<PrimaryInstallDetails> MakeProductDetails(
diff --git a/chrome/install_static/product_install_details.h b/chrome/install_static/product_install_details.h
index 99e2784d..c0777d4 100644
--- a/chrome/install_static/product_install_details.h
+++ b/chrome/install_static/product_install_details.h
@@ -32,9 +32,14 @@
 
 // Returns the install suffix embedded in |exe_path| or an empty string if none
 // is found. |exe_path| is expected be something similar to
-// "...\[kProductName][suffix]\Application".
+// "...\[kProductPathName][suffix]\Application".
 std::wstring GetInstallSuffix(const std::wstring& exe_path);
 
+// Returns the install suffix embedded in |user_data_path| or an empty string if
+// none is found. |user_data_path| is expected be something similar to
+// "...\[kProductPathName][suffix]\User Data".
+std::wstring GetUserDataSuffix(const std::wstring& user_data_path);
+
 // Creates product details for the process at |exe_path|.
 std::unique_ptr<PrimaryInstallDetails> MakeProductDetails(
     const std::wstring& exe_path);
diff --git a/chrome/install_static/product_install_details_unittest.cc b/chrome/install_static/product_install_details_unittest.cc
index 641a568..3331b7f 100644
--- a/chrome/install_static/product_install_details_unittest.cc
+++ b/chrome/install_static/product_install_details_unittest.cc
@@ -122,6 +122,26 @@
   }
 }
 
+TEST(ProductInstallDetailsTest, GetUserDataSuffix) {
+  std::wstring suffix;
+  const std::pair<const wchar_t*, const wchar_t*> kData[] = {
+      {L"%ls\\User Data", L""},
+      {L"%ls\\User Data\\", L""},
+      {L"\\%ls\\User Data", L""},
+      {L"\\%ls\\User Data\\", L""},
+      {L"C:\\foo\\%ls\\User Data\\foo.exe", L""},
+      {L"%ls Blorf\\User Data", L" Blorf"},
+      {L"%ls Blorf\\User Data\\", L" Blorf"},
+      {L"\\%ls Blorf\\User Data", L" Blorf"},
+      {L"\\%ls Blorf\\User Data\\", L" Blorf"},
+      {L"C:\\foo\\%ls Blorf\\User Data\\foo.exe", L" Blorf"},
+  };
+  for (const auto& data : kData) {
+    const std::wstring path = base::StringPrintf(data.first, kProductPathName);
+    EXPECT_EQ(std::wstring(data.second), GetUserDataSuffix(path)) << path;
+  }
+}
+
 struct TestData {
   const wchar_t* path;
   InstallConstantIndex index;
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 5c4d8b63..3751de95 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -52,8 +52,8 @@
     "chrome_render_frame_observer.h",
     "chrome_render_thread_observer.cc",
     "chrome_render_thread_observer.h",
-    "content_settings_observer.cc",
-    "content_settings_observer.h",
+    "content_settings_agent_impl.cc",
+    "content_settings_agent_impl.h",
     "custom_menu_commands.h",
     "instant_restricted_id_cache.h",
     "loadtimes_extension_bindings.cc",
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index ea89176..866cd0b 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -61,6 +61,7 @@
   "+media/base",
   "+ppapi/shared_impl",
   "+services/network/public/cpp",
+  "+services/network/public/mojom",
   "+skia",
   "+storage/common",
   "+third_party/blink/public/mojom",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 2a6fae06..5a7ec36 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -45,7 +45,7 @@
 #include "chrome/renderer/benchmarking_extension.h"
 #include "chrome/renderer/chrome_render_frame_observer.h"
 #include "chrome/renderer/chrome_render_thread_observer.h"
-#include "chrome/renderer/content_settings_observer.h"
+#include "chrome/renderer/content_settings_agent_impl.h"
 #include "chrome/renderer/loadtimes_extension_bindings.h"
 #include "chrome/renderer/media/flash_embed_rewrite.h"
 #include "chrome/renderer/media/webrtc_logging_agent_impl.h"
@@ -452,7 +452,7 @@
   bool should_whitelist_for_content_settings =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kInstantProcess);
-  ContentSettingsObserver* content_settings = new ContentSettingsObserver(
+  ContentSettingsAgentImpl* content_settings = new ContentSettingsAgentImpl(
       render_frame, should_whitelist_for_content_settings, registry);
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   content_settings->SetExtensionDispatcher(
@@ -783,8 +783,8 @@
       params.mime_type = WebString::FromUTF8(actual_mime_type);
     }
 
-    ContentSettingsObserver* observer =
-        ContentSettingsObserver::Get(render_frame);
+    ContentSettingsAgentImpl* content_settings_agent =
+        ContentSettingsAgentImpl::Get(render_frame);
 
     const ContentSettingsType content_type =
         ShouldUseJavaScriptSettingForPlugin(info)
@@ -793,7 +793,7 @@
 
     if ((status == chrome::mojom::PluginStatus::kUnauthorized ||
          status == chrome::mojom::PluginStatus::kBlocked) &&
-        observer->IsPluginTemporarilyAllowed(identifier)) {
+        content_settings_agent->IsPluginTemporarilyAllowed(identifier)) {
       status = chrome::mojom::PluginStatus::kAllowed;
     }
 
@@ -989,7 +989,7 @@
         render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
             plugin_auth_host.BindNewEndpointAndPassReceiver());
         plugin_auth_host->BlockedUnauthorizedPlugin(group_name, identifier);
-        observer->DidBlockContentType(content_type, group_name);
+        content_settings_agent->DidBlockContentType(content_type, group_name);
         break;
       }
       case chrome::mojom::PluginStatus::kBlocked: {
@@ -998,7 +998,7 @@
             l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, group_name));
         placeholder->AllowLoading();
         RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Blocked"));
-        observer->DidBlockContentType(content_type, group_name);
+        content_settings_agent->DidBlockContentType(content_type, group_name);
         break;
       }
       case chrome::mojom::PluginStatus::kBlockedByPolicy: {
@@ -1008,7 +1008,7 @@
                                        group_name));
         RenderThread::Get()->RecordAction(
             UserMetricsAction("Plugin_BlockedByPolicy"));
-        observer->DidBlockContentType(content_type, group_name);
+        content_settings_agent->DidBlockContentType(content_type, group_name);
         break;
       }
       case chrome::mojom::PluginStatus::kBlockedNoLoading: {
@@ -1016,7 +1016,7 @@
             IDR_BLOCKED_PLUGIN_HTML,
             l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED_NO_LOADING,
                                        group_name));
-        observer->DidBlockContentType(content_type, group_name);
+        content_settings_agent->DidBlockContentType(content_type, group_name);
         break;
       }
       case chrome::mojom::PluginStatus::kComponentUpdateRequired: {
diff --git a/chrome/renderer/chrome_render_thread_observer.cc b/chrome/renderer/chrome_render_thread_observer.cc
index 3cc9905..cf26f76 100644
--- a/chrome/renderer/chrome_render_thread_observer.cc
+++ b/chrome/renderer/chrome_render_thread_observer.cc
@@ -35,7 +35,6 @@
 #include "chrome/common/net/net_resource_provider.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/common/url_constants.h"
-#include "chrome/renderer/content_settings_observer.h"
 #include "components/visitedlink/renderer/visitedlink_slave.h"
 #include "content/public/child/child_thread.h"
 #include "content/public/common/content_switches.h"
diff --git a/chrome/renderer/chromeos_merge_session_loader_throttle.cc b/chrome/renderer/chromeos_merge_session_loader_throttle.cc
index 0354ac5..180b5ae 100644
--- a/chrome/renderer/chromeos_merge_session_loader_throttle.cc
+++ b/chrome/renderer/chromeos_merge_session_loader_throttle.cc
@@ -55,7 +55,7 @@
 
 void MergeSessionLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& /* response_head */,
+    const network::mojom::URLResponseHead& /* response_head */,
     bool* defer,
     std::vector<std::string>* to_be_removed_headers,
     net::HttpRequestHeaders* modified_headers) {
diff --git a/chrome/renderer/chromeos_merge_session_loader_throttle.h b/chrome/renderer/chromeos_merge_session_loader_throttle.h
index e75a8565..6cd1a2d 100644
--- a/chrome/renderer/chromeos_merge_session_loader_throttle.h
+++ b/chrome/renderer/chromeos_merge_session_loader_throttle.h
@@ -37,7 +37,7 @@
   void WillStartRequest(network::ResourceRequest* request,
                         bool* defer) override;
   void WillRedirectRequest(net::RedirectInfo* redirect_info,
-                           const network::ResourceResponseHead& response_head,
+                           const network::mojom::URLResponseHead& response_head,
                            bool* defer,
                            std::vector<std::string>* to_be_removed_headers,
                            net::HttpRequestHeaders* modified_headers) override;
diff --git a/chrome/renderer/content_settings_observer.cc b/chrome/renderer/content_settings_agent_impl.cc
similarity index 80%
rename from chrome/renderer/content_settings_observer.cc
rename to chrome/renderer/content_settings_agent_impl.cc
index e45e69f51..9947eb7 100644
--- a/chrome/renderer/content_settings_observer.cc
+++ b/chrome/renderer/content_settings_agent_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/renderer/content_settings_observer.h"
+#include "chrome/renderer/content_settings_agent_impl.h"
 
 #include <utility>
 #include <vector>
@@ -29,6 +29,7 @@
 #include "extensions/buildflags/buildflags.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/platform/url_conversion.h"
 #include "third_party/blink/public/platform/web_client_hints_type.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
@@ -110,21 +111,24 @@
 
 }  // namespace
 
-ContentSettingsObserver::ContentSettingsObserver(
+ContentSettingsAgentImpl::ContentSettingsAgentImpl(
     content::RenderFrame* render_frame,
     bool should_whitelist,
     service_manager::BinderRegistry* registry)
     : content::RenderFrameObserver(render_frame),
-      content::RenderFrameObserverTracker<ContentSettingsObserver>(
+      content::RenderFrameObserverTracker<ContentSettingsAgentImpl>(
           render_frame),
       should_whitelist_(should_whitelist) {
   ClearBlockedContentSettings();
   render_frame->GetWebFrame()->SetContentSettingsClient(this);
 
   render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
-      base::Bind(&ContentSettingsObserver::OnContentSettingsRendererRequest,
+      base::Bind(&ContentSettingsAgentImpl::OnContentSettingsAgentRequest,
                  base::Unretained(this)));
 
+  render_frame->GetBrowserInterfaceBroker()->GetInterface(
+      content_settings_manager_.BindNewPipeAndPassReceiver());
+
   content::RenderFrame* main_frame =
       render_frame->GetRenderView()->GetMainRenderFrame();
   // TODO(nasko): The main frame is not guaranteed to be in the same process
@@ -133,18 +137,18 @@
   if (main_frame && main_frame != render_frame) {
     // Copy all the settings from the main render frame to avoid race conditions
     // when initializing this data. See https://crbug.com/333308.
-    ContentSettingsObserver* parent = ContentSettingsObserver::Get(main_frame);
+    ContentSettingsAgentImpl* parent =
+        ContentSettingsAgentImpl::Get(main_frame);
     allow_running_insecure_content_ = parent->allow_running_insecure_content_;
     temporarily_allowed_plugins_ = parent->temporarily_allowed_plugins_;
     is_interstitial_page_ = parent->is_interstitial_page_;
   }
 }
 
-ContentSettingsObserver::~ContentSettingsObserver() {
-}
+ContentSettingsAgentImpl::~ContentSettingsAgentImpl() {}
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-void ContentSettingsObserver::SetExtensionDispatcher(
+void ContentSettingsAgentImpl::SetExtensionDispatcher(
     extensions::Dispatcher* extension_dispatcher) {
   DCHECK(!extension_dispatcher_)
       << "SetExtensionDispatcher() should only be called once.";
@@ -152,7 +156,7 @@
 }
 #endif
 
-void ContentSettingsObserver::SetContentSettingRules(
+void ContentSettingsAgentImpl::SetContentSettingRules(
     const RendererContentSettingRules* content_setting_rules) {
   content_setting_rules_ = content_setting_rules;
   UMA_HISTOGRAM_COUNTS_1M("ClientHints.CountRulesReceived",
@@ -160,11 +164,11 @@
 }
 
 const RendererContentSettingRules*
-ContentSettingsObserver::GetContentSettingRules() {
+ContentSettingsAgentImpl::GetContentSettingRules() {
   return content_setting_rules_;
 }
 
-bool ContentSettingsObserver::IsPluginTemporarilyAllowed(
+bool ContentSettingsAgentImpl::IsPluginTemporarilyAllowed(
     const std::string& identifier) {
   // If the empty string is in here, it means all plugins are allowed.
   // TODO(bauerb): Remove this once we only pass in explicit identifiers.
@@ -172,12 +176,12 @@
          base::Contains(temporarily_allowed_plugins_, std::string());
 }
 
-void ContentSettingsObserver::DidBlockContentType(
+void ContentSettingsAgentImpl::DidBlockContentType(
     ContentSettingsType settings_type) {
   DidBlockContentType(settings_type, base::string16());
 }
 
-void ContentSettingsObserver::DidBlockContentType(
+void ContentSettingsAgentImpl::DidBlockContentType(
     ContentSettingsType settings_type,
     const base::string16& details) {
   // Send multiple ContentBlocked messages if details are provided.
@@ -188,26 +192,21 @@
   }
 }
 
-bool ContentSettingsObserver::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserver, message)
-    IPC_MESSAGE_HANDLER(ChromeViewMsg_RequestFileSystemAccessAsyncResponse,
-                        OnRequestFileSystemAccessAsyncResponse)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  if (handled)
-    return true;
+void ContentSettingsAgentImpl::SetContentSettingsManagerForTesting(
+    mojo::Remote<chrome::mojom::ContentSettingsManager> manager) {
+  content_settings_manager_ = std::move(manager);
+}
 
+bool ContentSettingsAgentImpl::OnMessageReceived(const IPC::Message& message) {
   // Don't swallow LoadBlockedPlugins messages, as they're sent to every
   // blocked plugin.
-  IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserver, message)
+  IPC_BEGIN_MESSAGE_MAP(ContentSettingsAgentImpl, message)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins)
   IPC_END_MESSAGE_MAP()
-
   return false;
 }
 
-void ContentSettingsObserver::DidCommitProvisionalLoad(
+void ContentSettingsAgentImpl::DidCommitProvisionalLoad(
     bool is_same_document_navigation,
     ui::PageTransition transition) {
   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
@@ -231,11 +230,11 @@
          !url.SchemeIs(url::kDataScheme));
 }
 
-void ContentSettingsObserver::OnDestruct() {
+void ContentSettingsAgentImpl::OnDestruct() {
   delete this;
 }
 
-void ContentSettingsObserver::SetAllowRunningInsecureContent() {
+void ContentSettingsAgentImpl::SetAllowRunningInsecureContent() {
   allow_running_insecure_content_ = true;
 
   // Reload if we are the main frame.
@@ -244,53 +243,45 @@
     frame->StartReload(blink::WebFrameLoadType::kReload);
 }
 
-void ContentSettingsObserver::SetAsInterstitial() {
+void ContentSettingsAgentImpl::SetAsInterstitial() {
   is_interstitial_page_ = true;
 }
 
-void ContentSettingsObserver::OnContentSettingsRendererRequest(
-    mojo::PendingAssociatedReceiver<chrome::mojom::ContentSettingsRenderer>
+void ContentSettingsAgentImpl::OnContentSettingsAgentRequest(
+    mojo::PendingAssociatedReceiver<chrome::mojom::ContentSettingsAgent>
         receiver) {
   receivers_.Add(this, std::move(receiver));
 }
 
-bool ContentSettingsObserver::AllowDatabase() {
+bool ContentSettingsAgentImpl::AllowDatabase() {
   WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (IsUniqueFrame(frame))
     return false;
 
   bool result = false;
-  Send(new ChromeViewHostMsg_AllowDatabase(
-      routing_id(), frame->GetSecurityOrigin(),
-      frame->GetDocument().SiteForCookies(),
-      frame->GetDocument().TopFrameOrigin(), &result));
+  content_settings_manager_->AllowStorageAccess(
+      chrome::mojom::ContentSettingsManager::StorageType::DATABASE,
+      frame->GetSecurityOrigin(), frame->GetDocument().SiteForCookies(),
+      frame->GetDocument().TopFrameOrigin(), &result);
   return result;
 }
 
-void ContentSettingsObserver::RequestFileSystemAccessAsync(
+void ContentSettingsAgentImpl::RequestFileSystemAccessAsync(
     base::OnceCallback<void(bool)> callback) {
   WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (IsUniqueFrame(frame)) {
     std::move(callback).Run(false);
     return;
   }
-  ++current_request_id_;
-  bool inserted =
-      permission_requests_
-          .insert(std::make_pair(current_request_id_, std::move(callback)))
-          .second;
 
-  // Verify there are no duplicate insertions.
-  DCHECK(inserted);
-
-  Send(new ChromeViewHostMsg_RequestFileSystemAccessAsync(
-      routing_id(), current_request_id_, frame->GetSecurityOrigin(),
-      frame->GetDocument().SiteForCookies(),
-      frame->GetDocument().TopFrameOrigin()));
+  content_settings_manager_->AllowStorageAccess(
+      chrome::mojom::ContentSettingsManager::StorageType::FILE_SYSTEM,
+      frame->GetSecurityOrigin(), frame->GetDocument().SiteForCookies(),
+      frame->GetDocument().TopFrameOrigin(), std::move(callback));
 }
 
-bool ContentSettingsObserver::AllowImage(bool enabled_per_settings,
-                                         const WebURL& image_url) {
+bool ContentSettingsAgentImpl::AllowImage(bool enabled_per_settings,
+                                          const WebURL& image_url) {
   bool allow = enabled_per_settings;
   if (enabled_per_settings) {
     if (is_interstitial_page_)
@@ -310,34 +301,34 @@
   return allow;
 }
 
-bool ContentSettingsObserver::AllowIndexedDB(const WebSecurityOrigin& origin) {
+bool ContentSettingsAgentImpl::AllowIndexedDB(const WebSecurityOrigin& origin) {
   WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (IsUniqueFrame(frame))
     return false;
 
   bool result = false;
-  Send(new ChromeViewHostMsg_AllowIndexedDB(
-      routing_id(), frame->GetSecurityOrigin(),
-      frame->GetDocument().SiteForCookies(),
-      frame->GetDocument().TopFrameOrigin(), &result));
+  content_settings_manager_->AllowStorageAccess(
+      chrome::mojom::ContentSettingsManager::StorageType::INDEXED_DB,
+      frame->GetSecurityOrigin(), frame->GetDocument().SiteForCookies(),
+      frame->GetDocument().TopFrameOrigin(), &result);
   return result;
 }
 
-bool ContentSettingsObserver::AllowCacheStorage(
+bool ContentSettingsAgentImpl::AllowCacheStorage(
     const blink::WebSecurityOrigin& origin) {
   WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (IsUniqueFrame(frame))
     return false;
 
   bool result = false;
-  Send(new ChromeViewHostMsg_AllowCacheStorage(
-      routing_id(), frame->GetSecurityOrigin(),
-      frame->GetDocument().SiteForCookies(),
-      frame->GetDocument().TopFrameOrigin(), &result));
+  content_settings_manager_->AllowStorageAccess(
+      chrome::mojom::ContentSettingsManager::StorageType::CACHE,
+      frame->GetSecurityOrigin(), frame->GetDocument().SiteForCookies(),
+      frame->GetDocument().TopFrameOrigin(), &result);
   return result;
 }
 
-bool ContentSettingsObserver::AllowScript(bool enabled_per_settings) {
+bool ContentSettingsAgentImpl::AllowScript(bool enabled_per_settings) {
   if (!enabled_per_settings)
     return false;
   if (IsScriptDisabledForPreview(render_frame()))
@@ -366,7 +357,7 @@
   return allow;
 }
 
-bool ContentSettingsObserver::AllowScriptFromSource(
+bool ContentSettingsAgentImpl::AllowScriptFromSource(
     bool enabled_per_settings,
     const blink::WebURL& script_url) {
   if (!enabled_per_settings)
@@ -386,7 +377,7 @@
   return allow || IsWhitelistedForContentSettings();
 }
 
-bool ContentSettingsObserver::AllowStorage(bool local) {
+bool ContentSettingsAgentImpl::AllowStorage(bool local) {
   WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (IsUniqueFrame(frame))
     return false;
@@ -398,15 +389,17 @@
     return permissions->second;
 
   bool result = false;
-  Send(new ChromeViewHostMsg_AllowDOMStorage(
-      routing_id(), frame->GetSecurityOrigin(),
-      frame->GetDocument().SiteForCookies(),
-      frame->GetDocument().TopFrameOrigin(), local, &result));
+  content_settings_manager_->AllowStorageAccess(
+      local
+          ? chrome::mojom::ContentSettingsManager::StorageType::LOCAL_STORAGE
+          : chrome::mojom::ContentSettingsManager::StorageType::SESSION_STORAGE,
+      frame->GetSecurityOrigin(), frame->GetDocument().SiteForCookies(),
+      frame->GetDocument().TopFrameOrigin(), &result);
   cached_storage_permissions_[key] = result;
   return result;
 }
 
-bool ContentSettingsObserver::AllowReadFromClipboard(bool default_value) {
+bool ContentSettingsAgentImpl::AllowReadFromClipboard(bool default_value) {
   bool allowed = default_value;
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ScriptContext* current_context =
@@ -419,7 +412,7 @@
   return allowed;
 }
 
-bool ContentSettingsObserver::AllowWriteToClipboard(bool default_value) {
+bool ContentSettingsAgentImpl::AllowWriteToClipboard(bool default_value) {
   bool allowed = default_value;
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // All blessed extension pages could historically write to the clipboard, so
@@ -440,11 +433,11 @@
   return allowed;
 }
 
-bool ContentSettingsObserver::AllowMutationEvents(bool default_value) {
+bool ContentSettingsAgentImpl::AllowMutationEvents(bool default_value) {
   return IsPlatformApp() ? false : default_value;
 }
 
-bool ContentSettingsObserver::AllowRunningInsecureContent(
+bool ContentSettingsAgentImpl::AllowRunningInsecureContent(
     bool allowed_per_settings,
     const blink::WebSecurityOrigin& origin,
     const blink::WebURL& resource_url) {
@@ -471,7 +464,7 @@
   return allow;
 }
 
-bool ContentSettingsObserver::AllowAutoplay(bool default_value) {
+bool ContentSettingsAgentImpl::AllowAutoplay(bool default_value) {
   if (!content_setting_rules_)
     return default_value;
 
@@ -482,7 +475,7 @@
          CONTENT_SETTING_ALLOW;
 }
 
-bool ContentSettingsObserver::AllowPopupsAndRedirects(bool default_value) {
+bool ContentSettingsAgentImpl::AllowPopupsAndRedirects(bool default_value) {
   if (!content_setting_rules_)
     return default_value;
   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
@@ -492,7 +485,7 @@
          CONTENT_SETTING_ALLOW;
 }
 
-void ContentSettingsObserver::PassiveInsecureContentFound(
+void ContentSettingsAgentImpl::PassiveInsecureContentFound(
     const blink::WebURL& resource_url) {
   // Note: this implementation is a mirror of
   // Browser::PassiveInsecureContentFound.
@@ -500,7 +493,7 @@
   FilteredReportInsecureContentDisplayed(GURL(resource_url));
 }
 
-void ContentSettingsObserver::PersistClientHints(
+void ContentSettingsAgentImpl::PersistClientHints(
     const blink::WebEnabledClientHints& enabled_client_hints,
     base::TimeDelta duration,
     const blink::WebURL& url) {
@@ -551,7 +544,7 @@
                                     duration);
 }
 
-void ContentSettingsObserver::GetAllowedClientHintsFromSource(
+void ContentSettingsAgentImpl::GetAllowedClientHintsFromSource(
     const blink::WebURL& url,
     blink::WebEnabledClientHints* client_hints) const {
   if (!content_setting_rules_)
@@ -561,43 +554,29 @@
     return;
 
   client_hints::GetAllowedClientHintsFromSource(
-      url,
-      content_setting_rules_->client_hints_rules, client_hints);
+      url, content_setting_rules_->client_hints_rules, client_hints);
 }
 
-void ContentSettingsObserver::DidNotAllowPlugins() {
+void ContentSettingsAgentImpl::DidNotAllowPlugins() {
   DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS);
 }
 
-void ContentSettingsObserver::DidNotAllowScript() {
+void ContentSettingsAgentImpl::DidNotAllowScript() {
   DidBlockContentType(CONTENT_SETTINGS_TYPE_JAVASCRIPT);
 }
 
-void ContentSettingsObserver::OnLoadBlockedPlugins(
+void ContentSettingsAgentImpl::OnLoadBlockedPlugins(
     const std::string& identifier) {
   temporarily_allowed_plugins_.insert(identifier);
 }
 
-void ContentSettingsObserver::OnRequestFileSystemAccessAsyncResponse(
-    int request_id,
-    bool allowed) {
-  auto it = permission_requests_.find(request_id);
-  if (it == permission_requests_.end())
-    return;
-
-  base::OnceCallback<void(bool)> callback = std::move(it->second);
-  permission_requests_.erase(it);
-
-  std::move(callback).Run(allowed);
-}
-
-void ContentSettingsObserver::ClearBlockedContentSettings() {
+void ContentSettingsAgentImpl::ClearBlockedContentSettings() {
   content_blocked_.clear();
   cached_storage_permissions_.clear();
   cached_script_permissions_.clear();
 }
 
-bool ContentSettingsObserver::IsPlatformApp() {
+bool ContentSettingsAgentImpl::IsPlatformApp() {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   WebSecurityOrigin origin = frame->GetDocument().GetSecurityOrigin();
@@ -609,7 +588,7 @@
 }
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-const extensions::Extension* ContentSettingsObserver::GetExtension(
+const extensions::Extension* ContentSettingsAgentImpl::GetExtension(
     const WebSecurityOrigin& origin) const {
   if (origin.Protocol().Ascii() != extensions::kExtensionScheme)
     return nullptr;
@@ -623,7 +602,7 @@
 #endif
 
 // static
-bool ContentSettingsObserver::IsWhitelistedForContentSettings() const {
+bool ContentSettingsAgentImpl::IsWhitelistedForContentSettings() const {
   if (should_whitelist_)
     return true;
 
@@ -637,7 +616,7 @@
                                          document.Url());
 }
 
-bool ContentSettingsObserver::IsWhitelistedForContentSettings(
+bool ContentSettingsAgentImpl::IsWhitelistedForContentSettings(
     const WebSecurityOrigin& origin,
     const WebURL& document_url) {
   if (document_url.GetString() == content::kUnreachableWebDataURL)
diff --git a/chrome/renderer/content_settings_observer.h b/chrome/renderer/content_settings_agent_impl.h
similarity index 79%
rename from chrome/renderer/content_settings_observer.h
rename to chrome/renderer/content_settings_agent_impl.h
index e55f9c20..cb16b27 100644
--- a/chrome/renderer/content_settings_observer.h
+++ b/chrome/renderer/content_settings_agent_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_RENDERER_CONTENT_SETTINGS_OBSERVER_H_
-#define CHROME_RENDERER_CONTENT_SETTINGS_OBSERVER_H_
+#ifndef CHROME_RENDERER_CONTENT_SETTINGS_AGENT_IMPL_H_
+#define CHROME_RENDERER_CONTENT_SETTINGS_AGENT_IMPL_H_
 
 #include <string>
 #include <utility>
@@ -13,7 +13,8 @@
 #include "base/containers/flat_set.h"
 #include "base/gtest_prod_util.h"
 #include "base/time/time.h"
-#include "chrome/common/content_settings_renderer.mojom.h"
+#include "chrome/common/content_settings_agent.mojom.h"
+#include "chrome/common/content_settings_manager.mojom.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "content/public/renderer/render_frame_observer.h"
@@ -31,28 +32,31 @@
 class WebFrame;
 class WebSecurityOrigin;
 class WebURL;
-}
+}  // namespace blink
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 namespace extensions {
 class Dispatcher;
 class Extension;
-}
+}  // namespace extensions
 #endif
 
-// Handles blocking content per content settings for each RenderFrame.
-class ContentSettingsObserver
+// This class serves as an agent of the browser-side content settings machinery
+// to implement browser-specified rules directly within the renderer process.
+// In some cases it forwards requests on to the browser to determine policy.
+// An instance of this class is associated w/ each RenderFrame in the process.
+class ContentSettingsAgentImpl
     : public content::RenderFrameObserver,
-      public content::RenderFrameObserverTracker<ContentSettingsObserver>,
+      public content::RenderFrameObserverTracker<ContentSettingsAgentImpl>,
       public blink::WebContentSettingsClient,
-      public chrome::mojom::ContentSettingsRenderer {
+      public chrome::mojom::ContentSettingsAgent {
  public:
   // Set |should_whitelist| to true if |render_frame()| contains content that
   // should be whitelisted for content settings.
-  ContentSettingsObserver(content::RenderFrame* render_frame,
-                          bool should_whitelist,
-                          service_manager::BinderRegistry* registry);
-  ~ContentSettingsObserver() override;
+  ContentSettingsAgentImpl(content::RenderFrame* render_frame,
+                           bool should_whitelist,
+                           service_manager::BinderRegistry* registry);
+  ~ContentSettingsAgentImpl() override;
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // Sets the extension dispatcher. Call this right after constructing this
@@ -62,7 +66,7 @@
 
   // Sets the content setting rules which back |allowImage()|, |allowScript()|,
   // |allowScriptFromSource()| and |allowAutoplay()|. |content_setting_rules|
-  // must outlive this |ContentSettingsObserver|.
+  // must outlive this |ContentSettingsAgentImpl|.
   void SetContentSettingRules(
       const RendererContentSettingRules* content_setting_rules);
   const RendererContentSettingRules* GetContentSettingRules();
@@ -112,11 +116,14 @@
     return allow_running_insecure_content_;
   }
 
+  void SetContentSettingsManagerForTesting(
+      mojo::Remote<chrome::mojom::ContentSettingsManager> manager);
+
  private:
-  FRIEND_TEST_ALL_PREFIXES(ContentSettingsObserverTest, WhitelistedSchemes);
-  FRIEND_TEST_ALL_PREFIXES(ContentSettingsObserverBrowserTest,
+  FRIEND_TEST_ALL_PREFIXES(ContentSettingsAgentImplTest, WhitelistedSchemes);
+  FRIEND_TEST_ALL_PREFIXES(ContentSettingsAgentImplBrowserTest,
                            ContentSettingsInterstitialPages);
-  FRIEND_TEST_ALL_PREFIXES(ContentSettingsObserverBrowserTest,
+  FRIEND_TEST_ALL_PREFIXES(ContentSettingsAgentImplBrowserTest,
                            PluginsTemporarilyAllowed);
 
   // RenderFrameObserver implementation.
@@ -125,17 +132,16 @@
                                 ui::PageTransition transition) override;
   void OnDestruct() override;
 
-  // chrome::mojom::ContentSettingsRenderer:
+  // chrome::mojom::ContentSettingsAgent:
   void SetAllowRunningInsecureContent() override;
   void SetAsInterstitial() override;
 
-  void OnContentSettingsRendererRequest(
-      mojo::PendingAssociatedReceiver<chrome::mojom::ContentSettingsRenderer>
+  void OnContentSettingsAgentRequest(
+      mojo::PendingAssociatedReceiver<chrome::mojom::ContentSettingsAgent>
           receiver);
 
   // Message handlers.
   void OnLoadBlockedPlugins(const std::string& identifier);
-  void OnRequestFileSystemAccessAsyncResponse(int request_id, bool allowed);
 
   // Resets the |content_blocked_| array.
   void ClearBlockedContentSettings();
@@ -145,7 +151,7 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // If |origin| corresponds to an installed extension, returns that extension.
-  // Otherwise returns NULL.
+  // Otherwise returns null.
   const extensions::Extension* GetExtension(
       const blink::WebSecurityOrigin& origin) const;
 #endif
@@ -160,6 +166,8 @@
       const blink::WebSecurityOrigin& origin,
       const blink::WebURL& document_url);
 
+  mojo::Remote<chrome::mojom::ContentSettingsManager> content_settings_manager_;
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // Owned by ChromeContentRendererClient and outlive us.
   extensions::Dispatcher* extension_dispatcher_ = nullptr;
@@ -187,16 +195,12 @@
   base::flat_set<std::string> temporarily_allowed_plugins_;
   bool is_interstitial_page_ = false;
 
-  int current_request_id_ = 0;
-  base::flat_map<int, base::OnceCallback<void(bool)>> permission_requests_;
-
   // If true, IsWhitelistedForContentSettings will always return true.
   const bool should_whitelist_;
 
-  mojo::AssociatedReceiverSet<chrome::mojom::ContentSettingsRenderer>
-      receivers_;
+  mojo::AssociatedReceiverSet<chrome::mojom::ContentSettingsAgent> receivers_;
 
-  DISALLOW_COPY_AND_ASSIGN(ContentSettingsObserver);
+  DISALLOW_COPY_AND_ASSIGN(ContentSettingsAgentImpl);
 };
 
-#endif  // CHROME_RENDERER_CONTENT_SETTINGS_OBSERVER_H_
+#endif  // CHROME_RENDERER_CONTENT_SETTINGS_AGENT_IMPL_H_
diff --git a/chrome/renderer/content_settings_observer_browsertest.cc b/chrome/renderer/content_settings_agent_impl_browsertest.cc
similarity index 63%
rename from chrome/renderer/content_settings_observer_browsertest.cc
rename to chrome/renderer/content_settings_agent_impl_browsertest.cc
index 08ff54d..f77e73d 100644
--- a/chrome/renderer/content_settings_observer_browsertest.cc
+++ b/chrome/renderer/content_settings_agent_impl_browsertest.cc
@@ -10,7 +10,7 @@
 #include "base/run_loop.h"
 #include "base/values.h"
 #include "chrome/common/render_messages.h"
-#include "chrome/renderer/content_settings_observer.h"
+#include "chrome/renderer/content_settings_agent_impl.h"
 #include "chrome/test/base/chrome_render_view_test.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
@@ -18,6 +18,7 @@
 #include "content/public/renderer/render_view.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_test_sink.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
@@ -41,53 +42,83 @@
 constexpr char kScriptWithSrcHtml[] = R"HTML(
   <html>
   <head>
-    <script src="http://www.example.com/script.js"></script>
+    <script src='http://www.example.com/script.js'></script>
   </head>
   <body></body>
   </html>
 )HTML";
 
-class MockContentSettingsObserver : public ContentSettingsObserver {
+class MockContentSettingsManagerImpl
+    : public chrome::mojom::ContentSettingsManager {
  public:
-  MockContentSettingsObserver(content::RenderFrame* render_frame,
-                              service_manager::BinderRegistry* registry);
-  ~MockContentSettingsObserver() override {}
+  MockContentSettingsManagerImpl() = default;
+  ~MockContentSettingsManagerImpl() override = default;
+
+  // chrome::mojom::ContentSettingsManager methods:
+  void Clone(mojo::PendingReceiver<chrome::mojom::ContentSettingsManager>
+                 receiver) override {
+    ADD_FAILURE() << "Not reached";
+  }
+  void AllowStorageAccess(StorageType storage_type,
+                          const url::Origin& origin,
+                          const GURL& site_for_cookies,
+                          const url::Origin& top_frame_origin,
+                          base::OnceCallback<void(bool)> callback) override {
+    ++allow_storage_access_count_;
+    std::move(callback).Run(true);
+  }
+
+  int allow_storage_access_count() const { return allow_storage_access_count_; }
+
+ private:
+  int allow_storage_access_count_ = 0;
+};
+
+class MockContentSettingsAgentImpl : public ContentSettingsAgentImpl {
+ public:
+  MockContentSettingsAgentImpl(content::RenderFrame* render_frame,
+                               service_manager::BinderRegistry* registry);
+  ~MockContentSettingsAgentImpl() override {}
 
   bool Send(IPC::Message* message) override;
 
   MOCK_METHOD2(OnContentBlocked,
                void(ContentSettingsType, const base::string16&));
 
-  MOCK_METHOD6(OnAllowDOMStorage,
-               void(int,
-                    const url::Origin&,
-                    const GURL&,
-                    const url::Origin&,
-                    bool,
-                    IPC::Message*));
-
   const GURL& image_url() const { return image_url_; }
   const std::string& image_origin() const { return image_origin_; }
 
+  MockContentSettingsManagerImpl* mock_manager() const {
+    return static_cast<MockContentSettingsManagerImpl*>(
+        mock_manager_.get()->impl());
+  }
+
  private:
+  mojo::SelfOwnedReceiverRef<chrome::mojom::ContentSettingsManager>
+      mock_manager_;
   const GURL image_url_;
   const std::string image_origin_;
 
-  DISALLOW_COPY_AND_ASSIGN(MockContentSettingsObserver);
+  DISALLOW_COPY_AND_ASSIGN(MockContentSettingsAgentImpl);
 };
 
-MockContentSettingsObserver::MockContentSettingsObserver(
+MockContentSettingsAgentImpl::MockContentSettingsAgentImpl(
     content::RenderFrame* render_frame,
     service_manager::BinderRegistry* registry)
-    : ContentSettingsObserver(render_frame, false, registry),
+    : ContentSettingsAgentImpl(render_frame, false, registry),
       image_url_("http://www.foo.com/image.jpg"),
-      image_origin_("http://www.foo.com") {}
+      image_origin_("http://www.foo.com") {
+  // This raw pointer is kept alive by |mock_manager_remote|.
+  mojo::Remote<chrome::mojom::ContentSettingsManager> mock_manager_remote;
+  mock_manager_ = mojo::MakeSelfOwnedReceiver(
+      std::make_unique<MockContentSettingsManagerImpl>(),
+      mock_manager_remote.BindNewPipeAndPassReceiver());
+  SetContentSettingsManagerForTesting(std::move(mock_manager_remote));
+}
 
-bool MockContentSettingsObserver::Send(IPC::Message* message) {
-  IPC_BEGIN_MESSAGE_MAP(MockContentSettingsObserver, *message)
+bool MockContentSettingsAgentImpl::Send(IPC::Message* message) {
+  IPC_BEGIN_MESSAGE_MAP(MockContentSettingsAgentImpl, *message)
     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_ContentBlocked, OnContentBlocked)
-    IPC_MESSAGE_HANDLER_DELAY_REPLY(ChromeViewHostMsg_AllowDOMStorage,
-                                    OnAllowDOMStorage)
     IPC_MESSAGE_UNHANDLED(ADD_FAILURE())
   IPC_END_MESSAGE_MAP()
 
@@ -126,7 +157,7 @@
 
 }  // namespace
 
-class ContentSettingsObserverBrowserTest : public ChromeRenderViewTest {
+class ContentSettingsAgentImplBrowserTest : public ChromeRenderViewTest {
   void SetUp() override {
     ChromeRenderViewTest::SetUp();
 
@@ -134,45 +165,45 @@
     // a WebURLLoader.
     CreateFakeWebURLLoaderFactory();
 
-    // Unbind the ContentSettingsRenderer interface that would be registered by
-    // the ContentSettingsObserver created when the render frame is created.
+    // Unbind the ContentSettingsAgent interface that would be registered by
+    // the ContentSettingsAgentImpl created when the render frame is created.
     view_->GetMainRenderFrame()
         ->GetAssociatedInterfaceRegistry()
-        ->RemoveInterface(chrome::mojom::ContentSettingsRenderer::Name_);
+        ->RemoveInterface(chrome::mojom::ContentSettingsAgent::Name_);
   }
 };
 
-TEST_F(ContentSettingsObserverBrowserTest, DidBlockContentType) {
-  MockContentSettingsObserver observer(view_->GetMainRenderFrame(),
-                                       registry_.get());
-  EXPECT_CALL(observer, OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES,
-                                         base::string16()));
-  observer.DidBlockContentType(CONTENT_SETTINGS_TYPE_COOKIES);
+TEST_F(ContentSettingsAgentImplBrowserTest, DidBlockContentType) {
+  MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
+                                          registry_.get());
+  EXPECT_CALL(mock_agent, OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES,
+                                           base::string16()));
+  mock_agent.DidBlockContentType(CONTENT_SETTINGS_TYPE_COOKIES);
 
   // Blocking the same content type a second time shouldn't send a notification.
-  observer.DidBlockContentType(CONTENT_SETTINGS_TYPE_COOKIES);
-  ::testing::Mock::VerifyAndClearExpectations(&observer);
+  mock_agent.DidBlockContentType(CONTENT_SETTINGS_TYPE_COOKIES);
+  ::testing::Mock::VerifyAndClearExpectations(&mock_agent);
 }
 
 // Tests that multiple invokations of AllowDOMStorage result in a single IPC.
-TEST_F(ContentSettingsObserverBrowserTest, AllowDOMStorage) {
+TEST_F(ContentSettingsAgentImplBrowserTest, AllowDOMStorage) {
   // Load some HTML, so we have a valid security origin.
   LoadHTMLWithUrlOverride("<html></html>", "https://example.com/");
-  MockContentSettingsObserver observer(view_->GetMainRenderFrame(),
-                                       registry_.get());
-  ON_CALL(observer, OnAllowDOMStorage(_, _, _, _, _, _))
-      .WillByDefault(DeleteArg<5>());
-  EXPECT_CALL(observer, OnAllowDOMStorage(_, _, _, _, _, _));
-  observer.AllowStorage(true);
+  MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
+                                          registry_.get());
+  mock_agent.AllowStorage(true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, mock_agent.mock_manager()->allow_storage_access_count());
 
   // Accessing localStorage from the same origin again shouldn't result in a
   // new IPC.
-  observer.AllowStorage(true);
-  ::testing::Mock::VerifyAndClearExpectations(&observer);
+  mock_agent.AllowStorage(true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, mock_agent.mock_manager()->allow_storage_access_count());
 }
 
 // Regression test for http://crbug.com/35011
-TEST_F(ContentSettingsObserverBrowserTest, JSBlockSentAfterPageLoad) {
+TEST_F(ContentSettingsAgentImplBrowserTest, JSBlockSentAfterPageLoad) {
   // 1. Load page with JS.
   const char kHtml[] =
       "<html>"
@@ -194,9 +225,9 @@
       base::Value::FromUniquePtrValue(
           content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
       std::string(), false));
-  ContentSettingsObserver* observer = ContentSettingsObserver::Get(
-      view_->GetMainRenderFrame());
-  observer->SetContentSettingRules(&content_setting_rules);
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  agent->SetContentSettingRules(&content_setting_rules);
 
   // Make sure no pending messages are in the queue.
   base::RunLoop().RunUntilIdle();
@@ -225,41 +256,41 @@
   EXPECT_TRUE(HasSentChromeViewHostMsgContentBlocked(render_thread_.get()));
 }
 
-TEST_F(ContentSettingsObserverBrowserTest, PluginsTemporarilyAllowed) {
+TEST_F(ContentSettingsAgentImplBrowserTest, PluginsTemporarilyAllowed) {
   // Load some HTML.
   LoadHTML("<html>Foo</html>");
 
   std::string foo_plugin = "foo";
   std::string bar_plugin = "bar";
 
-  ContentSettingsObserver* observer =
-      ContentSettingsObserver::Get(view_->GetMainRenderFrame());
-  EXPECT_FALSE(observer->IsPluginTemporarilyAllowed(foo_plugin));
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  EXPECT_FALSE(agent->IsPluginTemporarilyAllowed(foo_plugin));
 
   // Temporarily allow the "foo" plugin.
-  observer->OnLoadBlockedPlugins(foo_plugin);
-  EXPECT_TRUE(observer->IsPluginTemporarilyAllowed(foo_plugin));
-  EXPECT_FALSE(observer->IsPluginTemporarilyAllowed(bar_plugin));
+  agent->OnLoadBlockedPlugins(foo_plugin);
+  EXPECT_TRUE(agent->IsPluginTemporarilyAllowed(foo_plugin));
+  EXPECT_FALSE(agent->IsPluginTemporarilyAllowed(bar_plugin));
 
   // Simulate same document navigation.
   OnSameDocumentNavigation(GetMainFrame(), true);
-  EXPECT_TRUE(observer->IsPluginTemporarilyAllowed(foo_plugin));
-  EXPECT_FALSE(observer->IsPluginTemporarilyAllowed(bar_plugin));
+  EXPECT_TRUE(agent->IsPluginTemporarilyAllowed(foo_plugin));
+  EXPECT_FALSE(agent->IsPluginTemporarilyAllowed(bar_plugin));
 
   // Navigate to a different page.
   LoadHTML("<html>Bar</html>");
-  EXPECT_FALSE(observer->IsPluginTemporarilyAllowed(foo_plugin));
-  EXPECT_FALSE(observer->IsPluginTemporarilyAllowed(bar_plugin));
+  EXPECT_FALSE(agent->IsPluginTemporarilyAllowed(foo_plugin));
+  EXPECT_FALSE(agent->IsPluginTemporarilyAllowed(bar_plugin));
 
   // Temporarily allow all plugins.
-  observer->OnLoadBlockedPlugins(std::string());
-  EXPECT_TRUE(observer->IsPluginTemporarilyAllowed(foo_plugin));
-  EXPECT_TRUE(observer->IsPluginTemporarilyAllowed(bar_plugin));
+  agent->OnLoadBlockedPlugins(std::string());
+  EXPECT_TRUE(agent->IsPluginTemporarilyAllowed(foo_plugin));
+  EXPECT_TRUE(agent->IsPluginTemporarilyAllowed(bar_plugin));
 }
 
-TEST_F(ContentSettingsObserverBrowserTest, ImagesBlockedByDefault) {
-  MockContentSettingsObserver mock_observer(view_->GetMainRenderFrame(),
-                                            registry_.get());
+TEST_F(ContentSettingsAgentImplBrowserTest, ImagesBlockedByDefault) {
+  MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
+                                          registry_.get());
 
   // Load some HTML.
   LoadHTML("<html>Foo</html>");
@@ -274,33 +305,34 @@
           content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
       std::string(), false));
 
-  ContentSettingsObserver* observer = ContentSettingsObserver::Get(
-      view_->GetMainRenderFrame());
-  observer->SetContentSettingRules(&content_setting_rules);
-  EXPECT_CALL(mock_observer,
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  agent->SetContentSettingRules(&content_setting_rules);
+  EXPECT_CALL(mock_agent,
               OnContentBlocked(CONTENT_SETTINGS_TYPE_IMAGES, base::string16()));
-  EXPECT_FALSE(observer->AllowImage(true, mock_observer.image_url()));
-  ::testing::Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_FALSE(agent->AllowImage(true, mock_agent.image_url()));
+  ::testing::Mock::VerifyAndClearExpectations(&agent);
 
   // Create an exception which allows the image.
   image_setting_rules.insert(
       image_setting_rules.begin(),
       ContentSettingPatternSource(
           ContentSettingsPattern::Wildcard(),
-          ContentSettingsPattern::FromString(mock_observer.image_origin()),
+          ContentSettingsPattern::FromString(mock_agent.image_origin()),
           base::Value::FromUniquePtrValue(
               content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
           std::string(), false));
 
-  EXPECT_CALL(mock_observer, OnContentBlocked(CONTENT_SETTINGS_TYPE_IMAGES,
-                                              base::string16())).Times(0);
-  EXPECT_TRUE(observer->AllowImage(true, mock_observer.image_url()));
-  ::testing::Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_CALL(mock_agent,
+              OnContentBlocked(CONTENT_SETTINGS_TYPE_IMAGES, base::string16()))
+      .Times(0);
+  EXPECT_TRUE(agent->AllowImage(true, mock_agent.image_url()));
+  ::testing::Mock::VerifyAndClearExpectations(&agent);
 }
 
-TEST_F(ContentSettingsObserverBrowserTest, ImagesAllowedByDefault) {
-  MockContentSettingsObserver mock_observer(view_->GetMainRenderFrame(),
-                                            registry_.get());
+TEST_F(ContentSettingsAgentImplBrowserTest, ImagesAllowedByDefault) {
+  MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
+                                          registry_.get());
 
   // Load some HTML.
   LoadHTML("<html>Foo</html>");
@@ -315,30 +347,31 @@
           content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
       std::string(), false));
 
-  ContentSettingsObserver* observer =
-      ContentSettingsObserver::Get(view_->GetMainRenderFrame());
-  observer->SetContentSettingRules(&content_setting_rules);
-  EXPECT_CALL(mock_observer, OnContentBlocked(CONTENT_SETTINGS_TYPE_IMAGES,
-                                              base::string16())).Times(0);
-  EXPECT_TRUE(observer->AllowImage(true, mock_observer.image_url()));
-  ::testing::Mock::VerifyAndClearExpectations(&observer);
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  agent->SetContentSettingRules(&content_setting_rules);
+  EXPECT_CALL(mock_agent,
+              OnContentBlocked(CONTENT_SETTINGS_TYPE_IMAGES, base::string16()))
+      .Times(0);
+  EXPECT_TRUE(agent->AllowImage(true, mock_agent.image_url()));
+  ::testing::Mock::VerifyAndClearExpectations(&agent);
 
   // Create an exception which blocks the image.
   image_setting_rules.insert(
       image_setting_rules.begin(),
       ContentSettingPatternSource(
           ContentSettingsPattern::Wildcard(),
-          ContentSettingsPattern::FromString(mock_observer.image_origin()),
+          ContentSettingsPattern::FromString(mock_agent.image_origin()),
           base::Value::FromUniquePtrValue(
               content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
           std::string(), false));
-  EXPECT_CALL(mock_observer,
+  EXPECT_CALL(mock_agent,
               OnContentBlocked(CONTENT_SETTINGS_TYPE_IMAGES, base::string16()));
-  EXPECT_FALSE(observer->AllowImage(true, mock_observer.image_url()));
-  ::testing::Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_FALSE(agent->AllowImage(true, mock_agent.image_url()));
+  ::testing::Mock::VerifyAndClearExpectations(&agent);
 }
 
-TEST_F(ContentSettingsObserverBrowserTest, ContentSettingsBlockScripts) {
+TEST_F(ContentSettingsAgentImplBrowserTest, ContentSettingsBlockScripts) {
   // Set the content settings for scripts.
   RendererContentSettingRules content_setting_rules;
   ContentSettingsForOneType& script_setting_rules =
@@ -349,9 +382,9 @@
           content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
       std::string(), false));
 
-  ContentSettingsObserver* observer =
-      ContentSettingsObserver::Get(view_->GetMainRenderFrame());
-  observer->SetContentSettingRules(&content_setting_rules);
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  agent->SetContentSettingRules(&content_setting_rules);
 
   // Load a page which contains a script.
   LoadHTML(kScriptHtml);
@@ -361,7 +394,7 @@
       ChromeViewHostMsg_ContentBlocked::ID));
 }
 
-TEST_F(ContentSettingsObserverBrowserTest, ContentSettingsAllowScripts) {
+TEST_F(ContentSettingsAgentImplBrowserTest, ContentSettingsAllowScripts) {
   // Set the content settings for scripts.
   RendererContentSettingRules content_setting_rules;
   ContentSettingsForOneType& script_setting_rules =
@@ -372,9 +405,9 @@
           content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
       std::string(), false));
 
-  ContentSettingsObserver* observer =
-      ContentSettingsObserver::Get(view_->GetMainRenderFrame());
-  observer->SetContentSettingRules(&content_setting_rules);
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  agent->SetContentSettingRules(&content_setting_rules);
 
   // Load a page which contains a script.
   LoadHTML(kScriptHtml);
@@ -384,7 +417,8 @@
       ChromeViewHostMsg_ContentBlocked::ID));
 }
 
-TEST_F(ContentSettingsObserverBrowserTest, ContentSettingsAllowScriptsWithSrc) {
+TEST_F(ContentSettingsAgentImplBrowserTest,
+       ContentSettingsAllowScriptsWithSrc) {
   // Set the content settings for scripts.
   RendererContentSettingRules content_setting_rules;
   ContentSettingsForOneType& script_setting_rules =
@@ -395,9 +429,9 @@
           content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
       std::string(), false));
 
-  ContentSettingsObserver* observer =
-      ContentSettingsObserver::Get(view_->GetMainRenderFrame());
-  observer->SetContentSettingRules(&content_setting_rules);
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  agent->SetContentSettingRules(&content_setting_rules);
 
   // Load a page which contains a script.
   LoadHTML(kScriptWithSrcHtml);
@@ -410,7 +444,7 @@
 // Regression test for crbug.com/232410: Load a page with JS blocked. Then,
 // allow JS and reload the page. In each case, only one of noscript or script
 // tags should be enabled, but never both.
-TEST_F(ContentSettingsObserverBrowserTest, ContentSettingsNoscriptTag) {
+TEST_F(ContentSettingsAgentImplBrowserTest, ContentSettingsNoscriptTag) {
   // 1. Block JavaScript.
   RendererContentSettingRules content_setting_rules;
   ContentSettingsForOneType& script_setting_rules =
@@ -421,9 +455,9 @@
           content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
       std::string(), false));
 
-  ContentSettingsObserver* observer =
-      ContentSettingsObserver::Get(view_->GetMainRenderFrame());
-  observer->SetContentSettingRules(&content_setting_rules);
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  agent->SetContentSettingRules(&content_setting_rules);
 
   // 2. Load a page which contains a noscript tag and a script tag. Note that
   // the page doesn't have a body tag.
@@ -453,7 +487,7 @@
       base::Value::FromUniquePtrValue(
           content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
       std::string(), false));
-  observer->SetContentSettingRules(&content_setting_rules);
+  agent->SetContentSettingRules(&content_setting_rules);
 
   // 4. Reload the page.
   std::string url_str = "data:text/html;charset=utf-8,";
@@ -476,10 +510,10 @@
 
 // Checks that same document navigations don't update content settings for the
 // page.
-TEST_F(ContentSettingsObserverBrowserTest,
+TEST_F(ContentSettingsAgentImplBrowserTest,
        ContentSettingsSameDocumentNavigation) {
-  MockContentSettingsObserver mock_observer(view_->GetMainRenderFrame(),
-                                            registry_.get());
+  MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
+                                          registry_.get());
   // Load a page which contains a script.
   LoadHTML(kScriptHtml);
 
@@ -497,19 +531,19 @@
           content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
       std::string(), false));
 
-  ContentSettingsObserver* observer =
-      ContentSettingsObserver::Get(view_->GetMainRenderFrame());
-  observer->SetContentSettingRules(&content_setting_rules);
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  agent->SetContentSettingRules(&content_setting_rules);
 
   // The page shouldn't see the change to script blocking setting after a
   // same document navigation.
   OnSameDocumentNavigation(GetMainFrame(), true);
-  EXPECT_TRUE(observer->AllowScript(true));
+  EXPECT_TRUE(agent->AllowScript(true));
 }
 
-TEST_F(ContentSettingsObserverBrowserTest, ContentSettingsInterstitialPages) {
-  MockContentSettingsObserver mock_observer(view_->GetMainRenderFrame(),
-                                            registry_.get());
+TEST_F(ContentSettingsAgentImplBrowserTest, ContentSettingsInterstitialPages) {
+  MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
+                                          registry_.get());
   // Block scripts.
   RendererContentSettingRules content_setting_rules;
   ContentSettingsForOneType& script_setting_rules =
@@ -528,10 +562,10 @@
           content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
       std::string(), false));
 
-  ContentSettingsObserver* observer =
-      ContentSettingsObserver::Get(view_->GetMainRenderFrame());
-  observer->SetContentSettingRules(&content_setting_rules);
-  observer->SetAsInterstitial();
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  agent->SetContentSettingRules(&content_setting_rules);
+  agent->SetAsInterstitial();
 
   // Load a page which contains a script.
   LoadHTML(kScriptHtml);
@@ -541,15 +575,16 @@
       ChromeViewHostMsg_ContentBlocked::ID));
 
   // Verify that images are allowed.
-  EXPECT_CALL(mock_observer, OnContentBlocked(CONTENT_SETTINGS_TYPE_IMAGES,
-                                              base::string16())).Times(0);
-  EXPECT_TRUE(observer->AllowImage(true, mock_observer.image_url()));
-  ::testing::Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_CALL(mock_agent,
+              OnContentBlocked(CONTENT_SETTINGS_TYPE_IMAGES, base::string16()))
+      .Times(0);
+  EXPECT_TRUE(agent->AllowImage(true, mock_agent.image_url()));
+  ::testing::Mock::VerifyAndClearExpectations(&agent);
 }
 
-TEST_F(ContentSettingsObserverBrowserTest, AutoplayContentSettings) {
-  MockContentSettingsObserver mock_observer(view_->GetMainRenderFrame(),
-                                            registry_.get());
+TEST_F(ContentSettingsAgentImplBrowserTest, AutoplayContentSettings) {
+  MockContentSettingsAgentImpl mock_agent(view_->GetMainRenderFrame(),
+                                          registry_.get());
 
   // Load some HTML.
   LoadHTML("<html>Foo</html>");
@@ -564,12 +599,12 @@
           content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW)),
       std::string(), false));
 
-  ContentSettingsObserver* observer =
-      ContentSettingsObserver::Get(view_->GetMainRenderFrame());
-  observer->SetContentSettingRules(&content_setting_rules);
+  ContentSettingsAgentImpl* agent =
+      ContentSettingsAgentImpl::Get(view_->GetMainRenderFrame());
+  agent->SetContentSettingRules(&content_setting_rules);
 
-  EXPECT_TRUE(observer->AllowAutoplay(false));
-  ::testing::Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_TRUE(agent->AllowAutoplay(false));
+  ::testing::Mock::VerifyAndClearExpectations(&agent);
 
   // Add rule to block autoplay.
   autoplay_setting_rules.insert(
@@ -581,6 +616,6 @@
               content_settings::ContentSettingToValue(CONTENT_SETTING_BLOCK)),
           std::string(), false));
 
-  EXPECT_FALSE(observer->AllowAutoplay(true));
-  ::testing::Mock::VerifyAndClearExpectations(&observer);
+  EXPECT_FALSE(agent->AllowAutoplay(true));
+  ::testing::Mock::VerifyAndClearExpectations(&agent);
 }
diff --git a/chrome/renderer/content_settings_observer_unittest.cc b/chrome/renderer/content_settings_agent_impl_unittest.cc
similarity index 68%
rename from chrome/renderer/content_settings_observer_unittest.cc
rename to chrome/renderer/content_settings_agent_impl_unittest.cc
index 7eb7dfc7..b7132683 100644
--- a/chrome/renderer/content_settings_observer_unittest.cc
+++ b/chrome/renderer/content_settings_agent_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/renderer/content_settings_observer.h"
+#include "chrome/renderer/content_settings_agent_impl.h"
 
 #include "chrome/common/url_constants.h"
 #include "content/public/common/url_constants.h"
@@ -18,36 +18,35 @@
 
 using blink::WebSecurityOrigin;
 
-typedef testing::Test ContentSettingsObserverTest;
+typedef testing::Test ContentSettingsAgentImplTest;
 
-TEST_F(ContentSettingsObserverTest, WhitelistedSchemes) {
+TEST_F(ContentSettingsAgentImplTest, WhitelistedSchemes) {
   std::string end_url = ":something";
 
   GURL chrome_ui_url =
       GURL(std::string(content::kChromeUIScheme).append(end_url));
-  EXPECT_TRUE(ContentSettingsObserver::IsWhitelistedForContentSettings(
+  EXPECT_TRUE(ContentSettingsAgentImpl::IsWhitelistedForContentSettings(
       WebSecurityOrigin::Create(chrome_ui_url), GURL()));
 
   GURL chrome_dev_tools_url =
       GURL(std::string(content::kChromeDevToolsScheme).append(end_url));
-  EXPECT_TRUE(ContentSettingsObserver::IsWhitelistedForContentSettings(
+  EXPECT_TRUE(ContentSettingsAgentImpl::IsWhitelistedForContentSettings(
       WebSecurityOrigin::Create(chrome_dev_tools_url), GURL()));
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   GURL extension_url =
       GURL(std::string(extensions::kExtensionScheme).append(end_url));
-  EXPECT_TRUE(ContentSettingsObserver::IsWhitelistedForContentSettings(
+  EXPECT_TRUE(ContentSettingsAgentImpl::IsWhitelistedForContentSettings(
       WebSecurityOrigin::Create(extension_url), GURL()));
 #endif
 
   GURL file_url("file:///dir/");
-  EXPECT_TRUE(ContentSettingsObserver::IsWhitelistedForContentSettings(
+  EXPECT_TRUE(ContentSettingsAgentImpl::IsWhitelistedForContentSettings(
       WebSecurityOrigin::Create(file_url), GURL("file:///dir/")));
-  EXPECT_FALSE(ContentSettingsObserver::IsWhitelistedForContentSettings(
+  EXPECT_FALSE(ContentSettingsAgentImpl::IsWhitelistedForContentSettings(
       WebSecurityOrigin::Create(file_url), GURL("file:///dir/file")));
 
-  GURL http_url =
-      GURL("http://server.com/path");
-  EXPECT_FALSE(ContentSettingsObserver::IsWhitelistedForContentSettings(
+  GURL http_url = GURL("http://server.com/path");
+  EXPECT_FALSE(ContentSettingsAgentImpl::IsWhitelistedForContentSettings(
       WebSecurityOrigin::Create(http_url), GURL()));
 }
diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
index 205c7398f..4b0389d 100644
--- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc
+++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
@@ -20,7 +20,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/renderer_resources.h"
 #include "chrome/renderer/chrome_content_renderer_client.h"
-#include "chrome/renderer/content_settings_observer.h"
+#include "chrome/renderer/content_settings_agent_impl.h"
 #include "chrome/renderer/custom_menu_commands.h"
 #include "chrome/renderer/plugins/plugin_preroller.h"
 #include "chrome/renderer/plugins/plugin_uma.h"
@@ -358,7 +358,7 @@
 
   if (status ==
       content::RenderFrame::PeripheralContentStatus::CONTENT_STATUS_TINY) {
-    ContentSettingsObserver::Get(render_frame())
+    ContentSettingsAgentImpl::Get(render_frame())
         ->DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS, title_);
   }
 
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc
index 1600fc6..8b82627 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc
@@ -14,7 +14,7 @@
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_request.h"
@@ -66,7 +66,7 @@
 
 void SubresourceRedirectURLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& response_head,
+    const network::mojom::URLResponseHead& response_head,
     bool* defer,
     std::vector<std::string>* to_be_removed_request_headers,
     net::HttpRequestHeaders* modified_request_headers) {
@@ -78,7 +78,7 @@
 
 void SubresourceRedirectURLLoaderThrottle::BeforeWillProcessResponse(
     const GURL& response_url,
-    const network::ResourceResponseHead& response_head,
+    const network::mojom::URLResponseHead& response_head,
     bool* defer) {
   // If response was not from the compression server, don't restart it.
   if (!response_url.is_valid())
@@ -112,7 +112,7 @@
 
 void SubresourceRedirectURLLoaderThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {
   // If response was not from the compression server, don't record any metrics.
   if (!response_url.is_valid())
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h
index e4df33e..2a59b25c 100644
--- a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h
+++ b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h
@@ -30,16 +30,16 @@
                         bool* defer) override;
   void WillRedirectRequest(
       net::RedirectInfo* redirect_info,
-      const network::ResourceResponseHead& response_head,
+      const network::mojom::URLResponseHead& response_head,
       bool* defer,
       std::vector<std::string>* to_be_removed_request_headers,
       net::HttpRequestHeaders* modified_request_headers) override;
   void BeforeWillProcessResponse(
       const GURL& response_url,
-      const network::ResourceResponseHead& response_head,
+      const network::mojom::URLResponseHead& response_head,
       bool* defer) override;
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override;
   void WillOnCompleteWithError(const network::URLLoaderCompletionStatus& status,
                                bool* defer) override;
diff --git a/chrome/renderer/worker_content_settings_client.cc b/chrome/renderer/worker_content_settings_client.cc
index 0f4fb5b..b04f22c 100644
--- a/chrome/renderer/worker_content_settings_client.cc
+++ b/chrome/renderer/worker_content_settings_client.cc
@@ -5,10 +5,11 @@
 #include "chrome/renderer/worker_content_settings_client.h"
 
 #include "chrome/common/render_messages.h"
-#include "chrome/renderer/content_settings_observer.h"
+#include "chrome/renderer/content_settings_agent_impl.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_thread.h"
 #include "ipc/ipc_sync_message_filter.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/platform/url_conversion.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/public/web/web_document.h"
@@ -17,19 +18,25 @@
 
 WorkerContentSettingsClient::WorkerContentSettingsClient(
     content::RenderFrame* render_frame)
-    : routing_id_(render_frame->GetRoutingID()), is_unique_origin_(false) {
+    : routing_id_(render_frame->GetRoutingID()),
+      sync_message_filter_(
+          content::RenderThread::Get()->GetSyncMessageFilter()) {
   blink::WebLocalFrame* frame = render_frame->GetWebFrame();
-  if (frame->GetDocument().GetSecurityOrigin().IsUnique() ||
+  const blink::WebDocument& document = frame->GetDocument();
+  if (document.GetSecurityOrigin().IsUnique() ||
       frame->Top()->GetSecurityOrigin().IsUnique())
     is_unique_origin_ = true;
-  sync_message_filter_ = content::RenderThread::Get()->GetSyncMessageFilter();
-  document_origin_ = frame->GetDocument().GetSecurityOrigin();
-  site_for_cookies_ = frame->GetDocument().SiteForCookies();
-  top_frame_origin_ = frame->GetDocument().TopFrameOrigin();
-  allow_running_insecure_content_ = ContentSettingsObserver::Get(render_frame)
-                                        ->allow_running_insecure_content();
-  content_setting_rules_ =
-      ContentSettingsObserver::Get(render_frame)->GetContentSettingRules();
+
+  document_origin_ = document.GetSecurityOrigin();
+  site_for_cookies_ = document.SiteForCookies();
+  top_frame_origin_ = document.TopFrameOrigin();
+
+  render_frame->GetBrowserInterfaceBroker()->GetInterface(
+      pending_content_settings_manager_.InitWithNewPipeAndPassReceiver());
+
+  ContentSettingsAgentImpl* agent = ContentSettingsAgentImpl::Get(render_frame);
+  allow_running_insecure_content_ = agent->allow_running_insecure_content();
+  content_setting_rules_ = agent->GetContentSettingRules();
 }
 
 WorkerContentSettingsClient::WorkerContentSettingsClient(
@@ -41,7 +48,11 @@
       top_frame_origin_(other.top_frame_origin_),
       allow_running_insecure_content_(other.allow_running_insecure_content_),
       sync_message_filter_(other.sync_message_filter_),
-      content_setting_rules_(other.content_setting_rules_) {}
+      content_setting_rules_(other.content_setting_rules_) {
+  other.EnsureContentSettingsManager();
+  other.content_settings_manager_->Clone(
+      pending_content_settings_manager_.InitWithNewPipeAndPassReceiver());
+}
 
 WorkerContentSettingsClient::~WorkerContentSettingsClient() {}
 
@@ -51,38 +62,20 @@
 }
 
 bool WorkerContentSettingsClient::RequestFileSystemAccessSync() {
-  if (is_unique_origin_)
-    return false;
-
-  bool result = false;
-  sync_message_filter_->Send(new ChromeViewHostMsg_RequestFileSystemAccessSync(
-      routing_id_, document_origin_, site_for_cookies_, top_frame_origin_,
-      &result));
-  return result;
+  return AllowStorageAccess(
+      chrome::mojom::ContentSettingsManager::StorageType::FILE_SYSTEM);
 }
 
 bool WorkerContentSettingsClient::AllowIndexedDB(
     const blink::WebSecurityOrigin&) {
-  if (is_unique_origin_)
-    return false;
-
-  bool result = false;
-  sync_message_filter_->Send(new ChromeViewHostMsg_AllowIndexedDB(
-      routing_id_, document_origin_, site_for_cookies_, top_frame_origin_,
-      &result));
-  return result;
+  return AllowStorageAccess(
+      chrome::mojom::ContentSettingsManager::StorageType::INDEXED_DB);
 }
 
 bool WorkerContentSettingsClient::AllowCacheStorage(
     const blink::WebSecurityOrigin&) {
-  if (is_unique_origin_)
-    return false;
-
-  bool result = false;
-  sync_message_filter_->Send(new ChromeViewHostMsg_AllowCacheStorage(
-      routing_id_, document_origin_, site_for_cookies_, top_frame_origin_,
-      &result));
-  return result;
+  return AllowStorageAccess(
+      chrome::mojom::ContentSettingsManager::StorageType::CACHE);
 }
 
 bool WorkerContentSettingsClient::AllowRunningInsecureContent(
@@ -121,3 +114,25 @@
 
   return true;
 }
+
+bool WorkerContentSettingsClient::AllowStorageAccess(
+    chrome::mojom::ContentSettingsManager::StorageType storage_type) {
+  if (is_unique_origin_)
+    return false;
+
+  EnsureContentSettingsManager();
+
+  bool result = false;
+  content_settings_manager_->AllowStorageAccess(storage_type, document_origin_,
+                                                site_for_cookies_,
+                                                top_frame_origin_, &result);
+  return result;
+}
+
+void WorkerContentSettingsClient::EnsureContentSettingsManager() const {
+  // Lazily bind |content_settings_manager_| so it is bound on the right thread.
+  if (content_settings_manager_)
+    return;
+  DCHECK(pending_content_settings_manager_);
+  content_settings_manager_.Bind(std::move(pending_content_settings_manager_));
+}
diff --git a/chrome/renderer/worker_content_settings_client.h b/chrome/renderer/worker_content_settings_client.h
index 7aeb81f..e267e11a 100644
--- a/chrome/renderer/worker_content_settings_client.h
+++ b/chrome/renderer/worker_content_settings_client.h
@@ -7,6 +7,8 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "chrome/common/content_settings_manager.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/platform/web_content_settings_client.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -17,16 +19,13 @@
 
 namespace content {
 class RenderFrame;
-}
-
-namespace blink {
-class WebSecurityOrigin;
-}
+}  // namespace content
 
 struct RendererContentSettingRules;
 
 // This client is created on the main renderer thread then passed onto the
-// blink's worker thread.
+// blink's worker thread. For workers created from other workers, Clone()
+// is called on the "parent" worker's thread.
 class WorkerContentSettingsClient : public blink::WebContentSettingsClient {
  public:
   explicit WorkerContentSettingsClient(content::RenderFrame* render_frame);
@@ -46,10 +45,13 @@
  private:
   explicit WorkerContentSettingsClient(
       const WorkerContentSettingsClient& other);
+  bool AllowStorageAccess(
+      chrome::mojom::ContentSettingsManager::StorageType storage_type);
+  void EnsureContentSettingsManager() const;
 
   // Loading document context for this worker.
   const int routing_id_;
-  bool is_unique_origin_;
+  bool is_unique_origin_ = false;
   url::Origin document_origin_;
   GURL site_for_cookies_;
   url::Origin top_frame_origin_;
@@ -57,6 +59,16 @@
   scoped_refptr<IPC::SyncMessageFilter> sync_message_filter_;
   const RendererContentSettingRules* content_setting_rules_;
 
+  // Because instances of this class are created on the parent's thread (i.e,
+  // on the renderer main thread or on the thread of the parent worker), it is
+  // necessary to lazily bind the |content_settings_manager_| remote. The
+  // pending remote is initialized on the parent thread and then the remote is
+  // bound when needed on the worker's thread.
+  mutable mojo::PendingRemote<chrome::mojom::ContentSettingsManager>
+      pending_content_settings_manager_;
+  mutable mojo::Remote<chrome::mojom::ContentSettingsManager>
+      content_settings_manager_;
+
   DISALLOW_ASSIGN(WorkerContentSettingsClient);
 };
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1df437e1..7f09bd9 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1346,7 +1346,7 @@
       "../renderer/autofill/password_generation_test_utils.h",
       "../renderer/chrome_content_renderer_client_browsertest.cc",
       "../renderer/chrome_render_frame_observer_browsertest.cc",
-      "../renderer/content_settings_observer_browsertest.cc",
+      "../renderer/content_settings_agent_impl_browsertest.cc",
       "../renderer/media/cast_session_browsertest.cc",
       "../renderer/translate/translate_helper_browsertest.cc",
       "../renderer/translate/translate_script_browsertest.cc",
@@ -3336,7 +3336,7 @@
     "../common/pref_names_util_unittest.cc",
     "../common/thread_profiler_unittest.cc",
     "../renderer/chrome_content_renderer_client_unittest.cc",
-    "../renderer/content_settings_observer_unittest.cc",
+    "../renderer/content_settings_agent_impl_unittest.cc",
     "../renderer/instant_restricted_id_cache_unittest.cc",
     "../renderer/media/chrome_key_systems_provider_unittest.cc",
     "../renderer/media/flash_embed_rewrite_unittest.cc",
diff --git a/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java b/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java
index 87281dd..50d54ec 100644
--- a/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java
+++ b/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java
@@ -19,7 +19,7 @@
  * Android activity for running chrome browser tests.
  */
 public class ChromeBrowserTestsActivity extends ChromeTabbedActivity {
-    private static final String TAG = "cr_browser_test";
+    private static final String TAG = "browser_test";
 
     private NativeTest mTest = new NativeTest();
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ActivityUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ActivityUtils.java
index d3c463c..f827a0c4 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ActivityUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ActivityUtils.java
@@ -34,7 +34,7 @@
  * Collection of activity utilities.
  */
 public class ActivityUtils {
-    private static final String TAG = "cr_ActivityUtils";
+    private static final String TAG = "ActivityUtils";
 
     private static final long ACTIVITY_START_TIMEOUT_MS = 3000L;
     private static final long CONDITION_POLL_INTERVAL_MS = 100;
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java
index 741a841..2f16965 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ChromeTabUtils.java
@@ -52,7 +52,7 @@
  * A utility class that contains methods generic to all Tabs tests.
  */
 public class ChromeTabUtils {
-    private static final String TAG = "cr_ChromeTabUtils";
+    private static final String TAG = "ChromeTabUtils";
     public static final int TITLE_UPDATE_TIMEOUT_MS = 3000;
 
     /**
diff --git a/chrome/test/chromedriver/logging.cc b/chrome/test/chromedriver/logging.cc
index dbea7fe..a672d71 100644
--- a/chrome/test/chromedriver/logging.cc
+++ b/chrome/test/chromedriver/logging.cc
@@ -276,7 +276,7 @@
   return min_level_;
 }
 
-bool InitLogging() {
+bool InitLogging(uint16_t port) {
   g_start_time = base::TimeTicks::Now().ToInternalValue();
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
 
@@ -348,7 +348,7 @@
   bool res = logging::InitLogging(logging_settings);
   if (cmd_line->HasSwitch("log-path") && res) {
     VLOG(0) << "Starting " << kChromeDriverProductFullName << " "
-            << kChromeDriverVersion;
+            << kChromeDriverVersion << " on port " << port;
     VLOG(0) << GetPortProtectionMessage();
   }
   return res;
diff --git a/chrome/test/chromedriver/logging.h b/chrome/test/chromedriver/logging.h
index afc52b9..0bb21c6 100644
--- a/chrome/test/chromedriver/logging.h
+++ b/chrome/test/chromedriver/logging.h
@@ -80,7 +80,7 @@
 };
 
 // Initializes logging system for ChromeDriver. Returns true on success.
-bool InitLogging();
+bool InitLogging(uint16_t port);
 
 // Creates |Log|s, |DevToolsEventListener|s, and |CommandListener|s based on
 // logging preferences.
diff --git a/chrome/test/chromedriver/server/chromedriver_server.cc b/chrome/test/chromedriver/server/chromedriver_server.cc
index e666804d..431cec4 100644
--- a/chrome/test/chromedriver/server/chromedriver_server.cc
+++ b/chrome/test/chromedriver/server/chromedriver_server.cc
@@ -593,7 +593,7 @@
     fflush(stdout);
   }
 
-  if (!InitLogging()) {
+  if (!InitLogging(port)) {
     printf("Unable to initialize logging. Exiting...\n");
     return 1;
   }
diff --git a/chrome/test/data/autofill/captured_sites/testcases.json b/chrome/test/data/autofill/captured_sites/testcases.json
index 991febbb..0508bdd 100644
--- a/chrome/test/data/autofill/captured_sites/testcases.json
+++ b/chrome/test/data/autofill/captured_sites/testcases.json
@@ -181,7 +181,7 @@
     { "site_name": "tradesy", "expectation":"PASS" },
     { "site_name": "walmart_ca", "expectation":"PASS" },
     { "site_name": "abebooks", "expectation":"PASS" },
-    { "site_name": "bhphotovideo", "expectation":"PASS" },
+    { "site_name": "bhphotovideo", "expectation":"PASS", "disabled":true, "bug_number":1017266 },
     { "site_name": "bloomingdales", "expectation":"PASS" },
     { "site_name": "hayneedle", "expectation":"PASS" },
     { "site_name": "sears", "expectation":"PASS" },
diff --git a/chrome/test/data/password/update_form_empty_fields.html b/chrome/test/data/password/update_form_empty_fields.html
new file mode 100644
index 0000000..6a3cd6e2
--- /dev/null
+++ b/chrome/test/data/password/update_form_empty_fields.html
@@ -0,0 +1,14 @@
+<html>
+
+<body>
+  Navigation complete. Below is the password update form again with empty fields.
+  <form action="update_form_empty_fields.html" id="chg_testform_wo_username">
+    <input type="password" id="password" name="password">
+    <input type="password" id="new_password_1" name="new_password_1">
+    <input type="password" id="new_password_2" name="new_password_2">
+    <input type="submit" id="chg_submit_wo_username_button" name="chg_submit_button">
+  </form>
+
+</body>
+
+</html>
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 62ba17c1..2a9fceef3 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -1288,6 +1288,18 @@
     "note": "This policy has no pref as it is only directly read by the policy system."
   },
 
+  "BlockExternalExtensions": {
+    "os": ["win", "linux", "mac"],
+    "test_policy": { "BlockExternalExtensions": true },
+    "pref_mappings": [
+      { "pref": "extensions.block_external_extensions",
+        "indicator_tests": [
+          { "policy": { "BlockExternalExtensions": true } }
+        ]
+      }
+    ]
+  },
+
   "ShowHomeButton": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
diff --git a/chromecast/base/java/src/org/chromium/chromecast/base/CastSettingsManager.java b/chromecast/base/java/src/org/chromium/chromecast/base/CastSettingsManager.java
index 5bcf4fa..fb0ad4a 100644
--- a/chromecast/base/java/src/org/chromium/chromecast/base/CastSettingsManager.java
+++ b/chromecast/base/java/src/org/chromium/chromecast/base/CastSettingsManager.java
@@ -26,7 +26,7 @@
  */
 @JNINamespace("chromecast")
 public final class CastSettingsManager {
-    private static final String TAG = "cr_CastSettingsManager";
+    private static final String TAG = "CastSettingsManager";
 
     private static final String PREFS_FILE_NAME = "CastSettings";
 
diff --git a/chromecast/base/java/src/org/chromium/chromecast/base/CastSwitches.java b/chromecast/base/java/src/org/chromium/chromecast/base/CastSwitches.java
index 0f53c15..57c9da6 100644
--- a/chromecast/base/java/src/org/chromium/chromecast/base/CastSwitches.java
+++ b/chromecast/base/java/src/org/chromium/chromecast/base/CastSwitches.java
@@ -14,7 +14,7 @@
  * on Android.
  */
 public abstract class CastSwitches {
-    private static final String TAG = "cr_CastSwitches";
+    private static final String TAG = "CastSwitches";
 
     // Background color to use when chromium hasn't rendered anything yet. This will often be
     // displayed briefly when loading a Cast app. Format is a #ARGB in hex. (Black: #FF000000,
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index ef0bfc0..a058da6 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -210,6 +210,7 @@
       "android/cast_content_window_android.cc",
       "android/cast_content_window_android.h",
       "android/cast_metrics_helper_android.cc",
+      "android/cast_web_contents_manager_android.cc",
     ]
     deps += [
       ":jni_headers",
@@ -245,9 +246,13 @@
     sources += [
       "cast_content_window_aura.cc",
       "cast_content_window_aura.h",
+      "cast_web_contents_manager_aura.cc",
     ]
 
-    deps += [ "//ui/views:views" ]
+    deps += [
+      "//chromecast/ui:media_control_ui",
+      "//ui/views:views",
+    ]
   }
 
   if (!is_fuchsia) {
@@ -443,6 +448,7 @@
   deps = [
     "//base",
     "//chromecast/graphics",
+    "//chromecast/ui/mojom",
     "//content/public/common",
     "//ui/events",
     "//url",
diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS
index 8854a67..40adb0e 100644
--- a/chromecast/browser/DEPS
+++ b/chromecast/browser/DEPS
@@ -12,6 +12,7 @@
   "+chromecast/metrics",
   "+chromecast/net",
   "+chromecast/service",
+  "+chromecast/ui",
   "+components/cdm/browser",
   "+components/crash",
   "+components/download/public/common",
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastContentWindowAndroid.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastContentWindowAndroid.java
index 4c8396d3..8a3dbac8 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastContentWindowAndroid.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastContentWindowAndroid.java
@@ -19,10 +19,10 @@
  * <p>
  * See chromecast/browser/cast_content_window_android.* for the native half.
  */
-@JNINamespace("chromecast::shell")
+@JNINamespace("chromecast")
 public class CastContentWindowAndroid implements CastWebContentsComponent.OnComponentClosedHandler,
                                                  CastWebContentsComponent.SurfaceEventHandler {
-    private static final String TAG = "cr_CastContentWindow";
+    private static final String TAG = "CastContentWindow";
     private static final boolean DEBUG = true;
 
     // Note: CastContentWindowAndroid may outlive the native object. The native
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java
index f449108..8d27d273 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java
@@ -12,7 +12,7 @@
  */
 @JNINamespace("chromecast")
 public final class CastCrashHandler {
-    private static final String TAG = "cr_CastCrashHandler";
+    private static final String TAG = "CastCrashHandler";
 
     @CalledByNative
     public static void uploadOnce(String crashDumpPath, String crashReportsPath, String uuid,
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java
index 53d247bd..08368c5 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java
@@ -35,7 +35,7 @@
  * blocks any post-dump hooks or uploading for Android builds.
  */
 public final class CastCrashUploader {
-    private static final String TAG = "cr_CastCrashUploader";
+    private static final String TAG = "CastCrashUploader";
     private static final String CRASH_REPORT_HOST = "clients2.google.com";
     private static final String CAST_SHELL_USER_AGENT = android.os.Build.MODEL + "/CastShell";
     // Multipart dump filename has format "[random string].dmp[pid]", e.g.
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
index 340be96aa..c48e121 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
@@ -39,7 +39,7 @@
  * activity is destroyed, CastContentWindowAndroid should be notified by intent.
  */
 public class CastWebContentsActivity extends Activity {
-    private static final String TAG = "cr_CastWebActivity";
+    private static final String TAG = "CastWebActivity";
     private static final boolean DEBUG = true;
 
     // Tracks whether this Activity is between onCreate() and onDestroy().
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java
index fb323dca4..b114443 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsComponent.java
@@ -64,7 +64,7 @@
 
     @VisibleForTesting
     class ActivityDelegate implements Delegate {
-        private static final String TAG = "cr_CastWebContent_AD";
+        private static final String TAG = "CastWebContent_AD";
         private boolean mStarted;
 
         @Override
@@ -84,7 +84,7 @@
     }
 
     private class FragmentDelegate implements Delegate {
-        private static final String TAG = "cr_CastWebContent_FD";
+        private static final String TAG = "CastWebContent_FD";
 
         @Override
         public void start(StartParams params) {
@@ -118,7 +118,7 @@
     }
 
     private class ServiceDelegate implements Delegate {
-        private static final String TAG = "cr_CastWebContent_SD";
+        private static final String TAG = "CastWebContent_SD";
 
         private ServiceConnection mConnection = new ServiceConnection() {
             @Override
@@ -147,7 +147,7 @@
         }
     }
 
-    private static final String TAG = "cr_CastWebComponent";
+    private static final String TAG = "CastWebComponent";
     private static final boolean DEBUG = true;
 
     private final OnComponentClosedHandler mComponentClosedHandler;
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsFragment.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsFragment.java
index 9e1dfb8..300ddba 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsFragment.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsFragment.java
@@ -35,7 +35,7 @@
  * TODO(vincentli): Add a test case to test its lifecycle
  */
 public class CastWebContentsFragment extends Fragment {
-    private static final String TAG = "cr_CastWebContentFrg";
+    private static final String TAG = "CastWebContentFrg";
 
     private final Controller<Unit> mResumedState = new Controller<>();
 
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsIntentUtils.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsIntentUtils.java
index e3fee9b..e3da919 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsIntentUtils.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsIntentUtils.java
@@ -20,7 +20,7 @@
  * classes communicate with it.
  */
 public class CastWebContentsIntentUtils {
-    private static final String TAG = "cr_CastWebUtil";
+    private static final String TAG = "CastWebUtil";
 
     static final String ACTION_DATA_SCHEME = "cast";
     static final String ACTION_DATA_AUTHORITY = "webcontents";
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java
index bc68308e..76d9bab2 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsService.java
@@ -32,7 +32,7 @@
  * service via CastWebContentsComponent.
  */
 public class CastWebContentsService extends Service {
-    private static final String TAG = "cr_CastWebService";
+    private static final String TAG = "CastWebService";
     private static final boolean DEBUG = true;
     private static final int CAST_NOTIFICATION_ID = 100;
     private static final String NOTIFICATION_CHANNEL_ID =
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java
index 5bfd19f..3e0e84e 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java
@@ -35,7 +35,7 @@
  * notified by intent.
  */
 class CastWebContentsSurfaceHelper {
-    private static final String TAG = "cr_CastWebContents";
+    private static final String TAG = "CastWebContents";
 
     private static final int TEARDOWN_GRACE_PERIOD_TIMEOUT_MILLIS = 300;
 
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsView.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsView.java
index f794749..1fe1d82 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsView.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsView.java
@@ -30,7 +30,7 @@
  * it is destroyed, CastContentWindowAndroid should be notified by intent.
  */
 public class CastWebContentsView extends FrameLayout {
-    private static final String TAG = "cr_CastWebContentV";
+    private static final String TAG = "CastWebContentV";
 
     private CastWebContentsSurfaceHelper mSurfaceHelper;
 
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/DumpStreamUtils.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/DumpStreamUtils.java
index 60f227e..89cb67c 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/DumpStreamUtils.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/DumpStreamUtils.java
@@ -18,7 +18,7 @@
  *
  */
 public final class DumpStreamUtils {
-    private static final String TAG = "cr_DumpStreamUtils";
+    private static final String TAG = "DumpStreamUtils";
 
     /**
      * Gets the first line from an input stream
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/ElidedLogcatProvider.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/ElidedLogcatProvider.java
index e7f8d48..05e6ddb 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/ElidedLogcatProvider.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/ElidedLogcatProvider.java
@@ -20,7 +20,7 @@
  * Javascript console messages.
  */
 abstract class ElidedLogcatProvider {
-    private static final String TAG = "cr_ElidedLogcatProv";
+    private static final String TAG = "ElidedLogcatProv";
 
     protected abstract void getRawLogcat(RawLogcatCallback rawLogcatCallback);
 
diff --git a/chromecast/browser/android/cast_content_window_android.cc b/chromecast/browser/android/cast_content_window_android.cc
index 573cc7d6..018932f 100644
--- a/chromecast/browser/android/cast_content_window_android.cc
+++ b/chromecast/browser/android/cast_content_window_android.cc
@@ -19,8 +19,6 @@
 
 using base::android::ConvertUTF8ToJavaString;
 
-namespace shell {
-
 namespace {
 
 base::android::ScopedJavaLocalRef<jobject> CreateJavaWindow(
@@ -39,12 +37,6 @@
 
 }  // namespace
 
-// static
-std::unique_ptr<CastContentWindow> CastContentWindow::Create(
-    const CastContentWindow::CreateParams& params) {
-  return base::WrapUnique(new CastContentWindowAndroid(params));
-}
-
 CastContentWindowAndroid::CastContentWindowAndroid(
     const CastContentWindow::CreateParams& params)
     : delegate_(params.delegate),
@@ -63,14 +55,13 @@
 }
 
 void CastContentWindowAndroid::CreateWindowForWebContents(
-    content::WebContents* web_contents,
-    CastWindowManager* /* window_manager */,
-    CastWindowManager::WindowId /* z_order */,
+    CastWebContents* cast_web_contents,
+    mojom::ZOrder /* z_order */,
     VisibilityPriority visibility_priority) {
-  DCHECK(web_contents);
+  DCHECK(cast_web_contents);
   JNIEnv* env = base::android::AttachCurrentThread();
   base::android::ScopedJavaLocalRef<jobject> java_web_contents =
-      web_contents->GetJavaWebContents();
+      cast_web_contents->web_contents()->GetJavaWebContents();
 
   Java_CastContentWindowAndroid_createWindowForWebContents(
       env, java_window_, java_web_contents,
@@ -141,5 +132,5 @@
     const base::android::JavaParamRef<jobject>& jcaller) {
   return ConvertUTF8ToJavaString(env, delegate_->GetId());
 }
-}  // namespace shell
+
 }  // namespace chromecast
diff --git a/chromecast/browser/android/cast_content_window_android.h b/chromecast/browser/android/cast_content_window_android.h
index b4c7d51..67ce1f8 100644
--- a/chromecast/browser/android/cast_content_window_android.h
+++ b/chromecast/browser/android/cast_content_window_android.h
@@ -16,19 +16,19 @@
 }  // namespace content
 
 namespace chromecast {
-namespace shell {
 
 // Android implementation of CastContentWindow, which displays WebContents in
 // CastWebContentsActivity.
 class CastContentWindowAndroid : public CastContentWindow {
  public:
+  explicit CastContentWindowAndroid(
+      const CastContentWindow::CreateParams& params);
   ~CastContentWindowAndroid() override;
 
   // CastContentWindow implementation:
   void CreateWindowForWebContents(
-      content::WebContents* web_contents,
-      CastWindowManager* window_manager,
-      CastWindowManager::WindowId z_order,
+      CastWebContents* cast_web_contents,
+      mojom::ZOrder z_order,
       VisibilityPriority visibility_priority) override;
   void GrantScreenAccess() override;
   void RevokeScreenAccess() override;
@@ -53,18 +53,12 @@
       const base::android::JavaParamRef<jobject>& jcaller);
 
  private:
-  friend class CastContentWindow;
-
-  // This class should only be instantiated by CastContentWindow::Create.
-  CastContentWindowAndroid(const CastContentWindow::CreateParams& params);
-
   CastContentWindow::Delegate* const delegate_;
   base::android::ScopedJavaGlobalRef<jobject> java_window_;
 
   DISALLOW_COPY_AND_ASSIGN(CastContentWindowAndroid);
 };
 
-}  // namespace shell
 }  // namespace chromecast
 
 #endif  // CHROMECAST_BROWSER_ANDROID_CAST_CONTENT_WINDOW_ANDROID_H_
diff --git a/chromecast/browser/android/cast_web_contents_manager_android.cc b/chromecast/browser/android/cast_web_contents_manager_android.cc
new file mode 100644
index 0000000..e06487c
--- /dev/null
+++ b/chromecast/browser/android/cast_web_contents_manager_android.cc
@@ -0,0 +1,18 @@
+// 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 "chromecast/browser/cast_web_contents_manager.h"
+
+#include <memory>
+
+#include "chromecast/browser/android/cast_content_window_android.h"
+
+namespace chromecast {
+
+std::unique_ptr<CastContentWindow> CastWebContentsManager::CreateWindow(
+    const CastContentWindow::CreateParams& params) {
+  return std::make_unique<CastContentWindowAndroid>(params);
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_content_gesture_handler.cc b/chromecast/browser/cast_content_gesture_handler.cc
index 6eef197a..141f649 100644
--- a/chromecast/browser/cast_content_gesture_handler.cc
+++ b/chromecast/browser/cast_content_gesture_handler.cc
@@ -7,7 +7,6 @@
 #include "chromecast/base/chromecast_switches.h"
 
 namespace chromecast {
-namespace shell {
 
 namespace {
 constexpr int kDefaultBackGestureHorizontalThreshold = 80;
@@ -117,5 +116,4 @@
   delegate_->ConsumeGesture(GestureType::TAP);
 }
 
-}  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_content_gesture_handler.h b/chromecast/browser/cast_content_gesture_handler.h
index 8b889e79..1291ac04 100644
--- a/chromecast/browser/cast_content_gesture_handler.h
+++ b/chromecast/browser/cast_content_gesture_handler.h
@@ -12,8 +12,6 @@
 
 namespace chromecast {
 
-namespace shell {
-
 // Receives root window level gestures, interprets them, and hands them to the
 // CastContentWindow::Delegate.
 class CastContentGestureHandler : public CastGestureHandler {
@@ -47,7 +45,6 @@
   base::ElapsedTimer current_swipe_time_;
 };
 
-}  // namespace shell
 }  // namespace chromecast
 
 #endif  // CHROMECAST_BROWSER_CAST_CONTENT_GESTURE_HANDLER_H_
diff --git a/chromecast/browser/cast_content_gesture_handler_test.cc b/chromecast/browser/cast_content_gesture_handler_test.cc
index 91aeffe..03e1917f2 100644
--- a/chromecast/browser/cast_content_gesture_handler_test.cc
+++ b/chromecast/browser/cast_content_gesture_handler_test.cc
@@ -18,7 +18,6 @@
 using testing::WithArg;
 
 namespace chromecast {
-namespace shell {
 
 namespace {
 
@@ -255,5 +254,5 @@
   dispatcher_.HandleSideSwipe(CastSideSwipeEvent::END,
                               CastSideSwipeOrigin::LEFT, kRightGestureEndPoint);
 }
-}  // namespace shell
+
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_content_window.cc b/chromecast/browser/cast_content_window.cc
index 2a8bef85..90adeab 100644
--- a/chromecast/browser/cast_content_window.cc
+++ b/chromecast/browser/cast_content_window.cc
@@ -5,13 +5,14 @@
 #include "chromecast/browser/cast_content_window.h"
 
 namespace chromecast {
-namespace shell {
 
 CastContentWindow::CastContentWindow() = default;
 
 CastContentWindow::~CastContentWindow() = default;
 
 CastContentWindow::CreateParams::CreateParams() = default;
+CastContentWindow::CreateParams::CreateParams(const CreateParams& other) =
+    default;
 
 void CastContentWindow::AddObserver(Observer* observer) {
   observer_list_.AddObserver(observer);
@@ -21,5 +22,8 @@
   observer_list_.RemoveObserver(observer);
 }
 
-}  // namespace shell
+mojom::MediaControlUi* CastContentWindow::media_controls() {
+  return nullptr;
+}
+
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_content_window.h b/chromecast/browser/cast_content_window.h
index 476d1f56..09ede75 100644
--- a/chromecast/browser/cast_content_window.h
+++ b/chromecast/browser/cast_content_window.h
@@ -12,13 +12,13 @@
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
-#include "chromecast/graphics/cast_window_manager.h"
+#include "chromecast/browser/cast_web_contents.h"
 #include "chromecast/graphics/gestures/cast_gesture_handler.h"
-#include "content/public/browser/web_contents.h"
+#include "chromecast/ui/mojom/media_control_ui.mojom.h"
+#include "chromecast/ui/mojom/ui_service.mojom.h"
 #include "ui/events/event.h"
 
 namespace chromecast {
-namespace shell {
 
 // Describes visual context of the window within the UI.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chromecast.shell
@@ -146,6 +146,7 @@
         CastGestureHandler::Priority::NONE;
 
     CreateParams();
+    CreateParams(const CreateParams& other);
   };
 
   class Observer : public base::CheckedObserver {
@@ -157,23 +158,18 @@
     ~Observer() override {}
   };
 
-  // Creates the platform specific CastContentWindow. |delegate| should outlive
-  // the created CastContentWindow.
-  static std::unique_ptr<CastContentWindow> Create(const CreateParams& params);
-
   CastContentWindow();
   virtual ~CastContentWindow();
 
-  // Creates a full-screen window for |web_contents| and displays it if screen
-  // access has been granted.
-  // |web_contents| should outlive this CastContentWindow.
-  // |window_manager| should outlive this CastContentWindow.
+  // Creates a full-screen window for |cast_web_contents| and displays it if
+  // screen access has been granted. |cast_web_contents| must outlive the
+  // CastContentWindow. |z_order| is provided so that windows which share the
+  // same parent have a well-defined order.
   // TODO(seantopping): This method probably shouldn't exist; this class should
   // use RAII instead.
   virtual void CreateWindowForWebContents(
-      content::WebContents* web_contents,
-      CastWindowManager* window_manager,
-      CastWindowManager::WindowId z_order,
+      CastWebContents* cast_web_contents,
+      mojom::ZOrder z_order,
       VisibilityPriority visibility_priority) = 0;
 
   // Allows the window to be shown on the screen. The window cannot be shown on
@@ -209,6 +205,9 @@
   // screen.
   virtual void RequestMoveOut() = 0;
 
+  // Media control interface. Non-null on Aura platforms.
+  virtual mojom::MediaControlUi* media_controls();
+
   // Observer interface:
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
@@ -217,7 +216,6 @@
   base::ObserverList<Observer> observer_list_;
 };
 
-}  // namespace shell
 }  // namespace chromecast
 
 #endif  // CHROMECAST_BROWSER_CAST_CONTENT_WINDOW_H_
diff --git a/chromecast/browser/cast_content_window_aura.cc b/chromecast/browser/cast_content_window_aura.cc
index 1868421..e9b1b76d 100644
--- a/chromecast/browser/cast_content_window_aura.cc
+++ b/chromecast/browser/cast_content_window_aura.cc
@@ -10,13 +10,13 @@
 #include "base/memory/ptr_util.h"
 #include "chromecast/chromecast_buildflags.h"
 #include "chromecast/graphics/cast_window_manager.h"
+#include "chromecast/ui/media_control_ui.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/aura/window.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 
 namespace chromecast {
-namespace shell {
 
 class TouchBlocker : public ui::EventHandler, public aura::WindowObserver {
  public:
@@ -69,15 +69,11 @@
   DISALLOW_COPY_AND_ASSIGN(TouchBlocker);
 };
 
-// static
-std::unique_ptr<CastContentWindow> CastContentWindow::Create(
-    const CastContentWindow::CreateParams& params) {
-  return base::WrapUnique(new CastContentWindowAura(params));
-}
-
 CastContentWindowAura::CastContentWindowAura(
-    const CastContentWindow::CreateParams& params)
+    const CastContentWindow::CreateParams& params,
+    CastWindowManager* window_manager)
     : delegate_(params.delegate),
+      window_manager_(window_manager),
       gesture_dispatcher_(
           std::make_unique<CastContentGestureHandler>(delegate_)),
       gesture_priority_(params.gesture_priority),
@@ -88,6 +84,7 @@
 }
 
 CastContentWindowAura::~CastContentWindowAura() {
+  CastWebContents::Observer::Observe(nullptr);
   if (window_manager_) {
     window_manager_->RemoveGestureHandler(gesture_dispatcher_.get());
   }
@@ -97,22 +94,23 @@
 }
 
 void CastContentWindowAura::CreateWindowForWebContents(
-    content::WebContents* web_contents,
-    CastWindowManager* window_manager,
-    CastWindowManager::WindowId z_order,
+    CastWebContents* cast_web_contents,
+    mojom::ZOrder z_order,
     VisibilityPriority visibility_priority) {
-  DCHECK(web_contents);
-  window_manager_ = window_manager;
-  DCHECK(window_manager_);
-  window_ = web_contents->GetNativeView();
+  DCHECK(cast_web_contents);
+  DCHECK(window_manager_) << "A CastWindowManager must be provided before "
+                          << "creating a window for WebContents.";
+  CastWebContents::Observer::Observe(cast_web_contents);
+  window_ = cast_web_contents->web_contents()->GetNativeView();
   if (!window_->HasObserver(this)) {
     window_->AddObserver(this);
   }
-  window_manager_->SetWindowId(window_, z_order);
+  window_manager_->SetZOrder(window_, z_order);
   window_manager_->AddWindow(window_);
   window_manager_->AddGestureHandler(gesture_dispatcher_.get());
 
   touch_blocker_ = std::make_unique<TouchBlocker>(window_, !is_touch_enabled_);
+  media_controls_ = std::make_unique<MediaControlUi>(window_manager_);
 
   if (has_screen_access_) {
     window_->Show();
@@ -150,6 +148,16 @@
   }
 }
 
+mojom::MediaControlUi* CastContentWindowAura::media_controls() {
+  return media_controls_.get();
+}
+
+void CastContentWindowAura::MainFrameResized(const gfx::Rect& bounds) {
+  if (media_controls_) {
+    media_controls_->SetBounds(bounds);
+  }
+}
+
 void CastContentWindowAura::RequestVisibility(
     VisibilityPriority visibility_priority) {}
 
@@ -180,5 +188,4 @@
   window_ = nullptr;
 }
 
-}  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_content_window_aura.h b/chromecast/browser/cast_content_window_aura.h
index c552cbf6..73e826b 100644
--- a/chromecast/browser/cast_content_window_aura.h
+++ b/chromecast/browser/cast_content_window_aura.h
@@ -8,32 +8,29 @@
 #include "base/macros.h"
 #include "chromecast/browser/cast_content_gesture_handler.h"
 #include "chromecast/browser/cast_content_window.h"
+#include "chromecast/ui/media_control_ui.h"
 #include "ui/aura/window_observer.h"
 
 namespace aura {
 class Window;
 }  // namespace aura
 
-namespace content {
-class WebContents;
-}  // namespace content
-
 namespace chromecast {
-namespace shell {
 
 class TouchBlocker;
 
 class CastContentWindowAura : public CastContentWindow,
+                              public CastWebContents::Observer,
                               public aura::WindowObserver {
  public:
-  explicit CastContentWindowAura(const CastContentWindow::CreateParams& params);
+  CastContentWindowAura(const CastContentWindow::CreateParams& params,
+                        CastWindowManager* window_manager);
   ~CastContentWindowAura() override;
 
   // CastContentWindow implementation:
   void CreateWindowForWebContents(
-      content::WebContents* web_contents,
-      CastWindowManager* window_manager,
-      CastWindowManager::WindowId z_order,
+      CastWebContents* cast_web_contents,
+      mojom::ZOrder z_order,
       VisibilityPriority visibility_priority) override;
   void GrantScreenAccess() override;
   void RevokeScreenAccess() override;
@@ -43,6 +40,10 @@
   void NotifyVisibilityChange(VisibilityType visibility_type) override;
   void RequestMoveOut() override;
   void EnableTouchInput(bool enabled) override;
+  mojom::MediaControlUi* media_controls() override;
+
+  // CastWebContents::Observer implementation:
+  void MainFrameResized(const gfx::Rect& bounds) override;
 
   // aura::WindowObserver implementation:
   void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
@@ -50,6 +51,7 @@
 
  private:
   CastContentWindow::Delegate* const delegate_;
+  CastWindowManager* const window_manager_;
 
   // Utility class for detecting and dispatching gestures to delegates.
   std::unique_ptr<CastContentGestureHandler> gesture_dispatcher_;
@@ -58,15 +60,14 @@
   const bool is_touch_enabled_;
   std::unique_ptr<TouchBlocker> touch_blocker_;
 
-  // TODO(seantopping): Inject in constructor.
-  CastWindowManager* window_manager_ = nullptr;
+  std::unique_ptr<MediaControlUi> media_controls_;
+
   aura::Window* window_;
   bool has_screen_access_;
 
   DISALLOW_COPY_AND_ASSIGN(CastContentWindowAura);
 };
 
-}  // namespace shell
 }  // namespace chromecast
 
 #endif  // CHROMECAST_BROWSER_CAST_CONTENT_WINDOW_AURA_H_
diff --git a/chromecast/browser/cast_web_contents_manager.cc b/chromecast/browser/cast_web_contents_manager.cc
index 89e9664a..944c202 100644
--- a/chromecast/browser/cast_web_contents_manager.cc
+++ b/chromecast/browser/cast_web_contents_manager.cc
@@ -25,9 +25,11 @@
 
 CastWebContentsManager::CastWebContentsManager(
     content::BrowserContext* browser_context,
-    CastWebViewFactory* web_view_factory)
+    CastWebViewFactory* web_view_factory,
+    CastWindowManager* window_manager)
     : browser_context_(browser_context),
       web_view_factory_(web_view_factory),
+      window_manager_(window_manager),
       task_runner_(base::SequencedTaskRunnerHandle::Get()),
       weak_factory_(this) {
   DCHECK(browser_context_);
diff --git a/chromecast/browser/cast_web_contents_manager.h b/chromecast/browser/cast_web_contents_manager.h
index 85af3f80..3aaedd4 100644
--- a/chromecast/browser/cast_web_contents_manager.h
+++ b/chromecast/browser/cast_web_contents_manager.h
@@ -26,6 +26,7 @@
 namespace chromecast {
 
 class CastWebViewFactory;
+class CastWindowManager;
 
 // This class dispenses CastWebView objects which are used to wrap WebContents
 // in cast_shell. This class can take ownership of a WebContents instance when
@@ -35,7 +36,8 @@
 class CastWebContentsManager {
  public:
   CastWebContentsManager(content::BrowserContext* browser_context,
-                         CastWebViewFactory* web_view_factory);
+                         CastWebViewFactory* web_view_factory,
+                         CastWindowManager* window_manager);
   ~CastWebContentsManager();
 
   std::unique_ptr<CastWebView> CreateWebView(
@@ -47,6 +49,9 @@
       const CastWebView::CreateParams& params,
       const GURL& initial_url);
 
+  std::unique_ptr<CastContentWindow> CreateWindow(
+      const CastContentWindow::CreateParams& params);
+
   // Take ownership of |web_contents| and delete after |time_delta|, or sooner
   // if necessary.
   void DelayWebContentsDeletion(
@@ -58,6 +63,7 @@
 
   content::BrowserContext* const browser_context_;
   CastWebViewFactory* const web_view_factory_;
+  CastWindowManager* const window_manager_;
   base::flat_set<std::unique_ptr<content::WebContents>> expiring_web_contents_;
 
   const scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/chromecast/browser/cast_web_contents_manager_aura.cc b/chromecast/browser/cast_web_contents_manager_aura.cc
new file mode 100644
index 0000000..e4b51da
--- /dev/null
+++ b/chromecast/browser/cast_web_contents_manager_aura.cc
@@ -0,0 +1,18 @@
+// 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 "chromecast/browser/cast_web_contents_manager.h"
+
+#include <memory>
+
+#include "chromecast/browser/cast_content_window_aura.h"
+
+namespace chromecast {
+
+std::unique_ptr<CastContentWindow> CastWebContentsManager::CreateWindow(
+    const CastContentWindow::CreateParams& params) {
+  return std::make_unique<CastContentWindowAura>(params, window_manager_);
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_web_view.h b/chromecast/browser/cast_web_view.h
index 905e0ed..dec69dd 100644
--- a/chromecast/browser/cast_web_view.h
+++ b/chromecast/browser/cast_web_view.h
@@ -15,22 +15,18 @@
 #include "base/values.h"
 #include "chromecast/browser/cast_content_window.h"
 #include "chromecast/browser/cast_web_contents.h"
-#include "chromecast/graphics/cast_window_manager.h"
+#include "chromecast/ui/mojom/ui_service.mojom.h"
 #include "content/public/browser/bluetooth_chooser.h"
 #include "content/public/browser/web_contents.h"
 #include "url/gurl.h"
 
 namespace chromecast {
 
-class CastWindowManager;
-
-using shell::VisibilityPriority;
-
 // A simplified interface for loading and displaying WebContents in cast_shell.
 class CastWebView {
  public:
   class Delegate : public CastWebContents::Delegate,
-                   public shell::CastContentWindow::Delegate {
+                   public CastContentWindow::Delegate {
    public:
     // Invoked by CastWebView when WebContentsDelegate::RunBluetoothChooser is
     // called. Returns a BluetoothChooser, a class used to solicit bluetooth
@@ -65,7 +61,7 @@
     CastWebContents::InitParams web_contents_params;
 
     // Parameters for creating the content window for this CastWebView.
-    shell::CastContentWindow::CreateParams window_params;
+    CastContentWindow::CreateParams window_params;
 
     // Identifies the activity that is hosted by this CastWebView.
     std::string activity_id = "";
@@ -90,7 +86,7 @@
   CastWebView();
   virtual ~CastWebView();
 
-  virtual shell::CastContentWindow* window() const = 0;
+  virtual CastContentWindow* window() const = 0;
 
   virtual content::WebContents* web_contents() const = 0;
 
@@ -109,8 +105,7 @@
   // Adds the page to the window manager and makes it visible to the user if
   // |is_visible| is true. |z_order| determines how this window is layered in
   // relationt other windows (higher value == more foreground).
-  virtual void InitializeWindow(CastWindowManager* window_manager,
-                                CastWindowManager::WindowId z_order,
+  virtual void InitializeWindow(mojom::ZOrder z_order,
                                 VisibilityPriority initial_priority) = 0;
 
   // Allows the page to be shown on the screen. The page cannot be shown on the
diff --git a/chromecast/browser/cast_web_view_default.cc b/chromecast/browser/cast_web_view_default.cc
index 52f0dc1e..f052aa6 100644
--- a/chromecast/browser/cast_web_view_default.cc
+++ b/chromecast/browser/cast_web_view_default.cc
@@ -55,7 +55,7 @@
     CastWebContentsManager* web_contents_manager,
     content::BrowserContext* browser_context,
     scoped_refptr<content::SiteInstance> site_instance,
-    std::unique_ptr<shell::CastContentWindow> cast_content_window)
+    std::unique_ptr<CastContentWindow> cast_content_window)
     : web_contents_manager_(web_contents_manager),
       browser_context_(browser_context),
       site_instance_(std::move(site_instance)),
@@ -66,7 +66,7 @@
       cast_web_contents_(web_contents_.get(), params.web_contents_params),
       window_(cast_content_window
                   ? std::move(cast_content_window)
-                  : shell::CastContentWindow::Create(params.window_params)),
+                  : web_contents_manager->CreateWindow(params.window_params)),
       resize_window_when_navigation_starts_(true) {
   DCHECK(delegate_);
   DCHECK(web_contents_manager_);
@@ -90,7 +90,7 @@
 
 CastWebViewDefault::~CastWebViewDefault() {}
 
-shell::CastContentWindow* CastWebViewDefault::window() const {
+CastContentWindow* CastWebViewDefault::window() const {
   return window_.get();
 }
 
@@ -127,13 +127,11 @@
   cast_web_contents_.Stop(net::OK);
 }
 
-void CastWebViewDefault::InitializeWindow(CastWindowManager* window_manager,
-                                          CastWindowManager::WindowId z_order,
+void CastWebViewDefault::InitializeWindow(mojom::ZOrder z_order,
                                           VisibilityPriority initial_priority) {
-  DCHECK(window_manager);
   DCHECK(window_);
-  window_->CreateWindowForWebContents(web_contents_.get(), window_manager,
-                                      z_order, initial_priority);
+  window_->CreateWindowForWebContents(&cast_web_contents_, z_order,
+                                      initial_priority);
   web_contents_->Focus();
 }
 
diff --git a/chromecast/browser/cast_web_view_default.h b/chromecast/browser/cast_web_view_default.h
index 1fa1d39..1bb1b9e9 100644
--- a/chromecast/browser/cast_web_view_default.h
+++ b/chromecast/browser/cast_web_view_default.h
@@ -26,7 +26,6 @@
 namespace chromecast {
 
 class CastWebContentsManager;
-class CastWindowManager;
 
 // A simplified interface for loading and displaying WebContents in cast_shell.
 class CastWebViewDefault : public CastWebView,
@@ -34,22 +33,23 @@
                            content::WebContentsDelegate {
  public:
   // |web_contents_manager| and |browser_context| should outlive this object.
+  // If |cast_content_window| is not provided, an instance will be constructed
+  // from |web_contents_manager|.
   CastWebViewDefault(
       const CreateParams& params,
       CastWebContentsManager* web_contents_manager,
       content::BrowserContext* browser_context,
       scoped_refptr<content::SiteInstance> site_instance,
-      std::unique_ptr<shell::CastContentWindow> cast_content_window = nullptr);
+      std::unique_ptr<CastContentWindow> cast_content_window = nullptr);
   ~CastWebViewDefault() override;
 
   // CastWebView implementation:
-  shell::CastContentWindow* window() const override;
+  CastContentWindow* window() const override;
   content::WebContents* web_contents() const override;
   CastWebContents* cast_web_contents() override;
   void LoadUrl(GURL url) override;
   void ClosePage(const base::TimeDelta& shutdown_delay) override;
-  void InitializeWindow(CastWindowManager* window_manager,
-                        CastWindowManager::WindowId z_order,
+  void InitializeWindow(mojom::ZOrder z_order,
                         VisibilityPriority initial_priority) override;
   void GrantScreenAccess() override;
   void RevokeScreenAccess() override;
@@ -91,7 +91,7 @@
 
   std::unique_ptr<content::WebContents> web_contents_;
   CastWebContentsImpl cast_web_contents_;
-  std::unique_ptr<shell::CastContentWindow> window_;
+  std::unique_ptr<CastContentWindow> window_;
   bool resize_window_when_navigation_starts_;
   base::TimeDelta shutdown_delay_;
 
diff --git a/chromecast/browser/extension_page.cc b/chromecast/browser/extension_page.cc
index 197a9ac2..880d8f0 100644
--- a/chromecast/browser/extension_page.cc
+++ b/chromecast/browser/extension_page.cc
@@ -9,7 +9,6 @@
 #include "base/logging.h"
 #include "chromecast/browser/cast_content_window_aura.h"
 #include "chromecast/browser/cast_extension_host.h"
-#include "chromecast/graphics/cast_window_manager.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
@@ -19,12 +18,12 @@
 
 ExtensionPage::ExtensionPage(
     const CastWebContents::InitParams& init_params,
-    const shell::CastContentWindow::CreateParams& window_params,
+    const CastContentWindow::CreateParams& window_params,
     std::unique_ptr<CastExtensionHost> extension_host,
     CastWindowManager* window_manager)
-    : window_(std::make_unique<shell::CastContentWindowAura>(window_params)),
+    : window_(std::make_unique<CastContentWindowAura>(window_params,
+                                                      window_manager)),
       extension_host_(std::move(extension_host)),
-      window_manager_(window_manager),
       cast_web_contents_(extension_host_->host_contents(), init_params) {
   content::WebContentsObserver::Observe(web_contents());
 }
@@ -48,8 +47,8 @@
 void ExtensionPage::InitializeWindow() {
   window_->GrantScreenAccess();
   window_->CreateWindowForWebContents(
-      web_contents(), window_manager_, CastWindowManager::APP,
-      chromecast::shell::VisibilityPriority::STICKY_ACTIVITY);
+      &cast_web_contents_, mojom::ZOrder::APP,
+      chromecast::VisibilityPriority::STICKY_ACTIVITY);
 }
 
 void ExtensionPage::RenderViewCreated(
diff --git a/chromecast/browser/extension_page.h b/chromecast/browser/extension_page.h
index 1e9bbee..8acb9fa 100644
--- a/chromecast/browser/extension_page.h
+++ b/chromecast/browser/extension_page.h
@@ -12,10 +12,8 @@
 #include "content/public/browser/web_contents_observer.h"
 
 namespace chromecast {
-namespace shell {
-class CastContentWindowAura;
-}  // namespace shell
 
+class CastContentWindowAura;
 class CastExtensionHost;
 class CastWindowManager;
 
@@ -23,10 +21,9 @@
 class ExtensionPage : public content::WebContentsObserver {
  public:
   ExtensionPage(const CastWebContents::InitParams& init_params,
-                const shell::CastContentWindow::CreateParams& window_params,
+                const CastContentWindow::CreateParams& window_params,
                 std::unique_ptr<CastExtensionHost> extension_host,
                 CastWindowManager* window_manager);
-
   ~ExtensionPage() override;
 
   content::WebContents* web_contents() const;
@@ -39,9 +36,8 @@
   // WebContentsObserver implementation:
   void RenderViewCreated(content::RenderViewHost* render_view_host) override;
 
-  const std::unique_ptr<shell::CastContentWindowAura> window_;
+  const std::unique_ptr<CastContentWindowAura> window_;
   const std::unique_ptr<CastExtensionHost> extension_host_;
-  CastWindowManager* window_manager_;
   CastWebContentsImpl cast_web_contents_;
   scoped_refptr<content::SiteInstance> site_instance_;
 
diff --git a/chromecast/browser/service/cast_service_simple.cc b/chromecast/browser/service/cast_service_simple.cc
index 694fd54..0bbdc21 100644
--- a/chromecast/browser/service/cast_service_simple.cc
+++ b/chromecast/browser/service/cast_service_simple.cc
@@ -43,12 +43,11 @@
                                      PrefService* pref_service,
                                      CastWindowManager* window_manager)
     : CastService(browser_context, pref_service),
-      window_manager_(window_manager),
       web_view_factory_(std::make_unique<CastWebViewFactory>(browser_context)),
       web_contents_manager_(
           std::make_unique<CastWebContentsManager>(browser_context,
-                                                   web_view_factory_.get())) {
-  DCHECK(window_manager_);
+                                                   web_view_factory_.get(),
+                                                   window_manager)) {
   shell::CastBrowserProcess::GetInstance()->SetWebViewFactory(
       web_view_factory_.get());
 }
@@ -79,8 +78,8 @@
   cast_web_view_->LoadUrl(startup_url_);
   cast_web_view_->GrantScreenAccess();
   cast_web_view_->InitializeWindow(
-      window_manager_, CastWindowManager::APP,
-      chromecast::shell::VisibilityPriority::STICKY_ACTIVITY);
+      ::chromecast::mojom::ZOrder::APP,
+      chromecast::VisibilityPriority::STICKY_ACTIVITY);
 }
 
 void CastServiceSimple::StopInternal() {
diff --git a/chromecast/browser/service/cast_service_simple.h b/chromecast/browser/service/cast_service_simple.h
index 296b9b0..71c13fc 100644
--- a/chromecast/browser/service/cast_service_simple.h
+++ b/chromecast/browser/service/cast_service_simple.h
@@ -43,7 +43,6 @@
   std::string GetId() override;
 
  private:
-  CastWindowManager* const window_manager_;
   const std::unique_ptr<CastWebViewFactory> web_view_factory_;
   const std::unique_ptr<CastWebContentsManager> web_contents_manager_;
   std::unique_ptr<CastWebView> cast_web_view_;
diff --git a/chromecast/browser/test/cast_browser_test.cc b/chromecast/browser/test/cast_browser_test.cc
index f0e3298..4ca3862 100644
--- a/chromecast/browser/test/cast_browser_test.cc
+++ b/chromecast/browser/test/cast_browser_test.cc
@@ -49,7 +49,7 @@
       CastBrowserProcess::GetInstance()->browser_context());
   web_contents_manager_ = std::make_unique<CastWebContentsManager>(
       CastBrowserProcess::GetInstance()->browser_context(),
-      web_view_factory_.get());
+      web_view_factory_.get(), nullptr /* window_manager */);
 }
 
 void CastBrowserTest::PostRunTestOnMainThread() {
diff --git a/chromecast/browser/webview/web_content_controller.cc b/chromecast/browser/webview/web_content_controller.cc
index 43fbdaf..d1c8ca7 100644
--- a/chromecast/browser/webview/web_content_controller.cc
+++ b/chromecast/browser/webview/web_content_controller.cc
@@ -125,7 +125,7 @@
 void WebContentController::AttachTo(aura::Window* window, int window_id) {
   content::WebContents* contents = GetWebContents();
   auto* contents_window = contents->GetNativeView();
-  window->SetLayoutManager(new WebviewLayoutManager(window, contents_window));
+  window->SetLayoutManager(new WebviewLayoutManager(window));
   contents_window->set_id(window_id);
   contents_window->SetBounds(gfx::Rect(window->bounds().size()));
   // The aura window is hidden to avoid being shown via the usual layer method,
diff --git a/chromecast/browser/webview/webview_layout_manager.cc b/chromecast/browser/webview/webview_layout_manager.cc
index 1318b20..fed2b397 100644
--- a/chromecast/browser/webview/webview_layout_manager.cc
+++ b/chromecast/browser/webview/webview_layout_manager.cc
@@ -8,14 +8,13 @@
 
 namespace chromecast {
 
-WebviewLayoutManager::WebviewLayoutManager(aura::Window* parent,
-                                           aura::Window* web_contents_window)
-    : parent_(parent), web_contents_window_(web_contents_window) {}
+WebviewLayoutManager::WebviewLayoutManager(aura::Window* root) : root_(root) {}
 
 WebviewLayoutManager::~WebviewLayoutManager() {}
 
 void WebviewLayoutManager::OnWindowResized() {
-  web_contents_window_->SetBounds(gfx::Rect(parent_->bounds().size()));
+  for (aura::Window* child : root_->children())
+    SetChildBoundsDirect(child, gfx::Rect(root_->bounds().size()));
 }
 
 void WebviewLayoutManager::OnWindowAddedToLayout(aura::Window* child) {}
diff --git a/chromecast/browser/webview/webview_layout_manager.h b/chromecast/browser/webview/webview_layout_manager.h
index e26a250..194b85d 100644
--- a/chromecast/browser/webview/webview_layout_manager.h
+++ b/chromecast/browser/webview/webview_layout_manager.h
@@ -13,7 +13,7 @@
 // Resizes the provided window to be the parent window's size.
 class WebviewLayoutManager : public aura::LayoutManager {
  public:
-  WebviewLayoutManager(aura::Window* parent, aura::Window* web_contents_window);
+  WebviewLayoutManager(aura::Window* root);
   ~WebviewLayoutManager() override;
 
   void OnWindowResized() override;
@@ -26,8 +26,7 @@
                       const gfx::Rect& requested_bounds) override;
 
  private:
-  aura::Window* parent_;
-  aura::Window* web_contents_window_;
+  aura::Window* root_;
 
   DISALLOW_COPY_AND_ASSIGN(WebviewLayoutManager);
 };
diff --git a/chromecast/graphics/BUILD.gn b/chromecast/graphics/BUILD.gn
index 552f958..7271d7d 100644
--- a/chromecast/graphics/BUILD.gn
+++ b/chromecast/graphics/BUILD.gn
@@ -21,6 +21,10 @@
     "//ui/gfx",
   ]
 
+  public_deps = [
+    "//chromecast/ui/mojom",
+  ]
+
   if (use_aura) {
     sources += [
       "accessibility/accessibility_cursor_ring_layer.cc",
diff --git a/chromecast/graphics/DEPS b/chromecast/graphics/DEPS
index 25819e5e..d8c5185 100644
--- a/chromecast/graphics/DEPS
+++ b/chromecast/graphics/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+chromecast/ui",
   "+components/viz/common/frame_sinks",
   "+ui/aura",
   "+ui/base",
diff --git a/chromecast/graphics/cast_views_test.cc b/chromecast/graphics/cast_views_test.cc
index f2bf1b5..a7d76dd9 100644
--- a/chromecast/graphics/cast_views_test.cc
+++ b/chromecast/graphics/cast_views_test.cc
@@ -34,8 +34,7 @@
   widget->Init(std::move(params));
   widget->SetOpacity(0.6);
   widget->SetContentsView(progress_bar);
-  window_manager->SetWindowId(widget->GetNativeView(),
-                              CastWindowManager::VOLUME);
+  window_manager->SetZOrder(widget->GetNativeView(), mojom::ZOrder::VOLUME);
   widget->Show();
 
   EXPECT_TRUE(progress_bar->GetWidget());
diff --git a/chromecast/graphics/cast_window_manager.h b/chromecast/graphics/cast_window_manager.h
index 79177578..2dd4659 100644
--- a/chromecast/graphics/cast_window_manager.h
+++ b/chromecast/graphics/cast_window_manager.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "chromecast/ui/mojom/ui_service.mojom.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace ui {
@@ -66,8 +67,9 @@
   // causing it to initialize.
   virtual void AddWindow(gfx::NativeView window) = 0;
 
-  // Sets a window's ID.
-  virtual void SetWindowId(gfx::NativeView window, WindowId window_id) = 0;
+  // Sets the Z order for the window. This allows windows with the same parent
+  // to stack in a well-defined order.
+  virtual void SetZOrder(gfx::NativeView window, mojom::ZOrder z_order) = 0;
 
   // Return the root window that holds all top-level windows.
   virtual gfx::NativeView GetRootWindow() = 0;
diff --git a/chromecast/graphics/cast_window_manager_aura.cc b/chromecast/graphics/cast_window_manager_aura.cc
index ca32ce7a..0516326 100644
--- a/chromecast/graphics/cast_window_manager_aura.cc
+++ b/chromecast/graphics/cast_window_manager_aura.cc
@@ -294,9 +294,12 @@
   window_tree_host_.reset();
 }
 
-void CastWindowManagerAura::SetWindowId(gfx::NativeView window,
-                                        WindowId window_id) {
-  window->set_id(window_id);
+void CastWindowManagerAura::SetZOrder(gfx::NativeView window,
+                                      mojom::ZOrder z_order) {
+  // Use aura::Window ID to maintain z-order. When the window's visibility
+  // changes, we stack sibling windows based on this ID. Windows with higher
+  // IDs are stacked on top.
+  window->set_id(static_cast<int>(z_order));
 }
 
 void CastWindowManagerAura::InjectEvent(ui::Event* event) {
diff --git a/chromecast/graphics/cast_window_manager_aura.h b/chromecast/graphics/cast_window_manager_aura.h
index 8b62f6b7..5c4c03bc 100644
--- a/chromecast/graphics/cast_window_manager_aura.h
+++ b/chromecast/graphics/cast_window_manager_aura.h
@@ -44,7 +44,7 @@
   void AddWindow(gfx::NativeView window) override;
   gfx::NativeView GetRootWindow() override;
   std::vector<WindowId> GetWindowOrder() override;
-  void SetWindowId(gfx::NativeView window, WindowId window_id) override;
+  void SetZOrder(gfx::NativeView window, mojom::ZOrder z_order) override;
   void InjectEvent(ui::Event* event) override;
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
diff --git a/chromecast/graphics/cast_window_manager_default.cc b/chromecast/graphics/cast_window_manager_default.cc
index b61ea76..55222ce 100644
--- a/chromecast/graphics/cast_window_manager_default.cc
+++ b/chromecast/graphics/cast_window_manager_default.cc
@@ -25,8 +25,8 @@
 }
 
 void CastWindowManagerDefault::InjectEvent(ui::Event* event) {}
-void CastWindowManagerDefault::SetWindowId(gfx::NativeView window,
-                                           WindowId window_id) {}
+void CastWindowManagerDefault::SetZOrder(gfx::NativeView window,
+                                         mojom::ZOrder z_order) {}
 void CastWindowManagerDefault::AddObserver(Observer* observer) {}
 void CastWindowManagerDefault::RemoveObserver(Observer* observer) {}
 
diff --git a/chromecast/graphics/cast_window_manager_default.h b/chromecast/graphics/cast_window_manager_default.h
index 4fbef5a..aa33d5ea 100644
--- a/chromecast/graphics/cast_window_manager_default.h
+++ b/chromecast/graphics/cast_window_manager_default.h
@@ -20,7 +20,7 @@
   // CastWindowManager implementation:
   void TearDown() override;
   void AddWindow(gfx::NativeView window) override;
-  void SetWindowId(gfx::NativeView window, WindowId window_id) override;
+  void SetZOrder(gfx::NativeView window, mojom::ZOrder z_order) override;
   gfx::NativeView GetRootWindow() override;
   std::vector<WindowId> GetWindowOrder() override;
   void InjectEvent(ui::Event* event) override;
diff --git a/chromecast/graphics/rounded_window_corners_aura.cc b/chromecast/graphics/rounded_window_corners_aura.cc
index 4838c71f..df992b71 100644
--- a/chromecast/graphics/rounded_window_corners_aura.cc
+++ b/chromecast/graphics/rounded_window_corners_aura.cc
@@ -8,6 +8,7 @@
 
 #include "base/threading/thread_checker.h"
 #include "chromecast/graphics/cast_window_manager.h"
+#include "chromecast/ui/mojom/ui_service.mojom.h"
 #include "ui/aura/window.h"
 #include "ui/gfx/canvas.h"
 #include "ui/views/layout/layout_provider.h"
@@ -93,7 +94,7 @@
   add_view(kCornerRadius, false, true);
   add_view(kCornerRadius, true, true);
 
-  widget_.reset(new views::Widget);
+  widget_ = std::make_unique<views::Widget>();
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.context = window_manager->GetRootWindow();
@@ -104,8 +105,8 @@
   widget_->SetContentsView(main_view.release());
   widget_->GetNativeWindow()->SetName("RoundCorners");
 
-  window_manager->SetWindowId(widget_->GetNativeView(),
-                              CastWindowManager::CORNERS_OVERLAY);
+  window_manager->SetZOrder(widget_->GetNativeView(),
+                            mojom::ZOrder::CORNERS_OVERLAY);
 
   widget_->Show();
 }
diff --git a/chromecast/ui/BUILD.gn b/chromecast/ui/BUILD.gn
new file mode 100644
index 0000000..367682c
--- /dev/null
+++ b/chromecast/ui/BUILD.gn
@@ -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.
+
+import("//chromecast/chromecast.gni")
+import("//components/vector_icons/vector_icons.gni")
+
+cast_source_set("media_control_ui") {
+  sources = [
+    "media_control_ui.cc",
+    "media_control_ui.h",
+  ]
+
+  deps = [
+    ":vector_icons",
+    "//base",
+    "//chromecast/base",
+    "//chromecast/graphics",
+    "//chromecast/ui/mojom",
+    "//ui/aura:aura",
+    "//ui/views",
+  ]
+}
+
+# Rules for generating vector icon source files.
+# Adapted from //components/vector_icons/BUILD.gn
+
+aggregate_vector_icons("media_controls_vector_icons") {
+  icon_directory = "vector_icons"
+
+  icons = [
+    "back30.icon",
+    "forward30.icon",
+    "next.icon",
+    "pause.icon",
+    "play.icon",
+    "previous.icon",
+  ]
+}
+
+static_library("vector_icons") {
+  sources = get_target_outputs(":media_controls_vector_icons")
+
+  deps = [
+    ":media_controls_vector_icons",
+    "//base",
+    "//skia",
+    "//ui/gfx",
+  ]
+}
diff --git a/chromecast/ui/DEPS b/chromecast/ui/DEPS
new file mode 100644
index 0000000..c93cf39
--- /dev/null
+++ b/chromecast/ui/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+chromecast/graphics",
+  "+mojo/public",
+  "+ui/aura",
+  "+ui/gfx",
+  "+ui/views",
+]
diff --git a/chromecast/ui/OWNERS b/chromecast/ui/OWNERS
new file mode 100644
index 0000000..3bdcee8
--- /dev/null
+++ b/chromecast/ui/OWNERS
@@ -0,0 +1 @@
+seantopping@chromium.org
diff --git a/chromecast/ui/media_control_ui.cc b/chromecast/ui/media_control_ui.cc
new file mode 100644
index 0000000..9ceffca
--- /dev/null
+++ b/chromecast/ui/media_control_ui.cc
@@ -0,0 +1,344 @@
+// 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 "chromecast/ui/media_control_ui.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_checker.h"
+#include "chromecast/graphics/cast_window_manager.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/font_list.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/background.h"
+
+#define LOG_VIEW(name) DVLOG(1) << #name << ": " << name->bounds().ToString();
+
+namespace chromecast {
+
+namespace {
+
+constexpr base::TimeDelta kUpdateMediaTimePeriod =
+    base::TimeDelta::FromSeconds(1);
+const int kButtonSmallHeight = 56;
+const int kButtonBigHeight = 124;
+
+void SetButtonImage(views::ImageButton* button, const gfx::VectorIcon& icon) {
+  button->SetImage(
+      views::Button::STATE_NORMAL,
+      gfx::CreateVectorIcon(icon, button->height(), SK_ColorWHITE));
+}
+
+// A view that invokes an |on_tapped| closure whenever it detects a tap gesture.
+class TouchView : public views::View {
+ public:
+  explicit TouchView(base::RepeatingClosure on_tapped)
+      : on_tapped_(std::move(on_tapped)) {}
+
+ private:
+  // views::View implementation:
+  void OnGestureEvent(ui::GestureEvent* event) override {
+    if (event->type() == ui::ET_GESTURE_TAP) {
+      on_tapped_.Run();
+    }
+  }
+
+  base::RepeatingClosure on_tapped_;
+
+  DISALLOW_COPY_AND_ASSIGN(TouchView);
+};
+
+}  // namespace
+
+MediaControlUi::MediaControlUi(CastWindowManager* window_manager)
+    : window_manager_(window_manager),
+      app_is_fullscreen_(false),
+      is_paused_(false),
+      media_duration_(-1.0),
+      last_media_time_(0.0),
+      weak_factory_(this) {
+  DCHECK(window_manager_);
+
+  // |touch_view| will detect touch events and decide whether to show
+  // or hide |view_|, which contains the media controls.
+  touch_view_ = std::make_unique<TouchView>(base::BindRepeating(
+      &MediaControlUi::OnTapped, weak_factory_.GetWeakPtr()));
+
+  // Main view.
+  view_ = std::make_unique<views::View>();
+  view_->set_owned_by_client();
+  view_->SetVisible(false);
+  view_->SetBackground(
+      views::CreateSolidBackground(SkColorSetA(SK_ColorBLACK, 0x80)));
+  view_->SetBoundsRect(
+      window_manager_->GetRootWindow()->GetBoundsInRootWindow());
+  touch_view_->AddChildView(view_.get());
+
+  // Buttons.
+  btn_previous_ =
+      CreateImageButton(vector_icons::kPreviousIcon, kButtonSmallHeight);
+  btn_previous_->set_owned_by_client();
+  view_->AddChildView(btn_previous_.get());
+  btn_play_pause_ =
+      CreateImageButton(vector_icons::kPlayIcon, kButtonBigHeight);
+  btn_play_pause_->set_owned_by_client();
+  view_->AddChildView(btn_play_pause_.get());
+  btn_next_ = CreateImageButton(vector_icons::kNextIcon, kButtonSmallHeight);
+  btn_next_->set_owned_by_client();
+  view_->AddChildView(btn_next_.get());
+  btn_replay30_ =
+      CreateImageButton(vector_icons::kBack30Icon, kButtonSmallHeight);
+  btn_replay30_->set_owned_by_client();
+  view_->AddChildView(btn_replay30_.get());
+  btn_forward30_ =
+      CreateImageButton(vector_icons::kForward30Icon, kButtonSmallHeight);
+  btn_forward30_->set_owned_by_client();
+  view_->AddChildView(btn_forward30_.get());
+
+  // Labels.
+  lbl_title_ = std::make_unique<views::Label>(base::string16());
+  lbl_title_->set_owned_by_client();
+  view_->AddChildView(lbl_title_.get());
+  lbl_meta_ = std::make_unique<views::Label>(base::string16());
+  lbl_meta_->set_owned_by_client();
+  view_->AddChildView(lbl_meta_.get());
+
+  // Progress Bar.
+  progress_bar_ = std::make_unique<views::ProgressBar>();
+  progress_bar_->set_owned_by_client();
+  view_->AddChildView(progress_bar_.get());
+
+  LayoutElements();
+
+  // Main widget.
+  widget_.reset(new views::Widget);
+  views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
+  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  params.context = window_manager_->GetRootWindow();
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
+  params.bounds = window_manager_->GetRootWindow()->GetBoundsInRootWindow();
+  widget_->Init(std::move(params));
+  widget_->SetContentsView(touch_view_.release());  // Ownership passed.
+
+  window_manager_->SetZOrder(widget_->GetNativeView(),
+                             mojom::ZOrder::MEDIA_INFO);
+}
+
+MediaControlUi::~MediaControlUi() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void MediaControlUi::SetClient(
+    mojo::PendingRemote<mojom::MediaControlClient> client) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  client_.Bind(std::move(client));
+  MaybeShowWidget();
+}
+
+void MediaControlUi::SetAttributes(
+    mojom::MediaControlUiAttributesPtr attributes) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  last_media_timestamp_ = base::TimeTicks::Now();
+  last_media_time_ = attributes->current_time;
+
+  // Only update the media time if the player is playing.
+  if (attributes->is_playing) {
+    media_time_update_timer_.Start(
+        FROM_HERE, kUpdateMediaTimePeriod,
+        base::BindRepeating(&MediaControlUi::UpdateMediaTime,
+                            weak_factory_.GetWeakPtr()));
+  } else {
+    media_time_update_timer_.Stop();
+  }
+
+  if (attributes->is_paused != is_paused_) {
+    is_paused_ = attributes->is_paused;
+    if (is_paused_ && !visible()) {
+      ShowMediaControls(true);
+    }
+    SetButtonImage(btn_play_pause_.get(), is_paused_
+                                              ? vector_icons::kPlayIcon
+                                              : vector_icons::kPauseIcon);
+  }
+
+  if (attributes->duration > 0.0) {
+    media_duration_ = attributes->duration;
+  } else if (attributes->duration == 0.0) {
+    media_duration_ = -1.0;
+  }
+
+  if (attributes->duration > 0.0) {
+    progress_bar_->SetVisible(true);
+  } else if (attributes->duration == 0.0) {
+    progress_bar_->SetVisible(false);
+  }
+
+  lbl_title_->SetText(base::UTF8ToUTF16(attributes->title));
+  lbl_meta_->SetText(base::UTF8ToUTF16(attributes->metadata));
+}
+
+void MediaControlUi::SetBounds(const gfx::Rect& new_bounds) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto approx_equals = [](int a, int b) {
+    const int epsilon = 20;
+    return std::abs(a - b) < epsilon;
+  };
+
+  auto root_bounds = window_manager_->GetRootWindow()->bounds();
+  app_is_fullscreen_ = approx_equals(root_bounds.width(), new_bounds.width()) &&
+                       approx_equals(root_bounds.height(), new_bounds.height());
+  MaybeShowWidget();
+}
+
+void MediaControlUi::MaybeShowWidget() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (client_ && app_is_fullscreen_) {
+    LOG(INFO) << "Enabling platform media controls";
+    widget_->Show();
+  } else {
+    LOG(INFO) << "Disabling platform media controls";
+    widget_->Hide();
+  }
+}
+
+void MediaControlUi::ShowMediaControls(bool visible) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  view_->SetVisible(visible);
+}
+
+bool MediaControlUi::visible() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return (view_ && view_->GetVisible());
+}
+
+void MediaControlUi::UpdateMediaTime() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  double progress =
+      last_media_time_ +
+      (base::TimeTicks::Now() - last_media_timestamp_).InSecondsF();
+  if (media_duration_ > 0.0) {
+    progress = std::min(std::max(0.0, progress), media_duration_);
+    progress_bar_->SetValue(progress / media_duration_);
+  }
+}
+
+void MediaControlUi::ButtonPressed(views::Button* sender,
+                                   const ui::Event& event) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!client_) {
+    return;
+  }
+
+  if (sender == btn_previous_.get()) {
+    client_->Execute(mojom::MediaCommand::PREVIOUS);
+  } else if (sender == btn_play_pause_.get()) {
+    client_->Execute(mojom::MediaCommand::TOGGLE_PLAY_PAUSE);
+  } else if (sender == btn_next_.get()) {
+    client_->Execute(mojom::MediaCommand::NEXT);
+  } else if (sender == btn_replay30_.get()) {
+    client_->Execute(mojom::MediaCommand::REPLAY_30_SECONDS);
+  } else if (sender == btn_forward30_.get()) {
+    client_->Execute(mojom::MediaCommand::FORWARD_30_SECONDS);
+  } else {
+    NOTREACHED();
+  }
+}
+
+void MediaControlUi::OnTapped() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  ShowMediaControls(!visible());
+}
+
+std::unique_ptr<views::ImageButton> MediaControlUi::CreateImageButton(
+    const gfx::VectorIcon& icon,
+    int height) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  auto button = std::make_unique<views::ImageButton>(this);
+  button->SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
+  button->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
+  button->SetSize(gfx::Size(height, height));
+  SetButtonImage(button.get(), icon);
+
+  return button;
+}
+
+void MediaControlUi::LayoutElements() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const int kPrevousNextMargin = 112;
+  const int kSmallButtonBottomMargin = 56;
+  const int kLargeButtonBottomMargin = 22;
+  const int kReplayOffset = 236 / 3;
+  const int kParentWidth = view_->width();
+  const int kParentHeight = view_->height();
+
+  btn_previous_->SetPosition(gfx::Point(
+      kPrevousNextMargin,
+      kParentHeight - btn_previous_->height() - kSmallButtonBottomMargin));
+
+  btn_next_->SetPosition(gfx::Point(
+      kParentWidth - btn_next_->width() - kPrevousNextMargin,
+      kParentHeight - btn_next_->height() - kSmallButtonBottomMargin));
+
+  btn_play_pause_->SetPosition(gfx::Point(
+      (kParentWidth - btn_play_pause_->width()) / 2,
+      kParentHeight - btn_play_pause_->height() - kLargeButtonBottomMargin));
+
+  btn_replay30_->SetPosition(gfx::Point(
+      kParentWidth / 2 - kReplayOffset - btn_replay30_->width(),
+      kParentHeight - btn_replay30_->height() - kSmallButtonBottomMargin));
+
+  btn_forward30_->SetPosition(gfx::Point(
+      kParentWidth / 2 + kReplayOffset,
+      kParentHeight - btn_forward30_->height() - kSmallButtonBottomMargin));
+
+  const int kProgressMargin = 56;
+  const int kProgressBarHeight = 5;
+
+  progress_bar_->SetBounds(
+      kPrevousNextMargin, btn_previous_->y() - kProgressMargin,
+      kParentWidth - kPrevousNextMargin * 2, kProgressBarHeight);
+
+  const int kTitleMargin = 56;
+  const int kTitleLineHeight = 80;
+  const int kMetadataMargin = 20;
+  const int kMetadataLineHeight = 24;
+
+  lbl_title_->SetFontList(gfx::FontList("GoogleSans, 68px"));
+  lbl_title_->SetLineHeight(kTitleLineHeight);
+  lbl_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  lbl_title_->SetAutoColorReadabilityEnabled(false);
+  lbl_title_->SetEnabledColor(SkColorSetA(SK_ColorWHITE, 0xFF));
+  lbl_title_->SetBounds(
+      kPrevousNextMargin, progress_bar_->y() - kTitleMargin - kTitleLineHeight,
+      kParentWidth - 2 * kPrevousNextMargin, kTitleLineHeight);
+
+  lbl_meta_->SetFontList(gfx::FontList("GoogleSans, 24px"));
+  lbl_meta_->SetLineHeight(kMetadataLineHeight);
+  lbl_meta_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  lbl_meta_->SetAutoColorReadabilityEnabled(false);
+  lbl_meta_->SetEnabledColor(SkColorSetA(SK_ColorWHITE, 0xFF * 0.7));
+  lbl_meta_->SetBounds(kPrevousNextMargin,
+                       lbl_title_->y() - kMetadataMargin - kMetadataLineHeight,
+                       kParentWidth - 2 * kPrevousNextMargin,
+                       kMetadataLineHeight);
+
+  LOG_VIEW(view_);
+  LOG_VIEW(btn_previous_);
+  LOG_VIEW(btn_next_);
+  LOG_VIEW(btn_play_pause_);
+  LOG_VIEW(btn_replay30_);
+  LOG_VIEW(btn_forward30_);
+  LOG_VIEW(progress_bar_);
+  LOG_VIEW(lbl_title_);
+  LOG_VIEW(lbl_meta_);
+}
+
+}  // namespace chromecast
diff --git a/chromecast/ui/media_control_ui.h b/chromecast/ui/media_control_ui.h
new file mode 100644
index 0000000..5c29aa9d
--- /dev/null
+++ b/chromecast/ui/media_control_ui.h
@@ -0,0 +1,112 @@
+// 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 CHROMECAST_UI_MEDIA_CONTROL_UI_H_
+#define CHROMECAST_UI_MEDIA_CONTROL_UI_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chromecast/ui/mojom/media_control_ui.mojom.h"
+#include "chromecast/ui/vector_icons.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/progress_bar.h"
+#include "ui/views/layout/layout_provider.h"
+#include "ui/views/widget/widget.h"
+
+namespace chromecast {
+
+class CastWindowManager;
+
+// Provides a simple touch-based media UI for Aura platforms. This is used to
+// enable simple touch support for media apps which are not yet touch-enabled.
+// This class uses ui::views primitives to draw the UI.
+class MediaControlUi : public mojom::MediaControlUi,
+                       public views::ButtonListener {
+ public:
+  explicit MediaControlUi(CastWindowManager* window_manager);
+  ~MediaControlUi() override;
+
+  void SetBounds(const gfx::Rect& new_bounds);
+
+  // mojom::MediaControlUi implementation:
+  void SetClient(
+      mojo::PendingRemote<mojom::MediaControlClient> client) override;
+  void SetAttributes(mojom::MediaControlUiAttributesPtr attributes) override;
+
+ private:
+  // Only shows the media control widget if the media app window is full screen.
+  void MaybeShowWidget();
+  void ShowMediaControls(bool visible);
+  bool visible() const;
+  void OnTapped();
+  std::unique_ptr<views::ImageButton> CreateImageButton(
+      const gfx::VectorIcon& icon,
+      int height);
+
+  // Place elements in the locations specified by the UI spec.
+  // In this case, using views::LayoutManager is more difficult since we care
+  // about the position of each specific button, not the positions of view
+  // children in general.
+  void LayoutElements();
+
+  // Update the media time progress bar.
+  void UpdateMediaTime();
+
+  // views::ButtonListener implementation:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  CastWindowManager* const window_manager_;
+  mojo::Remote<mojom::MediaControlClient> client_;
+
+  // This must be true to enable media overlay.
+  bool app_is_fullscreen_;
+
+  // UI components
+  std::unique_ptr<views::Widget> widget_;
+  std::unique_ptr<views::View> touch_view_;
+  std::unique_ptr<views::View> view_;
+
+  // Controls
+  std::unique_ptr<views::ImageButton> btn_previous_;
+  std::unique_ptr<views::ImageButton> btn_play_pause_;
+  std::unique_ptr<views::ImageButton> btn_next_;
+  std::unique_ptr<views::ImageButton> btn_replay30_;
+  std::unique_ptr<views::ImageButton> btn_forward30_;
+
+  // Labels
+  std::unique_ptr<views::Label> lbl_meta_;
+  std::unique_ptr<views::Label> lbl_title_;
+
+  // Progress
+  std::unique_ptr<views::ProgressBar> progress_bar_;
+
+  bool is_paused_;
+
+  double media_duration_;
+  // Last media playback time reported from the app.
+  double last_media_time_;
+  // The absolute time that |last_media_time_| was reported. This is used to
+  // extrapolate the current media playback time as time progresses.
+  base::TimeTicks last_media_timestamp_;
+
+  base::RepeatingTimer media_time_update_timer_;
+
+  views::LayoutProvider layout_provider_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<MediaControlUi> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaControlUi);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_UI_MEDIA_CONTROL_UI_H_
diff --git a/chromecast/ui/mojom/BUILD.gn b/chromecast/ui/mojom/BUILD.gn
new file mode 100644
index 0000000..f4db663
--- /dev/null
+++ b/chromecast/ui/mojom/BUILD.gn
@@ -0,0 +1,12 @@
+# 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("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+  sources = [
+    "media_control_ui.mojom",
+    "ui_service.mojom",
+  ]
+}
diff --git a/chromecast/ui/mojom/OWNERS b/chromecast/ui/mojom/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/chromecast/ui/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromecast/ui/mojom/media_control_ui.mojom b/chromecast/ui/mojom/media_control_ui.mojom
new file mode 100644
index 0000000..9dbdfda
--- /dev/null
+++ b/chromecast/ui/mojom/media_control_ui.mojom
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chromecast.mojom;
+
+// API for a touch-based media overlay UI. The client receives commands that
+// are triggered from the UI. The client may also set various attributes on
+// the UI, so that only relevant controls are visible to the user.
+interface MediaControlUi {
+  // Set a client for the UI.
+  SetClient(pending_remote<MediaControlClient> client);
+  // Update the UI to match the new attributes.
+  SetAttributes(MediaControlUiAttributes attributes);
+};
+
+enum MediaCommand {
+  TOGGLE_PLAY_PAUSE,
+  NEXT,
+  PREVIOUS,
+  FORWARD_30_SECONDS,
+  REPLAY_30_SECONDS,
+};
+
+// Endpoint for plumbing media commands invoked by the UI.
+interface MediaControlClient {
+  // Executes a media command on behalf of the UI. These commands are only
+  // triggered by user interaction via the touch-based UI.
+  Execute(MediaCommand command);
+};
+
+struct MediaControlUiAttributes {
+  // UI attributes.
+  bool show_seek;
+  bool show_next;
+  bool show_previous;
+
+  // Media attributes.
+  string title;
+  string metadata;
+  double current_time;
+  double duration;
+  bool is_playing;
+  bool is_paused;
+};
+
+
diff --git a/chromecast/ui/mojom/ui_service.mojom b/chromecast/ui/mojom/ui_service.mojom
new file mode 100644
index 0000000..b9ff674
--- /dev/null
+++ b/chromecast/ui/mojom/ui_service.mojom
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chromecast.mojom;
+
+enum ZOrder {
+  // Base layer for web-based system UI, or for regular apps on Chromecast.
+  APP = -1,
+  // If the main system UI is web-based, then apps running in this layer won't
+  // be managed by the system web UI.
+  UNMANAGED_APP = 0,
+  // Diagnostic overlay for debugging.
+  DEBUG_OVERLAY = 1,
+  // Overlay apps with transparent backgrounds.
+  INFO_OVERLAY = 2,
+  // Unused.
+  SOFT_KEYBOARD = 3,
+  // Volume bar overlay.
+  VOLUME = 4,
+  // Touch-based media controls.
+  MEDIA_INFO = 5,
+  // Touch-based settings UI.
+  SETTINGS = 6,
+  // Boot animation.
+  BOOT_ANIMATION_OVERLAY = 7,
+  // Rounded corners for select devices.
+  CORNERS_OVERLAY = 8,
+};
diff --git a/chromecast/ui/vector_icons/back30.icon b/chromecast/ui/vector_icons/back30.icon
new file mode 100644
index 0000000..4f96533
--- /dev/null
+++ b/chromecast/ui/vector_icons/back30.icon
@@ -0,0 +1,86 @@
+MOVE_TO, 4, 24,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 3, 0,
+R_CUBIC_TO, 0, 9.39f, 7.61f, 17, 17, 17,
+R_CUBIC_TO, 9.39f, 0, 17, -7.61f, 17, -17,
+CUBIC_TO_SHORTHAND, 33.39f, 7, 24, 7,
+R_H_LINE_TO, -3.5f,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 0, -3,
+H_LINE_TO, 24,
+R_CUBIC_TO, 11.05f, 0, 20, 8.95f, 20, 20,
+R_CUBIC_TO, 0, 11.05f, -8.95f, 20, -20, 20,
+CUBIC_TO_SHORTHAND, 4, 35.05f, 4, 24,
+CLOSE,
+MOVE_TO, 20.62f, 5.5f,
+R_LINE_TO, 2.94f, 2.94f,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, -2.12f, 2.12f,
+R_LINE_TO, -4, -4,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 0, -2.12f,
+R_LINE_TO, 4, -4,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 2.12f, 2.12f,
+LINE_TO, 20.62f, 5.5f,
+CLOSE,
+MOVE_TO, 18.31f, 30.27f,
+R_ARC_TO, 4.67f, 4.67f, 0, 0, 1, -2.76f, -0.8f,
+ARC_TO, 4.15f, 4.15f, 0, 0, 1, 14, 27.14f,
+R_LINE_TO, 2.43f, -1,
+R_CUBIC_TO, 0.1f, 0.96f, 0.94f, 1.67f, 1.9f, 1.62f,
+R_ARC_TO, 1.7f, 1.7f, 0, 0, 0, 1.07f, -0.36f,
+R_ARC_TO, 1.15f, 1.15f, 0, 0, 0, 0.46f, -1,
+R_CUBIC_TO, 0, -0.92f, -0.67f, -1.38f, -2, -1.38f,
+R_H_LINE_TO, -1,
+R_V_LINE_TO, -2.23f,
+R_H_LINE_TO, 0.94f,
+R_CUBIC_TO, 0.2f, 0, 0.4f, -0.02f, 0.59f, -0.07f,
+R_CUBIC_TO, 0.18f, -0.04f, 0.36f, -0.11f, 0.52f, -0.21f,
+R_ARC_TO, 1.07f, 1.07f, 0, 0, 0, 0.37f, -0.38f,
+R_ARC_TO, 1.19f, 1.19f, 0, 0, 0, 0.14f, -0.59f,
+R_CUBIC_TO, 0, -0.29f, -0.12f, -0.57f, -0.33f, -0.78f,
+R_ARC_TO, 1.23f, 1.23f, 0, 0, 0, -0.93f, -0.34f,
+R_ARC_TO, 1.45f, 1.45f, 0, 0, 0, -1, 0.36f,
+R_ARC_TO, 1.79f, 1.79f, 0, 0, 0, -0.52f, 0.85f,
+R_LINE_TO, -2.33f, -1,
+R_ARC_TO, 4.21f, 4.21f, 0, 0, 1, 0.48f, -0.92f,
+R_CUBIC_TO, 0.22f, -0.31f, 0.49f, -0.59f, 0.8f, -0.82f,
+R_ARC_TO, 4.17f, 4.17f, 0, 0, 1, 1.14f, -0.59f,
+R_ARC_TO, 4.47f, 4.47f, 0, 0, 1, 1.49f, -0.23f,
+R_ARC_TO, 4.9f, 4.9f, 0, 0, 1, 1.57f, 0.24f,
+ARC_TO, 3.74f, 3.74f, 0, 0, 1, 21, 19,
+R_CUBIC_TO, 0.32f, 0.27f, 0.58f, 0.62f, 0.76f, 1,
+R_CUBIC_TO, 0.16f, 0.36f, 0.25f, 0.76f, 0.24f, 1.16f,
+R_ARC_TO, 3.25f, 3.25f, 0, 0, 1, -0.46f, 1.7f,
+R_ARC_TO, 2.54f, 2.54f, 0, 0, 1, -0.46f, 0.56f,
+R_ARC_TO, 2.11f, 2.11f, 0, 0, 1, -0.51f, 0.35f,
+R_V_LINE_TO, 0.17f,
+R_ARC_TO, 3, 3, 0, 0, 1, 1.34f, 1,
+R_ARC_TO, 3, 3, 0, 0, 1, 0.53f, 1.82f,
+R_ARC_TO, 3.32f, 3.32f, 0, 0, 1, -0.29f, 1.39f,
+R_ARC_TO, 3.21f, 3.21f, 0, 0, 1, -0.82f, 1.1f,
+ARC_TO, 3.85f, 3.85f, 0, 0, 1, 20, 30,
+R_ARC_TO, 5.31f, 5.31f, 0, 0, 1, -1.69f, 0.27f,
+CLOSE,
+R_MOVE_TO, 10.69f, 0,
+R_ARC_TO, 4.49f, 4.49f, 0, 0, 1, -3.64f, -1.73f,
+R_ARC_TO, 5.9f, 5.9f, 0, 0, 1, -1, -1.93f,
+R_ARC_TO, 8.59f, 8.59f, 0, 0, 1, 0, -4.84f,
+R_ARC_TO, 5.91f, 5.91f, 0, 0, 1, 1, -1.93f,
+R_ARC_TO, 4.64f, 4.64f, 0, 0, 1, 7.28f, 0,
+R_ARC_TO, 5.91f, 5.91f, 0, 0, 1, 1, 1.93f,
+R_ARC_TO, 8.57f, 8.57f, 0, 0, 1, 0, 4.84f,
+R_ARC_TO, 5.9f, 5.9f, 0, 0, 1, -1, 1.93f,
+ARC_TO, 4.51f, 4.51f, 0, 0, 1, 29, 30.27f,
+CLOSE,
+R_MOVE_TO, 0, -2.38f,
+R_ARC_TO, 1.91f, 1.91f, 0, 0, 0, 1, -0.28f,
+R_ARC_TO, 2.44f, 2.44f, 0, 0, 0, 0.77f, -0.77f,
+R_ARC_TO, 3.8f, 3.8f, 0, 0, 0, 0.47f, -1.17f,
+R_ARC_TO, 6.83f, 6.83f, 0, 0, 0, 0, -3,
+R_ARC_TO, 3.81f, 3.81f, 0, 0, 0, -0.47f, -1.17f,
+R_ARC_TO, 2.44f, 2.44f, 0, 0, 0, -0.77f, -0.77f,
+R_ARC_TO, 2, 2, 0, 0, 0, -2.06f, 0,
+R_ARC_TO, 2.44f, 2.44f, 0, 0, 0, -0.77f, 0.77f,
+R_ARC_TO, 3.81f, 3.81f, 0, 0, 0, -0.47f, 1.17f,
+R_ARC_TO, 6.83f, 6.83f, 0, 0, 0, 0, 3,
+R_CUBIC_TO, 0.09f, 0.41f, 0.25f, 0.81f, 0.47f, 1.17f,
+R_CUBIC_TO, 0.2f, 0.31f, 0.46f, 0.57f, 0.77f, 0.77f,
+R_ARC_TO, 1.9f, 1.9f, 0, 0, 0, 1.06f, 0.28f,
+CLOSE
diff --git a/chromecast/ui/vector_icons/forward30.icon b/chromecast/ui/vector_icons/forward30.icon
new file mode 100644
index 0000000..df2a65d
--- /dev/null
+++ b/chromecast/ui/vector_icons/forward30.icon
@@ -0,0 +1,86 @@
+MOVE_TO, 41, 24,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 3, 0,
+R_CUBIC_TO, 0, 11.05f, -8.95f, 20, -20, 20,
+CUBIC_TO_SHORTHAND, 4, 35.05f, 4, 24,
+CUBIC_TO_SHORTHAND, 12.95f, 4, 24, 4,
+R_H_LINE_TO, 3.5f,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 0, 3,
+H_LINE_TO, 24,
+CUBIC_TO, 14.61f, 7, 7, 14.61f, 7, 24,
+R_CUBIC_TO, 0, 9.39f, 7.61f, 17, 17, 17,
+R_CUBIC_TO, 9.39f, 0, 17, -7.61f, 17, -17,
+CLOSE,
+MOVE_TO, 24.44f, 2.56f,
+ARC_TO, 1.5f, 1.5f, 0, 0, 1, 26.56f, 0.44f,
+R_LINE_TO, 4, 4,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 0, 2.12f,
+R_LINE_TO, -4, 4,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, -2.12f, -2.12f,
+R_LINE_TO, 2.94f, -2.94f,
+R_LINE_TO, -2.94f, -2.94f,
+CLOSE,
+R_MOVE_TO, -6.13f, 27.71f,
+R_ARC_TO, 4.67f, 4.67f, 0, 0, 1, -2.76f, -0.8f,
+ARC_TO, 4.15f, 4.15f, 0, 0, 1, 14, 27.14f,
+R_LINE_TO, 2.43f, -1,
+R_CUBIC_TO, 0.1f, 0.96f, 0.94f, 1.67f, 1.9f, 1.62f,
+R_ARC_TO, 1.7f, 1.7f, 0, 0, 0, 1.07f, -0.36f,
+R_ARC_TO, 1.15f, 1.15f, 0, 0, 0, 0.46f, -1,
+R_CUBIC_TO, 0, -0.92f, -0.67f, -1.38f, -2, -1.38f,
+R_H_LINE_TO, -1,
+R_V_LINE_TO, -2.23f,
+R_H_LINE_TO, 0.94f,
+R_CUBIC_TO, 0.2f, 0, 0.4f, -0.02f, 0.59f, -0.07f,
+R_CUBIC_TO, 0.18f, -0.04f, 0.36f, -0.11f, 0.52f, -0.21f,
+R_ARC_TO, 1.07f, 1.07f, 0, 0, 0, 0.37f, -0.38f,
+R_ARC_TO, 1.19f, 1.19f, 0, 0, 0, 0.14f, -0.59f,
+R_CUBIC_TO, 0, -0.29f, -0.12f, -0.57f, -0.33f, -0.78f,
+R_ARC_TO, 1.23f, 1.23f, 0, 0, 0, -0.93f, -0.34f,
+R_ARC_TO, 1.45f, 1.45f, 0, 0, 0, -1, 0.36f,
+R_ARC_TO, 1.79f, 1.79f, 0, 0, 0, -0.52f, 0.85f,
+R_LINE_TO, -2.33f, -1,
+R_ARC_TO, 4.21f, 4.21f, 0, 0, 1, 0.48f, -0.92f,
+R_CUBIC_TO, 0.22f, -0.31f, 0.49f, -0.59f, 0.8f, -0.82f,
+R_ARC_TO, 4.17f, 4.17f, 0, 0, 1, 1.14f, -0.59f,
+R_ARC_TO, 4.47f, 4.47f, 0, 0, 1, 1.49f, -0.23f,
+R_ARC_TO, 4.9f, 4.9f, 0, 0, 1, 1.57f, 0.24f,
+ARC_TO, 3.74f, 3.74f, 0, 0, 1, 21, 19,
+R_CUBIC_TO, 0.32f, 0.27f, 0.58f, 0.62f, 0.76f, 1,
+R_CUBIC_TO, 0.16f, 0.36f, 0.25f, 0.76f, 0.24f, 1.16f,
+R_ARC_TO, 3.25f, 3.25f, 0, 0, 1, -0.46f, 1.7f,
+R_ARC_TO, 2.54f, 2.54f, 0, 0, 1, -0.46f, 0.56f,
+R_ARC_TO, 2.11f, 2.11f, 0, 0, 1, -0.51f, 0.35f,
+R_V_LINE_TO, 0.17f,
+R_ARC_TO, 3, 3, 0, 0, 1, 1.34f, 1,
+R_ARC_TO, 3, 3, 0, 0, 1, 0.53f, 1.82f,
+R_ARC_TO, 3.32f, 3.32f, 0, 0, 1, -0.29f, 1.39f,
+R_ARC_TO, 3.21f, 3.21f, 0, 0, 1, -0.82f, 1.1f,
+ARC_TO, 3.85f, 3.85f, 0, 0, 1, 20, 30,
+R_ARC_TO, 5.31f, 5.31f, 0, 0, 1, -1.69f, 0.27f,
+CLOSE,
+R_MOVE_TO, 10.69f, 0,
+R_ARC_TO, 4.49f, 4.49f, 0, 0, 1, -3.64f, -1.73f,
+R_ARC_TO, 5.9f, 5.9f, 0, 0, 1, -1, -1.93f,
+R_ARC_TO, 8.59f, 8.59f, 0, 0, 1, 0, -4.84f,
+R_ARC_TO, 5.91f, 5.91f, 0, 0, 1, 1, -1.93f,
+R_ARC_TO, 4.64f, 4.64f, 0, 0, 1, 7.28f, 0,
+R_ARC_TO, 5.91f, 5.91f, 0, 0, 1, 1, 1.93f,
+R_ARC_TO, 8.57f, 8.57f, 0, 0, 1, 0, 4.84f,
+R_ARC_TO, 5.9f, 5.9f, 0, 0, 1, -1, 1.93f,
+ARC_TO, 4.51f, 4.51f, 0, 0, 1, 29, 30.27f,
+CLOSE,
+R_MOVE_TO, 0, -2.38f,
+R_ARC_TO, 1.91f, 1.91f, 0, 0, 0, 1, -0.28f,
+R_ARC_TO, 2.44f, 2.44f, 0, 0, 0, 0.77f, -0.77f,
+R_ARC_TO, 3.8f, 3.8f, 0, 0, 0, 0.47f, -1.17f,
+R_ARC_TO, 6.83f, 6.83f, 0, 0, 0, 0, -3,
+R_ARC_TO, 3.81f, 3.81f, 0, 0, 0, -0.47f, -1.17f,
+R_ARC_TO, 2.44f, 2.44f, 0, 0, 0, -0.77f, -0.77f,
+R_ARC_TO, 2, 2, 0, 0, 0, -2.06f, 0,
+R_ARC_TO, 2.44f, 2.44f, 0, 0, 0, -0.77f, 0.77f,
+R_ARC_TO, 3.81f, 3.81f, 0, 0, 0, -0.47f, 1.17f,
+R_ARC_TO, 6.83f, 6.83f, 0, 0, 0, 0, 3,
+R_CUBIC_TO, 0.09f, 0.41f, 0.25f, 0.81f, 0.47f, 1.17f,
+R_CUBIC_TO, 0.2f, 0.31f, 0.46f, 0.57f, 0.77f, 0.77f,
+R_ARC_TO, 1.9f, 1.9f, 0, 0, 0, 1.06f, 0.28f,
+CLOSE
diff --git a/chromecast/ui/vector_icons/next.icon b/chromecast/ui/vector_icons/next.icon
new file mode 100644
index 0000000..69a1a9b7
--- /dev/null
+++ b/chromecast/ui/vector_icons/next.icon
@@ -0,0 +1,17 @@
+MOVE_TO, 37, 9.5f,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 3, 0,
+R_V_LINE_TO, 29,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, -3, 0,
+CLOSE,
+MOVE_TO, 11, 12.22f,
+R_V_LINE_TO, 23.56f,
+LINE_TO, 29.69f, 24,
+CLOSE,
+R_MOVE_TO, 22.3f, 13.05f,
+R_LINE_TO, -23, 14.5f,
+ARC_TO, 1.5f, 1.5f, 0, 0, 1, 8, 38.5f,
+R_V_LINE_TO, -29,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 2.3f, -1.27f,
+R_LINE_TO, 23, 14.5f,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 0, 2.54f,
+CLOSE
diff --git a/chromecast/ui/vector_icons/pause.icon b/chromecast/ui/vector_icons/pause.icon
new file mode 100644
index 0000000..c367240
--- /dev/null
+++ b/chromecast/ui/vector_icons/pause.icon
@@ -0,0 +1,12 @@
+MOVE_TO, 30, 9.5f,
+R_V_LINE_TO, 29,
+R_H_LINE_TO, 1,
+R_V_LINE_TO, -29,
+R_H_LINE_TO, -1,
+CLOSE,
+MOVE_TO, 17, 9.5f,
+R_V_LINE_TO, 29,
+R_H_LINE_TO, 1,
+R_V_LINE_TO, -29,
+R_H_LINE_TO, -1,
+CLOSE
diff --git a/chromecast/ui/vector_icons/play.icon b/chromecast/ui/vector_icons/play.icon
new file mode 100644
index 0000000..a53b0a80
--- /dev/null
+++ b/chromecast/ui/vector_icons/play.icon
@@ -0,0 +1,5 @@
+MOVE_TO, 38.5f, 24,
+R_LINE_TO, -23, 14.5f,
+R_V_LINE_TO, -29,
+R_LINE_TO, 23, 14.5f,
+CLOSE
diff --git a/chromecast/ui/vector_icons/previous.icon b/chromecast/ui/vector_icons/previous.icon
new file mode 100644
index 0000000..3cfe726
--- /dev/null
+++ b/chromecast/ui/vector_icons/previous.icon
@@ -0,0 +1,16 @@
+MOVE_TO, 8, 9.5f,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 3, 0,
+R_V_LINE_TO, 29,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, -3, 0,
+CLOSE,
+MOVE_TO, 14.7f, 25.27f,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, 0, -2.54f,
+R_LINE_TO, 23, -14.5f,
+ARC_TO, 1.5f, 1.5f, 0, 0, 1, 40, 9.5f,
+R_V_LINE_TO, 29,
+R_ARC_TO, 1.5f, 1.5f, 0, 0, 1, -2.3f, 1.27f,
+CLOSE,
+MOVE_TO, 37, 35.78f,
+V_LINE_TO, 12.22f,
+LINE_TO, 18.31f, 24,
+CLOSE
diff --git a/chromecast/ui/vector_icons/vector_icons.cc.template b/chromecast/ui/vector_icons/vector_icons.cc.template
new file mode 100644
index 0000000..aa7b889
--- /dev/null
+++ b/chromecast/ui/vector_icons/vector_icons.cc.template
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// vector_icons.cc.template is used to generate vector_icons.cc. Edit the former
+// rather than the latter.
+
+#include "chromecast/ui/vector_icons.h"
+
+#include "base/logging.h"
+#include "ui/gfx/vector_icon_types.h"
+
+#include "components/vector_icons/cc_macros.h"
+
+namespace chromecast {
+namespace vector_icons {
+
+using namespace gfx;
+
+TEMPLATE_PLACEHOLDER
+
+}  // namespace vector_icons
+}  // namespace chromecast
diff --git a/chromecast/ui/vector_icons/vector_icons.h.template b/chromecast/ui/vector_icons/vector_icons.h.template
new file mode 100644
index 0000000..d762130
--- /dev/null
+++ b/chromecast/ui/vector_icons/vector_icons.h.template
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// vector_icons.h.template is used to generate vector_icons.h. Edit the former
+// rather than the latter.
+
+#ifndef CHROMECAST_UI_VECTOR_ICONS_VECTOR_ICONS_H_
+#define CHROMECAST_UI_VECTOR_ICONS_VECTOR_ICONS_H_
+
+namespace gfx {
+struct VectorIcon;
+}
+
+#define VECTOR_ICON_TEMPLATE_H(icon_name) \
+extern const gfx::VectorIcon icon_name;
+
+namespace chromecast {
+namespace vector_icons {
+
+TEMPLATE_PLACEHOLDER
+
+}  // namespace vector_icons
+}  // namespace chromecast
+
+#undef VECTOR_ICON_TEMPLATE_H
+
+#endif //CHROMECAST_UI_VECTOR_ICONS_VECTOR_ICONS_H_
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn
index 87620b14..c5925e2 100644
--- a/chromeos/services/device_sync/BUILD.gn
+++ b/chromeos/services/device_sync/BUILD.gn
@@ -194,6 +194,8 @@
     "fake_cryptauth_metadata_syncer.h",
     "fake_cryptauth_scheduler.cc",
     "fake_cryptauth_scheduler.h",
+    "fake_cryptauth_v2_device_manager.cc",
+    "fake_cryptauth_v2_device_manager.h",
     "fake_cryptauth_v2_enroller.cc",
     "fake_cryptauth_v2_enroller.h",
     "fake_device_sync.cc",
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc b/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
index 07a9b0b..084b359a 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl.cc
@@ -39,16 +39,14 @@
 constexpr base::TimeDelta kWaitingForEncryptedDeviceMetadataProcessingTimeout =
     kMaxAsyncExecutionTime;
 
-void RecordGroupPrivateKeyDecryptionMetrics(
-    const base::TimeDelta& execution_time) {
+void RecordGroupPrivateKeyDecryptionMetrics(base::TimeDelta execution_time) {
   LogAsyncExecutionTimeMetric(
       "CryptAuth.DeviceSyncV2.DeviceSyncer.ExecutionTime."
       "GroupPrivateKeyDecryption",
       execution_time);
 }
 
-void RecordDeviceMetadataDecryptionMetrics(
-    const base::TimeDelta& execution_time) {
+void RecordDeviceMetadataDecryptionMetrics(base::TimeDelta execution_time) {
   LogAsyncExecutionTimeMetric(
       "CryptAuth.DeviceSyncV2.DeviceSyncer.ExecutionTime."
       "DeviceMetadataDecryption",
@@ -341,14 +339,15 @@
 }
 
 void CryptAuthDeviceSyncerImpl::OnGetFeatureStatusesFinished(
-    const CryptAuthFeatureStatusGetter::IdToFeatureStatusMap&
-        id_to_feature_status_map,
+    const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap&
+        id_to_device_software_feature_info_map,
     CryptAuthDeviceSyncResult::ResultCode device_sync_result_code) {
   DCHECK_EQ(State::kWaitingForFeatureStatuses, state_);
 
   // We require that the local device feature statuses are returned; the local
   // device is needed in the registry.
-  if (!base::Contains(id_to_feature_status_map, request_context_.device_id())) {
+  if (!base::Contains(id_to_device_software_feature_info_map,
+                      request_context_.device_id())) {
     FinishAttempt(CryptAuthDeviceSyncResult::ResultCode::
                       kErrorMissingLocalDeviceFeatureStatuses);
     return;
@@ -359,7 +358,7 @@
       did_non_fatal_error_occur_ = true;
       FALLTHROUGH;
     case CryptAuthDeviceSyncResult::ResultType::kSuccess:
-      BuildNewDeviceRegistry(id_to_feature_status_map);
+      BuildNewDeviceRegistry(id_to_device_software_feature_info_map);
       AttemptNextStep();
       return;
     case CryptAuthDeviceSyncResult::ResultType::kFatalError:
@@ -369,18 +368,17 @@
 }
 
 void CryptAuthDeviceSyncerImpl::BuildNewDeviceRegistry(
-    const CryptAuthFeatureStatusGetter::IdToFeatureStatusMap&
-        id_to_feature_status_map) {
+    const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap&
+        id_to_device_software_feature_info_map) {
   // Add all device information to the new registry except the remote device
   // BetterTogether metadata that will be decrypted and added later if possible.
   new_device_registry_map_ = CryptAuthDeviceRegistry::InstanceIdToDeviceMap();
-  for (const auto& id_feature_status_pair : id_to_feature_status_map) {
-    const std::string& id = id_feature_status_pair.first;
-    const CryptAuthFeatureStatusGetter::FeatureStatusMap& feature_status_map =
-        id_feature_status_pair.second;
+  for (const auto& id_device_software_feature_info_pair :
+       id_to_device_software_feature_info_map) {
+    const std::string& id = id_device_software_feature_info_pair.first;
 
-    // The IDs in |id_to_feature_status_map| should be a subset of those in
-    // |id_to_device_metadata_packet_map|.
+    // The IDs in |id_to_device_software_feature_info_map| should be a subset of
+    // those in |id_to_device_metadata_packet_map|.
     const auto packet_it = id_to_device_metadata_packet_map_.find(id);
     DCHECK(packet_it != id_to_device_metadata_packet_map_.end());
     const cryptauthv2::DeviceMetadataPacket& packet = packet_it->second;
@@ -390,11 +388,11 @@
     if (id == request_context_.device_id())
       beto_metadata = local_better_together_device_metadata_;
 
-    // TODO(https://crbug.com/990441): Add last_update_time when CryptAuth
-    // starts returning it.
     new_device_registry_map_->try_emplace(
-        id, id, packet.device_name(), packet.device_public_key(), base::Time(),
-        beto_metadata, feature_status_map);
+        id, id, packet.device_name(), packet.device_public_key(),
+        id_device_software_feature_info_pair.second.last_modified_time,
+        beto_metadata,
+        id_device_software_feature_info_pair.second.feature_state_map);
   }
 }
 
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl.h b/chromeos/services/device_sync/cryptauth_device_syncer_impl.h
index cb99b47..78094930 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl.h
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl.h
@@ -126,15 +126,15 @@
 
   void GetFeatureStatuses();
   void OnGetFeatureStatusesFinished(
-      const CryptAuthFeatureStatusGetter::IdToFeatureStatusMap&
-          id_to_feature_status_map,
+      const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap&
+          id_to_device_software_feature_info_map,
       CryptAuthDeviceSyncResult::ResultCode device_sync_result_code);
 
   // Builds a new device registry map with all device information except
   // decrypted BetterTogetherDeviceMetadata for remote devices.
   void BuildNewDeviceRegistry(
-      const CryptAuthFeatureStatusGetter::IdToFeatureStatusMap&
-          id_to_feature_status_map);
+      const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap&
+          id_to_device_software_feature_info_map);
 
   // If an encrypted group private key was sent by CryptAuth, decrypt it. Even
   // if we already have the unencrypted group private key in the key registry,
diff --git a/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
index f69b359..2f7cdc6 100644
--- a/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_syncer_impl_unittest.cc
@@ -260,14 +260,16 @@
   void FinishFeatureStatusGetterAttempt(
       const base::flat_set<std::string>& device_ids,
       CryptAuthDeviceSyncResult::ResultCode device_sync_result_code) {
-    CryptAuthFeatureStatusGetter::IdToFeatureStatusMap id_to_feature_status_map;
+    CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap
+        id_to_device_software_feature_info_map;
     for (const std::string& id : device_ids) {
-      id_to_feature_status_map.insert_or_assign(
-          id, GetTestDeviceWithId(id).feature_states);
+      id_to_device_software_feature_info_map.try_emplace(
+          id, GetTestDeviceWithId(id).feature_states,
+          GetTestDeviceWithId(id).last_update_time);
     }
 
-    feature_status_getter()->FinishAttempt(id_to_feature_status_map,
-                                           device_sync_result_code);
+    feature_status_getter()->FinishAttempt(
+        id_to_device_software_feature_info_map, device_sync_result_code);
   }
 
   void RunGroupPrivateKeyDecryptor(
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_getter.cc b/chromeos/services/device_sync/cryptauth_feature_status_getter.cc
index 04ed2d00..2a8d9da7 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_getter.cc
+++ b/chromeos/services/device_sync/cryptauth_feature_status_getter.cc
@@ -10,6 +10,21 @@
 
 namespace device_sync {
 
+CryptAuthFeatureStatusGetter::DeviceSoftwareFeatureInfo::
+    DeviceSoftwareFeatureInfo() = default;
+
+CryptAuthFeatureStatusGetter::DeviceSoftwareFeatureInfo::
+    DeviceSoftwareFeatureInfo(const SoftwareFeatureStateMap& feature_state_map,
+                              base::Time last_modified_time)
+    : feature_state_map(feature_state_map),
+      last_modified_time(last_modified_time) {}
+
+CryptAuthFeatureStatusGetter::DeviceSoftwareFeatureInfo::
+    DeviceSoftwareFeatureInfo(const DeviceSoftwareFeatureInfo&) = default;
+
+CryptAuthFeatureStatusGetter::DeviceSoftwareFeatureInfo::
+    ~DeviceSoftwareFeatureInfo() = default;
+
 CryptAuthFeatureStatusGetter::CryptAuthFeatureStatusGetter() = default;
 
 CryptAuthFeatureStatusGetter::~CryptAuthFeatureStatusGetter() = default;
@@ -28,10 +43,12 @@
 }
 
 void CryptAuthFeatureStatusGetter::OnAttemptFinished(
-    const IdToFeatureStatusMap& id_to_feature_status_map,
+    const IdToDeviceSoftwareFeatureInfoMap&
+        id_to_device_software_feature_info_map,
     CryptAuthDeviceSyncResult::ResultCode device_sync_result_code) {
   DCHECK(callback_);
-  std::move(callback_).Run(id_to_feature_status_map, device_sync_result_code);
+  std::move(callback_).Run(id_to_device_software_feature_info_map,
+                           device_sync_result_code);
 }
 
 }  // namespace device_sync
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_getter.h b/chromeos/services/device_sync/cryptauth_feature_status_getter.h
index 2941a27..d663d865 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_getter.h
+++ b/chromeos/services/device_sync/cryptauth_feature_status_getter.h
@@ -12,6 +12,7 @@
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
+#include "base/time/time.h"
 #include "chromeos/components/multidevice/software_feature.h"
 #include "chromeos/components/multidevice/software_feature_state.h"
 #include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
@@ -26,17 +27,34 @@
 
 // Handles the BatchGetFeatureStatuses portion of the CryptAuth v2 DeviceSync
 // protocol. Returns the feature statuses for each input device ID as a map from
-// multidevice::SoftwareFeature to multidevice::SoftwareFeatureState.
+// multidevice::SoftwareFeature to multidevice::SoftwareFeatureState along with
+// the last time any feature was modified.
 //
 // A CryptAuthFeatureStatusGetter object is designed to be used for only one
 // GetFeatureStatuses() call. For a new attempt, a new object should be created.
 class CryptAuthFeatureStatusGetter {
  public:
-  using FeatureStatusMap =
+  using SoftwareFeatureStateMap =
       std::map<multidevice::SoftwareFeature, multidevice::SoftwareFeatureState>;
-  using IdToFeatureStatusMap = base::flat_map<std::string, FeatureStatusMap>;
+
+  struct DeviceSoftwareFeatureInfo {
+    DeviceSoftwareFeatureInfo();
+    DeviceSoftwareFeatureInfo(const SoftwareFeatureStateMap& feature_state_map,
+                              base::Time last_modified_time);
+    DeviceSoftwareFeatureInfo(const DeviceSoftwareFeatureInfo&);
+    ~DeviceSoftwareFeatureInfo();
+
+    // A map from SoftwareFeature to SoftwareFeatureState.
+    SoftwareFeatureStateMap feature_state_map;
+
+    // The last time any feature state was modified in CryptAuth.
+    base::Time last_modified_time;
+  };
+
+  using IdToDeviceSoftwareFeatureInfoMap =
+      base::flat_map<std::string, DeviceSoftwareFeatureInfo>;
   using GetFeatureStatusesAttemptFinishedCallback =
-      base::OnceCallback<void(const IdToFeatureStatusMap&,
+      base::OnceCallback<void(const IdToDeviceSoftwareFeatureInfoMap&,
                               CryptAuthDeviceSyncResult::ResultCode)>;
 
   virtual ~CryptAuthFeatureStatusGetter();
@@ -58,7 +76,8 @@
       const base::flat_set<std::string>& device_ids) = 0;
 
   void OnAttemptFinished(
-      const IdToFeatureStatusMap& id_to_feature_status_map,
+      const IdToDeviceSoftwareFeatureInfoMap&
+          id_to_device_software_feature_info_map,
       CryptAuthDeviceSyncResult::ResultCode device_sync_result_code);
 
  private:
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.cc b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.cc
index 9261a3f6..358f5cd 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.cc
@@ -69,7 +69,7 @@
   }
 }
 
-CryptAuthFeatureStatusGetter::FeatureStatusMap
+CryptAuthFeatureStatusGetter::SoftwareFeatureStateMap
 ConvertFeatureStatusesToSoftwareFeatureMap(
     const ::google::protobuf::RepeatedPtrField<
         cryptauthv2::DeviceFeatureStatus::FeatureStatus>& feature_statuses,
@@ -105,7 +105,7 @@
     }
   }
 
-  CryptAuthFeatureStatusGetter::FeatureStatusMap feature_states;
+  CryptAuthFeatureStatusGetter::SoftwareFeatureStateMap feature_states;
   for (const multidevice::SoftwareFeature& feature : kAllSoftwareFeatures) {
     bool is_marked_supported = base::Contains(marked_supported, feature);
     bool is_marked_enabled = base::Contains(marked_enabled, feature);
@@ -130,7 +130,20 @@
   return feature_states;
 }
 
-void RecordGetFeatureStatusesMetrics(const base::TimeDelta& execution_time) {
+base::Time GetMaxLastModifiedTimeFromFeatureStatuses(
+    const ::google::protobuf::RepeatedPtrField<
+        cryptauthv2::DeviceFeatureStatus::FeatureStatus>& feature_statuses) {
+  int64_t max_last_modified_time_millis = 0;
+  for (const cryptauthv2::DeviceFeatureStatus::FeatureStatus& status :
+       feature_statuses) {
+    max_last_modified_time_millis = std::max(status.last_modified_time_millis(),
+                                             max_last_modified_time_millis);
+  }
+
+  return base::Time::FromJavaTime(max_last_modified_time_millis);
+}
+
+void RecordGetFeatureStatusesMetrics(base::TimeDelta execution_time) {
   LogAsyncExecutionTimeMetric(
       "CryptAuth.DeviceSyncV2.FeatureStatusGetter.ExecutionTime."
       "GetFeatureStatuses",
@@ -211,7 +224,7 @@
 void CryptAuthFeatureStatusGetterImpl::OnBatchGetFeatureStatusesSuccess(
     const base::flat_set<std::string>& input_device_ids,
     const cryptauthv2::BatchGetFeatureStatusesResponse& feature_response) {
-  DCHECK(id_to_feature_status_map_.empty());
+  DCHECK(id_to_device_software_feature_info_map_.empty());
 
   RecordGetFeatureStatusesMetrics(base::TimeTicks::Now() -
                                   start_get_feature_statuses_timestamp_);
@@ -232,7 +245,8 @@
     }
 
     // TODO(https://crbug.com/936273): Log metrics for duplicate device IDs.
-    bool is_duplicate_id = base::Contains(id_to_feature_status_map_, id);
+    bool is_duplicate_id =
+        base::Contains(id_to_device_software_feature_info_map_, id);
     if (is_duplicate_id) {
       PA_LOG(ERROR) << "Duplicate device IDs (" << id
                     << ") in BatchGetFeatureStatuses response.";
@@ -240,14 +254,18 @@
       continue;
     }
 
-    id_to_feature_status_map_[device_feature_status.device_id()] =
+    id_to_device_software_feature_info_map_.try_emplace(
+        device_feature_status.device_id(),
         ConvertFeatureStatusesToSoftwareFeatureMap(
             device_feature_status.feature_statuses(),
-            &did_non_fatal_error_occur);
+            &did_non_fatal_error_occur),
+        GetMaxLastModifiedTimeFromFeatureStatuses(
+            device_feature_status.feature_statuses()));
   }
 
   // TODO(https://crbug.com/936273): Log metrics for missing devices.
-  if (input_device_ids.size() != id_to_feature_status_map_.size()) {
+  if (input_device_ids.size() !=
+      id_to_device_software_feature_info_map_.size()) {
     PA_LOG(ERROR) << "Devices missing in BatchGetFeatureStatuses response.";
     did_non_fatal_error_occur = true;
   }
@@ -280,7 +298,7 @@
   cryptauth_client_.reset();
   timer_->Stop();
 
-  OnAttemptFinished(id_to_feature_status_map_, result_code);
+  OnAttemptFinished(id_to_device_software_feature_info_map_, result_code);
 }
 
 }  // namespace device_sync
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h
index 19f2bd9c..5612389a 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h
+++ b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h
@@ -62,7 +62,7 @@
 
   void FinishAttempt(CryptAuthDeviceSyncResult::ResultCode result_code);
 
-  IdToFeatureStatusMap id_to_feature_status_map_;
+  IdToDeviceSoftwareFeatureInfoMap id_to_device_software_feature_info_map_;
 
   // The CryptAuthClient for the latest CryptAuth request. The client can only
   // be used for one call; therefore, for each API call, a new client needs to
diff --git a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
index 98aad16..7418e3d 100644
--- a/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_feature_status_getter_impl_unittest.cc
@@ -62,6 +62,8 @@
     const base::flat_set<std::string>& feature_types) {
   cryptauthv2::DeviceFeatureStatus device_feature_status;
   device_feature_status.set_device_id(device.instance_id());
+
+  int64_t last_modified_time_offset_millis = 0;
   for (const std::string& feature_type : feature_types) {
     bool is_supported_feature_type =
         base::Contains(GetSupportedCryptAuthFeatureTypeStrings(), feature_type);
@@ -76,6 +78,15 @@
 
     cryptauthv2::DeviceFeatureStatus::FeatureStatus* feature_status =
         device_feature_status.add_feature_statuses();
+
+    // The first feature type in the set will have the device.last_update_time
+    // as the last_modified_time_millis. All other feature types will have
+    // smaller last_modified_time_millis.
+    feature_status->set_last_modified_time_millis(
+        std::max(0L, device.last_update_time.ToJavaTime() -
+                         last_modified_time_offset_millis));
+    ++last_modified_time_offset_millis;
+
     feature_status->set_feature_type(feature_type);
     if (is_supported_feature_type) {
       feature_status->set_enabled(is_supported);
@@ -187,13 +198,17 @@
       const base::flat_set<std::string>& expected_device_ids,
       CryptAuthDeviceSyncResult::ResultCode expected_result_code) {
     ASSERT_TRUE(device_sync_result_code_);
-    EXPECT_EQ(expected_device_ids.size(), id_to_feature_status_map_.size());
+    EXPECT_EQ(expected_device_ids.size(),
+              id_to_device_software_feature_info_map_.size());
     EXPECT_EQ(expected_result_code, device_sync_result_code_);
 
     for (const std::string& id : expected_device_ids) {
-      const auto it = id_to_feature_status_map_.find(id);
-      ASSERT_TRUE(it != id_to_feature_status_map_.end());
-      EXPECT_EQ(GetTestDeviceWithId(id).feature_states, it->second);
+      const auto it = id_to_device_software_feature_info_map_.find(id);
+      ASSERT_TRUE(it != id_to_device_software_feature_info_map_.end());
+      EXPECT_EQ(GetTestDeviceWithId(id).feature_states,
+                it->second.feature_state_map);
+      EXPECT_EQ(GetTestDeviceWithId(id).last_update_time,
+                it->second.last_modified_time);
     }
   }
 
@@ -214,10 +229,11 @@
   }
 
   void OnGetFeatureStatusesComplete(
-      const CryptAuthFeatureStatusGetter::IdToFeatureStatusMap&
-          id_to_feature_status_map,
+      const CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap&
+          id_to_device_software_feature_info_map,
       CryptAuthDeviceSyncResult::ResultCode device_sync_result_code) {
-    id_to_feature_status_map_ = id_to_feature_status_map;
+    id_to_device_software_feature_info_map_ =
+        id_to_device_software_feature_info_map;
     device_sync_result_code_ = device_sync_result_code;
   }
 
@@ -227,7 +243,8 @@
       batch_get_feature_statuses_success_callback_;
   CryptAuthClient::ErrorCallback batch_get_feature_statuses_failure_callback_;
 
-  CryptAuthFeatureStatusGetter::IdToFeatureStatusMap id_to_feature_status_map_;
+  CryptAuthFeatureStatusGetter::IdToDeviceSoftwareFeatureInfoMap
+      id_to_device_software_feature_info_map_;
   base::Optional<CryptAuthDeviceSyncResult::ResultCode>
       device_sync_result_code_;
 
diff --git a/chromeos/services/device_sync/cryptauth_v2_device_manager.h b/chromeos/services/device_sync/cryptauth_v2_device_manager.h
index cde63390..6d70c3bd 100644
--- a/chromeos/services/device_sync/cryptauth_v2_device_manager.h
+++ b/chromeos/services/device_sync/cryptauth_v2_device_manager.h
@@ -56,7 +56,7 @@
   //               by a GCM message or if no session ID was included in the GCM
   //               message.
   virtual void ForceDeviceSyncNow(
-      const cryptauthv2::ClientMetadata::InvocationReason&,
+      const cryptauthv2::ClientMetadata::InvocationReason& invocation_reason,
       const base::Optional<std::string>& session_id) = 0;
 
   // Returns true if a v2 DeviceSync attempt is currently in progress.
diff --git a/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc b/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
index a1b184c3..2b713ed 100644
--- a/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
+++ b/chromeos/services/device_sync/cryptauth_v2_device_sync_test_devices.cc
@@ -38,7 +38,7 @@
     return CryptAuthDevice(
         cryptauthv2::GetClientAppMetadataForTest().instance_id(),
         kLocalDeviceDeviceName, kLocalDeviceDeviceSyncBetterTogetherPublicKey,
-        base::Time(), bt_metadata,
+        base::Time::FromJsTime(100), bt_metadata,
         {
             {multidevice::SoftwareFeature::kBetterTogetherHost,
              multidevice::SoftwareFeatureState::kNotSupported},
@@ -85,7 +85,7 @@
         kRemoteDeviceNeedsGroupPrivateKeyId,
         kRemoteDeviceNeedsGroupPrivateKeyDeviceName,
         kRemoteDeviceNeedsGroupPrivateKeyDeviceSyncBetterTogetherPublicKey,
-        base::Time(), bt_metadata,
+        base::Time::FromJsTime(200), bt_metadata,
         {
             {multidevice::SoftwareFeature::kBetterTogetherHost,
              multidevice::SoftwareFeatureState::kEnabled},
@@ -132,7 +132,7 @@
         kRemoteDeviceHasGroupPrivateKeyId,
         kRemoteDeviceHasGroupPrivateKeyDeviceName,
         kRemoteDeviceHasGroupPrivateKeyDeviceSyncBetterTogetherPublicKey,
-        base::Time(), bt_metadata,
+        base::Time::FromJsTime(300), bt_metadata,
         {
             {multidevice::SoftwareFeature::kBetterTogetherHost,
              multidevice::SoftwareFeatureState::kNotSupported},
diff --git a/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.cc b/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.cc
index d07ace3..6488223 100644
--- a/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.cc
+++ b/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.cc
@@ -13,12 +13,14 @@
 FakeCryptAuthFeatureStatusGetter::~FakeCryptAuthFeatureStatusGetter() = default;
 
 void FakeCryptAuthFeatureStatusGetter::FinishAttempt(
-    const IdToFeatureStatusMap& id_to_feature_status_map,
+    const IdToDeviceSoftwareFeatureInfoMap&
+        id_to_device_software_feature_info_map,
     CryptAuthDeviceSyncResult::ResultCode device_sync_result_code) {
   DCHECK(request_context_);
   DCHECK(device_ids_);
 
-  OnAttemptFinished(id_to_feature_status_map, device_sync_result_code);
+  OnAttemptFinished(id_to_device_software_feature_info_map,
+                    device_sync_result_code);
 }
 
 void FakeCryptAuthFeatureStatusGetter::OnAttemptStarted(
diff --git a/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h b/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h
index 99967e5..9531a784 100644
--- a/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h
+++ b/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h
@@ -43,7 +43,8 @@
 
   // Calls OnAttemptFinished() with the same input parameters.
   void FinishAttempt(
-      const IdToFeatureStatusMap& id_to_feature_status_map,
+      const IdToDeviceSoftwareFeatureInfoMap&
+          id_to_device_software_feature_info_map,
       CryptAuthDeviceSyncResult::ResultCode device_sync_result_code);
 
  private:
diff --git a/chromeos/services/device_sync/fake_cryptauth_v2_device_manager.cc b/chromeos/services/device_sync/fake_cryptauth_v2_device_manager.cc
new file mode 100644
index 0000000..c5bc70a8
--- /dev/null
+++ b/chromeos/services/device_sync/fake_cryptauth_v2_device_manager.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 "chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+FakeCryptAuthV2DeviceManager::FakeCryptAuthV2DeviceManager() = default;
+
+FakeCryptAuthV2DeviceManager::~FakeCryptAuthV2DeviceManager() = default;
+
+void FakeCryptAuthV2DeviceManager::Start() {
+  has_started_ = true;
+}
+
+const CryptAuthDeviceRegistry::InstanceIdToDeviceMap&
+FakeCryptAuthV2DeviceManager::GetSyncedDevices() const {
+  return synced_devices_;
+}
+
+void FakeCryptAuthV2DeviceManager::ForceDeviceSyncNow(
+    const cryptauthv2::ClientMetadata::InvocationReason& invocation_reason,
+    const base::Optional<std::string>& session_id) {
+  DCHECK(has_started_);
+
+  cryptauthv2::ClientMetadata client_metadata;
+  client_metadata.set_invocation_reason(invocation_reason);
+  if (session_id)
+    client_metadata.set_session_id(*session_id);
+
+  force_device_sync_now_requests_.push(client_metadata);
+
+  NotifyDeviceSyncStarted(client_metadata);
+}
+
+bool FakeCryptAuthV2DeviceManager::IsDeviceSyncInProgress() const {
+  return !force_device_sync_now_requests_.empty();
+}
+
+bool FakeCryptAuthV2DeviceManager::IsRecoveringFromFailure() const {
+  return is_recovering_from_failure_;
+}
+
+base::Optional<base::Time> FakeCryptAuthV2DeviceManager::GetLastDeviceSyncTime()
+    const {
+  return last_device_sync_time_;
+}
+
+base::Optional<base::TimeDelta>
+FakeCryptAuthV2DeviceManager::GetTimeToNextAttempt() const {
+  return time_to_next_attempt_;
+}
+
+void FakeCryptAuthV2DeviceManager::FinishNextForcedDeviceSync(
+    const CryptAuthDeviceSyncResult& device_sync_result,
+    base::Time device_sync_finish_time) {
+  DCHECK(IsDeviceSyncInProgress());
+
+  if (device_sync_result.IsSuccess()) {
+    last_device_sync_time_ = device_sync_finish_time;
+    is_recovering_from_failure_ = false;
+  } else {
+    is_recovering_from_failure_ = true;
+  }
+
+  force_device_sync_now_requests_.pop();
+
+  NotifyDeviceSyncFinished(device_sync_result);
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h b/chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h
new file mode 100644
index 0000000..18d6d7b
--- /dev/null
+++ b/chromeos/services/device_sync/fake_cryptauth_v2_device_manager.h
@@ -0,0 +1,87 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_CRYPTAUTH_V2_DEVICE_MANAGER_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_CRYPTAUTH_V2_DEVICE_MANAGER_H_
+
+#include <string>
+
+#include "base/containers/queue.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "chromeos/services/device_sync/cryptauth_device.h"
+#include "chromeos/services/device_sync/cryptauth_device_registry.h"
+#include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
+#include "chromeos/services/device_sync/cryptauth_v2_device_manager.h"
+#include "chromeos/services/device_sync/proto/cryptauth_common.pb.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+// An implementation of CryptAuthV2DeviceManager used for tests. This
+// implementation queues DeviceSync requests made via ForceDeviceSyncNow().
+// These requests are sequentially processed by calls to
+// FinishNextForcedDeviceSync(), which also updates parameters such as the last
+// DeviceSync time.
+class FakeCryptAuthV2DeviceManager : public CryptAuthV2DeviceManager {
+ public:
+  FakeCryptAuthV2DeviceManager();
+  ~FakeCryptAuthV2DeviceManager() override;
+
+  // CryptAuthV2DeviceManager:
+  void Start() override;
+  const CryptAuthDeviceRegistry::InstanceIdToDeviceMap& GetSyncedDevices()
+      const override;
+  void ForceDeviceSyncNow(
+      const cryptauthv2::ClientMetadata::InvocationReason& invocation_reason,
+      const base::Optional<std::string>& session_id) override;
+  bool IsDeviceSyncInProgress() const override;
+  bool IsRecoveringFromFailure() const override;
+  base::Optional<base::Time> GetLastDeviceSyncTime() const override;
+  base::Optional<base::TimeDelta> GetTimeToNextAttempt() const override;
+
+  bool has_started() const { return has_started_; }
+
+  const base::queue<cryptauthv2::ClientMetadata>&
+  force_device_sync_now_requests() const {
+    return force_device_sync_now_requests_;
+  }
+
+  void set_synced_devices(
+      const CryptAuthDeviceRegistry::InstanceIdToDeviceMap& synced_devices) {
+    synced_devices_ = synced_devices;
+  }
+
+  void set_time_to_next_attempt(
+      const base::Optional<base::TimeDelta>& time_to_next_attempt) {
+    time_to_next_attempt_ = time_to_next_attempt;
+  }
+
+  // Finishes the next forced DeviceSync request in the queue. Should only be
+  // called if the queue of requests if not empty. If |device_sync_result|
+  // indicates success, |device_sync_finish_time| will be stored as the last
+  // DeviceSync time and will be returned by future calls to
+  // GetLastDeviceSyncTime().
+  void FinishNextForcedDeviceSync(
+      const CryptAuthDeviceSyncResult& device_sync_result,
+      base::Time device_sync_finish_time);
+
+ private:
+  bool has_started_ = false;
+  bool is_recovering_from_failure_ = false;
+  base::Optional<base::Time> last_device_sync_time_;
+  base::Optional<base::TimeDelta> time_to_next_attempt_;
+  CryptAuthDeviceRegistry::InstanceIdToDeviceMap synced_devices_;
+  base::queue<cryptauthv2::ClientMetadata> force_device_sync_now_requests_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeCryptAuthV2DeviceManager);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_CRYPTAUTH_V2_DEVICE_MANAGER_H_
diff --git a/components/arc/session/arc_vm_client_adapter.cc b/components/arc/session/arc_vm_client_adapter.cc
index faa0d73..c7f29b2c 100644
--- a/components/arc/session/arc_vm_client_adapter.cc
+++ b/components/arc/session/arc_vm_client_adapter.cc
@@ -30,10 +30,10 @@
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chromeos/dbus/concierge_client.h"
+#include "chromeos/dbus/dbus_method_call_status.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
 #include "chromeos/dbus/upstart/upstart_client.h"
@@ -418,10 +418,40 @@
  private:
   void OnIsDevMode(chromeos::VoidDBusMethodCallback callback,
                    bool is_dev_mode) {
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), true));
+    // Make sure to kill a stale arcvm-server-proxy job (if any).
+    chromeos::UpstartClient::Get()->StopJob(
+        kArcVmServerProxyJobName, /*environment=*/{},
+        base::BindOnce(&ArcVmClientAdapter::OnArcVmServerProxyJobStopped,
+                       weak_factory_.GetWeakPtr(), std::move(callback)));
     is_dev_mode_ = is_dev_mode;
+  }
+
+  void OnArcVmServerProxyJobStopped(chromeos::VoidDBusMethodCallback callback,
+                                    bool result) {
+    VLOG(1) << "OnArcVmServerProxyJobStopped: job "
+            << (result ? "stopped" : "not running?");
+
     should_notify_observers_ = true;
+    // Always run the |callback| with true ignoring the |result|. |result| can
+    // be false when the proxy job has already been stopped for other reasons,
+    // but it's not considered as an error.
+    std::move(callback).Run(true);
+  }
+
+  void OnArcVmServerProxyJobStarted(UpgradeParams params,
+                                    chromeos::VoidDBusMethodCallback callback,
+                                    bool result) {
+    if (!result) {
+      LOG(ERROR) << "Failed to start arcvm-server-proxy job";
+      std::move(callback).Run(false);
+      return;
+    }
+
+    VLOG(1) << "Starting Concierge service";
+    chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->StartConcierge(
+        base::BindOnce(&ArcVmClientAdapter::OnConciergeStarted,
+                       weak_factory_.GetWeakPtr(), std::move(params),
+                       std::move(callback)));
   }
 
   void OnConciergeStarted(UpgradeParams params,
@@ -443,7 +473,6 @@
         base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace,
                        base::FilePath(kHomeDirectory)),
         base::BindOnce(&ArcVmClientAdapter::CreateDiskImageAfterSizeCheck,
-
                        weak_factory_.GetWeakPtr(), std::move(params),
                        std::move(callback)));
   }
@@ -550,8 +579,7 @@
     // implemented.
     chromeos::UpstartClient::Get()->StopJob(
         kArcVmServerProxyJobName, /*environment=*/{},
-        base::BindOnce(&ArcVmClientAdapter::OnArcVmServerProxyJobStopped,
-                       weak_factory_.GetWeakPtr()));
+        chromeos::EmptyVoidDBusMethodCallback());
 
     // If this method is called before even mini VM is started (e.g. very early
     // vm_concierge crash), or this method is called twice (e.g. crosvm crash
@@ -567,7 +595,7 @@
   void OnStopVmReply(
       base::Optional<vm_tools::concierge::StopVmResponse> reply) {
     // If the reply indicates the D-Bus call is successfully done, do nothing.
-    // Concierge call OnVmStopped() eventually.
+    // Concierge will call OnVmStopped() eventually.
     if (reply.has_value() && reply.value().success())
       return;
 
@@ -577,26 +605,6 @@
     OnArcInstanceStopped();
   }
 
-  void OnArcVmServerProxyJobStarted(UpgradeParams params,
-                                    chromeos::VoidDBusMethodCallback callback,
-                                    bool result) {
-    if (!result) {
-      LOG(ERROR) << "Failed to start arcvm-server-proxy job";
-      std::move(callback).Run(false);
-      return;
-    }
-
-    VLOG(1) << "Starting Concierge service";
-    chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->StartConcierge(
-        base::BindOnce(&ArcVmClientAdapter::OnConciergeStarted,
-                       weak_factory_.GetWeakPtr(), std::move(params),
-                       std::move(callback)));
-  }
-
-  void OnArcVmServerProxyJobStopped(bool result) {
-    VLOG(1) << "OnArcVmServerProxyJobStopped result=" << result;
-  }
-
   const version_info::Channel channel_;
 
   base::Optional<bool> is_dev_mode_;
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 5a6915c2..3ded213 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1768,7 +1768,8 @@
   form->unique_renderer_id = form_element.UniqueRendererFormId();
   form->url = GetCanonicalOriginForDocument(frame->GetDocument());
   form->action = GetCanonicalActionForForm(form_element);
-  form->is_action_empty = form_element.Action().IsNull();
+  form->is_action_empty =
+      form_element.Action().IsNull() || form_element.Action().IsEmpty();
   if (IsAutofillFieldMetadataEnabled()) {
     SCOPED_UMA_HISTOGRAM_TIMER(
         "PasswordManager.ButtonTitlePerformance.HasFormTag");
diff --git a/components/data_reduction_proxy/DEPS b/components/data_reduction_proxy/DEPS
index a5bfa08c..24bcbd0d 100644
--- a/components/data_reduction_proxy/DEPS
+++ b/components/data_reduction_proxy/DEPS
@@ -9,6 +9,7 @@
   "+mojo/public/cpp",
   "+net",
   "+services/network/public/cpp",
+  "+services/network/public/mojom",
 
   "-components"
 
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
index bcc40f7..9436b638 100644
--- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
+++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
@@ -13,7 +13,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h"
 #include "components/data_reduction_proxy/core/common/uma_util.h"
 #include "net/base/load_flags.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 
 namespace net {
 class HttpRequestHeaders;
@@ -105,7 +105,7 @@
 
 void DataReductionProxyURLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& response_head,
+    const network::mojom::URLResponseHead& response_head,
     bool* defer,
     std::vector<std::string>* to_be_removed_request_headers,
     net::HttpRequestHeaders* modified_request_headers) {
@@ -117,7 +117,7 @@
 
 void DataReductionProxyURLLoaderThrottle::BeforeWillProcessResponse(
     const GURL& response_url,
-    const network::ResourceResponseHead& response_head,
+    const network::mojom::URLResponseHead& response_head,
     bool* defer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -185,7 +185,7 @@
 
 void DataReductionProxyURLLoaderThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h
index a31fac2..5d12f775 100644
--- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h
+++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h
@@ -42,16 +42,16 @@
                         bool* defer) override;
   void WillRedirectRequest(
       net::RedirectInfo* redirect_info,
-      const network::ResourceResponseHead& response_head,
+      const network::mojom::URLResponseHead& response_head,
       bool* defer,
       std::vector<std::string>* to_be_removed_request_headers,
       net::HttpRequestHeaders* modified_request_headers) override;
   void BeforeWillProcessResponse(
       const GURL& response_url,
-      const network::ResourceResponseHead& response_head,
+      const network::mojom::URLResponseHead& response_head,
       bool* defer) override;
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override;
   void WillOnCompleteWithError(const network::URLLoaderCompletionStatus& status,
                                bool* defer) override;
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc
index d3ea771..6006fc4f6 100644
--- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc
+++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc
@@ -19,7 +19,7 @@
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace data_reduction_proxy {
@@ -283,12 +283,12 @@
           ? drp_server.proxy_server()
           : net::ProxyServer::FromPacString("HTTPS otherproxy");
 
-  network::ResourceResponseHead response_head;
-  response_head.proxy_server = proxy_used_for_response;
-  response_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
-  response_head.headers->AddHeader("Chrome-Proxy: block-once");
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->proxy_server = proxy_used_for_response;
+  response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+  response_head->headers->AddHeader("Chrome-Proxy: block-once");
 
-  throttle.BeforeWillProcessResponse(request.url, response_head, &defer);
+  throttle.BeforeWillProcessResponse(request.url, *response_head, &defer);
   EXPECT_FALSE(defer);
   EXPECT_EQ(0u, delegate.resume_called);
 
@@ -347,12 +347,12 @@
 
   // Construct a response that did not come from a proxy server, however
   // includes a Chrome-Proxy header.
-  network::ResourceResponseHead response_head;
-  response_head.proxy_server = net::ProxyServer::Direct();
-  response_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
-  response_head.headers->AddHeader("Chrome-Proxy: block-once");
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->proxy_server = net::ProxyServer::Direct();
+  response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+  response_head->headers->AddHeader("Chrome-Proxy: block-once");
 
-  throttle.BeforeWillProcessResponse(request.url, response_head, &defer);
+  throttle.BeforeWillProcessResponse(request.url, *response_head, &defer);
   EXPECT_FALSE(defer);
   EXPECT_EQ(0u, delegate.resume_called);
   EXPECT_EQ(0u, delegate.restart_with_flags_called);
@@ -378,12 +378,12 @@
   EXPECT_EQ(0u, delegate.resume_called);
   EXPECT_EQ(0u, delegate.restart_with_flags_called);
 
-  network::ResourceResponseHead response_head;
-  response_head.proxy_server = drp_server.proxy_server();
-  response_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->proxy_server = drp_server.proxy_server();
+  response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
       "HTTP/1.1 500 Server error\n");
 
-  throttle.BeforeWillProcessResponse(request.url, response_head, &defer);
+  throttle.BeforeWillProcessResponse(request.url, *response_head, &defer);
 
   // The throttle should have marked the proxy as bad.
   EXPECT_TRUE(defer);
@@ -424,12 +424,12 @@
   throttle.WillStartRequest(&request, &defer);
   EXPECT_FALSE(defer);
 
-  network::ResourceResponseHead response_head;
-  response_head.proxy_server = drp_server.proxy_server();
-  response_head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->proxy_server = drp_server.proxy_server();
+  response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
       "HTTP/1.1 500 Server error\n");
 
-  throttle.BeforeWillProcessResponse(request.url, response_head, &defer);
+  throttle.BeforeWillProcessResponse(request.url, *response_head, &defer);
 
   // The throttle should have marked the proxy as bad.
   EXPECT_TRUE(defer);
diff --git a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
index 6bd593a..d70d47e 100644
--- a/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
+++ b/components/embedder_support/android/java/src/org/chromium/components/embedder_support/view/ContentView.java
@@ -40,7 +40,7 @@
 public class ContentView extends FrameLayout
         implements ViewEventSink.InternalAccessDelegate, SmartClipProvider,
                    OnHierarchyChangeListener, OnSystemUiVisibilityChangeListener {
-    private static final String TAG = "cr.ContentView";
+    private static final String TAG = "ContentView";
 
     // Default value to signal that the ContentView's size need not be overridden.
     public static final int DEFAULT_MEASURE_SPEC =
diff --git a/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/InvalidationClientService.java b/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/InvalidationClientService.java
index c1eb44a..15c6a72 100644
--- a/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/InvalidationClientService.java
+++ b/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/InvalidationClientService.java
@@ -69,7 +69,7 @@
     @VisibleForTesting
     static final int CLIENT_TYPE = ClientType.CHROME_SYNC_ANDROID;
 
-    private static final String TAG = "cr_invalidation";
+    private static final String TAG = "invalidation";
     private static final String CLIENT_SERVICE_KEY = "ipc.invalidation.ticl.listener_service_class";
 
     protected static boolean sShouldCreateService = true;
diff --git a/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/InvalidationService.java b/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/InvalidationService.java
index 29f107a..5d817c6 100644
--- a/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/InvalidationService.java
+++ b/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/InvalidationService.java
@@ -30,7 +30,7 @@
 public class InvalidationService {
     private final long mNativeInvalidationServiceAndroid;
 
-    private static final String TAG = "cr_invalidation";
+    private static final String TAG = "invalidation";
 
     private InvalidationService(long nativeInvalidationServiceAndroid) {
         mNativeInvalidationServiceAndroid = nativeInvalidationServiceAndroid;
diff --git a/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/PendingInvalidation.java b/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/PendingInvalidation.java
index c9420799..4557124 100644
--- a/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/PendingInvalidation.java
+++ b/components/invalidation/impl/android/java/src/org/chromium/components/invalidation/PendingInvalidation.java
@@ -22,7 +22,7 @@
  * (storing in prefStore) and Bundle (ContentProvider).
  */
 public class PendingInvalidation {
-    private static final String TAG = "cr.invalidation";
+    private static final String TAG = "invalidation";
 
     private static final String INVALIDATION_OBJECT_SOURCE_KEY = "objectSource";
     private static final String INVALIDATION_OBJECT_ID_KEY = "objectId";
diff --git a/components/password_manager/core/browser/mock_password_form_manager_for_ui.h b/components/password_manager/core/browser/mock_password_form_manager_for_ui.h
index 291fc520..7ed8ea2 100644
--- a/components/password_manager/core/browser/mock_password_form_manager_for_ui.h
+++ b/components/password_manager/core/browser/mock_password_form_manager_for_ui.h
@@ -31,8 +31,10 @@
   MOCK_METHOD0(Save, void());
   MOCK_METHOD1(Update,
                void(const autofill::PasswordForm& credentials_to_update));
-  MOCK_METHOD1(UpdateUsername, void(const base::string16& new_username));
-  MOCK_METHOD1(UpdatePasswordValue, void(const base::string16& new_password));
+  MOCK_METHOD1(OnUpdateUsernameFromPrompt,
+               void(const base::string16& new_username));
+  MOCK_METHOD1(OnUpdatePasswordFromPrompt,
+               void(const base::string16& new_password));
   MOCK_METHOD0(OnNopeUpdateClicked, void());
   MOCK_METHOD0(OnNeverClicked, void());
   MOCK_METHOD1(OnNoInteraction, void(bool));
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 0537d21..d9cb0b9 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -127,14 +127,6 @@
   return differences_bitmask;
 }
 
-// Since empty or unspecified form's action is automatically set to the page
-// origin, this function checks if a form's action is empty by comparing it to
-// its origin.
-bool HasNonEmptyAction(const FormData& form) {
-  // TODO(crbug.com/1008798): The logic isn't accurate and should be fixed.
-  return form.action != form.url;
-}
-
 bool FormContainsFieldWithName(const FormData& form,
                                const base::string16& element) {
   if (element.empty())
@@ -240,8 +232,8 @@
   if (IsHttpAuth())
     return false;
 
-  if (form.action.is_valid() && HasNonEmptyAction(form) &&
-      HasNonEmptyAction(submitted_form_) &&
+  if (form.action.is_valid() && !form.is_action_empty &&
+      !submitted_form_.is_action_empty &&
       submitted_form_.action == form.action) {
     return true;
   }
@@ -361,7 +353,8 @@
   client_->UpdateFormManagers();
 }
 
-void PasswordFormManager::UpdateUsername(const base::string16& new_username) {
+void PasswordFormManager::OnUpdateUsernameFromPrompt(
+    const base::string16& new_username) {
   DCHECK(parsed_submitted_form_);
   parsed_submitted_form_->username_value = new_username;
   parsed_submitted_form_->username_element.clear();
@@ -389,7 +382,7 @@
   CreatePendingCredentials();
 }
 
-void PasswordFormManager::UpdatePasswordValue(
+void PasswordFormManager::OnUpdatePasswordFromPrompt(
     const base::string16& new_password) {
   DCHECK(parsed_submitted_form_);
   parsed_submitted_form_->password_value = new_password;
diff --git a/components/password_manager/core/browser/password_form_manager.h b/components/password_manager/core/browser/password_form_manager.h
index d2b3929..6f0584d 100644
--- a/components/password_manager/core/browser/password_form_manager.h
+++ b/components/password_manager/core/browser/password_form_manager.h
@@ -147,8 +147,8 @@
 
   void Save() override;
   void Update(const autofill::PasswordForm& credentials_to_update) override;
-  void UpdateUsername(const base::string16& new_username) override;
-  void UpdatePasswordValue(const base::string16& new_password) override;
+  void OnUpdateUsernameFromPrompt(const base::string16& new_username) override;
+  void OnUpdatePasswordFromPrompt(const base::string16& new_password) override;
 
   void OnNopeUpdateClicked() override;
   void OnNeverClicked() override;
diff --git a/components/password_manager/core/browser/password_form_manager_for_ui.h b/components/password_manager/core/browser/password_form_manager_for_ui.h
index ce1c01d..ddfecaa 100644
--- a/components/password_manager/core/browser/password_form_manager_for_ui.h
+++ b/components/password_manager/core/browser/password_form_manager_for_ui.h
@@ -70,12 +70,14 @@
   // Updates the username value. Called when user edits the username and clicks
   // the save button. Updates the username and modifies internal state
   // accordingly.
-  virtual void UpdateUsername(const base::string16& new_username) = 0;
+  virtual void OnUpdateUsernameFromPrompt(
+      const base::string16& new_username) = 0;
 
   // Updates the password value. Called when user selects a password from the
   // password selection dropdown and clicks the save button. Updates the
   // password and modifies internal state accordingly.
-  virtual void UpdatePasswordValue(const base::string16& new_password) = 0;
+  virtual void OnUpdatePasswordFromPrompt(
+      const base::string16& new_password) = 0;
 
   // Called when the user chose not to update password.
   virtual void OnNopeUpdateClicked() = 0;
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index 87e85b8c..f6e53d6 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -1075,7 +1075,7 @@
   expected.username_value = new_username;
   expected.username_element.clear();
 
-  form_manager_->UpdateUsername(new_username);
+  form_manager_->OnUpdateUsernameFromPrompt(new_username);
 
   CheckPendingCredentials(expected, form_manager_->GetPendingCredentials());
   EXPECT_TRUE(form_manager_->IsNewLogin());
@@ -1095,7 +1095,7 @@
   EXPECT_EQ(automatically_chosen_username,
             form_manager_->GetPendingCredentials().username_value);
 
-  form_manager_->UpdateUsername(user_chosen_username);
+  form_manager_->OnUpdateUsernameFromPrompt(user_chosen_username);
 
   EXPECT_EQ(user_chosen_username,
             form_manager_->GetPendingCredentials().username_value);
@@ -1126,7 +1126,7 @@
   PasswordForm expected = saved_match_;
   expected.password_value = expected_password;
 
-  form_manager_->UpdateUsername(new_username);
+  form_manager_->OnUpdateUsernameFromPrompt(new_username);
 
   CheckPendingCredentials(expected, form_manager_->GetPendingCredentials());
   EXPECT_FALSE(form_manager_->IsNewLogin());
@@ -1145,7 +1145,7 @@
   expected.password_value = new_password;
   expected.password_element.clear();
 
-  form_manager_->UpdatePasswordValue(new_password);
+  form_manager_->OnUpdatePasswordFromPrompt(new_password);
 
   CheckPendingCredentials(expected, form_manager_->GetPendingCredentials());
   EXPECT_TRUE(form_manager_->IsNewLogin());
@@ -1169,7 +1169,7 @@
 
   // The user changes password to already saved one.
   base::string16 password = saved_match_.password_value;
-  form_manager_->UpdatePasswordValue(password);
+  form_manager_->OnUpdatePasswordFromPrompt(password);
 
   CheckPendingCredentials(saved_match_, form_manager_->GetPendingCredentials());
   EXPECT_FALSE(form_manager_->IsNewLogin());
@@ -1196,7 +1196,7 @@
   expected.password_element = form.fields[0].name;
 
   // Simulate that the user updates value to save for the first password field.
-  form_manager_->UpdatePasswordValue(password);
+  form_manager_->OnUpdatePasswordFromPrompt(password);
 
   // Check that newly created pending credentials are correct.
   CheckPendingCredentials(expected, form_manager_->GetPendingCredentials());
diff --git a/components/password_manager/core/browser/password_generation_state.cc b/components/password_manager/core/browser/password_generation_state.cc
index 5549b26..0b595d54 100644
--- a/components/password_manager/core/browser/password_generation_state.cc
+++ b/components/password_manager/core/browser/password_generation_state.cc
@@ -56,8 +56,8 @@
   bool IsBlacklisted() const override;
   void Save() override;
   void Update(const PasswordForm& credentials_to_update) override;
-  void UpdateUsername(const base::string16& new_username) override;
-  void UpdatePasswordValue(const base::string16& new_password) override;
+  void OnUpdateUsernameFromPrompt(const base::string16& new_username) override;
+  void OnUpdatePasswordFromPrompt(const base::string16& new_password) override;
   void OnNopeUpdateClicked() override;
   void OnNeverClicked() override;
   void OnNoInteraction(bool is_update) override;
@@ -141,11 +141,12 @@
   NOTREACHED();
 }
 
-void PasswordDataForUI::UpdateUsername(const base::string16& new_username) {
+void PasswordDataForUI::OnUpdateUsernameFromPrompt(
+    const base::string16& new_username) {
   pending_form_.username_value = new_username;
 }
 
-void PasswordDataForUI::UpdatePasswordValue(
+void PasswordDataForUI::OnUpdatePasswordFromPrompt(
     const base::string16& new_password) {
   // Ignore. The generated password can be edited in-place.
 }
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 2d7bc0f..e6ca135 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -59,9 +59,13 @@
 // already.
 using Logger = autofill::SavePasswordProgressLogger;
 
-bool AreAllFieldsEmpty(const PasswordForm& form) {
-  return form.username_value.empty() && form.password_value.empty() &&
-         form.new_password_value.empty();
+bool AreAllFieldsEmpty(const FormData& form_data) {
+  for (const auto& field : form_data.fields) {
+    if (!field.value.empty())
+      return false;
+  }
+
+  return true;
 }
 
 // Returns true if the user needs to be prompted before a password can be
@@ -741,8 +745,7 @@
   }
 
   // Record all visible forms from the frame.
-  all_visible_forms_.insert(all_visible_forms_.end(),
-                            visible_forms.begin(),
+  all_visible_forms_.insert(all_visible_forms_.end(), visible_forms.begin(),
                             visible_forms.end());
 
   if (!did_stop_loading &&
@@ -768,7 +771,7 @@
     for (const PasswordForm& form : all_visible_forms_) {
       if (submitted_manager->IsEqualToSubmittedForm(form.form_data)) {
         if (submitted_manager->IsPossibleChangePasswordFormWithoutUsername() &&
-            AreAllFieldsEmpty(form)) {
+            AreAllFieldsEmpty(form.form_data)) {
           continue;
         }
         submitted_manager->GetMetricsRecorder()->LogSubmitFailed();
diff --git a/components/password_manager/core/browser/password_reuse_detection_manager.cc b/components/password_manager/core/browser/password_reuse_detection_manager.cc
index 928b0ee..57b7fa0 100644
--- a/components/password_manager/core/browser/password_reuse_detection_manager.cc
+++ b/components/password_manager/core/browser/password_reuse_detection_manager.cc
@@ -5,6 +5,7 @@
 #include "components/password_manager/core/browser/password_reuse_detection_manager.h"
 
 #include "base/time/default_clock.h"
+#include "build/build_config.h"
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 #include "components/password_manager/core/browser/password_manager.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
@@ -40,7 +41,20 @@
   reuse_on_this_page_was_found_ = false;
 }
 
-void PasswordReuseDetectionManager::OnKeyPressed(const base::string16& text) {
+void PasswordReuseDetectionManager::OnKeyPressedCommitted(
+    const base::string16& text) {
+  OnKeyPressed(text, /*is_committed*/ true);
+}
+
+#if defined(OS_ANDROID)
+void PasswordReuseDetectionManager::OnKeyPressedUncommitted(
+    const base::string16& text) {
+  OnKeyPressed(text, /*is_committed*/ false);
+}
+#endif
+
+void PasswordReuseDetectionManager::OnKeyPressed(const base::string16& text,
+                                                 const bool is_committed) {
   // Do not check reuse if it was already found on this page.
   if (reuse_on_this_page_was_found_)
     return;
@@ -59,16 +73,21 @@
     return;
   }
 
-  input_characters_ += text;
+  if (is_committed)
+    input_characters_ += text;
+
   if (input_characters_.size() > kMaxNumberOfCharactersToStore) {
     input_characters_.erase(
         0, input_characters_.size() - kMaxNumberOfCharactersToStore);
   }
+
+  const base::string16 text_to_check =
+      is_committed ? input_characters_ : input_characters_ + text;
+
   PasswordStore* store = client_->GetProfilePasswordStore();
   if (!store)
     return;
-  store->CheckReuse(input_characters_, main_frame_url_.GetOrigin().spec(),
-                    this);
+  store->CheckReuse(text_to_check, main_frame_url_.GetOrigin().spec(), this);
 }
 
 void PasswordReuseDetectionManager::OnPaste(const base::string16 text) {
diff --git a/components/password_manager/core/browser/password_reuse_detection_manager.h b/components/password_manager/core/browser/password_reuse_detection_manager.h
index d1fbf5f..6667082 100644
--- a/components/password_manager/core/browser/password_reuse_detection_manager.h
+++ b/components/password_manager/core/browser/password_reuse_detection_manager.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_reuse_detector_consumer.h"
 #include "url/gurl.h"
@@ -28,7 +29,10 @@
   explicit PasswordReuseDetectionManager(PasswordManagerClient* client);
   ~PasswordReuseDetectionManager() override;
   void DidNavigateMainFrame(const GURL& main_frame_url);
-  void OnKeyPressed(const base::string16& text);
+  void OnKeyPressedCommitted(const base::string16& text);
+#if defined(OS_ANDROID)
+  void OnKeyPressedUncommitted(const base::string16& text);
+#endif
   void OnPaste(const base::string16 text);
 
   // PasswordReuseDetectorConsumer implementation
@@ -41,6 +45,7 @@
   void SetClockForTesting(base::Clock* clock);
 
  private:
+  void OnKeyPressed(const base::string16& text, const bool is_committed);
   // Determines the type of password being reused.
   metrics_util::PasswordType GetReusedPasswordType(
       base::Optional<PasswordHashData> reused_protected_password_hash,
diff --git a/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc b/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc
index 16f4f82..7b5bc51 100644
--- a/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_reuse_detection_manager_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/task_environment.h"
+#include "build/build_config.h"
 #include "components/password_manager/core/browser/mock_password_store.h"
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -83,7 +84,7 @@
       EXPECT_CALL(
           *store_,
           CheckReuse(expected_input, gurls[test].GetOrigin().spec(), &manager));
-      manager.OnKeyPressed(input[test].substr(i, 1));
+      manager.OnKeyPressedCommitted(input[test].substr(i, 1));
       testing::Mock::VerifyAndClearExpectations(store_.get());
     }
   }
@@ -103,13 +104,13 @@
   manager.SetClockForTesting(&clock);
 
   EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
-  manager.OnKeyPressed(base::ASCIIToUTF16("1"));
+  manager.OnKeyPressedCommitted(base::ASCIIToUTF16("1"));
 
   // Simulate 10 seconds of inactivity.
   clock.SetNow(now + base::TimeDelta::FromSeconds(10));
   // Expect that a keystroke typed before inactivity is cleared.
   EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("2"), _, _));
-  manager.OnKeyPressed(base::ASCIIToUTF16("2"));
+  manager.OnKeyPressedCommitted(base::ASCIIToUTF16("2"));
 }
 
 // Verify that the keystroke buffer is cleared after user presses enter.
@@ -119,15 +120,15 @@
   PasswordReuseDetectionManager manager(&client_);
 
   EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
-  manager.OnKeyPressed(base::ASCIIToUTF16("1"));
+  manager.OnKeyPressedCommitted(base::ASCIIToUTF16("1"));
 
   base::string16 enter_text(1, ui::VKEY_RETURN);
   EXPECT_CALL(*store_, CheckReuse(_, _, _)).Times(0);
-  manager.OnKeyPressed(enter_text);
+  manager.OnKeyPressedCommitted(enter_text);
 
   // Expect only a keystroke typed after enter.
   EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("2"), _, _));
-  manager.OnKeyPressed(base::ASCIIToUTF16("2"));
+  manager.OnKeyPressedCommitted(base::ASCIIToUTF16("2"));
 }
 
 // Verify that after reuse found, no reuse checking happens till next main frame
@@ -142,12 +143,12 @@
 
   // Expect no checking of reuse.
   EXPECT_CALL(*store_, CheckReuse(_, _, _)).Times(0);
-  manager.OnKeyPressed(base::ASCIIToUTF16("1"));
+  manager.OnKeyPressedCommitted(base::ASCIIToUTF16("1"));
 
   // Expect that after main frame navigation checking is restored.
   manager.DidNavigateMainFrame(GURL("https://www.example.com"));
   EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
-  manager.OnKeyPressed(base::ASCIIToUTF16("1"));
+  manager.OnKeyPressedCommitted(base::ASCIIToUTF16("1"));
 }
 
 // Verify that keystroke buffer is cleared only on cross host navigation.
@@ -158,17 +159,17 @@
 
   manager.DidNavigateMainFrame(GURL("https://www.example1.com/123"));
   EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("1"), _, _));
-  manager.OnKeyPressed(base::ASCIIToUTF16("1"));
+  manager.OnKeyPressedCommitted(base::ASCIIToUTF16("1"));
 
   // Check that the buffer is not cleared on the same host navigation.
   manager.DidNavigateMainFrame(GURL("https://www.example1.com/456"));
   EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("12"), _, _));
-  manager.OnKeyPressed(base::ASCIIToUTF16("2"));
+  manager.OnKeyPressedCommitted(base::ASCIIToUTF16("2"));
 
   // Check that the buffer is cleared on the cross host navigation.
   manager.DidNavigateMainFrame(GURL("https://www.example2.com/123"));
   EXPECT_CALL(*store_, CheckReuse(base::ASCIIToUTF16("3"), _, _));
-  manager.OnKeyPressed(base::ASCIIToUTF16("3"));
+  manager.OnKeyPressedCommitted(base::ASCIIToUTF16("3"));
 }
 
 // Verify that CheckReuse is called on a paste event.
@@ -196,6 +197,33 @@
     testing::Mock::VerifyAndClearExpectations(store_.get());
   }
 }
+
+#if defined(OS_ANDROID)
+TEST_F(PasswordReuseDetectionManagerTest,
+       CheckReusedCalledWithUncommittedText) {
+  EXPECT_CALL(client_, GetProfilePasswordStore())
+      .WillRepeatedly(testing::Return(store_.get()));
+  PasswordReuseDetectionManager manager(&client_);
+  GURL test_url("https://www.example.com");
+  manager.DidNavigateMainFrame(test_url);
+
+  base::string16 init_text = base::ASCIIToUTF16("init_text");
+  base::string16 uncommitted_text = base::ASCIIToUTF16("uncommitted_text");
+  base::string16 committed_text = base::ASCIIToUTF16("committed_text");
+
+  EXPECT_CALL(*store_,
+              CheckReuse(init_text, test_url.GetOrigin().spec(), &manager));
+  manager.OnKeyPressedCommitted(init_text);
+  EXPECT_CALL(*store_, CheckReuse(init_text + uncommitted_text,
+                                  test_url.GetOrigin().spec(), &manager));
+  manager.OnKeyPressedUncommitted(uncommitted_text);
+  // Uncommitted text should not be stored.
+  EXPECT_CALL(*store_, CheckReuse(init_text + committed_text,
+                                  test_url.GetOrigin().spec(), &manager));
+  manager.OnKeyPressedCommitted(committed_text);
+}
+#endif
+
 }  // namespace
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_ui_utils.cc b/components/password_manager/core/browser/password_ui_utils.cc
index 93b1993..63d0358 100644
--- a/components/password_manager/core/browser/password_ui_utils.cc
+++ b/components/password_manager/core/browser/password_ui_utils.cc
@@ -82,7 +82,7 @@
   bool username_edited = pending_credentials.username_value != username;
   bool password_changed = pending_credentials.password_value != password;
   if (username_edited) {
-    form_manager->UpdateUsername(username);
+    form_manager->OnUpdateUsernameFromPrompt(username);
     if (form_manager->GetMetricsRecorder()) {
       form_manager->GetMetricsRecorder()->RecordDetailedUserAction(
           password_manager::PasswordFormMetricsRecorder::DetailedUserAction::
@@ -90,7 +90,7 @@
     }
   }
   if (password_changed) {
-    form_manager->UpdatePasswordValue(password);
+    form_manager->OnUpdatePasswordFromPrompt(password);
     if (form_manager->GetMetricsRecorder()) {
       form_manager->GetMetricsRecorder()->RecordDetailedUserAction(
           password_manager::PasswordFormMetricsRecorder::DetailedUserAction::
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 33b1e037..d193eef 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -454,6 +454,7 @@
         'ExtensionAllowInsecureUpdates',
         'ExtensionSettings',
         'ExtensionInstallListsMergeEnabled',
+        'BlockExternalExtensions',
       ],
     },
     {
@@ -4092,6 +4093,29 @@
       If you disable this setting or leave it unset, only list entries from the highest priority source are taken and all other sources are shown as conflicts but ignored.''',
     },
     {
+      'name': 'BlockExternalExtensions',
+      'owners': ['file://components/policy/resources/OWNERS'],
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome.*:80-'],
+      'features': {
+        'dynamic_refresh': False,
+        'per_profile': True,
+      },
+      'example_value': True,
+      'id': 637,
+      'caption': '''Blocks external extensions from being installed''',
+      'tags': [],
+      'desc': '''Controls external extensions installation.
+
+      Enabling this setting blocks external extensions from being installed.
+
+      Disabling this setting or leaving it unset allows external extensions to be installed.
+
+      External extensions and their installation are documented at https://developer.chrome.com/apps/external_extensions.
+      ''',
+    },
+    {
       'name': 'ShowHomeButton',
       'owners': ['file://components/policy/resources/OWNERS'],
       'type': 'main',
@@ -19489,6 +19513,6 @@
   ],
   'placeholders': [],
   'deleted_policy_ids': [412, 546, 562, 569],
-  'highest_id_currently_used': 636,
+  'highest_id_currently_used': 637,
   'highest_atomic_group_id_currently_used': 38
 }
diff --git a/components/safe_browsing/browser/browser_url_loader_throttle.cc b/components/safe_browsing/browser/browser_url_loader_throttle.cc
index 25ebf3c2..d9a03454 100644
--- a/components/safe_browsing/browser/browser_url_loader_throttle.cc
+++ b/components/safe_browsing/browser/browser_url_loader_throttle.cc
@@ -17,7 +17,7 @@
 #include "net/log/net_log_event_type.h"
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 
 namespace safe_browsing {
 
@@ -198,7 +198,7 @@
 
 void BrowserURLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& /* response_head */,
+    const network::mojom::URLResponseHead& /* response_head */,
     bool* defer,
     std::vector<std::string>* /* to_be_removed_headers */,
     net::HttpRequestHeaders* /* modified_headers */) {
@@ -224,7 +224,7 @@
 
 void BrowserURLLoaderThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (blocked_) {
diff --git a/components/safe_browsing/browser/browser_url_loader_throttle.h b/components/safe_browsing/browser/browser_url_loader_throttle.h
index 96a2c5a2d..88e5bac 100644
--- a/components/safe_browsing/browser/browser_url_loader_throttle.h
+++ b/components/safe_browsing/browser/browser_url_loader_throttle.h
@@ -56,12 +56,12 @@
   void WillStartRequest(network::ResourceRequest* request,
                         bool* defer) override;
   void WillRedirectRequest(net::RedirectInfo* redirect_info,
-                           const network::ResourceResponseHead& response_head,
+                           const network::mojom::URLResponseHead& response_head,
                            bool* defer,
                            std::vector<std::string>* to_be_removed_headers,
                            net::HttpRequestHeaders* modified_headers) override;
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override;
 
  private:
diff --git a/components/safe_browsing/renderer/renderer_url_loader_throttle.cc b/components/safe_browsing/renderer/renderer_url_loader_throttle.cc
index 52847fe..f398e8c 100644
--- a/components/safe_browsing/renderer/renderer_url_loader_throttle.cc
+++ b/components/safe_browsing/renderer/renderer_url_loader_throttle.cc
@@ -67,7 +67,7 @@
 
 void RendererURLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& /* response_head */,
+    const network::mojom::URLResponseHead& /* response_head */,
     bool* /* defer */,
     std::vector<std::string>* /* to_be_removed_headers */,
     net::HttpRequestHeaders* /* modified_headers */) {
@@ -89,7 +89,7 @@
 
 void RendererURLLoaderThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {
   // If |blocked_| is true, the resource load has been canceled and there
   // shouldn't be such a notification.
diff --git a/components/safe_browsing/renderer/renderer_url_loader_throttle.h b/components/safe_browsing/renderer/renderer_url_loader_throttle.h
index f9b54930..421976a 100644
--- a/components/safe_browsing/renderer/renderer_url_loader_throttle.h
+++ b/components/safe_browsing/renderer/renderer_url_loader_throttle.h
@@ -38,12 +38,12 @@
   void WillStartRequest(network::ResourceRequest* request,
                         bool* defer) override;
   void WillRedirectRequest(net::RedirectInfo* redirect_info,
-                           const network::ResourceResponseHead& response_head,
+                           const network::mojom::URLResponseHead& response_head,
                            bool* defer,
                            std::vector<std::string>* to_be_removed_headers,
                            net::HttpRequestHeaders* modified_headers) override;
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override;
 
   // mojom::UrlCheckNotifier implementation.
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.cc b/components/sync/nigori/nigori_sync_bridge_impl.cc
index 25f8571..ecc6aea 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl.cc
@@ -436,6 +436,14 @@
   metadata_batch.entity_metadata = deserialized_data->entity_metadata();
   processor_->ModelReadyToSync(this, std::move(metadata_batch));
 
+  if (state_.passphrase_type == NigoriSpecifics::UNKNOWN) {
+    // Commit with keystore initialization wasn't successfully completed before
+    // the restart, so trigger it again here.
+    DCHECK(!state_.keystore_keys_cryptographer->IsEmpty());
+    QueuePendingLocalCommit(
+        PendingLocalNigoriCommit::ForKeystoreInitialization());
+  }
+
   // Keystore key rotation might be not performed, but required.
   MaybeTriggerKeystoreKeyRotation();
 }
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
index 4613f92..a99b5da8 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -13,6 +13,8 @@
 #include "components/sync/base/time.h"
 #include "components/sync/engine/sync_engine_switches.h"
 #include "components/sync/model/entity_data.h"
+#include "components/sync/nigori/keystore_keys_cryptographer.h"
+#include "components/sync/nigori/nigori_state.h"
 #include "components/sync/nigori/nigori_storage.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -1156,6 +1158,52 @@
   EXPECT_THAT(cryptographer, HasDefaultKeyDerivedFrom(kKeystoreKeyParams));
 }
 
+// Commit with keystore Nigori initialization might be not completed before
+// the browser restart. This test emulates loading non-initialized Nigori
+// after restart and expects that bridge will trigger initialization after
+// loading.
+TEST(NigoriSyncBridgeImplPersistenceTest,
+     ShouldInitializeKeystoreNigoriWhenLoadedFromStorage) {
+  const KeyParams kKeystoreKeyParams = Pbkdf2KeyParams("keystore_key");
+  NigoriState unitialized_state_with_keystore_keys;
+  unitialized_state_with_keystore_keys.keystore_keys_cryptographer =
+      KeystoreKeysCryptographer::FromKeystoreKeys(
+          {kKeystoreKeyParams.password});
+
+  sync_pb::NigoriLocalData nigori_local_data;
+  *nigori_local_data.mutable_nigori_model() =
+      unitialized_state_with_keystore_keys.ToLocalProto();
+
+  auto storage = std::make_unique<testing::NiceMock<MockNigoriStorage>>();
+  ON_CALL(*storage, RestoreData()).WillByDefault(Return(nigori_local_data));
+
+  auto processor =
+      std::make_unique<testing::NiceMock<MockNigoriLocalChangeProcessor>>();
+  ON_CALL(*processor, IsTrackingMetadata()).WillByDefault(Return(true));
+  MockNigoriLocalChangeProcessor* not_owned_processor = processor.get();
+
+  const FakeEncryptor kEncryptor;
+  // Calling bridge constructor triggers a commit cycle but doesn't immediately
+  // expose the new state, until the commit completes.
+  EXPECT_CALL(*not_owned_processor, Put(HasKeystoreNigori()));
+  auto bridge = std::make_unique<NigoriSyncBridgeImpl>(
+      std::move(processor), std::move(storage), &kEncryptor,
+      base::BindRepeating(&Nigori::GenerateScryptSalt),
+      /*packed_explicit_passphrase_key=*/std::string());
+  EXPECT_THAT(bridge->GetData(), HasKeystoreNigori());
+
+  // Emulate commit completeness.
+  EXPECT_THAT(bridge->ApplySyncChanges(base::nullopt), Eq(base::nullopt));
+  EXPECT_THAT(bridge->GetData(), HasKeystoreNigori());
+  EXPECT_THAT(bridge->GetKeystoreMigrationTime(), Not(NullTime()));
+  EXPECT_EQ(bridge->GetPassphraseTypeForTesting(),
+            sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
+
+  const Cryptographer& cryptographer = bridge->GetCryptographerForTesting();
+  EXPECT_THAT(cryptographer, CanDecryptWith(kKeystoreKeyParams));
+  EXPECT_THAT(cryptographer, HasDefaultKeyDerivedFrom(kKeystoreKeyParams));
+}
+
 // Tests the initial sync with a trusted vault Nigori. Observers should be
 // notified about encryption state changes and cryptographer shouldn't be ready
 // (by having pending keys) until the passphrase is received by means other than
diff --git a/components/sync/test/fake_server/fake_server.cc b/components/sync/test/fake_server/fake_server.cc
index 73eb95e..edc2e6e 100644
--- a/components/sync/test/fake_server/fake_server.cc
+++ b/components/sync/test/fake_server/fake_server.cc
@@ -34,7 +34,8 @@
 namespace fake_server {
 
 FakeServer::FakeServer()
-    : error_type_(sync_pb::SyncEnums::SUCCESS),
+    : commit_error_type_(sync_pb::SyncEnums::SUCCESS),
+      error_type_(sync_pb::SyncEnums::SUCCESS),
       alternate_triggered_errors_(false),
       request_counter_(0) {
   base::ThreadRestrictions::SetIOAllowed(true);
@@ -48,7 +49,8 @@
 }
 
 FakeServer::FakeServer(const base::FilePath& user_data_dir)
-    : error_type_(sync_pb::SyncEnums::SUCCESS),
+    : commit_error_type_(sync_pb::SyncEnums::SUCCESS),
+      error_type_(sync_pb::SyncEnums::SUCCESS),
       alternate_triggered_errors_(false),
       request_counter_(0) {
   base::ThreadRestrictions::SetIOAllowed(true);
@@ -179,18 +181,23 @@
     return *http_error_status_code_;
   }
 
+  sync_pb::ClientToServerMessage message;
+  bool parsed = message.ParseFromString(request);
+  DCHECK(parsed) << "Unable to parse the ClientToServerMessage.";
+
   sync_pb::ClientToServerResponse response_proto;
-  if (error_type_ != sync_pb::SyncEnums::SUCCESS &&
+  if (message.message_contents() == sync_pb::ClientToServerMessage::COMMIT &&
+      commit_error_type_ != sync_pb::SyncEnums::SUCCESS &&
       ShouldSendTriggeredError()) {
+    response_proto.set_error_code(commit_error_type_);
+  } else if (error_type_ != sync_pb::SyncEnums::SUCCESS &&
+             ShouldSendTriggeredError()) {
     response_proto.set_error_code(error_type_);
   } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) {
     sync_pb::ClientToServerResponse_Error* error =
         response_proto.mutable_error();
     error->CopyFrom(*(triggered_actionable_error_.get()));
   } else {
-    sync_pb::ClientToServerMessage message;
-    bool parsed = message.ParseFromString(request);
-    DCHECK(parsed) << "Unable to parse the ClientToServerMessage.";
     switch (message.message_contents()) {
       case sync_pb::ClientToServerMessage::GET_UPDATES:
         last_getupdates_message_ = message;
@@ -367,27 +374,28 @@
   client_command_ = client_command;
 }
 
-bool FakeServer::TriggerError(const sync_pb::SyncEnums::ErrorType& error_type) {
+void FakeServer::TriggerCommitError(
+    const sync_pb::SyncEnums::ErrorType& error_type) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (triggered_actionable_error_) {
-    DVLOG(1) << "Only one type of error can be triggered at any given time.";
-    return false;
-  }
+  DCHECK(error_type == sync_pb::SyncEnums::SUCCESS || !HasTriggeredError());
 
-  error_type_ = error_type;
-  return true;
+  commit_error_type_ = error_type;
 }
 
-bool FakeServer::TriggerActionableError(
+void FakeServer::TriggerError(const sync_pb::SyncEnums::ErrorType& error_type) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(error_type == sync_pb::SyncEnums::SUCCESS || !HasTriggeredError());
+
+  error_type_ = error_type;
+}
+
+void FakeServer::TriggerActionableError(
     const sync_pb::SyncEnums::ErrorType& error_type,
     const std::string& description,
     const std::string& url,
     const sync_pb::SyncEnums::Action& action) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (error_type_ != sync_pb::SyncEnums::SUCCESS) {
-    DVLOG(1) << "Only one type of error can be triggered at any given time.";
-    return false;
-  }
+  DCHECK(!HasTriggeredError());
 
   sync_pb::ClientToServerResponse_Error* error =
       new sync_pb::ClientToServerResponse_Error();
@@ -396,7 +404,6 @@
   error->set_url(url);
   error->set_action(action);
   triggered_actionable_error_.reset(error);
-  return true;
 }
 
 void FakeServer::ClearActionableError() {
@@ -426,6 +433,12 @@
   return request_counter_ % 2 != 0;
 }
 
+bool FakeServer::HasTriggeredError() const {
+  return commit_error_type_ != sync_pb::SyncEnums::SUCCESS ||
+         error_type_ != sync_pb::SyncEnums::SUCCESS ||
+         triggered_actionable_error_;
+}
+
 void FakeServer::AddObserver(Observer* observer) {
   DCHECK(thread_checker_.CalledOnValidThread());
   observers_.AddObserver(observer);
diff --git a/components/sync/test/fake_server/fake_server.h b/components/sync/test/fake_server/fake_server.h
index e17a4d03..da7fced 100644
--- a/components/sync/test/fake_server/fake_server.h
+++ b/components/sync/test/fake_server/fake_server.h
@@ -138,16 +138,21 @@
   void SetClientCommand(const sync_pb::ClientCommand& client_command);
 
   // Force the server to return |error_type| in the error_code field of
-  // ClientToServerResponse on all subsequent sync requests. This method should
-  // not be called if TriggerActionableError has previously been called. Returns
-  // true if error triggering was successfully configured.
-  bool TriggerError(const sync_pb::SyncEnums::ErrorType& error_type);
+  // ClientToServerResponse on all subsequent commit requests. If any of errors
+  // triggerings currently configured it must be called only with
+  // sync_pb::SyncEnums::SUCCESS.
+  void TriggerCommitError(const sync_pb::SyncEnums::ErrorType& error_type);
+
+  // Force the server to return |error_type| in the error_code field of
+  // ClientToServerResponse on all subsequent sync requests. If any of errors
+  // triggerings currently configured it must be called only with
+  // sync_pb::SyncEnums::SUCCESS.
+  void TriggerError(const sync_pb::SyncEnums::ErrorType& error_type);
 
   // Force the server to return the given data as part of the error field of
-  // ClientToServerResponse on all subsequent sync requests. This method should
-  // not be called if TriggerError has previously been called. Returns true if
-  // error triggering was successfully configured.
-  bool TriggerActionableError(const sync_pb::SyncEnums::ErrorType& error_type,
+  // ClientToServerResponse on all subsequent sync requests. Must not be called
+  // if any of errors triggerings currently configured.
+  void TriggerActionableError(const sync_pb::SyncEnums::ErrorType& error_type,
                               const std::string& description,
                               const std::string& url,
                               const sync_pb::SyncEnums::Action& action);
@@ -203,6 +208,7 @@
  private:
   // Returns whether a triggered error should be sent for the request.
   bool ShouldSendTriggeredError() const;
+  bool HasTriggeredError() const;
   net::HttpStatusCode SendToLoopbackServer(const std::string& request,
                                            std::string* response);
   void InjectClientCommand(std::string* response);
@@ -217,8 +223,11 @@
   // All URLs received via history sync (powered by SESSIONS).
   std::set<std::string> committed_history_urls_;
 
-  // Used as the error_code field of ClientToServerResponse on all responses
-  // except when |triggered_actionable_error_| is set.
+  // Used as the error_code field of ClientToServerResponse on all commit
+  // requests.
+  sync_pb::SyncEnums::ErrorType commit_error_type_;
+
+  // Used as the error_code field of ClientToServerResponse on all responses.
   sync_pb::SyncEnums::ErrorType error_type_;
 
   // Used as the error field of ClientToServerResponse when its pointer is not
diff --git a/components/sync_preferences/pref_model_associator.cc b/components/sync_preferences/pref_model_associator.cc
index 15f1126..b7eb23c 100644
--- a/components/sync_preferences/pref_model_associator.cc
+++ b/components/sync_preferences/pref_model_associator.cc
@@ -458,10 +458,6 @@
   return value.release();
 }
 
-bool PrefModelAssociator::IsPrefSynced(const std::string& name) const {
-  return synced_preferences_.find(name) != synced_preferences_.end();
-}
-
 void PrefModelAssociator::AddSyncedPrefObserver(const std::string& name,
                                                 SyncedPrefObserver* observer) {
   auto& observers = synced_pref_observers_[name];
@@ -480,6 +476,11 @@
   observer_iter->second->RemoveObserver(observer);
 }
 
+bool PrefModelAssociator::IsPrefSyncedForTesting(
+    const std::string& name) const {
+  return synced_preferences_.find(name) != synced_preferences_.end();
+}
+
 void PrefModelAssociator::RegisterPref(const std::string& name) {
   DCHECK(registered_preferences_.count(name) == 0);
   registered_preferences_.insert(name);
diff --git a/components/sync_preferences/pref_model_associator.h b/components/sync_preferences/pref_model_associator.h
index 2a6db01..886cd62 100644
--- a/components/sync_preferences/pref_model_associator.h
+++ b/components/sync_preferences/pref_model_associator.h
@@ -104,9 +104,6 @@
   bool CreatePrefSyncData(const std::string& name,
                           const base::Value& value,
                           syncer::SyncData* sync_data) const;
-  // Returns true if the pref under the given name is pulled down from sync.
-  // Note this does not refer to SYNCABLE_PREF.
-  bool IsPrefSynced(const std::string& name) const;
 
   // Returns true if the specified preference is registered for syncing.
   bool IsPrefRegistered(const std::string& name) const;
@@ -126,9 +123,11 @@
   // Returns the PrefModelAssociatorClient for this object.
   const PrefModelAssociatorClient* client() const { return client_; }
 
- private:
-  friend class PrefServiceSyncableTest;
+  // Returns true if the pref under the given name is pulled down from sync.
+  // Note this does not refer to SYNCABLE_PREF.
+  bool IsPrefSyncedForTesting(const std::string& name) const;
 
+ private:
   // Create an association for a given preference. If |sync_pref| is valid,
   // signifying that sync has data for this preference, we reconcile their data
   // with ours and append a new UPDATE SyncChange to |sync_changes|. If
diff --git a/components/sync_preferences/pref_service_syncable.cc b/components/sync_preferences/pref_service_syncable.cc
index c58fd3e..f743daf 100644
--- a/components/sync_preferences/pref_service_syncable.cc
+++ b/components/sync_preferences/pref_service_syncable.cc
@@ -155,20 +155,6 @@
   return false;
 }
 
-bool PrefServiceSyncable::IsPrefSynced(const std::string& name) const {
-  if (pref_sync_associator_.IsPrefSynced(name) ||
-      priority_pref_sync_associator_.IsPrefSynced(name)) {
-    return true;
-  }
-#if defined(OS_CHROMEOS)
-  if (os_pref_sync_associator_.IsPrefSynced(name) ||
-      os_priority_pref_sync_associator_.IsPrefSynced(name)) {
-    return true;
-  }
-#endif
-  return false;
-}
-
 void PrefServiceSyncable::AddObserver(PrefServiceSyncableObserver* observer) {
   observer_list_.AddObserver(observer);
 }
diff --git a/components/sync_preferences/pref_service_syncable.h b/components/sync_preferences/pref_service_syncable.h
index 155fc77..f1c1292 100644
--- a/components/sync_preferences/pref_service_syncable.h
+++ b/components/sync_preferences/pref_service_syncable.h
@@ -70,10 +70,6 @@
   // priority preferences.
   bool IsPrioritySyncing();
 
-  // Returns true if the pref under the given name is pulled down from sync.
-  // Note this does not refer to SYNCABLE_PREF.
-  bool IsPrefSynced(const std::string& name) const;
-
   void AddObserver(PrefServiceSyncableObserver* observer);
   void RemoveObserver(PrefServiceSyncableObserver* observer);
 
diff --git a/components/sync_preferences/pref_service_syncable_unittest.cc b/components/sync_preferences/pref_service_syncable_unittest.cc
index 91c82be25..fdbd53e 100644
--- a/components/sync_preferences/pref_service_syncable_unittest.cc
+++ b/components/sync_preferences/pref_service_syncable_unittest.cc
@@ -406,12 +406,7 @@
         kDefaultCharsetPrefName, kDefaultCharsetValue,
         user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 
-    // Downcast to PrefModelAssociator so that tests can access its' specific
-    // behavior. This is a smell. The roles between PrefServiceSyncable and
-    // PrefModelAssociator are not clearly separated (and this test should only
-    // test against the SyncableService interface).
-    pref_sync_service_ =  // static_cast<PrefModelAssociator*>(
-        prefs_.GetSyncableService(syncer::PREFERENCES);  //);
+    pref_sync_service_ = prefs_.GetSyncableService(syncer::PREFERENCES);
     ASSERT_THAT(pref_sync_service_, NotNull());
   }
 
@@ -653,8 +648,9 @@
   remote_changes.push_back(MakeRemoteChange(
       1, pref_name, base::Value("remote_value2"), SyncChange::ACTION_UPDATE));
   pref_sync_service_->ProcessSyncChanges(FROM_HERE, remote_changes);
-  EXPECT_THAT(prefs_.IsPrefSynced(pref_name), Eq(false));
-
+  // The pref isn't synced.
+  EXPECT_THAT(pref_sync_service_->GetAllSyncData(syncer::PREFERENCES),
+              IsEmpty());
   EXPECT_THAT(GetPreferenceValue(pref_name).GetString(), Eq("default_value"));
 }
 
@@ -741,7 +737,7 @@
 
   const base::Value& actual = GetPreferenceValue(kStringPrefName);
   EXPECT_TRUE(expected.Equals(&actual));
-  EXPECT_TRUE(pref_sync_service_->IsPrefSynced(kStringPrefName));
+  EXPECT_TRUE(pref_sync_service_->IsPrefSyncedForTesting(kStringPrefName));
 }
 
 TEST_F(PrefServiceSyncableTest, UpdatedSyncNodeUnknownPreference) {
@@ -1007,14 +1003,14 @@
   InitSyncForAllTypes();
   auto* associator = static_cast<PrefModelAssociator*>(
       prefs_->GetSyncableService(syncer::OS_PREFERENCES));
-  EXPECT_FALSE(associator->IsPrefSynced("os_pref"));
+  EXPECT_FALSE(associator->IsPrefSyncedForTesting("os_pref"));
 
   syncer::SyncChangeList list;
   list.push_back(MakeRemoteChange(1, "os_pref", base::Value("value"),
                                   SyncChange::ACTION_ADD,
                                   syncer::OS_PREFERENCES));
   associator->ProcessSyncChanges(FROM_HERE, list);
-  EXPECT_TRUE(associator->IsPrefSynced("os_pref"));
+  EXPECT_TRUE(associator->IsPrefSyncedForTesting("os_pref"));
 }
 
 TEST_F(PrefServiceSyncableChromeOsTest, IsPrefSynced_OsPriorityPref) {
@@ -1023,14 +1019,14 @@
   InitSyncForAllTypes();
   auto* associator = static_cast<PrefModelAssociator*>(
       prefs_->GetSyncableService(syncer::OS_PRIORITY_PREFERENCES));
-  EXPECT_FALSE(associator->IsPrefSynced("os_priority_pref"));
+  EXPECT_FALSE(associator->IsPrefSyncedForTesting("os_priority_pref"));
 
   syncer::SyncChangeList list;
   list.push_back(MakeRemoteChange(1, "os_priority_pref", base::Value("value"),
                                   SyncChange::ACTION_ADD,
                                   syncer::OS_PRIORITY_PREFERENCES));
   associator->ProcessSyncChanges(FROM_HERE, list);
-  EXPECT_TRUE(associator->IsPrefSynced("os_priority_pref"));
+  EXPECT_TRUE(associator->IsPrefSyncedForTesting("os_priority_pref"));
 }
 
 TEST_F(PrefServiceSyncableChromeOsTest, SyncedPrefObserver_OsPref) {
@@ -1093,7 +1089,7 @@
   // Future changes will be synced back to browser preferences as well.
   auto* associator = static_cast<PrefModelAssociator*>(
       prefs_->GetSyncableService(syncer::PREFERENCES));
-  EXPECT_TRUE(associator->IsPrefSynced("os_pref"));
+  EXPECT_TRUE(associator->IsPrefSyncedForTesting("os_pref"));
 }
 
 TEST_F(PrefServiceSyncableChromeOsTest,
@@ -1117,7 +1113,7 @@
   // Future changes will be synced back to browser preferences as well.
   auto* associator = static_cast<PrefModelAssociator*>(
       prefs_->GetSyncableService(syncer::PREFERENCES));
-  EXPECT_TRUE(associator->IsPrefSynced("os_pref"));
+  EXPECT_TRUE(associator->IsPrefSyncedForTesting("os_pref"));
 }
 
 TEST_F(PrefServiceSyncableChromeOsTest,
@@ -1149,7 +1145,7 @@
 
   // The pref is not considered to be syncing, because it still has its default
   // value.
-  EXPECT_FALSE(browser_associator->IsPrefSynced("os_pref"));
+  EXPECT_FALSE(browser_associator->IsPrefSyncedForTesting("os_pref"));
 
   // Observers were not notified of changes.
   EXPECT_EQ(0, observer.changed_count_);
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index d571c127..be42091 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -111,8 +111,6 @@
     "display_embedder/gl_output_surface.h",
     "display_embedder/gl_output_surface_buffer_queue.cc",
     "display_embedder/gl_output_surface_buffer_queue.h",
-    "display_embedder/gl_output_surface_chromeos.cc",
-    "display_embedder/gl_output_surface_chromeos.h",
     "display_embedder/gl_output_surface_offscreen.cc",
     "display_embedder/gl_output_surface_offscreen.h",
     "display_embedder/in_process_gpu_memory_buffer_manager.cc",
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 7caa546d..d8b39e2 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -527,16 +527,13 @@
   // The CompositorFrame provided by the SurfaceAggregator includes the display
   // transform while |current_surface_size_| is the pre-transform size received
   // from the client.
-  const gfx::OverlayTransform current_display_transform =
-      output_surface_->GetDisplayTransform();
   const gfx::Transform display_transform = gfx::OverlayTransformToTransform(
-      current_display_transform, current_surface_size_);
+      output_surface_->GetDisplayTransform(), current_surface_size_);
   const gfx::Size current_surface_size =
       cc::MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
           display_transform, gfx::Rect(current_surface_size_))
           .size();
   if (settings_.auto_resize_output_surface &&
-      last_display_transform_swapped_ == current_display_transform &&
       last_render_pass.output_rect.size() != current_surface_size &&
       last_render_pass.damage_rect == last_render_pass.output_rect &&
       !current_surface_size.IsEmpty()) {
@@ -621,7 +618,6 @@
                                  "Graphics.Pipeline.DrawAndSwap",
                                  swapped_trace_id_, "Swap");
     swapped_since_resize_ = true;
-    last_display_transform_swapped_ = current_display_transform;
 
     ui::LatencyInfo::TraceIntermediateFlowEvents(frame.metadata.latency_info,
                                                  "Display::DrawAndSwap");
diff --git a/components/viz/service/display/display.h b/components/viz/service/display/display.h
index 8ecf3be..6ee4efa 100644
--- a/components/viz/service/display/display.h
+++ b/components/viz/service/display/display.h
@@ -31,7 +31,6 @@
 #include "components/viz/service/viz_service_export.h"
 #include "gpu/command_buffer/common/texture_in_use_response.h"
 #include "ui/gfx/color_space.h"
-#include "ui/gfx/overlay_transform.h"
 #include "ui/gfx/swap_result.h"
 #include "ui/latency/latency_info.h"
 
@@ -264,9 +263,6 @@
   int64_t swapped_trace_id_ = 0;
   int64_t last_presented_trace_id_ = 0;
 
-  gfx::OverlayTransform last_display_transform_swapped_ =
-      gfx::OVERLAY_TRANSFORM_NONE;
-
   DISALLOW_COPY_AND_ASSIGN(Display);
 };
 
diff --git a/components/viz/service/display_embedder/gl_output_surface_chromeos.cc b/components/viz/service/display_embedder/gl_output_surface_chromeos.cc
deleted file mode 100644
index d3cc855..0000000
--- a/components/viz/service/display_embedder/gl_output_surface_chromeos.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/viz/service/display_embedder/gl_output_surface_chromeos.h"
-
-namespace viz {
-
-GLOutputSurfaceChromeOS::GLOutputSurfaceChromeOS(
-    scoped_refptr<VizProcessContextProvider> context_provider,
-    gpu::SurfaceHandle surface_handle)
-    : GLOutputSurface(context_provider, surface_handle) {}
-
-GLOutputSurfaceChromeOS::~GLOutputSurfaceChromeOS() = default;
-
-void GLOutputSurfaceChromeOS::SetDisplayTransformHint(
-    gfx::OverlayTransform transform) {
-  display_transform_ = transform;
-}
-
-gfx::OverlayTransform GLOutputSurfaceChromeOS::GetDisplayTransform() {
-  return display_transform_;
-}
-
-}  // namespace viz
diff --git a/components/viz/service/display_embedder/gl_output_surface_chromeos.h b/components/viz/service/display_embedder/gl_output_surface_chromeos.h
deleted file mode 100644
index 7f7b70c..0000000
--- a/components/viz/service/display_embedder/gl_output_surface_chromeos.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_CHROMEOS_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_CHROMEOS_H_
-
-#include "components/viz/service/display_embedder/gl_output_surface.h"
-
-namespace viz {
-
-class GLOutputSurfaceChromeOS : public GLOutputSurface {
- public:
-  GLOutputSurfaceChromeOS(
-      scoped_refptr<VizProcessContextProvider> context_provider,
-      gpu::SurfaceHandle surface_handle);
-  ~GLOutputSurfaceChromeOS() override;
-
-  // GLOutputSurface:
-  void SetDisplayTransformHint(gfx::OverlayTransform transform) override;
-  gfx::OverlayTransform GetDisplayTransform() override;
-
- private:
-  gfx::OverlayTransform display_transform_ = gfx::OVERLAY_TRANSFORM_NONE;
-
-  DISALLOW_COPY_AND_ASSIGN(GLOutputSurfaceChromeOS);
-};
-
-}  // namespace viz
-
-#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_CHROMEOS_H_
diff --git a/components/viz/service/display_embedder/output_surface_provider_impl.cc b/components/viz/service/display_embedder/output_surface_provider_impl.cc
index 8718732e..e17b24a 100644
--- a/components/viz/service/display_embedder/output_surface_provider_impl.cc
+++ b/components/viz/service/display_embedder/output_surface_provider_impl.cc
@@ -64,7 +64,6 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-#include "components/viz/service/display_embedder/gl_output_surface_chromeos.h"
 #include "components/viz/service/display_embedder/output_surface_unified.h"
 #endif
 
@@ -207,9 +206,6 @@
 #elif defined(OS_ANDROID)
       output_surface = std::make_unique<GLOutputSurfaceAndroid>(
           std::move(context_provider), surface_handle);
-#elif defined(OS_CHROMEOS)
-      output_surface = std::make_unique<GLOutputSurfaceChromeOS>(
-          std::move(context_provider), surface_handle);
 #else
       output_surface = std::make_unique<GLOutputSurface>(
           std::move(context_provider), surface_handle);
diff --git a/content/browser/android/ime_adapter_android.cc b/content/browser/android/ime_adapter_android.cc
index 1bc59a6c..a9f3634 100644
--- a/content/browser/android/ime_adapter_android.cc
+++ b/content/browser/android/ime_adapter_android.cc
@@ -319,7 +319,6 @@
 
   rwhi->ImeCommitText(text16, ime_text_spans, gfx::Range::InvalidRange(),
                       relative_cursor_pos);
-  rwhi->OnImeTextCommittedEvent(text16);
 }
 
 void ImeAdapterAndroid::FinishComposingText(JNIEnv* env,
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.cc b/content/browser/compositor/gpu_browser_compositor_output_surface.cc
index d969419..b5091a5 100644
--- a/content/browser/compositor/gpu_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/gpu_browser_compositor_output_surface.cc
@@ -202,15 +202,6 @@
   return 0;
 }
 
-void GpuBrowserCompositorOutputSurface::SetDisplayTransformHint(
-    gfx::OverlayTransform transform) {
-  display_transform_ = transform;
-}
-
-gfx::OverlayTransform GpuBrowserCompositorOutputSurface::GetDisplayTransform() {
-  return display_transform_;
-}
-
 gpu::SurfaceHandle GpuBrowserCompositorOutputSurface::GetSurfaceHandle() const {
   return surface_handle_;
 }
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.h b/content/browser/compositor/gpu_browser_compositor_output_surface.h
index 3bc3238..48ccdcfd 100644
--- a/content/browser/compositor/gpu_browser_compositor_output_surface.h
+++ b/content/browser/compositor/gpu_browser_compositor_output_surface.h
@@ -65,8 +65,6 @@
   unsigned GetOverlayTextureId() const override;
   gfx::BufferFormat GetOverlayBufferFormat() const override;
   unsigned UpdateGpuFence() override;
-  void SetDisplayTransformHint(gfx::OverlayTransform transform) override;
-  gfx::OverlayTransform GetDisplayTransform() override;
 
   void SetDrawRectangle(const gfx::Rect& rect) override;
 
@@ -89,7 +87,6 @@
 
  private:
   const gpu::SurfaceHandle surface_handle_;
-  gfx::OverlayTransform display_transform_ = gfx::OVERLAY_TRANSFORM_NONE;
   base::WeakPtrFactory<GpuBrowserCompositorOutputSurface> weak_ptr_factory_{
       this};
 
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index d6b1ec60..a66cb2fd 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -487,7 +487,6 @@
       features::IsVizHitTestingSurfaceLayerEnabled());
   data->display->Resize(compositor->size());
   data->display->SetOutputIsSecure(data->output_is_secure);
-  data->display->SetDisplayTransformHint(compositor->display_transform());
   compositor->SetLayerTreeFrameSink(std::move(layer_tree_frame_sink));
 }
 
@@ -745,18 +744,6 @@
       std::make_unique<viz::VSyncParameterListener>(std::move(observer));
 }
 
-void GpuProcessTransportFactory::SetDisplayTransformHint(
-    ui::Compositor* compositor,
-    gfx::OverlayTransform transform) {
-  auto it = per_compositor_data_.find(compositor);
-  if (it == per_compositor_data_.end())
-    return;
-  PerCompositorData* data = it->second.get();
-  DCHECK(data);
-  if (data->display)
-    data->display->SetDisplayTransformHint(transform);
-}
-
 void GpuProcessTransportFactory::AddObserver(
     ui::ContextFactoryObserver* observer) {
   observer_list_.AddObserver(observer);
diff --git a/content/browser/compositor/gpu_process_transport_factory.h b/content/browser/compositor/gpu_process_transport_factory.h
index a73e508..09aca908 100644
--- a/content/browser/compositor/gpu_process_transport_factory.h
+++ b/content/browser/compositor/gpu_process_transport_factory.h
@@ -106,8 +106,6 @@
   void AddVSyncParameterObserver(
       ui::Compositor* compositor,
       viz::mojom::VSyncParameterObserverPtr observer) override;
-  void SetDisplayTransformHint(ui::Compositor* compositor,
-                               gfx::OverlayTransform transform) override;
 
   // ImageTransportFactory implementation.
   void DisableGpuCompositing() override;
diff --git a/content/browser/compositor/test/test_image_transport_factory.h b/content/browser/compositor/test/test_image_transport_factory.h
index 4b613c0..68ed98ce 100644
--- a/content/browser/compositor/test/test_image_transport_factory.h
+++ b/content/browser/compositor/test/test_image_transport_factory.h
@@ -85,8 +85,6 @@
   void AddVSyncParameterObserver(
       ui::Compositor* compositor,
       viz::mojom::VSyncParameterObserverPtr observer) override {}
-  void SetDisplayTransformHint(ui::Compositor* compositor,
-                               gfx::OverlayTransform transform) override {}
 
   // ImageTransportFactory implementation.
   void DisableGpuCompositing() override;
diff --git a/content/browser/contacts/contacts_manager_impl.cc b/content/browser/contacts/contacts_manager_impl.cc
index 0803b0d4..f0dcea7 100644
--- a/content/browser/contacts/contacts_manager_impl.cc
+++ b/content/browser/contacts/contacts_manager_impl.cc
@@ -79,10 +79,11 @@
                                  bool include_names,
                                  bool include_emails,
                                  bool include_tel,
+                                 bool include_addresses,
                                  SelectCallback mojom_callback) {
   if (contacts_provider_) {
     contacts_provider_->Select(
-        multiple, include_names, include_emails, include_tel,
+        multiple, include_names, include_emails, include_tel, include_addresses,
         base::BindOnce(&OnContactsSelected, std::move(mojom_callback),
                        source_id_));
   } else {
diff --git a/content/browser/contacts/contacts_manager_impl.h b/content/browser/contacts/contacts_manager_impl.h
index 91fde81..8fa291a 100644
--- a/content/browser/contacts/contacts_manager_impl.h
+++ b/content/browser/contacts/contacts_manager_impl.h
@@ -28,6 +28,7 @@
               bool include_names,
               bool include_emails,
               bool include_tel,
+              bool include_addresses,
               SelectCallback mojom_callback) override;
 
  private:
diff --git a/content/browser/contacts/contacts_provider.h b/content/browser/contacts/contacts_provider.h
index 62f4a26..3aa045f 100644
--- a/content/browser/contacts/contacts_provider.h
+++ b/content/browser/contacts/contacts_provider.h
@@ -27,6 +27,7 @@
                       bool include_names,
                       bool include_emails,
                       bool include_tel,
+                      bool include_addresses,
                       ContactsSelectedCallback callback) = 0;
 };
 
diff --git a/content/browser/contacts/contacts_provider_android.cc b/content/browser/contacts/contacts_provider_android.cc
index c2f9587..c861101e 100644
--- a/content/browser/contacts/contacts_provider_android.cc
+++ b/content/browser/contacts/contacts_provider_android.cc
@@ -50,6 +50,7 @@
                                      bool include_names,
                                      bool include_emails,
                                      bool include_tel,
+                                     bool include_addresses,
                                      ContactsSelectedCallback callback) {
   if (!dialog_) {
     std::move(callback).Run(base::nullopt, /*percentage_shared=*/-1,
@@ -96,8 +97,11 @@
     tel = tel_vector;
   }
 
+  base::Optional<std::vector<payments::mojom::PaymentAddressPtr>> addresses;
+
   blink::mojom::ContactInfoPtr contact =
-      blink::mojom::ContactInfo::New(names, emails, tel);
+      blink::mojom::ContactInfo::New(std::move(names), std::move(emails),
+                                     std::move(tel), std::move(addresses));
 
   contacts_.push_back(std::move(contact));
 }
diff --git a/content/browser/contacts/contacts_provider_android.h b/content/browser/contacts/contacts_provider_android.h
index a7a46e0..f6221a7 100644
--- a/content/browser/contacts/contacts_provider_android.h
+++ b/content/browser/contacts/contacts_provider_android.h
@@ -26,6 +26,7 @@
               bool include_names,
               bool include_emails,
               bool include_tel,
+              bool include_addresses,
               ContactsSelectedCallback callback) override;
 
   // Adds one contact to the list of contacts selected. Note, EndContactsList
diff --git a/content/browser/loader/loader_browsertest.cc b/content/browser/loader/loader_browsertest.cc
index d04f38f9..363ce80 100644
--- a/content/browser/loader/loader_browsertest.cc
+++ b/content/browser/loader/loader_browsertest.cc
@@ -1108,7 +1108,7 @@
 
   void WillRedirectRequest(
       net::RedirectInfo* redirect_info,
-      const network::ResourceResponseHead& response_head,
+      const network::mojom::URLResponseHead& response_head,
       bool* defer,
       std::vector<std::string>* to_be_removed_request_headers,
       net::HttpRequestHeaders* modified_request_headers) override {
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index d46936e..08375e6 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -762,23 +762,16 @@
   process_->UpdateClientPriority(this);
 }
 
-void RenderWidgetHostImpl::OnImeTextCommittedEvent(
-    const base::string16& text_str) {
-  for (auto& observer : ime_text_committed_observers_) {
-    observer.OnImeTextCommittedEvent(text_str);
+void RenderWidgetHostImpl::AddImeInputEventObserver(
+    RenderWidgetHost::InputEventObserver* observer) {
+  if (!ime_input_event_observers_.HasObserver(observer)) {
+    ime_input_event_observers_.AddObserver(observer);
   }
 }
 
-void RenderWidgetHostImpl::AddImeTextCommittedEventObserver(
+void RenderWidgetHostImpl::RemoveImeInputEventObserver(
     RenderWidgetHost::InputEventObserver* observer) {
-  if (!ime_text_committed_observers_.HasObserver(observer)) {
-    ime_text_committed_observers_.AddObserver(observer);
-  }
-}
-
-void RenderWidgetHostImpl::RemoveImeTextCommittedEventObserver(
-    RenderWidgetHost::InputEventObserver* observer) {
-  ime_text_committed_observers_.RemoveObserver(observer);
+  ime_input_event_observers_.RemoveObserver(observer);
 }
 #endif
 
@@ -1955,6 +1948,11 @@
     int selection_end) {
   GetWidgetInputHandler()->ImeSetComposition(
       text, ime_text_spans, replacement_range, selection_start, selection_end);
+#if defined(OS_ANDROID)
+  for (auto& observer : ime_input_event_observers_) {
+    observer.OnImeSetComposingTextEvent(text);
+  }
+#endif
 }
 
 void RenderWidgetHostImpl::ImeCommitText(
@@ -1965,10 +1963,20 @@
   GetWidgetInputHandler()->ImeCommitText(text, ime_text_spans,
                                          replacement_range, relative_cursor_pos,
                                          base::OnceClosure());
+#if defined(OS_ANDROID)
+  for (auto& observer : ime_input_event_observers_) {
+    observer.OnImeTextCommittedEvent(text);
+  }
+#endif
 }
 
 void RenderWidgetHostImpl::ImeFinishComposingText(bool keep_selection) {
   GetWidgetInputHandler()->ImeFinishComposingText(keep_selection);
+#if defined(OS_ANDROID)
+  for (auto& observer : ime_input_event_observers_) {
+    observer.OnImeFinishComposingTextEvent();
+  }
+#endif
 }
 
 void RenderWidgetHostImpl::ImeCancelComposition() {
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 87b88bb..5916c80 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -349,10 +349,9 @@
   void SetImportance(ChildProcessImportance importance);
   ChildProcessImportance importance() const { return importance_; }
 
-  void OnImeTextCommittedEvent(const base::string16& text_str);
-  void AddImeTextCommittedEventObserver(
+  void AddImeInputEventObserver(
       RenderWidgetHost::InputEventObserver* observer) override;
-  void RemoveImeTextCommittedEventObserver(
+  void RemoveImeInputEventObserver(
       RenderWidgetHost::InputEventObserver* observer) override;
 #endif
 
@@ -832,6 +831,10 @@
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostTest,
                            ShorterDelayInputEventAckTimeout);
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostTest, SynchronizeVisualProperties);
+  FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostTest,
+                           AddAndRemoveInputEventObserver);
+  FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostTest,
+                           AddAndRemoveImeInputEventObserver);
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest, AutoResizeWithScale);
   FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraTest,
                            AutoResizeWithBrowserInitiatedResize);
@@ -1125,11 +1128,10 @@
       input_event_observers_;
 
 #if defined(OS_ANDROID)
-  // Ime Text Committed callbacks. This is separated from
-  // input_event_observers_, because text events are not triggered by input
-  // events on Android.
+  // Ime input event callbacks. This is separated from input_event_observers_,
+  // because not all text events are triggered by input events on Android.
   base::ObserverList<RenderWidgetHost::InputEventObserver>::Unchecked
-      ime_text_committed_observers_;
+      ime_input_event_observers_;
 #endif
 
   // The observers watching us.
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 18b38b7..9626750 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -68,6 +68,7 @@
 #include "ui/gfx/canvas.h"
 
 #if defined(OS_ANDROID)
+#include "base/strings/utf_string_conversions.h"
 #include "content/browser/renderer_host/render_widget_host_view_android.h"
 #include "ui/android/screen_android.h"
 #endif
@@ -296,6 +297,18 @@
     : receiver_(std::move(receiver)),
       client_remote_(std::move(client_remote)) {}
 
+// MockInputEventObserver -------------------------------------------------
+class MockInputEventObserver : public RenderWidgetHost::InputEventObserver {
+ public:
+  MOCK_METHOD1(OnInputEvent, void(const blink::WebInputEvent&));
+#if defined(OS_ANDROID)
+  MOCK_METHOD1(OnImeTextCommittedEvent, void(const base::string16& text_str));
+  MOCK_METHOD1(OnImeSetComposingTextEvent,
+               void(const base::string16& text_str));
+  MOCK_METHOD0(OnImeFinishComposingTextEvent, void());
+#endif
+};
+
 // MockRenderWidgetHostDelegate --------------------------------------------
 
 class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
@@ -2125,4 +2138,46 @@
   EXPECT_NE(0u, dispatched_events.size());
 }
 
+TEST_F(RenderWidgetHostTest, AddAndRemoveInputEventObserver) {
+  MockInputEventObserver observer;
+
+  // Add InputEventObserver.
+  host_->AddInputEventObserver(&observer);
+
+  // Confirm OnInputEvent is triggered.
+  NativeWebKeyboardEvent native_event(WebInputEvent::Type::kChar, 0,
+                                      GetNextSimulatedEventTime());
+  ui::LatencyInfo latency_info = ui::LatencyInfo();
+  EXPECT_CALL(observer, OnInputEvent(_)).Times(1);
+  host_->DispatchInputEventWithLatencyInfo(native_event, &latency_info);
+
+  // Remove InputEventObserver.
+  host_->RemoveInputEventObserver(&observer);
+
+  // Confirm InputEventObserver is removed.
+  EXPECT_CALL(observer, OnInputEvent(_)).Times(0);
+  latency_info = ui::LatencyInfo();
+  host_->DispatchInputEventWithLatencyInfo(native_event, &latency_info);
+}
+
+#if defined(OS_ANDROID)
+TEST_F(RenderWidgetHostTest, AddAndRemoveImeInputEventObserver) {
+  MockInputEventObserver observer;
+
+  // Add ImeInputEventObserver.
+  host_->AddImeInputEventObserver(&observer);
+
+  // Confirm ImeFinishComposingTextEvent is triggered.
+  EXPECT_CALL(observer, OnImeFinishComposingTextEvent()).Times(1);
+  host_->ImeFinishComposingText(true);
+
+  // Remove ImeInputEventObserver.
+  host_->RemoveImeInputEventObserver(&observer);
+
+  // Confirm ImeInputEventObserver is removed.
+  EXPECT_CALL(observer, OnImeFinishComposingTextEvent()).Times(0);
+  host_->ImeFinishComposingText(true);
+}
+#endif
+
 }  // namespace content
diff --git a/content/browser/utility_process_host_browsertest.cc b/content/browser/utility_process_host_browsertest.cc
index cc80c53d9..b64e854 100644
--- a/content/browser/utility_process_host_browsertest.cc
+++ b/content/browser/utility_process_host_browsertest.cc
@@ -158,7 +158,7 @@
 
 // Disabled because currently this causes a WER dialog to appear.
 IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest,
-                       LaunchElevatedProcessAndCrash_DISABLED) {
+                       DISABLED_LaunchElevatedProcessAndCrash) {
   RunUtilityProcess(true, true);
 }
 #endif
diff --git a/content/browser/web_package/signed_exchange_cert_fetcher_unittest.cc b/content/browser/web_package/signed_exchange_cert_fetcher_unittest.cc
index d3ea563..70ee01a5 100644
--- a/content/browser/web_package/signed_exchange_cert_fetcher_unittest.cc
+++ b/content/browser/web_package/signed_exchange_cert_fetcher_unittest.cc
@@ -21,9 +21,9 @@
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
 #include "services/network/public/cpp/features.h"
-#include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
@@ -45,7 +45,7 @@
 
   void WillRedirectRequest(
       net::RedirectInfo* redirect_info,
-      const network::ResourceResponseHead& /* response_head */,
+      const network::mojom::URLResponseHead& /* response_head */,
       bool* defer,
       std::vector<std::string>* /* to_be_removed_headers */,
       net::HttpRequestHeaders* /* modified_headers */) override {
@@ -54,7 +54,7 @@
   }
 
   void WillProcessResponse(const GURL& response_url_,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override {
     will_process_response_called_ = true;
     *defer = true;
@@ -222,13 +222,14 @@
   }
 
   void CallOnReceiveResponse() {
-    network::ResourceResponseHead resource_response;
-    resource_response.headers =
+    auto response_head = network::mojom::URLResponseHead::New();
+    response_head->headers =
         base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
-    resource_response.headers->AddHeader(
+    response_head->headers->AddHeader(
         "Content-Type: application/cert-chain+cbor");
-    resource_response.mime_type = "application/cert-chain+cbor";
-    mock_loader_factory_.client_ptr()->OnReceiveResponse(resource_response);
+    response_head->mime_type = "application/cert-chain+cbor";
+    mock_loader_factory_.client_ptr()->OnReceiveResponse(
+        std::move(response_head));
   }
 
   DeferringURLLoaderThrottle* InitializeDeferringURLLoaderThrottle() {
@@ -421,11 +422,12 @@
 
   std::unique_ptr<SignedExchangeCertFetcher> fetcher =
       CreateFetcherAndStart(url_, false /* force_fetch */);
-  network::ResourceResponseHead resource_response;
-  resource_response.headers =
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->headers =
       base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
-  resource_response.content_length = message.size();
-  mock_loader_factory_.client_ptr()->OnReceiveResponse(resource_response);
+  response_head->content_length = message.size();
+  mock_loader_factory_.client_ptr()->OnReceiveResponse(
+      std::move(response_head));
   mojo::DataPipe data_pipe(message.size());
   CHECK(mojo::BlockingCopyFromString(message, data_pipe.producer_handle));
   data_pipe.producer_handle.reset();
@@ -443,10 +445,9 @@
 TEST_F(SignedExchangeCertFetcherTest, Abort_Redirect) {
   std::unique_ptr<SignedExchangeCertFetcher> fetcher =
       CreateFetcherAndStart(url_, false /* force_fetch */);
-  network::ResourceResponseHead response_head;
   net::RedirectInfo redirect_info;
-  mock_loader_factory_.client_ptr()->OnReceiveRedirect(redirect_info,
-                                                       response_head);
+  mock_loader_factory_.client_ptr()->OnReceiveRedirect(
+      redirect_info, network::mojom::URLResponseHead::New());
   RunUntilIdle();
 
   EXPECT_TRUE(callback_called_);
@@ -457,10 +458,11 @@
 TEST_F(SignedExchangeCertFetcherTest, Abort_404) {
   std::unique_ptr<SignedExchangeCertFetcher> fetcher =
       CreateFetcherAndStart(url_, false /* force_fetch */);
-  network::ResourceResponseHead resource_response;
-  resource_response.headers =
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->headers =
       base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 404 Not Found");
-  mock_loader_factory_.client_ptr()->OnReceiveResponse(resource_response);
+  mock_loader_factory_.client_ptr()->OnReceiveResponse(
+      std::move(response_head));
   RunUntilIdle();
 
   EXPECT_TRUE(callback_called_);
@@ -471,13 +473,13 @@
 TEST_F(SignedExchangeCertFetcherTest, WrongMimeType) {
   std::unique_ptr<SignedExchangeCertFetcher> fetcher =
       CreateFetcherAndStart(url_, false /* force_fetch */);
-  network::ResourceResponseHead resource_response;
-  resource_response.headers =
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->headers =
       base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
-  resource_response.headers->AddHeader(
-      "Content-Type: application/octet-stream");
-  resource_response.mime_type = "application/octet-stream";
-  mock_loader_factory_.client_ptr()->OnReceiveResponse(resource_response);
+  response_head->headers->AddHeader("Content-Type: application/octet-stream");
+  response_head->mime_type = "application/octet-stream";
+  mock_loader_factory_.client_ptr()->OnReceiveResponse(
+      std::move(response_head));
   RunUntilIdle();
 
   EXPECT_TRUE(callback_called_);
@@ -589,11 +591,10 @@
 
   RunUntilIdle();
 
-  network::ResourceResponseHead response_head;
   net::RedirectInfo redirect_info;
 
-  mock_loader_factory_.client_ptr()->OnReceiveRedirect(redirect_info,
-                                                       response_head);
+  mock_loader_factory_.client_ptr()->OnReceiveRedirect(
+      redirect_info, network::mojom::URLResponseHead::New());
   RunUntilIdle();
 
   EXPECT_TRUE(throttle->will_redirect_request_called());
diff --git a/content/browser/webui/web_ui_data_source_impl.cc b/content/browser/webui/web_ui_data_source_impl.cc
index a1d40a0..ca4eac89 100644
--- a/content/browser/webui/web_ui_data_source_impl.cc
+++ b/content/browser/webui/web_ui_data_source_impl.cc
@@ -294,7 +294,7 @@
   }
 
   int resource_id = PathToIdrOrDefault(CleanUpPath(path));
-  DCHECK_NE(resource_id, -1);
+  DCHECK_NE(resource_id, -1) << " for " << path;
   scoped_refptr<base::RefCountedMemory> response(
       GetContentClient()->GetDataResourceBytes(resource_id));
   callback.Run(response.get());
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index 9c737a7..a6714fb 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -213,16 +213,39 @@
     const SharedWorkerInstance& instance,
     int client_process_id,
     int frame_id) {
-  for (Observer& observer : observers_)
-    observer.OnClientAdded(instance, client_process_id, frame_id);
+  auto insertion_result = shared_worker_client_counts_.insert(
+      {{instance, GlobalFrameRoutingId(client_process_id, frame_id)}, 0});
+
+  int& count = insertion_result.first->second;
+  ++count;
+
+  // Only notify if this is the first time that this frame connects to that
+  // shared worker.
+  if (insertion_result.second) {
+    for (Observer& observer : observers_)
+      observer.OnClientAdded(instance, client_process_id, frame_id);
+  }
 }
 
 void SharedWorkerServiceImpl::NotifyClientRemoved(
     const SharedWorkerInstance& instance,
     int client_process_id,
     int frame_id) {
-  for (Observer& observer : observers_)
-    observer.OnClientRemoved(instance, client_process_id, frame_id);
+  auto it = shared_worker_client_counts_.find(std::make_pair(
+      instance, GlobalFrameRoutingId(client_process_id, frame_id)));
+  DCHECK(it != shared_worker_client_counts_.end());
+
+  int& count = it->second;
+  DCHECK_GT(count, 0);
+  --count;
+
+  // Only notify if there are no longer any active connections from this frame
+  // to that shared worker.
+  if (count == 0) {
+    shared_worker_client_counts_.erase(it);
+    for (Observer& observer : observers_)
+      observer.OnClientRemoved(instance, client_process_id, frame_id);
+  }
 }
 
 void SharedWorkerServiceImpl::CreateWorker(
diff --git a/content/browser/worker_host/shared_worker_service_impl.h b/content/browser/worker_host/shared_worker_service_impl.h
index 9694327..1219ecd 100644
--- a/content/browser/worker_host/shared_worker_service_impl.h
+++ b/content/browser/worker_host/shared_worker_service_impl.h
@@ -16,6 +16,7 @@
 #include "base/observer_list.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/worker_host/shared_worker_host.h"
+#include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/shared_worker_service.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/cpp/resource_response.h"
@@ -154,6 +155,13 @@
   scoped_refptr<ChromeAppCacheService> appcache_service_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_override_;
 
+  // Keeps a reference count of each worker-client pair so as to not send
+  // duplicate OnClientAdded() notifications if the same frame connects multiple
+  // times to the same shared worker. Note that this is a situation unique to
+  // shared worker and cannot happen with dedicated workers and service workers.
+  base::flat_map<std::pair<SharedWorkerInstance, GlobalFrameRoutingId>, int>
+      shared_worker_client_counts_;
+
   base::ObserverList<Observer> observers_;
 
   base::WeakPtrFactory<SharedWorkerServiceImpl> weak_factory_{this};
diff --git a/content/browser/worker_host/shared_worker_service_impl_unittest.cc b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
index 05a5fbb..cd131da 100644
--- a/content/browser/worker_host/shared_worker_service_impl_unittest.cc
+++ b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
@@ -1364,4 +1364,94 @@
   EXPECT_EQ(0u, observer.GetClientCount());
 }
 
+TEST_F(SharedWorkerServiceImplTest, CollapseDuplicateNotifications) {
+  TestSharedWorkerServiceObserver observer;
+
+  ScopedObserver<SharedWorkerService, SharedWorkerService::Observer>
+      scoped_observer(&observer);
+  scoped_observer.Add(content::BrowserContext::GetDefaultStoragePartition(
+                          browser_context_.get())
+                          ->GetSharedWorkerService());
+
+  const GURL kUrl("http://example.com/w.js");
+  const char kName[] = "name";
+
+  // The first renderer host.
+  std::unique_ptr<TestWebContents> web_contents =
+      CreateWebContents(GURL("http://example.com/"));
+  TestRenderFrameHost* render_frame_host = web_contents->GetMainFrame();
+  MockRenderProcessHost* renderer_host = render_frame_host->GetProcess();
+  const int process_id = renderer_host->GetID();
+  renderer_host->OverrideBinderForTesting(
+      blink::mojom::SharedWorkerFactory::Name_,
+      base::BindRepeating(&SharedWorkerServiceImplTest::BindSharedWorkerFactory,
+                          base::Unretained(this), process_id));
+
+  // First client, creates worker.
+
+  MockSharedWorkerClient client0;
+  MessagePortChannel local_port0;
+  ConnectToSharedWorker(MakeSharedWorkerConnector(
+                            renderer_host, render_frame_host->GetRoutingID()),
+                        kUrl, kName, &client0, &local_port0);
+  base::RunLoop().RunUntilIdle();
+
+  mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver =
+      WaitForFactoryReceiver(process_id);
+  MockSharedWorkerFactory factory(std::move(factory_receiver));
+  base::RunLoop().RunUntilIdle();
+
+  mojo::Remote<blink::mojom::SharedWorkerHost> worker_host;
+  mojo::PendingReceiver<blink::mojom::SharedWorker> worker_receiver;
+  EXPECT_TRUE(factory.CheckReceivedCreateSharedWorker(
+      kUrl, kName, blink::mojom::ContentSecurityPolicyType::kReport,
+      &worker_host, &worker_receiver));
+  MockSharedWorker worker(std::move(worker_receiver));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(worker.CheckReceivedConnect(nullptr, nullptr));
+  EXPECT_TRUE(client0.CheckReceivedOnCreated());
+
+  // The observer now sees one worker with one client.
+  EXPECT_EQ(1u, observer.GetWorkerCount());
+  EXPECT_EQ(1u, observer.GetClientCount());
+
+  // Now the same frame connects to the same worker.
+  MockSharedWorkerClient client1;
+  MessagePortChannel local_port1;
+  ConnectToSharedWorker(MakeSharedWorkerConnector(
+                            renderer_host, render_frame_host->GetRoutingID()),
+                        kUrl, kName, &client1, &local_port1);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(CheckNotReceivedFactoryReceiver(process_id));
+
+  EXPECT_TRUE(worker.CheckReceivedConnect(nullptr, nullptr));
+  EXPECT_TRUE(client1.CheckReceivedOnCreated());
+
+  // Duplicate notification for the same client/worker pair are not sent.
+  EXPECT_EQ(1u, observer.GetWorkerCount());
+  EXPECT_EQ(1u, observer.GetClientCount());
+
+  // Cleanup
+
+  client0.Close();
+  base::RunLoop().RunUntilIdle();
+
+  // With the first connection closed, the observer is still aware of one
+  // client.
+  EXPECT_EQ(1u, observer.GetWorkerCount());
+  EXPECT_EQ(1u, observer.GetClientCount());
+
+  client1.Close();
+  base::RunLoop().RunUntilIdle();
+
+  // Both connection are closed, the worker is stopped and there's no active
+  // clients.
+  EXPECT_EQ(0u, observer.GetWorkerCount());
+  EXPECT_EQ(0u, observer.GetClientCount());
+
+  EXPECT_TRUE(worker.CheckReceivedTerminate());
+}
+
 }  // namespace content
diff --git a/content/common/throttling_url_loader.cc b/content/common/throttling_url_loader.cc
index ee5e64a..27beb5375 100644
--- a/content/common/throttling_url_loader.cc
+++ b/content/common/throttling_url_loader.cc
@@ -12,7 +12,6 @@
 #include "net/http/http_util.h"
 #include "net/url_request/redirect_util.h"
 #include "services/network/public/cpp/features.h"
-#include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 
 namespace content {
@@ -534,7 +533,7 @@
     for (auto& entry : throttles_) {
       auto* throttle = entry.throttle.get();
       bool throttle_deferred = false;
-      throttle->BeforeWillProcessResponse(response_url_, response_head,
+      throttle->BeforeWillProcessResponse(response_url_, *response_head,
                                           &throttle_deferred);
       if (!HandleThrottleResult(throttle, throttle_deferred, &deferred))
         return;
@@ -553,13 +552,12 @@
   }
 
   // Dispatch WillProcessResponse().
-  network::ResourceResponseHead response_head_copy = response_head;
   if (!throttles_.empty()) {
     bool deferred = false;
     for (auto& entry : throttles_) {
       auto* throttle = entry.throttle.get();
       bool throttle_deferred = false;
-      throttle->WillProcessResponse(response_url_, &response_head_copy,
+      throttle->WillProcessResponse(response_url_, response_head.get(),
                                     &throttle_deferred);
       if (!HandleThrottleResult(throttle, throttle_deferred, &deferred))
         return;
@@ -567,13 +565,13 @@
 
     if (deferred) {
       deferred_stage_ = DEFERRED_RESPONSE;
-      response_info_ = std::make_unique<ResponseInfo>(response_head_copy);
+      response_info_ = std::make_unique<ResponseInfo>(std::move(response_head));
       client_binding_.PauseIncomingMethodCallProcessing();
       return;
     }
   }
 
-  forwarding_client_->OnReceiveResponse(response_head_copy);
+  forwarding_client_->OnReceiveResponse(std::move(response_head));
 }
 
 void ThrottlingURLLoader::OnReceiveRedirect(
@@ -592,7 +590,7 @@
       std::vector<std::string> removed_headers;
       net::HttpRequestHeaders modified_headers;
       net::RedirectInfo redirect_info_copy = redirect_info;
-      throttle->WillRedirectRequest(&redirect_info_copy, response_head,
+      throttle->WillRedirectRequest(&redirect_info_copy, *response_head,
                                     &throttle_deferred, &removed_headers,
                                     &modified_headers);
       if (redirect_info_copy.new_url != redirect_info.new_url) {
diff --git a/content/common/throttling_url_loader_unittest.cc b/content/common/throttling_url_loader_unittest.cc
index 145bd2f..8a472a2a 100644
--- a/content/common/throttling_url_loader_unittest.cc
+++ b/content/common/throttling_url_loader_unittest.cc
@@ -293,7 +293,7 @@
   }
 
   void WillRedirectRequest(net::RedirectInfo* redirect_info,
-                           const network::ResourceResponseHead& response_head,
+                           const network::mojom::URLResponseHead& response_head,
                            bool* defer,
                            std::vector<std::string>* removed_headers,
                            net::HttpRequestHeaders* modified_headers) override {
@@ -305,7 +305,7 @@
   }
 
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override {
     will_process_response_called_++;
     if (will_process_response_callback_)
@@ -315,7 +315,7 @@
 
   void BeforeWillProcessResponse(
       const GURL& response_url,
-      const network::ResourceResponseHead& response_head,
+      const network::mojom::URLResponseHead& response_head,
       bool* defer) override {
     before_will_process_response_called_++;
     if (before_will_process_response_callback_)
diff --git a/content/public/android/java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java b/content/public/android/java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java
index 008a192..39d6c82a 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java
@@ -40,7 +40,7 @@
 @JNINamespace("content")
 @VisibleForTesting
 public class BackgroundSyncNetworkObserver implements NetworkChangeNotifierAutoDetect.Observer {
-    private static final String TAG = "cr_BgSyncNetObserver";
+    private static final String TAG = "BgSyncNetObserver";
     private static boolean sSetConnectionTypeForTesting;
     private NetworkChangeNotifierAutoDetect mNotifier;
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/BindingManager.java b/content/public/android/java/src/org/chromium/content/browser/BindingManager.java
index 548e2f5..6d42e1a 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BindingManager.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BindingManager.java
@@ -22,7 +22,7 @@
  * This object must only be accessed from the launcher thread.
  */
 class BindingManager implements ComponentCallbacks2 {
-    private static final String TAG = "cr_BindingManager";
+    private static final String TAG = "BindingManager";
 
     // Low reduce ratio of moderate binding.
     private static final float MODERATE_BINDING_LOW_REDUCE_RATIO = 0.25f;
diff --git a/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProviderImpl.java b/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProviderImpl.java
index 0bd7406..3bb2270 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProviderImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProviderImpl.java
@@ -40,7 +40,7 @@
                 new ScreenOrientationProviderImpl();
     }
 
-    private static final String TAG = "cr.ScreenOrientation";
+    private static final String TAG = "ScreenOrientation";
 
     private ScreenOrientationDelegate mDelegate;
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/TracingControllerAndroidImpl.java b/content/public/android/java/src/org/chromium/content/browser/TracingControllerAndroidImpl.java
index 5961c98..a42a0f4 100644
--- a/content/public/android/java/src/org/chromium/content/browser/TracingControllerAndroidImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/TracingControllerAndroidImpl.java
@@ -46,7 +46,7 @@
  */
 @JNINamespace("content")
 public class TracingControllerAndroidImpl implements TracingControllerAndroid {
-    private static final String TAG = "cr.TracingController";
+    private static final String TAG = "TracingController";
 
     private static final String ACTION_START = "GPU_PROFILER_START";
     private static final String ACTION_STOP = "GPU_PROFILER_STOP";
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
index 983e5d4..ca900db 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -433,6 +433,10 @@
                     mAccessibilityFocusId = View.NO_ID;
                     mAccessibilityFocusRect = null;
                 }
+                if (mLastHoverId == virtualViewId) {
+                    sendAccessibilityEvent(mLastHoverId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+                    mLastHoverId = View.NO_ID;
+                }
                 return true;
             case AccessibilityNodeInfo.ACTION_CLICK:
                 if (!mView.hasFocus()) mView.requestFocus();
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/InputMethodManagerWrapperImpl.java b/content/public/android/java/src/org/chromium/content/browser/input/InputMethodManagerWrapperImpl.java
index 30a64fce..e835c524 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/InputMethodManagerWrapperImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/InputMethodManagerWrapperImpl.java
@@ -24,7 +24,7 @@
  */
 public class InputMethodManagerWrapperImpl implements InputMethodManagerWrapper {
     private static final boolean DEBUG_LOGS = false;
-    private static final String TAG = "cr_IMM";
+    private static final String TAG = "IMM";
 
     private final Context mContext;
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java
index 62a3b83..4c5e54e8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java
@@ -40,7 +40,7 @@
  * details.
  */
 class ThreadedInputConnection extends BaseInputConnection implements ChromiumBaseInputConnection {
-    private static final String TAG = "cr_Ime";
+    private static final String TAG = "Ime";
     private static final boolean DEBUG_LOGS = false;
 
     private static final TextInputState UNBLOCKER = new TextInputState(
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
index 7cd6b9e..e04d530 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
@@ -21,7 +21,7 @@
 // TODO(changwan): add unit tests once Robolectric supports Android API level >= 21.
 // See crbug.com/588547 for details.
 public class ThreadedInputConnectionFactory implements ChromiumBaseInputConnection.Factory {
-    private static final String TAG = "cr_Ime";
+    private static final String TAG = "Ime";
     private static final boolean DEBUG_LOGS = false;
 
     // Most of the time we do not need to retry. But if we have lost window focus while triggering
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionProxyView.java b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionProxyView.java
index 54a0d2f..aa3f6f9 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionProxyView.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionProxyView.java
@@ -22,7 +22,7 @@
  * This is a fake View that is only exposed to InputMethodManager.
  */
 public class ThreadedInputConnectionProxyView extends View {
-    private static final String TAG = "cr_Ime";
+    private static final String TAG = "Ime";
     private static final boolean DEBUG_LOGS = false;
 
     private final Handler mImeThreadHandler;
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/LGEmailActionModeWorkaroundImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/LGEmailActionModeWorkaroundImpl.java
index 9d299a2..807863e11 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/LGEmailActionModeWorkaroundImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/LGEmailActionModeWorkaroundImpl.java
@@ -35,7 +35,7 @@
  * This is a version code limited workaround to avoid crashes in the app.
  */
 public final class LGEmailActionModeWorkaroundImpl {
-    private static final String TAG = "cr_Ime";
+    private static final String TAG = "Ime";
 
     // This is the last broken version shipped on LG V20/NRD90M.
     public static final int LGEmailWorkaroundMaxVersion = 67502100;
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index 6de8c45..4a2e4b6 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -64,7 +64,7 @@
  */
 @JNINamespace("content")
 public class WebContentsImpl implements WebContents, RenderFrameHostDelegate, WindowEventObserver {
-    private static final String TAG = "cr_WebContentsImpl";
+    private static final String TAG = "WebContentsImpl";
 
     private static final String PARCEL_VERSION_KEY = "version";
     private static final String PARCEL_WEBCONTENTS_KEY = "webcontents";
diff --git a/content/public/browser/render_widget_host.h b/content/public/browser/render_widget_host.h
index 69df059..ceaeff19 100644
--- a/content/public/browser/render_widget_host.h
+++ b/content/public/browser/render_widget_host.h
@@ -241,10 +241,20 @@
     virtual void OnInputEventAck(InputEventAckSource source,
                                  InputEventAckState state,
                                  const blink::WebInputEvent&) {}
-    // Key events are not triggered through InputEvent on Android.
-    // This function is triggered by IME commitText.
+
 #if defined(OS_ANDROID)
+    // Not all key events are triggered through InputEvent on Android.
+    // InputEvents are only triggered when user typed in through number bar on
+    // Android keyboard. This function is triggered when text is committed in
+    // input form.
     virtual void OnImeTextCommittedEvent(const base::string16& text_str) {}
+    // This function is triggered when composing text is updated. Note that
+    // text_str contains all text that is currently under composition rather
+    // than updated text only.
+    virtual void OnImeSetComposingTextEvent(const base::string16& text_str) {}
+    // This function is triggered when composing text is filled into the input
+    // form.
+    virtual void OnImeFinishComposingTextEvent() {}
 #endif
   };
 
@@ -253,11 +263,9 @@
   virtual void RemoveInputEventObserver(InputEventObserver* observer) = 0;
 
 #if defined(OS_ANDROID)
-  // Add/remove an Ime text committed event observer.
-  virtual void AddImeTextCommittedEventObserver(
-      RenderWidgetHost::InputEventObserver* observer) = 0;
-  virtual void RemoveImeTextCommittedEventObserver(
-      InputEventObserver* observer) = 0;
+  // Add/remove an Ime input event observer.
+  virtual void AddImeInputEventObserver(InputEventObserver* observer) = 0;
+  virtual void RemoveImeInputEventObserver(InputEventObserver* observer) = 0;
 #endif
 
   // Add and remove observers for widget host events. The order in which
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 76d348dc..183d55cf 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -852,8 +852,7 @@
 // Flag used by WebUI test runners to wait for debugger to be attached.
 const char kWaitForDebuggerWebUI[] = "wait-for-debugger-webui";
 
-// Set the antialiasing method used for webgl. (none, explicit, implicit, or
-// screenspace)
+// Set the antialiasing method used for webgl. (none, explicit, implicit)
 const char kWebglAntialiasingMode[] = "webgl-antialiasing-mode";
 
 // Set a default sample count for webgl if msaa is enabled.
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestInputMethodManagerWrapper.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestInputMethodManagerWrapper.java
index 0e65697..bc527c9 100644
--- a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestInputMethodManagerWrapper.java
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/TestInputMethodManagerWrapper.java
@@ -25,7 +25,7 @@
  * Overrides InputMethodManagerWrapper for testing purposes.
  */
 public class TestInputMethodManagerWrapper implements InputMethodManagerWrapper {
-    private static final String TAG = "cr_Ime";
+    private static final String TAG = "Ime";
 
     private final InputConnectionProvider mInputConnectionProvider;
     private InputConnection mInputConnection;
diff --git a/content/renderer/webgraphicscontext3d_provider_impl.cc b/content/renderer/webgraphicscontext3d_provider_impl.cc
index d0e38db..7fa392d 100644
--- a/content/renderer/webgraphicscontext3d_provider_impl.cc
+++ b/content/renderer/webgraphicscontext3d_provider_impl.cc
@@ -69,9 +69,11 @@
         base::CommandLine::ForCurrentProcess();
     auto gpu_feature_info = GetGpuFeatureInfo();
 
-    if (gpu_feature_info.IsWorkaroundEnabled(MAX_MSAA_SAMPLE_COUNT_4)) {
+    if (gpu_feature_info.IsWorkaroundEnabled(MAX_MSAA_SAMPLE_COUNT_2))
+      prefs.msaa_sample_count = 2;
+    else if (gpu_feature_info.IsWorkaroundEnabled(MAX_MSAA_SAMPLE_COUNT_4))
       prefs.msaa_sample_count = 4;
-    }
+
     if (command_line->HasSwitch(switches::kWebglMSAASampleCount)) {
       std::string sample_count =
           command_line->GetSwitchValueASCII(switches::kWebglMSAASampleCount);
@@ -90,9 +92,6 @@
         prefs.anti_aliasing_mode = blink::kAntialiasingModeMSAAExplicitResolve;
       } else if (mode == "implicit") {
         prefs.anti_aliasing_mode = blink::kAntialiasingModeMSAAImplicitResolve;
-      } else if (mode == "screenspace") {
-        prefs.anti_aliasing_mode =
-            blink::kAntialiasingModeScreenSpaceAntialiasing;
       } else {
         prefs.anti_aliasing_mode = blink::kAntialiasingModeUnspecified;
       }
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 1400a8c..86fa9c0 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -478,6 +478,10 @@
       "browser/shell_plugin_service_filter.h",
     ]
   }
+
+  if (enable_cast_renderer) {
+    deps += [ "//media/mojo/mojom:constants" ]
+  }
 }
 
 grit("content_shell_resources_grit") {
diff --git a/content/shell/android/browsertests/src/org/chromium/content_shell/browsertests/ContentShellBrowserTestActivity.java b/content/shell/android/browsertests/src/org/chromium/content_shell/browsertests/ContentShellBrowserTestActivity.java
index 5f881f9..4cd609b 100644
--- a/content/shell/android/browsertests/src/org/chromium/content_shell/browsertests/ContentShellBrowserTestActivity.java
+++ b/content/shell/android/browsertests/src/org/chromium/content_shell/browsertests/ContentShellBrowserTestActivity.java
@@ -27,8 +27,7 @@
 
 /** An Activity base class for running browser tests against ContentShell. */
 public abstract class ContentShellBrowserTestActivity extends NativeBrowserTestActivity {
-
-    private static final String TAG = "cr.native_test";
+    private static final String TAG = "native_test";
 
     private ShellManager mShellManager;
     private WindowAndroid mWindowAndroid;
diff --git a/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java b/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java
index 21eacc2..046d51a 100644
--- a/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java
+++ b/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java
@@ -15,7 +15,7 @@
  * Android activity for running content_public.browser.tests
  */
 public class ContentBrowserTestsActivity extends ContentShellBrowserTestActivity {
-    private static final String TAG = "cr.native_test";
+    private static final String TAG = "native_test";
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index d7457bf..a6d7035 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -97,10 +97,13 @@
 # TODO(crbug.com/979444): once this is passing on the passthrough
 # command decoder on Android, simplify this expectation to just not
 # declare any OS.
+# TODO(crbug.com/1017162): This test is very flaky on Win/Vulkan/Nvidia,
+# even with RetryOnFailure. Once this issue is resolved, we can probably
+# cover this with RetryOnFailure again.
 crbug.com/979444 [ chromeos ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ linux ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ mac no-passthrough ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
-crbug.com/979444 [ win ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
+crbug.com/979444 [ win ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
 crbug.com/979444 [ android ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 
 # Win / AMD / Passthrough command decoder / D3D11
@@ -264,6 +267,11 @@
 crbug.com/angleproject/2930 [ win nvidia vulkan passthrough ] conformance/textures/misc/texture-size-cube-maps.html [ Failure ]
 crbug.com/angleproject/3481 [ win nvidia vulkan passthrough ] conformance/textures/misc/texture-sub-image-cube-maps.html [ Failure ]
 crbug.com/angleproject/2926 [ win nvidia vulkan passthrough ] deqp/data/gles2/shaders/conversions.html [ Failure ]
+# Flaky since crbug.com/1017162
+crbug.com/1017162 [ win nvidia vulkan passthrough ] conformance/extensions/angle-instanced-arrays.html [ Failure ]
+crbug.com/1017162 [ win nvidia vulkan passthrough ] conformance/ogles/GL/radians/radians_001_to_006.html [ Failure ]
+crbug.com/1017162 [ win nvidia vulkan passthrough ] conformance/ogles/GL/reflect/reflect_001_to_006.html [ Failure ]
+crbug.com/1017162 [ win nvidia vulkan passthrough ] conformance/ogles/GL/refract/refract_001_to_006.html [ Failure ]
 
 # Vulkan / Win / AMD / Passthrough command decoder
 crbug.com/angleproject/2898 [ win amd vulkan passthrough ] conformance/extensions/oes-texture-float-with-canvas.html [ Failure ]
diff --git a/docs/accessibility/android.md b/docs/accessibility/android.md
new file mode 100644
index 0000000..45c60ae
--- /dev/null
+++ b/docs/accessibility/android.md
@@ -0,0 +1,72 @@
+# Chrome Accessibility on Android
+
+Chrome plays an important role on Android - not only is it the default
+browser, but Chrome powers WebView, which is used by many built-in and
+third-party apps to display all sorts of content.
+
+This document covers some of the technical details of how Chrome
+implements its accessibility support on Android.
+
+As background reading, you should be familiar with
+[https://developer.android.com/guide/topics/ui/accessibility](Android Accessibility)
+and in particular
+[https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo](AccessibilityNodeInfo)
+and
+[https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeProvider](AccessibilityNodeProvider).
+
+## WebContentsAccessibility
+
+The main Java class that implements the accessibility protocol in Chrome is
+[https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java](WebContentsAccessibilityImpl.java). It implements the AccessibilityNodeProvider
+interface, so a single Android View can be represented by an entire tree
+of virtual views. Note that WebContentsAccessibilityImpl represents an
+entire web page, including all frames. The ids in the java code are unique IDs,
+not frame-local IDs.
+
+On most platforms, we create a native object for every AXNode in a web page,
+and we implement a bunch of methods on that object that assistive technology
+can query.
+
+Android is different - it's more lightweight in one way, in that we only
+create a native AccessibilityNodeInfo when specifically requested, when
+an Android accessibility service is exploring the virtual tree. In another
+sense it's more heavyweight, though, because every time a virtual view is
+requested we have to populate it with every possible accessibility attribute,
+and there are quite a few.
+
+## Populating AccessibilityNodeInfo
+
+Populating AccessibilityNodeInfo is a bit complicated for reasons of
+Android version compatibility and also code efficiency.
+
+WebContentsAccessibilityImpl.createAccessibilityNodeInfo is the starting
+point. That's called by the Android framework when we need to provide the
+info about one virtual view (a web node).
+
+We call into C++ code - 
+[https://cs.chromium.org/chromium/src/content/browser/accessibility/web_contents_accessibility_android.cc](web_contents_accessibility_android.cc) from
+there, because all of the information about the accessibility tree is
+using the shared C++ BrowserAccessibilityManager code.
+
+However, the C++ code then calls back into Java in order to set all of the
+properties of AccessibilityNodeInfo, because those have to be set in Java.
+Each of those methods, like setAccessibilityNodeInfoBooleanAttributes, is
+often overridden by an Android-version-specific subclass to take advantage
+of newer APIs where available.
+
+Having the Java code query C++ for every individual attribute one at a time
+would be too expensive, we'd be going across the JNI boundary too many times.
+That's why it's structured the way it is now.
+
+## Touch Exploration
+
+The way touch exploration works on Android is complicated:
+
+* When the user taps or drags their finger, our View gets a hover event.
+* Accessibility code sends a hit test action to the renderer process
+* The renderer process fires a HOVER accessibility event on the accessibility
+  node at that coordinate
+* WebContentsAccessibilityImpl.handleHover is called with that node. We fire
+  an Android TYPE_VIEW_HOVER_ENTER event on that node and a
+  TYPE_VIEW_HOVER_EXIT event on the previous node.
+* Finally, TalkBack sets accessibility focus to that node.
diff --git a/docs/infra/using_led.md b/docs/infra/using_led.md
new file mode 100644
index 0000000..a5617c18
--- /dev/null
+++ b/docs/infra/using_led.md
@@ -0,0 +1,77 @@
+# Using LED
+
+LED is an infrastructure tool used to manually trigger builds on any builder
+running on LUCI. It's designed to help debug build failures or experiment with
+new builder changes. This doc describes how to use it with Chromium's builders.
+
+[TOC]
+
+## When to use it
+
+Use cases include, but are not limited to, the following:
+* **Testing a recipe change**: Much of the code in the following repos
+define what and how a builder runs (also known as the builder's "recipe"):
+[recipes-py][1], [depot_tools][2], and [tools/build][3]. Changes to these
+repos can first be tested out on any builder prior to submitting via LED.
+* **Debugging a waterfall failure**: If a waterfall builder (that is, *not* a
+trybot) is exhibitting frequent or strange failures that can't be reproduced
+locally, LED can be used to retrigger any given build for debugging.
+
+## When *not* to use it
+
+Certain types of changes to a trybot (this includes all builders on the CQ)
+can be sufficiently tested without the use of LED. This includes changes to a
+trybot's:
+* **GN args**: A trybot's build args are configured via
+[mb_config.pyl][4].
+* **tests**: The list of tests a trybot runs are set via the \*.pyl files in
+[//testing/buildbot/][5]. (Some trybots may not be present in
+those files. Instead, change the waterfall builders they mirror. This mapping is
+configured in tools/build's [trybots.py][6].)
+
+Simply edit the needed files in a local chromium/src checkout, upload the change
+to Gerrit, then select the affected trybot(s) via the "select tryjobs" menu.
+
+## How to use it
+
+Provided that a local depot_tools checkout is present on $PATH, LED can be
+used by simply invoking `led` on the command line. A common use-case for LED is
+to modify the build steps of any given builder. The process for doing this is
+outlined below. (However, LED can be used for many other purposes. See the full
+list of features via `led help`.)
+
+1. Select a builder whose builds you'd like to reproduce. (Example:
+[linux-rel][7])
+2. Record its full builder name, along with its bucket. (The bucket name is
+present in the URL of the builder page, and is very likely "chromium/ci".)
+3. Checkout the [tools/build][3] repo (if not already present) and navigate to
+the [chromium][8] and/or [chromium_tests][9] recipe modules. These, along with
+the other recipe_modules located in tools/build, are how the majority of a
+Chromium builder's recipe is defined.
+4. Make the desired recipe change. (Consider running local recipe unittests
+before proceeding by running `recipes.py test train` via the [recipes.py][10]
+script.
+5. Launch a build with the given recipe change. This can be done with a single
+chained LED invocation, eg:
+`led get-builder chromium/ci:linux-rel | led edit-recipe-bundle | led launch`
+6. The LED invocation above will print out a link to the build that was
+launched. Repeat steps 4 & 5 until the triggered builds behave as expected
+with the new recipe change.
+
+## Questions? Feedback?
+
+If you're in need of further assistance, if you're not sure about
+one or more steps, or if you found this documentation lacking, please
+reach out to infra-dev@chromium.org or [file a bug][11]!
+
+[1]: https://chromium.googlesource.com/infra/luci/recipes-py/
+[2]: https://chromium.googlesource.com/chromium/tools/depot_tools/
+[3]: https://chromium.googlesource.com/chromium/tools/build/
+[4]: /tools/mb/mb_config.pyl
+[5]: /testing/buildbot/
+[6]: https://chromium.googlesource.com/chromium/tools/build/+/HEAD/scripts/slave/recipe_modules/chromium_tests/trybots.py
+[7]: https://ci.chromium.org/p/chromium/builders/ci/linux-rel
+[8]: https://chromium.googlesource.com/chromium/tools/build/+/master/scripts/slave/recipe_modules/chromium/api.py
+[9]: https://chromium.googlesource.com/chromium/tools/build/+/master/scripts/slave/recipe_modules/chromium_tests/api.py
+[10]: https://chromium.googlesource.com/chromium/tools/build/+/master/scripts/slave/recipes.py
+[11]: https://g.co/bugatrooper
diff --git a/extensions/browser/api/declarative_net_request/composite_matcher_unittest.cc b/extensions/browser/api/declarative_net_request/composite_matcher_unittest.cc
index 1806322..5043ac09 100644
--- a/extensions/browser/api/declarative_net_request/composite_matcher_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/composite_matcher_unittest.cc
@@ -14,6 +14,7 @@
 #include "extensions/browser/api/declarative_net_request/ruleset_matcher.h"
 #include "extensions/browser/api/declarative_net_request/ruleset_source.h"
 #include "extensions/browser/api/declarative_net_request/test_utils.h"
+#include "extensions/common/api/declarative_net_request.h"
 #include "extensions/common/api/declarative_net_request/constants.h"
 #include "extensions/common/api/declarative_net_request/test_utils.h"
 #include "extensions/common/features/feature_channel.h"
@@ -29,6 +30,8 @@
 using PageAccess = PermissionsData::PageAccess;
 using RedirectActionInfo = CompositeMatcher::RedirectActionInfo;
 
+namespace dnr_api = api::declarative_net_request;
+
 class CompositeMatcherTest : public ::testing::Test {
  public:
   CompositeMatcherTest() : channel_(::version_info::Channel::UNKNOWN) {}
@@ -157,7 +160,9 @@
   std::unique_ptr<RulesetMatcher> matcher_1;
   ASSERT_TRUE(CreateVerifiedMatcher(
       {allow_rule_1, remove_headers_rule_1},
-      CreateTemporarySource(kSource1ID, kSource1Priority), &matcher_1));
+      CreateTemporarySource(kSource1ID, kSource1Priority,
+                            dnr_api::SOURCE_TYPE_MANIFEST),
+      &matcher_1));
 
   // Now set up rules and the second matcher.
   TestRule allow_rule_2 = allow_rule_1;
@@ -176,9 +181,11 @@
   const size_t kSource2ID = 2;
   const size_t kSource2Priority = 2;
   std::unique_ptr<RulesetMatcher> matcher_2;
-  ASSERT_TRUE(CreateVerifiedMatcher(
-      {allow_rule_2, redirect_rule_2},
-      CreateTemporarySource(kSource2ID, kSource2Priority), &matcher_2));
+  ASSERT_TRUE(
+      CreateVerifiedMatcher({allow_rule_2, redirect_rule_2},
+                            CreateTemporarySource(kSource2ID, kSource2Priority,
+                                                  dnr_api::SOURCE_TYPE_DYNAMIC),
+                            &matcher_2));
 
   // Create a composite matcher with the two rulesets.
   std::vector<std::unique_ptr<RulesetMatcher>> matchers;
@@ -223,12 +230,16 @@
   matcher_1.reset();
   matcher_2.reset();
   matchers.clear();
-  ASSERT_TRUE(CreateVerifiedMatcher(
-      {allow_rule_1, remove_headers_rule_1},
-      CreateTemporarySource(kSource1ID, kSource2Priority), &matcher_1));
+  ASSERT_TRUE(
+      CreateVerifiedMatcher({allow_rule_1, remove_headers_rule_1},
+                            CreateTemporarySource(kSource1ID, kSource2Priority,
+                                                  dnr_api::SOURCE_TYPE_DYNAMIC),
+                            &matcher_1));
   ASSERT_TRUE(CreateVerifiedMatcher(
       {allow_rule_2, redirect_rule_2},
-      CreateTemporarySource(kSource2ID, kSource1Priority), &matcher_2));
+      CreateTemporarySource(kSource2ID, kSource1Priority,
+                            dnr_api::SOURCE_TYPE_MANIFEST),
+      &matcher_2));
   matchers.push_back(std::move(matcher_1));
   matchers.push_back(std::move(matcher_2));
   composite_matcher = std::make_unique<CompositeMatcher>(std::move(matchers));
@@ -252,8 +263,9 @@
                                example_params, 0u, &remove_header_actions));
   ASSERT_EQ(1u, remove_header_actions.size());
 
-  RequestAction expected_action =
-      CreateRequestActionForTesting(RequestAction::Type::REMOVE_HEADERS);
+  RequestAction expected_action = CreateRequestActionForTesting(
+      RequestAction::Type::REMOVE_HEADERS, *remove_headers_rule_1.id,
+      dnr_api::SOURCE_TYPE_DYNAMIC);
   expected_action.request_headers_to_remove.push_back(
       net::HttpRequestHeaders::kReferer);
   expected_action.response_headers_to_remove.push_back("set-cookie");
@@ -294,16 +306,20 @@
   std::unique_ptr<RulesetMatcher> matcher_1;
   ASSERT_TRUE(CreateVerifiedMatcher(
       {static_rule_1, static_rule_2},
-      CreateTemporarySource(kSource1ID, kSource1Priority), &matcher_1));
+      CreateTemporarySource(kSource1ID, kSource1Priority,
+                            dnr_api::SOURCE_TYPE_MANIFEST),
+      &matcher_1));
 
   // Create a second ruleset matcher, which matches all requests from
   // |google.com|.
   const size_t kSource2ID = 2;
   const size_t kSource2Priority = 2;
   std::unique_ptr<RulesetMatcher> matcher_2;
-  ASSERT_TRUE(CreateVerifiedMatcher(
-      {dynamic_rule_1, dynamic_rule_2},
-      CreateTemporarySource(kSource2ID, kSource2Priority), &matcher_2));
+  ASSERT_TRUE(
+      CreateVerifiedMatcher({dynamic_rule_1, dynamic_rule_2},
+                            CreateTemporarySource(kSource2ID, kSource2Priority,
+                                                  dnr_api::SOURCE_TYPE_DYNAMIC),
+                            &matcher_2));
 
   // Create a composite matcher with the two rulesets.
   std::vector<std::unique_ptr<RulesetMatcher>> matchers;
@@ -329,18 +345,21 @@
   // Construct expected request actions to be taken for a request to google.com.
   // Static actions are attributed to |matcher_1| and dynamic actions are
   // attributed to |matcher_2|.
-  RequestAction static_action_1 =
-      CreateRequestActionForTesting(RequestAction::Type::REMOVE_HEADERS);
+  RequestAction static_action_1 = CreateRequestActionForTesting(
+      RequestAction::Type::REMOVE_HEADERS, *static_rule_1.id,
+      dnr_api::SOURCE_TYPE_MANIFEST);
   static_action_1.request_headers_to_remove.push_back(
       net::HttpRequestHeaders::kCookie);
 
-  RequestAction dynamic_action_1 =
-      CreateRequestActionForTesting(RequestAction::Type::REMOVE_HEADERS);
+  RequestAction dynamic_action_1 = CreateRequestActionForTesting(
+      RequestAction::Type::REMOVE_HEADERS, *dynamic_rule_1.id,
+      dnr_api::SOURCE_TYPE_DYNAMIC);
   dynamic_action_1.request_headers_to_remove.push_back(
       net::HttpRequestHeaders::kReferer);
 
-  RequestAction dynamic_action_2 =
-      CreateRequestActionForTesting(RequestAction::Type::REMOVE_HEADERS);
+  RequestAction dynamic_action_2 = CreateRequestActionForTesting(
+      RequestAction::Type::REMOVE_HEADERS, *dynamic_rule_2.id,
+      dnr_api::SOURCE_TYPE_DYNAMIC);
   dynamic_action_2.response_headers_to_remove.push_back("set-cookie");
 
   EXPECT_THAT(actions, ::testing::UnorderedElementsAre(
@@ -358,13 +377,17 @@
   EXPECT_EQ(expected_mask, composite_matcher->GetRemoveHeadersMask(
                                gmail_params, 0u, &actions));
 
+  static_action_1 = CreateRequestActionForTesting(
+      RequestAction::Type::REMOVE_HEADERS, *static_rule_1.id,
+      dnr_api::SOURCE_TYPE_MANIFEST);
   static_action_1.request_headers_to_remove.push_back(
       net::HttpRequestHeaders::kCookie);
   static_action_1.request_headers_to_remove.push_back(
       net::HttpRequestHeaders::kReferer);
 
-  RequestAction static_action_2 =
-      CreateRequestActionForTesting(RequestAction::Type::REMOVE_HEADERS);
+  RequestAction static_action_2 = CreateRequestActionForTesting(
+      RequestAction::Type::REMOVE_HEADERS, *static_rule_2.id,
+      dnr_api::SOURCE_TYPE_MANIFEST);
   static_action_2.response_headers_to_remove.push_back("set-cookie");
 
   EXPECT_THAT(actions, ::testing::UnorderedElementsAre(
diff --git a/extensions/browser/api/declarative_net_request/constants.h b/extensions/browser/api/declarative_net_request/constants.h
index 477829e..9528258 100644
--- a/extensions/browser/api/declarative_net_request/constants.h
+++ b/extensions/browser/api/declarative_net_request/constants.h
@@ -5,6 +5,7 @@
 #ifndef EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_CONSTANTS_H_
 #define EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_CONSTANTS_H_
 
+#include <cstddef>
 #include <cstdint>
 
 #include "extensions/common/api/declarative_net_request/constants.h"
diff --git a/extensions/browser/api/declarative_net_request/file_sequence_helper.cc b/extensions/browser/api/declarative_net_request/file_sequence_helper.cc
index 705f376..6611980 100644
--- a/extensions/browser/api/declarative_net_request/file_sequence_helper.cc
+++ b/extensions/browser/api/declarative_net_request/file_sequence_helper.cc
@@ -245,9 +245,9 @@
   // Initially write the new JSON and indexed rulesets to temporary files to
   // ensure we don't leave the actual files in an inconsistent state.
   std::unique_ptr<RulesetSource> temporary_source =
-      RulesetSource::CreateTemporarySource(source.id(), source.priority(),
-                                           source.rule_count_limit(),
-                                           source.extension_id());
+      RulesetSource::CreateTemporarySource(
+          source.id(), source.priority(), source.type(),
+          source.rule_count_limit(), source.extension_id());
   if (!temporary_source) {
     *error = kInternalErrorUpdatingDynamicRules;
     *status = UpdateDynamicRulesStatus::kErrorCreateTemporarySource;
diff --git a/extensions/browser/api/declarative_net_request/request_action.cc b/extensions/browser/api/declarative_net_request/request_action.cc
index af7c61e..254d796 100644
--- a/extensions/browser/api/declarative_net_request/request_action.cc
+++ b/extensions/browser/api/declarative_net_request/request_action.cc
@@ -7,9 +7,15 @@
 namespace extensions {
 namespace declarative_net_request {
 
-RequestAction::RequestAction(RequestAction::Type type,
-                             const ExtensionId& extension_id)
-    : type(type), extension_id(extension_id) {}
+RequestAction::RequestAction(
+    RequestAction::Type type,
+    int rule_id,
+    api::declarative_net_request::SourceType source_type,
+    const ExtensionId& extension_id)
+    : type(type),
+      rule_id(rule_id),
+      source_type(source_type),
+      extension_id(extension_id) {}
 RequestAction::~RequestAction() = default;
 RequestAction::RequestAction(RequestAction&&) = default;
 RequestAction& RequestAction::operator=(RequestAction&&) = default;
diff --git a/extensions/browser/api/declarative_net_request/request_action.h b/extensions/browser/api/declarative_net_request/request_action.h
index 4aad9cea..8afdc88 100644
--- a/extensions/browser/api/declarative_net_request/request_action.h
+++ b/extensions/browser/api/declarative_net_request/request_action.h
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "base/optional.h"
+#include "extensions/common/api/declarative_net_request.h"
 #include "extensions/common/extension_id.h"
 #include "url/gurl.h"
 
@@ -30,7 +31,10 @@
     REMOVE_HEADERS,
   };
 
-  RequestAction(Type type, const ExtensionId& extension_id);
+  RequestAction(Type type,
+                int rule_id,
+                api::declarative_net_request::SourceType source_type,
+                const ExtensionId& extension_id);
   ~RequestAction();
   RequestAction(RequestAction&&);
   RequestAction& operator=(RequestAction&&);
@@ -40,6 +44,12 @@
   // Valid iff |type| is |REDIRECT|.
   base::Optional<GURL> redirect_url;
 
+  // The ID of the matching rule for this action.
+  int rule_id;
+
+  // The source type of the matching rule for this action.
+  api::declarative_net_request::SourceType source_type;
+
   // The id of the extension the action is attributed to.
   ExtensionId extension_id;
 
diff --git a/extensions/browser/api/declarative_net_request/ruleset_matcher.cc b/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
index fa193b0..318648e 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
+++ b/extensions/browser/api/declarative_net_request/ruleset_matcher.cc
@@ -20,7 +20,7 @@
 #include "extensions/browser/api/declarative_net_request/ruleset_source.h"
 #include "extensions/browser/api/declarative_net_request/utils.h"
 #include "extensions/browser/api/web_request/web_request_info.h"
-#include "extensions/common/api/declarative_net_request.h"
+#include "extensions/common/api/declarative_net_request/constants.h"
 #include "extensions/common/api/declarative_net_request/utils.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/url_util.h"
@@ -318,8 +318,11 @@
 
 // Populates the list of headers corresponding to |mask|.
 RequestAction GetRemoveHeadersActionForMask(const ExtensionId& extension_id,
-                                            uint8_t mask) {
-  RequestAction action(RequestAction::Type::REMOVE_HEADERS, extension_id);
+                                            uint8_t mask,
+                                            int rule_id,
+                                            dnr_api::SourceType source_type) {
+  RequestAction action(RequestAction::Type::REMOVE_HEADERS, rule_id,
+                       source_type, extension_id);
 
   for (int i = 0; mask && i <= dnr_api::REMOVE_HEADER_TYPE_LAST; ++i) {
     switch (i) {
@@ -391,9 +394,9 @@
 
   // Using WrapUnique instead of make_unique since this class has a private
   // constructor.
-  *matcher = base::WrapUnique(new RulesetMatcher(std::move(ruleset_data),
-                                                 source.id(), source.priority(),
-                                                 source.extension_id()));
+  *matcher = base::WrapUnique(new RulesetMatcher(
+      std::move(ruleset_data), source.id(), source.priority(), source.type(),
+      source.extension_id()));
   return kLoadSuccess;
 }
 
@@ -407,8 +410,10 @@
     return base::nullopt;
 
   return ShouldCollapseResourceType(params.element_type)
-             ? RequestAction(RequestAction::Type::COLLAPSE, extension_id_)
-             : RequestAction(RequestAction::Type::BLOCK, extension_id_);
+             ? RequestAction(RequestAction::Type::COLLAPSE, rule->id(),
+                             source_type_, extension_id_)
+             : RequestAction(RequestAction::Type::BLOCK, rule->id(),
+                             source_type_, extension_id_);
 }
 
 base::Optional<RequestAction> RulesetMatcher::GetRedirectAction(
@@ -420,7 +425,9 @@
   if (!redirect_rule)
     return base::nullopt;
 
-  RequestAction redirect_action(RequestAction::Type::REDIRECT, extension_id_);
+  RequestAction redirect_action(RequestAction::Type::REDIRECT,
+                                redirect_rule->id(), source_type_,
+                                extension_id_);
   redirect_action.redirect_url = std::move(redirect_rule_url);
 
   return redirect_action;
@@ -433,7 +440,8 @@
   if (!upgrade_rule)
     return base::nullopt;
 
-  RequestAction upgrade_action(RequestAction::Type::REDIRECT, extension_id_);
+  RequestAction upgrade_action(RequestAction::Type::REDIRECT,
+                               upgrade_rule->id(), source_type_, extension_id_);
   upgrade_action.redirect_url = GetUpgradedUrl(*params.url);
 
   return upgrade_action;
@@ -443,6 +451,7 @@
 RulesetMatcher::GetRedirectOrUpgradeActionByPriority(
     const RequestParams& params) const {
   GURL redirect_rule_url;
+  int rule_id = kMinValidID - 1;
   const flat_rule::UrlRule* redirect_rule =
       GetRedirectRule(params, &redirect_rule_url);
   const flat_rule::UrlRule* upgrade_rule = GetUpgradeRule(params);
@@ -453,15 +462,25 @@
   GURL highest_priority_url;
   if (!upgrade_rule) {
     highest_priority_url = std::move(redirect_rule_url);
+    rule_id = redirect_rule->id();
   } else if (!redirect_rule) {
+    rule_id = upgrade_rule->id();
     highest_priority_url = GetUpgradedUrl(*params.url);
   } else {
     highest_priority_url = upgrade_rule->priority() > redirect_rule->priority()
                                ? GetUpgradedUrl(*params.url)
                                : std::move(redirect_rule_url);
+
+    rule_id = upgrade_rule->priority() > redirect_rule->priority()
+                  ? upgrade_rule->id()
+                  : redirect_rule->id();
   }
 
-  RequestAction action(RequestAction::Type::REDIRECT, extension_id_);
+  // Check that the resultant |rule_id| is valid.
+  DCHECK_GE(rule_id, kMinValidID);
+
+  RequestAction action(RequestAction::Type::REDIRECT, rule_id, source_type_,
+                       extension_id_);
   action.redirect_url = std::move(highest_priority_url);
 
   return action;
@@ -520,8 +539,9 @@
   for (auto it = rule_id_to_mask.begin(); it != rule_id_to_mask.end(); ++it) {
     uint8_t mask_for_rule = it->second;
     DCHECK(mask_for_rule);
-    remove_headers_actions->push_back(
-        GetRemoveHeadersActionForMask(extension_id_, mask_for_rule));
+
+    remove_headers_actions->push_back(GetRemoveHeadersActionForMask(
+        extension_id_, mask_for_rule, it->first /* rule_id */, source_type_));
   }
 
   DCHECK(!(mask & ignored_mask));
@@ -531,6 +551,7 @@
 RulesetMatcher::RulesetMatcher(std::string ruleset_data,
                                size_t id,
                                size_t priority,
+                               dnr_api::SourceType source_type,
                                const ExtensionId& extension_id)
     : ruleset_data_(std::move(ruleset_data)),
       root_(flat::GetExtensionIndexedRuleset(ruleset_data_.data())),
@@ -538,6 +559,7 @@
       metadata_list_(root_->extension_metadata()),
       id_(id),
       priority_(priority),
+      source_type_(source_type),
       extension_id_(extension_id),
       is_extra_headers_matcher_(IsExtraHeadersMatcherInternal(*root_)) {}
 
diff --git a/extensions/browser/api/declarative_net_request/ruleset_matcher.h b/extensions/browser/api/declarative_net_request/ruleset_matcher.h
index 886273f..ed2cba0 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_matcher.h
+++ b/extensions/browser/api/declarative_net_request/ruleset_matcher.h
@@ -14,6 +14,7 @@
 #include "base/optional.h"
 #include "components/url_pattern_index/url_pattern_index.h"
 #include "extensions/browser/api/declarative_net_request/flat/extension_ruleset_generated.h"
+#include "extensions/common/api/declarative_net_request.h"
 #include "extensions/common/extension_id.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -150,6 +151,7 @@
   explicit RulesetMatcher(std::string ruleset_data,
                           size_t id,
                           size_t priority,
+                          api::declarative_net_request::SourceType source_type,
                           const ExtensionId& extension_id);
 
   // Returns the ruleset's matching redirect rule and populates
@@ -182,6 +184,8 @@
   const size_t id_;
   const size_t priority_;
 
+  const api::declarative_net_request::SourceType source_type_;
+
   // The ID of the extension from which this matcher's ruleset originates from.
   const ExtensionId extension_id_;
 
diff --git a/extensions/browser/api/declarative_net_request/ruleset_matcher_unittest.cc b/extensions/browser/api/declarative_net_request/ruleset_matcher_unittest.cc
index 883d19f..c4d84e4 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_matcher_unittest.cc
+++ b/extensions/browser/api/declarative_net_request/ruleset_matcher_unittest.cc
@@ -18,6 +18,7 @@
 #include "extensions/browser/api/declarative_net_request/ruleset_source.h"
 #include "extensions/browser/api/declarative_net_request/test_utils.h"
 #include "extensions/browser/api/declarative_net_request/utils.h"
+#include "extensions/common/api/declarative_net_request.h"
 #include "extensions/common/api/declarative_net_request/constants.h"
 #include "extensions/common/api/declarative_net_request/test_utils.h"
 #include "extensions/common/features/feature_channel.h"
@@ -276,13 +277,13 @@
   params.element_type = url_pattern_index::flat::ElementType_SUBDOCUMENT;
   params.is_third_party = true;
 
-  RequestAction rule_1_action =
-      CreateRequestActionForTesting(RequestAction::Type::REMOVE_HEADERS);
+  RequestAction rule_1_action = CreateRequestActionForTesting(
+      RequestAction::Type::REMOVE_HEADERS, *rule_1.id);
   rule_1_action.request_headers_to_remove.push_back(
       net::HttpRequestHeaders::kReferer);
 
-  RequestAction rule_2_action =
-      CreateRequestActionForTesting(RequestAction::Type::REMOVE_HEADERS);
+  RequestAction rule_2_action = CreateRequestActionForTesting(
+      RequestAction::Type::REMOVE_HEADERS, *rule_2.id);
   rule_2_action.request_headers_to_remove.push_back(
       net::HttpRequestHeaders::kCookie);
 
@@ -311,7 +312,10 @@
   const size_t kPriority = 1;
   const size_t kRuleCountLimit = 10;
   ASSERT_TRUE(CreateVerifiedMatcher(
-      {rule}, CreateTemporarySource(kId, kPriority, kRuleCountLimit),
+      {rule},
+      CreateTemporarySource(kId, kPriority,
+                            api::declarative_net_request::SOURCE_TYPE_MANIFEST,
+                            kRuleCountLimit),
       &matcher));
 
   GURL example_url("http://example.com");
diff --git a/extensions/browser/api/declarative_net_request/ruleset_source.cc b/extensions/browser/api/declarative_net_request/ruleset_source.cc
index a9db83fa..ab84fa8 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_source.cc
+++ b/extensions/browser/api/declarative_net_request/ruleset_source.cc
@@ -240,7 +240,8 @@
   return RulesetSource(
       declarative_net_request::DNRManifestData::GetRulesetPath(extension),
       file_util::GetIndexedRulesetPath(extension.path()), kStaticRulesetID,
-      kStaticRulesetPriority, dnr_api::MAX_NUMBER_OF_RULES, extension.id());
+      kStaticRulesetPriority, dnr_api::SOURCE_TYPE_MANIFEST,
+      dnr_api::MAX_NUMBER_OF_RULES, extension.id());
 }
 
 // static
@@ -253,7 +254,7 @@
   return RulesetSource(
       dynamic_ruleset_directory.AppendASCII(kDynamicRulesJSONFilename),
       dynamic_ruleset_directory.AppendASCII(kDynamicIndexedRulesFilename),
-      kDynamicRulesetID, kDynamicRulesetPriority,
+      kDynamicRulesetID, kDynamicRulesetPriority, dnr_api::SOURCE_TYPE_DYNAMIC,
       dnr_api::MAX_NUMBER_OF_DYNAMIC_RULES, extension.id());
 }
 
@@ -261,6 +262,7 @@
 std::unique_ptr<RulesetSource> RulesetSource::CreateTemporarySource(
     size_t id,
     size_t priority,
+    dnr_api::SourceType type,
     size_t rule_count_limit,
     ExtensionId extension_id) {
   base::FilePath temporary_file_indexed;
@@ -272,19 +274,21 @@
 
   return std::make_unique<RulesetSource>(
       std::move(temporary_file_json), std::move(temporary_file_indexed), id,
-      priority, rule_count_limit, std::move(extension_id));
+      priority, type, rule_count_limit, std::move(extension_id));
 }
 
 RulesetSource::RulesetSource(base::FilePath json_path,
                              base::FilePath indexed_path,
                              size_t id,
                              size_t priority,
+                             dnr_api::SourceType type,
                              size_t rule_count_limit,
                              ExtensionId extension_id)
     : json_path_(std::move(json_path)),
       indexed_path_(std::move(indexed_path)),
       id_(id),
       priority_(priority),
+      type_(type),
       rule_count_limit_(rule_count_limit),
       extension_id_(std::move(extension_id)) {}
 
@@ -293,7 +297,7 @@
 RulesetSource& RulesetSource::operator=(RulesetSource&&) = default;
 
 RulesetSource RulesetSource::Clone() const {
-  return RulesetSource(json_path_, indexed_path_, id_, priority_,
+  return RulesetSource(json_path_, indexed_path_, id_, priority_, type_,
                        rule_count_limit_, extension_id_);
 }
 
diff --git a/extensions/browser/api/declarative_net_request/ruleset_source.h b/extensions/browser/api/declarative_net_request/ruleset_source.h
index f33bfcf..b2277fd 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_source.h
+++ b/extensions/browser/api/declarative_net_request/ruleset_source.h
@@ -12,6 +12,7 @@
 #include "base/callback_forward.h"
 #include "base/files/file_path.h"
 #include "base/time/time.h"
+#include "extensions/common/api/declarative_net_request.h"
 #include "extensions/common/extension_id.h"
 
 namespace base {
@@ -137,6 +138,7 @@
   static std::unique_ptr<RulesetSource> CreateTemporarySource(
       size_t id,
       size_t priority,
+      api::declarative_net_request::SourceType type,
       size_t rule_count_limit,
       ExtensionId extension_id);
 
@@ -144,6 +146,7 @@
                 base::FilePath indexed_path,
                 size_t id,
                 size_t priority,
+                api::declarative_net_request::SourceType type,
                 size_t rule_count_limit,
                 ExtensionId extension_id);
   ~RulesetSource();
@@ -162,6 +165,9 @@
   size_t id() const { return id_; }
   size_t priority() const { return priority_; }
 
+  // The origin type for this ruleset. Can be from the manifest or dynamic.
+  api::declarative_net_request::SourceType type() const { return type_; }
+
   // The maximum number of rules that will be indexed from this source.
   size_t rule_count_limit() const { return rule_count_limit_; }
 
@@ -207,6 +213,7 @@
   base::FilePath indexed_path_;
   size_t id_;
   size_t priority_;
+  api::declarative_net_request::SourceType type_;
   size_t rule_count_limit_;
   ExtensionId extension_id_;
 
diff --git a/extensions/browser/api/declarative_net_request/test_utils.cc b/extensions/browser/api/declarative_net_request/test_utils.cc
index 0a18c9e..313d3b9 100644
--- a/extensions/browser/api/declarative_net_request/test_utils.cc
+++ b/extensions/browser/api/declarative_net_request/test_utils.cc
@@ -21,9 +21,13 @@
 namespace extensions {
 namespace declarative_net_request {
 
+namespace dnr_api = api::declarative_net_request;
+
 RequestAction CreateRequestActionForTesting(RequestAction::Type type,
+                                            int rule_id,
+                                            dnr_api::SourceType source_type,
                                             const ExtensionId& extension_id) {
-  return RequestAction(type, extension_id);
+  return RequestAction(type, rule_id, source_type, extension_id);
 }
 
 // Note: This is not declared in the anonymous namespace so that we can use it
@@ -40,8 +44,11 @@
            std::set<base::StringPiece>(b.begin(), b.end());
   };
 
+  bool are_matched_rules_equal =
+      lhs.rule_id == rhs.rule_id && lhs.source_type == rhs.source_type;
+
   return lhs.type == rhs.type && lhs.redirect_url == rhs.redirect_url &&
-         lhs.extension_id == rhs.extension_id &&
+         lhs.extension_id == rhs.extension_id && are_matched_rules_equal &&
          are_vectors_equal(lhs.request_headers_to_remove,
                            rhs.request_headers_to_remove) &&
          are_vectors_equal(lhs.response_headers_to_remove,
@@ -92,10 +99,11 @@
 
 RulesetSource CreateTemporarySource(size_t id,
                                     size_t priority,
+                                    dnr_api::SourceType source_type,
                                     size_t rule_count_limit,
                                     ExtensionId extension_id) {
   std::unique_ptr<RulesetSource> source = RulesetSource::CreateTemporarySource(
-      id, priority, rule_count_limit, std::move(extension_id));
+      id, priority, source_type, rule_count_limit, std::move(extension_id));
   CHECK(source);
   return source->Clone();
 }
diff --git a/extensions/browser/api/declarative_net_request/test_utils.h b/extensions/browser/api/declarative_net_request/test_utils.h
index 31d7d83f1..370cb0622 100644
--- a/extensions/browser/api/declarative_net_request/test_utils.h
+++ b/extensions/browser/api/declarative_net_request/test_utils.h
@@ -9,6 +9,8 @@
 #include <vector>
 
 #include "extensions/browser/api/declarative_net_request/request_action.h"
+#include "extensions/common/api/declarative_net_request.h"
+#include "extensions/common/api/declarative_net_request/constants.h"
 #include "extensions/common/extension_id.h"
 
 namespace content {
@@ -35,6 +37,9 @@
 // optionally, an ExtensionId.
 RequestAction CreateRequestActionForTesting(
     RequestAction::Type type,
+    int rule_id = kMinValidID,
+    api::declarative_net_request::SourceType source_type =
+        api::declarative_net_request::SOURCE_TYPE_MANIFEST,
     const ExtensionId& extension_id = "extensionid");
 
 bool operator==(const RequestAction& lhs, const RequestAction& rhs);
@@ -52,10 +57,13 @@
                            int* expected_checksum = nullptr);
 
 // Helper to return a RulesetSource bound to temporary files.
-RulesetSource CreateTemporarySource(size_t id = 1,
-                                    size_t priority = 1,
-                                    size_t rule_count_limit = 100,
-                                    ExtensionId extension_id = "extensionid");
+RulesetSource CreateTemporarySource(
+    size_t id = 1,
+    size_t priority = 1,
+    api::declarative_net_request::SourceType source_type =
+        api::declarative_net_request::SOURCE_TYPE_MANIFEST,
+    size_t rule_count_limit = 100,
+    ExtensionId extension_id = "extensionid");
 
 }  // namespace declarative_net_request
 }  // namespace extensions
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
index ab68297..02d4582 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.cc
@@ -284,15 +284,6 @@
       url, allowed_by_default, std::move(callback));
 }
 
-void WebViewPermissionHelper::FileSystemAccessedAsync(int render_process_id,
-                                                      int render_frame_id,
-                                                      int request_id,
-                                                      const GURL& url,
-                                                      bool blocked_by_policy) {
-  web_view_permission_helper_delegate_->FileSystemAccessedAsync(
-      render_process_id, render_frame_id, request_id, url, blocked_by_policy);
-}
-
 int WebViewPermissionHelper::RequestPermission(
     WebViewPermissionType permission_type,
     const base::DictionaryValue& request_info,
diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper.h b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
index 33cb12ef..bb3af37 100644
--- a/extensions/browser/guest_view/web_view/web_view_permission_helper.h
+++ b/extensions/browser/guest_view/web_view/web_view_permission_helper.h
@@ -84,22 +84,6 @@
                                    bool allowed_by_default,
                                    base::OnceCallback<void(bool)> callback);
 
-  // Called when file system access is requested by the guest content using the
-  // asynchronous HTML5 file system API. The request is plumbed through the
-  // <webview> permission request API. The request will be:
-  // - Allowed if the embedder explicitly allowed it.
-  // - Denied if the embedder explicitly denied.
-  // - Determined by the guest's content settings if the embedder does not
-  // perform an explicit action.
-  // If access was blocked due to the page's content settings,
-  // |blocked_by_policy| should be true, and this function should invoke
-  // OnContentBlocked.
-  void FileSystemAccessedAsync(int render_process_id,
-                               int render_frame_id,
-                               int request_id,
-                               const GURL& url,
-                               bool blocked_by_policy);
-
   enum PermissionResponseAction { DENY, ALLOW, DEFAULT };
 
   enum SetPermissionResult {
diff --git a/extensions/common/api/networking_onc.idl b/extensions/common/api/networking_onc.idl
index 08da976d..f691e96 100644
--- a/extensions/common/api/networking_onc.idl
+++ b/extensions/common/api/networking_onc.idl
@@ -5,7 +5,7 @@
 // <p>
 //   The <code>chrome.networking.onc</code> API is used for configuring
 //   network connections (Cellular, Ethernet, VPN or WiFi).
-//   This API is available in Chrome OS kiosk sessions.
+//   This API is available in auto-launched Chrome OS kiosk sessions.
 // </p>
 // <p>
 //   Network connection configurations are specified following
diff --git a/extensions/renderer/extension_throttle_manager.cc b/extensions/renderer/extension_throttle_manager.cc
index 854ffd88..243c189 100644
--- a/extensions/renderer/extension_throttle_manager.cc
+++ b/extensions/renderer/extension_throttle_manager.cc
@@ -13,7 +13,7 @@
 #include "extensions/common/constants.h"
 #include "extensions/renderer/extension_url_loader_throttle.h"
 #include "net/base/url_util.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 
@@ -108,7 +108,7 @@
 
 void ExtensionThrottleManager::WillProcessResponse(
     const GURL& response_url,
-    const network::ResourceResponseHead& response_head) {
+    const network::mojom::URLResponseHead& response_head) {
   if (response_head.network_accessed) {
     // An entry GC when requests are outstanding can purge entries so check
     // before use.
diff --git a/extensions/renderer/extension_throttle_manager.h b/extensions/renderer/extension_throttle_manager.h
index f22c99cc..e6694f1 100644
--- a/extensions/renderer/extension_throttle_manager.h
+++ b/extensions/renderer/extension_throttle_manager.h
@@ -13,6 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
 #include "extensions/renderer/extension_throttle_entry.h"
+#include "services/network/public/mojom/url_response_head.mojom-forward.h"
 #include "url/gurl.h"
 
 namespace blink {
@@ -24,10 +25,6 @@
 struct RedirectInfo;
 }  // namespace net
 
-namespace network {
-struct ResourceResponseHead;
-}  // namespace network
-
 namespace extensions {
 
 // This is a thread safe class that registers URL request throttler entries for
@@ -58,8 +55,9 @@
                             const net::RedirectInfo& redirect_info);
 
   // Must be called when the |response_head| for a request has been received.
-  void WillProcessResponse(const GURL& response_url,
-                           const network::ResourceResponseHead& response_head);
+  void WillProcessResponse(
+      const GURL& response_url,
+      const network::mojom::URLResponseHead& response_head);
 
   // Set the network status online state as specified in |is_online|.
   void SetOnline(bool is_online);
diff --git a/extensions/renderer/extension_throttle_unittest.cc b/extensions/renderer/extension_throttle_unittest.cc
index a9bbe32..b4eea09 100644
--- a/extensions/renderer/extension_throttle_unittest.cc
+++ b/extensions/renderer/extension_throttle_unittest.cc
@@ -12,7 +12,7 @@
 #include "extensions/renderer/extension_throttle_manager.h"
 #include "extensions/renderer/extension_throttle_test_support.h"
 #include "net/url_request/redirect_info.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::TimeDelta;
@@ -396,8 +396,8 @@
   EXPECT_FALSE(manager.ShouldRejectRedirect(test_url, redirect_info));
   manager.SetOnline(/*is_online=*/false);
   manager.SetOnline(/*is_online=*/true);
-  network::ResourceResponseHead response_head;
-  manager.WillProcessResponse(redirect_info.new_url, response_head);
+  auto response_head = network::mojom::URLResponseHead::New();
+  manager.WillProcessResponse(redirect_info.new_url, *response_head);
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/extension_url_loader_throttle.cc b/extensions/renderer/extension_url_loader_throttle.cc
index 5059e2c..bf05715 100644
--- a/extensions/renderer/extension_url_loader_throttle.cc
+++ b/extensions/renderer/extension_url_loader_throttle.cc
@@ -32,7 +32,7 @@
 
 void ExtensionURLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& /* response_head */,
+    const network::mojom::URLResponseHead& /* response_head */,
     bool* /* defer */,
     std::vector<std::string>* /* to_be_removed_request_headers */,
     net::HttpRequestHeaders* /* modified_request_headers */) {
@@ -43,7 +43,7 @@
 
 void ExtensionURLLoaderThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {
   manager_->WillProcessResponse(response_url, *response_head);
 }
diff --git a/extensions/renderer/extension_url_loader_throttle.h b/extensions/renderer/extension_url_loader_throttle.h
index c12c836..88a8b6b 100644
--- a/extensions/renderer/extension_url_loader_throttle.h
+++ b/extensions/renderer/extension_url_loader_throttle.h
@@ -30,12 +30,12 @@
                         bool* defer) override;
   void WillRedirectRequest(
       net::RedirectInfo* redirect_info,
-      const network::ResourceResponseHead& response_head,
+      const network::mojom::URLResponseHead& response_head,
       bool* defer,
       std::vector<std::string>* to_be_removed_request_headers,
       net::HttpRequestHeaders* modified_request_headers) override;
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override;
 
  private:
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
index de7fb10..79b787f 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
@@ -17,7 +17,7 @@
 #include "extensions/common/guest_view/extensions_guest_view_messages.h"
 #include "extensions/renderer/extension_frame_helper.h"
 #include "ipc/ipc_sync_channel.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_request.h"
@@ -65,7 +65,7 @@
  private:
   // blink::URLLoaderThrottle overrides;
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override {
     if (!container_) {
       // In the embedder case if the plugin element is removed right after an
@@ -91,11 +91,14 @@
     transferrable_loader->url_loader = original_loader.PassInterface();
     transferrable_loader->url_loader_client = std::move(original_client);
 
-    // Make a deep copy of ResourceResponseHead before passing it cross-thread.
-    auto resource_response = base::MakeRefCounted<network::ResourceResponse>();
-    resource_response->head = *response_head;
-    auto deep_copied_response = resource_response->DeepCopy();
-    transferrable_loader->head = std::move(deep_copied_response->head);
+    // Make a deep copy of URLResponseHead before passing it cross-thread.
+    auto deep_copied_response = response_head->Clone();
+    if (response_head->headers) {
+      deep_copied_response->headers =
+          base::MakeRefCounted<net::HttpResponseHeaders>(
+              response_head->headers->raw_headers());
+    }
+    transferrable_loader->head = std::move(deep_copied_response);
     container_->SetEmbeddedLoader(std::move(transferrable_loader));
   }
 
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index c2f1a00..c508cee 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -290,7 +290,6 @@
     "command_buffer/service/shared_image_manager_unittest.cc",
     "command_buffer/tests/compressed_texture_test.cc",
     "command_buffer/tests/es3_misc_functions_unittest.cc",
-    "command_buffer/tests/gl_apply_screen_space_antialiasing_CHROMIUM_unittest.cc",
     "command_buffer/tests/gl_bgra_mipmap_unittest.cc",
     "command_buffer/tests/gl_bind_uniform_location_unittest.cc",
     "command_buffer/tests/gl_chromium_framebuffer_mixed_samples_unittest.cc",
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h
index f5c24e0..30c8d3e 100644
--- a/gpu/GLES2/gl2chromium_autogen.h
+++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -387,8 +387,6 @@
 #define glCoverageModulationCHROMIUM GLES2_GET_FUN(CoverageModulationCHROMIUM)
 #define glGetGraphicsResetStatusKHR GLES2_GET_FUN(GetGraphicsResetStatusKHR)
 #define glBlendBarrierKHR GLES2_GET_FUN(BlendBarrierKHR)
-#define glApplyScreenSpaceAntialiasingCHROMIUM \
-  GLES2_GET_FUN(ApplyScreenSpaceAntialiasingCHROMIUM)
 #define glBindFragDataLocationIndexedEXT \
   GLES2_GET_FUN(BindFragDataLocationIndexedEXT)
 #define glBindFragDataLocationEXT GLES2_GET_FUN(BindFragDataLocationEXT)
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index 14c5df3..7720bd67 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1765,13 +1765,6 @@
     'impl_func': False,
     'client_test': False,
   },
-  'ApplyScreenSpaceAntialiasingCHROMIUM': {
-    'decoder_func': 'DoApplyScreenSpaceAntialiasingCHROMIUM',
-    'extension': 'CHROMIUM_screen_space_antialiasing',
-    'extension_flag': 'chromium_screen_space_antialiasing',
-    'unit_test': False,
-    'client_test': False,
-  },
   'AttachShader': {'decoder_func': 'DoAttachShader'},
   'BindAttribLocation': {
     'type': 'GLchar',
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index 52e11e5..7d6d92fd 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -1823,9 +1823,6 @@
 void GL_APIENTRY GLES2BlendBarrierKHR() {
   gles2::GetGLContext()->BlendBarrierKHR();
 }
-void GL_APIENTRY GLES2ApplyScreenSpaceAntialiasingCHROMIUM() {
-  gles2::GetGLContext()->ApplyScreenSpaceAntialiasingCHROMIUM();
-}
 void GL_APIENTRY GLES2BindFragDataLocationIndexedEXT(GLuint program,
                                                      GLuint colorNumber,
                                                      GLuint index,
@@ -3354,11 +3351,6 @@
         reinterpret_cast<GLES2FunctionPointer>(glBlendBarrierKHR),
     },
     {
-        "glApplyScreenSpaceAntialiasingCHROMIUM",
-        reinterpret_cast<GLES2FunctionPointer>(
-            glApplyScreenSpaceAntialiasingCHROMIUM),
-    },
-    {
         "glBindFragDataLocationIndexedEXT",
         reinterpret_cast<GLES2FunctionPointer>(
             glBindFragDataLocationIndexedEXT),
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index a00f5bf1..9c231e56 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -3361,14 +3361,6 @@
   }
 }
 
-void ApplyScreenSpaceAntialiasingCHROMIUM() {
-  gles2::cmds::ApplyScreenSpaceAntialiasingCHROMIUM* c =
-      GetCmdSpace<gles2::cmds::ApplyScreenSpaceAntialiasingCHROMIUM>();
-  if (c) {
-    c->Init();
-  }
-}
-
 void BindFragDataLocationIndexedEXTBucket(GLuint program,
                                           GLuint colorNumber,
                                           GLuint index,
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index 9639c2d..13d8ada4 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -1286,8 +1286,6 @@
 
 void BlendBarrierKHR() override;
 
-void ApplyScreenSpaceAntialiasingCHROMIUM() override;
-
 void BindFragDataLocationIndexedEXT(GLuint program,
                                     GLuint colorNumber,
                                     GLuint index,
diff --git a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
index 11918e1..ea02c67 100644
--- a/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_impl_autogen.h
@@ -3680,15 +3680,6 @@
   CheckGLError();
 }
 
-void GLES2Implementation::ApplyScreenSpaceAntialiasingCHROMIUM() {
-  GPU_CLIENT_SINGLE_THREAD_CHECK();
-  GPU_CLIENT_LOG("[" << GetLogPrefix()
-                     << "] glApplyScreenSpaceAntialiasingCHROMIUM("
-                     << ")");
-  helper_->ApplyScreenSpaceAntialiasingCHROMIUM();
-  CheckGLError();
-}
-
 void GLES2Implementation::UniformMatrix4fvStreamTextureMatrixCHROMIUM(
     GLint location,
     GLboolean transpose,
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h
index 138c7a9..b312081 100644
--- a/gpu/command_buffer/client/gles2_interface_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -969,7 +969,6 @@
 virtual void CoverageModulationCHROMIUM(GLenum components) = 0;
 virtual GLenum GetGraphicsResetStatusKHR() = 0;
 virtual void BlendBarrierKHR() = 0;
-virtual void ApplyScreenSpaceAntialiasingCHROMIUM() = 0;
 virtual void BindFragDataLocationIndexedEXT(GLuint program,
                                             GLuint colorNumber,
                                             GLuint index,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
index a35b78ff..0b1f2f7 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -940,7 +940,6 @@
 void CoverageModulationCHROMIUM(GLenum components) override;
 GLenum GetGraphicsResetStatusKHR() override;
 void BlendBarrierKHR() override;
-void ApplyScreenSpaceAntialiasingCHROMIUM() override;
 void BindFragDataLocationIndexedEXT(GLuint program,
                                     GLuint colorNumber,
                                     GLuint index,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
index afb9efd..aade0ea 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -1243,7 +1243,6 @@
   return 0;
 }
 void GLES2InterfaceStub::BlendBarrierKHR() {}
-void GLES2InterfaceStub::ApplyScreenSpaceAntialiasingCHROMIUM() {}
 void GLES2InterfaceStub::BindFragDataLocationIndexedEXT(
     GLuint /* program */,
     GLuint /* colorNumber */,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
index 81b31c3c..7ba20ed 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -940,7 +940,6 @@
 void CoverageModulationCHROMIUM(GLenum components) override;
 GLenum GetGraphicsResetStatusKHR() override;
 void BlendBarrierKHR() override;
-void ApplyScreenSpaceAntialiasingCHROMIUM() override;
 void BindFragDataLocationIndexedEXT(GLuint program,
                                     GLuint colorNumber,
                                     GLuint index,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
index 56cd190..6fea3ff0 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -2609,12 +2609,6 @@
   gl_->BlendBarrierKHR();
 }
 
-void GLES2TraceImplementation::ApplyScreenSpaceAntialiasingCHROMIUM() {
-  TRACE_EVENT_BINARY_EFFICIENT0(
-      "gpu", "GLES2Trace::ApplyScreenSpaceAntialiasingCHROMIUM");
-  gl_->ApplyScreenSpaceAntialiasingCHROMIUM();
-}
-
 void GLES2TraceImplementation::BindFragDataLocationIndexedEXT(
     GLuint program,
     GLuint colorNumber,
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index d180e54b..671af4e 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -16607,34 +16607,6 @@
 static_assert(offsetof(BlendBarrierKHR, header) == 0,
               "offset of BlendBarrierKHR header should be 0");
 
-struct ApplyScreenSpaceAntialiasingCHROMIUM {
-  typedef ApplyScreenSpaceAntialiasingCHROMIUM ValueType;
-  static const CommandId kCmdId = kApplyScreenSpaceAntialiasingCHROMIUM;
-  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
-  static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
-
-  static uint32_t ComputeSize() {
-    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
-  }
-
-  void SetHeader() { header.SetCmd<ValueType>(); }
-
-  void Init() { SetHeader(); }
-
-  void* Set(void* cmd) {
-    static_cast<ValueType*>(cmd)->Init();
-    return NextCmdAddress<ValueType>(cmd);
-  }
-
-  gpu::CommandHeader header;
-};
-
-static_assert(sizeof(ApplyScreenSpaceAntialiasingCHROMIUM) == 4,
-              "size of ApplyScreenSpaceAntialiasingCHROMIUM should be 4");
-static_assert(
-    offsetof(ApplyScreenSpaceAntialiasingCHROMIUM, header) == 0,
-    "offset of ApplyScreenSpaceAntialiasingCHROMIUM header should be 0");
-
 struct BindFragDataLocationIndexedEXTBucket {
   typedef BindFragDataLocationIndexedEXTBucket ValueType;
   static const CommandId kCmdId = kBindFragDataLocationIndexedEXTBucket;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index 314e2a7bf..41b9db0 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -5456,17 +5456,6 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
-TEST_F(GLES2FormatTest, ApplyScreenSpaceAntialiasingCHROMIUM) {
-  cmds::ApplyScreenSpaceAntialiasingCHROMIUM& cmd =
-      *GetBufferAs<cmds::ApplyScreenSpaceAntialiasingCHROMIUM>();
-  void* next_cmd = cmd.Set(&cmd);
-  EXPECT_EQ(
-      static_cast<uint32_t>(cmds::ApplyScreenSpaceAntialiasingCHROMIUM::kCmdId),
-      cmd.header.command);
-  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
-  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
-}
-
 TEST_F(GLES2FormatTest, BindFragDataLocationIndexedEXTBucket) {
   cmds::BindFragDataLocationIndexedEXTBucket& cmd =
       *GetBufferAs<cmds::BindFragDataLocationIndexedEXTBucket>();
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index 456a345b..56b74f7 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -341,30 +341,29 @@
   OP(ContextVisibilityHintCHROMIUM)                        /* 582 */ \
   OP(CoverageModulationCHROMIUM)                           /* 583 */ \
   OP(BlendBarrierKHR)                                      /* 584 */ \
-  OP(ApplyScreenSpaceAntialiasingCHROMIUM)                 /* 585 */ \
-  OP(BindFragDataLocationIndexedEXTBucket)                 /* 586 */ \
-  OP(BindFragDataLocationEXTBucket)                        /* 587 */ \
-  OP(GetFragDataIndexEXT)                                  /* 588 */ \
-  OP(UniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate) /* 589 */ \
-  OP(OverlayPromotionHintCHROMIUM)                         /* 590 */ \
-  OP(SwapBuffersWithBoundsCHROMIUMImmediate)               /* 591 */ \
-  OP(SetDrawRectangleCHROMIUM)                             /* 592 */ \
-  OP(SetEnableDCLayersCHROMIUM)                            /* 593 */ \
-  OP(InitializeDiscardableTextureCHROMIUM)                 /* 594 */ \
-  OP(UnlockDiscardableTextureCHROMIUM)                     /* 595 */ \
-  OP(LockDiscardableTextureCHROMIUM)                       /* 596 */ \
-  OP(TexStorage2DImageCHROMIUM)                            /* 597 */ \
-  OP(SetColorSpaceMetadataCHROMIUM)                        /* 598 */ \
-  OP(WindowRectanglesEXTImmediate)                         /* 599 */ \
-  OP(CreateGpuFenceINTERNAL)                               /* 600 */ \
-  OP(WaitGpuFenceCHROMIUM)                                 /* 601 */ \
-  OP(DestroyGpuFenceCHROMIUM)                              /* 602 */ \
-  OP(SetReadbackBufferShadowAllocationINTERNAL)            /* 603 */ \
-  OP(FramebufferTextureMultiviewOVR)                       /* 604 */ \
-  OP(MaxShaderCompilerThreadsKHR)                          /* 605 */ \
-  OP(CreateAndTexStorage2DSharedImageINTERNALImmediate)    /* 606 */ \
-  OP(BeginSharedImageAccessDirectCHROMIUM)                 /* 607 */ \
-  OP(EndSharedImageAccessDirectCHROMIUM)                   /* 608 */
+  OP(BindFragDataLocationIndexedEXTBucket)                 /* 585 */ \
+  OP(BindFragDataLocationEXTBucket)                        /* 586 */ \
+  OP(GetFragDataIndexEXT)                                  /* 587 */ \
+  OP(UniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate) /* 588 */ \
+  OP(OverlayPromotionHintCHROMIUM)                         /* 589 */ \
+  OP(SwapBuffersWithBoundsCHROMIUMImmediate)               /* 590 */ \
+  OP(SetDrawRectangleCHROMIUM)                             /* 591 */ \
+  OP(SetEnableDCLayersCHROMIUM)                            /* 592 */ \
+  OP(InitializeDiscardableTextureCHROMIUM)                 /* 593 */ \
+  OP(UnlockDiscardableTextureCHROMIUM)                     /* 594 */ \
+  OP(LockDiscardableTextureCHROMIUM)                       /* 595 */ \
+  OP(TexStorage2DImageCHROMIUM)                            /* 596 */ \
+  OP(SetColorSpaceMetadataCHROMIUM)                        /* 597 */ \
+  OP(WindowRectanglesEXTImmediate)                         /* 598 */ \
+  OP(CreateGpuFenceINTERNAL)                               /* 599 */ \
+  OP(WaitGpuFenceCHROMIUM)                                 /* 600 */ \
+  OP(DestroyGpuFenceCHROMIUM)                              /* 601 */ \
+  OP(SetReadbackBufferShadowAllocationINTERNAL)            /* 602 */ \
+  OP(FramebufferTextureMultiviewOVR)                       /* 603 */ \
+  OP(MaxShaderCompilerThreadsKHR)                          /* 604 */ \
+  OP(CreateAndTexStorage2DSharedImageINTERNALImmediate)    /* 605 */ \
+  OP(BeginSharedImageAccessDirectCHROMIUM)                 /* 606 */ \
+  OP(EndSharedImageAccessDirectCHROMIUM)                   /* 607 */
 
 enum CommandId {
   kOneBeforeStartPoint =
diff --git a/gpu/command_buffer/gles2_cmd_buffer_functions.txt b/gpu/command_buffer/gles2_cmd_buffer_functions.txt
index d5dcb20..36b617b 100644
--- a/gpu/command_buffer/gles2_cmd_buffer_functions.txt
+++ b/gpu/command_buffer/gles2_cmd_buffer_functions.txt
@@ -377,9 +377,6 @@
 // Extension KHR_blend_equation_advanced
 GL_APICALL void GL_APIENTRY glBlendBarrierKHR (void);
 
-// Extension GL_CHROMIUM_screen_space_antialiasing
-GL_APICALL void GL_APIENTRY glApplyScreenSpaceAntialiasingCHROMIUM (void);
-
 // Extension EXT_blend_func_extended
 GL_APICALL void         GL_APIENTRY glBindFragDataLocationIndexedEXT (GLidProgram program, GLuint colorNumber, GLuint index, const char* name);
 GL_APICALL void         GL_APIENTRY glBindFragDataLocationEXT (GLidProgram program, GLuint colorNumber, const char* name);
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index c62afb23..77834c7 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -136,8 +136,6 @@
     "gl_state_restorer_impl.h",
     "gl_utils.cc",
     "gl_utils.h",
-    "gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc",
-    "gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h",
     "gles2_cmd_clear_framebuffer.cc",
     "gles2_cmd_clear_framebuffer.h",
     "gles2_cmd_copy_tex_image.cc",
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index a737820a..d730814 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -991,26 +991,6 @@
     validators_.capability.AddValue(GL_SAMPLE_ALPHA_TO_ONE_EXT);
   }
 
-  if (gfx::HasExtension(extensions, "GL_INTEL_framebuffer_CMAA")) {
-    feature_flags_.chromium_screen_space_antialiasing = true;
-    AddExtensionString("GL_CHROMIUM_screen_space_antialiasing");
-  } else if (gl_version_info_->IsAtLeastGLES(3, 1) ||
-             (gl_version_info_->IsAtLeastGL(3, 0) &&
-              gfx::HasExtension(extensions,
-                                "GL_ARB_shading_language_420pack") &&
-              gfx::HasExtension(extensions, "GL_ARB_texture_storage") &&
-              gfx::HasExtension(extensions, "GL_ARB_texture_gather") &&
-              gfx::HasExtension(extensions,
-                                "GL_ARB_explicit_uniform_location") &&
-              gfx::HasExtension(extensions,
-                                "GL_ARB_explicit_attrib_location") &&
-              gfx::HasExtension(extensions,
-                                "GL_ARB_shader_image_load_store"))) {
-    feature_flags_.chromium_screen_space_antialiasing = true;
-    feature_flags_.use_chromium_screen_space_antialiasing_via_shaders = true;
-    AddExtensionString("GL_CHROMIUM_screen_space_antialiasing");
-  }
-
   if (gfx::HasExtension(extensions, "GL_OES_depth24") ||
       gl::HasDesktopGLFeatures() || gl_version_info_->is_es3) {
     AddExtensionString("GL_OES_depth24");
diff --git a/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc b/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc
deleted file mode 100644
index 433277b4..0000000
--- a/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.cc
+++ /dev/null
@@ -1,1851 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h"
-
-#include "base/logging.h"
-#include "gpu/command_buffer/service/framebuffer_manager.h"
-#include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
-#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
-#include "gpu/command_buffer/service/texture_manager.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/gl_gl_api_implementation.h"
-#include "ui/gl/gl_version_info.h"
-
-#define SHADER(Src) #Src
-
-namespace gpu {
-namespace gles2 {
-
-ApplyFramebufferAttachmentCMAAINTELResourceManager::
-    ApplyFramebufferAttachmentCMAAINTELResourceManager()
-    : initialized_(false),
-      textures_initialized_(false),
-      is_in_gamma_correct_mode_(false),
-      supports_usampler_(true),
-      supports_r8_image_(true),
-      is_gles31_compatible_(false),
-      frame_id_(0),
-      width_(0),
-      height_(0),
-      edges0_shader_(0),
-      edges1_shader_(0),
-      edges_combine_shader_(0),
-      process_and_apply_shader_(0),
-      debug_display_edges_shader_(0),
-      cmaa_framebuffer_(0),
-      rgba8_texture_(0),
-      working_color_texture_(0),
-      edges0_texture_(0),
-      edges1_texture_(0),
-      mini4_edge_texture_(0),
-      mini4_edge_depth_texture_(0),
-      edges0_shader_result_rgba_texture_slot1_(0),
-      edges0_shader_target_texture_slot2_(0),
-      edges1_shader_result_edge_texture_(0),
-      process_and_apply_shader_result_rgba_texture_slot1_(0),
-      edges_combine_shader_result_edge_texture_(0) {}
-
-ApplyFramebufferAttachmentCMAAINTELResourceManager::
-    ~ApplyFramebufferAttachmentCMAAINTELResourceManager() {
-  Destroy();
-}
-
-void ApplyFramebufferAttachmentCMAAINTELResourceManager::Initialize(
-    gles2::GLES2Decoder* decoder) {
-  DCHECK(decoder);
-  is_gles31_compatible_ =
-      decoder->GetGLContext()->GetVersionInfo()->IsAtLeastGLES(3, 1);
-
-  if (is_gles31_compatible_) {
-    supports_r8_image_ =
-        decoder->GetGLContext()->HasExtension("GL_NV_image_formats");
-
-    // ES 3.0 requires GL_RGBA8UI is color renderable.
-    supports_usampler_ = true;
-  } else {
-    // CMAA requires GL_ARB_shader_image_load_store for GL, and it requires r8
-    // image texture.
-    DCHECK(decoder->GetGLContext()->HasExtension(
-        "GL_ARB_shader_image_load_store"));
-    supports_r8_image_ = true;
-
-    // Check if RGBA8UI is supported as an FBO colour target with depth.
-    // If not supported, GLSL needs to convert the data to/from float so there
-    // is a small extra cost.
-    {
-      glActiveTexture(GL_TEXTURE0);
-
-      GLuint rgba8ui_texture = 0, depth_texture = 0;
-      glGenTextures(1, &rgba8ui_texture);
-      glBindTexture(GL_TEXTURE_2D, rgba8ui_texture);
-      glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8UI, 4, 4);
-
-      glGenTextures(1, &depth_texture);
-      glBindTexture(GL_TEXTURE_2D, depth_texture);
-      glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT16, 4, 4);
-
-      // Create the FBO
-      GLuint rgba8ui_framebuffer = 0;
-      glGenFramebuffersEXT(1, &rgba8ui_framebuffer);
-      glBindFramebufferEXT(GL_FRAMEBUFFER, rgba8ui_framebuffer);
-
-      // Bind to the FBO to test support
-      glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                                GL_TEXTURE_2D, rgba8ui_texture, 0);
-      glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
-                                GL_TEXTURE_2D, depth_texture, 0);
-      GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
-
-      supports_usampler_ = (status == GL_FRAMEBUFFER_COMPLETE);
-
-      glDeleteFramebuffersEXT(1, &rgba8ui_framebuffer);
-      glDeleteTextures(1, &rgba8ui_texture);
-      glDeleteTextures(1, &depth_texture);
-
-      decoder->RestoreTextureUnitBindings(0);
-      decoder->RestoreActiveTexture();
-      decoder->RestoreFramebufferBindings();
-    }
-  }
-
-  VLOG(1) << "ApplyFramebufferAttachmentCMAAINTEL: "
-          << "Supports USampler is " << (supports_usampler_ ? "true" : "false");
-  VLOG(1) << "ApplyFramebufferAttachmentCMAAINTEL: "
-          << "Supports R8 Images is "
-          << (supports_r8_image_ ? "true" : "false");
-
-  // Create the shaders
-  std::ostringstream defines, edge1, edge2, combineEdges, blur, displayEdges,
-      cmaa_frag;
-
-  cmaa_frag << cmaa_frag_s1_ << cmaa_frag_s2_;
-  std::string cmaa_frag_string = cmaa_frag.str();
-  const char* cmaa_frag_c_str = cmaa_frag_string.c_str();
-
-  if (supports_usampler_) {
-    defines << "#define SUPPORTS_USAMPLER2D\n";
-  }
-
-  if (is_in_gamma_correct_mode_) {
-    defines << "#define IN_GAMMA_CORRECT_MODE\n";
-  }
-
-  if (supports_r8_image_) {
-    defines << "#define EDGE_READ_FORMAT r8\n";
-  } else {
-    defines << "#define EDGE_READ_FORMAT r32f\n";
-  }
-
-  displayEdges << defines.str() << "#define DISPLAY_EDGES\n";
-  debug_display_edges_shader_ =
-      CreateProgram(displayEdges.str().c_str(), vert_str_, cmaa_frag_c_str);
-
-  edge1 << defines.str() << "#define DETECT_EDGES1\n";
-  edges0_shader_ =
-      CreateProgram(edge1.str().c_str(), vert_str_, cmaa_frag_c_str);
-
-  edge2 << defines.str() << "#define DETECT_EDGES2\n";
-  edges1_shader_ =
-      CreateProgram(edge2.str().c_str(), vert_str_, cmaa_frag_c_str);
-
-  combineEdges << defines.str() << "#define COMBINE_EDGES\n";
-  edges_combine_shader_ =
-      CreateProgram(combineEdges.str().c_str(), vert_str_, cmaa_frag_c_str);
-
-  blur << defines.str() << "#define BLUR_EDGES\n";
-  process_and_apply_shader_ =
-      CreateProgram(blur.str().c_str(), vert_str_, cmaa_frag_c_str);
-
-  edges0_shader_result_rgba_texture_slot1_ =
-      glGetUniformLocation(edges0_shader_, "g_resultRGBATextureSlot1");
-  edges0_shader_target_texture_slot2_ =
-      glGetUniformLocation(edges0_shader_, "g_targetTextureSlot2");
-  edges1_shader_result_edge_texture_ =
-      glGetUniformLocation(edges1_shader_, "g_resultEdgeTexture");
-  edges_combine_shader_result_edge_texture_ =
-      glGetUniformLocation(edges_combine_shader_, "g_resultEdgeTexture");
-  process_and_apply_shader_result_rgba_texture_slot1_ = glGetUniformLocation(
-      process_and_apply_shader_, "g_resultRGBATextureSlot1");
-
-  initialized_ = true;
-}
-
-void ApplyFramebufferAttachmentCMAAINTELResourceManager::Destroy() {
-  if (!initialized_)
-    return;
-
-  ReleaseTextures();
-
-  glDeleteProgram(process_and_apply_shader_);
-  glDeleteProgram(edges_combine_shader_);
-  glDeleteProgram(edges1_shader_);
-  glDeleteProgram(edges0_shader_);
-  glDeleteProgram(debug_display_edges_shader_);
-
-  initialized_ = false;
-}
-
-// Apply CMAA(Conservative Morphological Anti-Aliasing) algorithm to the
-// color attachments of currently bound draw framebuffer.
-// Reference GL_INTEL_framebuffer_CMAA for details.
-void ApplyFramebufferAttachmentCMAAINTELResourceManager::
-    ApplyFramebufferAttachmentCMAAINTEL(
-        GLES2Decoder* decoder,
-        Framebuffer* framebuffer,
-        CopyTextureCHROMIUMResourceManager* copier,
-        TextureManager* texture_manager) {
-  DCHECK(decoder);
-  DCHECK(initialized_);
-  if (!framebuffer)
-    return;
-
-  glDisable(GL_SCISSOR_TEST);
-  glDisable(GL_STENCIL_TEST);
-  glDisable(GL_CULL_FACE);
-  glDisable(GL_BLEND);
-  if (decoder->GetFeatureInfo()->feature_flags().ext_window_rectangles) {
-    glWindowRectanglesEXT(GL_EXCLUSIVE_EXT, 0, nullptr);
-  }
-
-  // Process each color attachment of the current draw framebuffer.
-  uint32_t max_draw_buffers = decoder->GetContextGroup()->max_draw_buffers();
-  for (uint32_t i = 0; i < max_draw_buffers; i++) {
-    const gles2::Framebuffer::Attachment* attachment =
-        framebuffer->GetAttachment(GL_COLOR_ATTACHMENT0 + i);
-    if (attachment && attachment->IsTextureAttachment()) {
-      // Get the texture info.
-      GLuint source_texture_client_id = attachment->object_name();
-      GLuint source_texture = 0;
-      if (!decoder->GetServiceTextureId(source_texture_client_id,
-                                        &source_texture))
-        continue;
-      GLsizei width = attachment->width();
-      GLsizei height = attachment->height();
-      GLenum internal_format = attachment->internal_format();
-
-      // Resize internal structures - only if needed.
-      OnSize(width, height);
-
-      // CMAA internally expects GL_RGBA8 textures.
-      // Process using a GL_RGBA8 copy if this is not the case.
-      DCHECK(attachment->object_name());
-      TextureRef* texture =
-          texture_manager->GetTexture(attachment->object_name());
-      const bool rgba_immutable =
-          texture->texture()->HasImmutableStorage() &&
-          TextureManager::ExtractFormatFromStorageFormat(internal_format) ==
-              GL_RGBA;
-      const bool do_copy = !rgba_immutable;
-
-      // CMAA Effect
-      if (do_copy) {
-        ApplyCMAAEffectTexture(source_texture, rgba8_texture_, do_copy);
-
-        // Source format for DoCopySubTexture is always GL_RGBA8.
-        CopyTextureMethod method = CopyTextureMethod::DIRECT_COPY;
-        bool copy_tex_image_format_valid =
-            !GLES2Util::IsIntegerFormat(internal_format) &&
-            GLES2Util::GetColorEncodingFromInternalFormat(internal_format) !=
-                GL_SRGB &&
-            internal_format != GL_BGRA_EXT && internal_format != GL_BGRA8_EXT;
-        if (GLES2Util::IsSizedColorFormat(internal_format)) {
-          int dr, dg, db, da;
-          GLES2Util::GetColorFormatComponentSizes(internal_format, 0, &dr, &dg,
-                                                  &db, &da);
-          if ((dr > 0 && dr != 8) || (dg > 0 && dg != 8) ||
-              (db > 0 && db != 8) || (da > 0 && da != 8)) {
-            copy_tex_image_format_valid = false;
-          }
-        }
-        if (!copy_tex_image_format_valid)
-          method = CopyTextureMethod::DIRECT_DRAW;
-        bool color_renderable =
-            Texture::ColorRenderable(decoder->GetFeatureInfo(), internal_format,
-                                     texture->texture()->IsImmutable());
-#if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
-        // glDrawArrays is faster than glCopyTexSubImage2D on IA Mesa driver,
-        // although opposite in Android.
-        // TODO(dshwang): After Mesa fixes this issue, remove this hack.
-        // https://bugs.freedesktop.org/show_bug.cgi?id=98478, crbug.com/535198.
-        if (color_renderable)
-          method = CopyTextureMethod::DIRECT_DRAW;
-#endif
-        if (method == CopyTextureMethod::DIRECT_DRAW && !color_renderable)
-          method = CopyTextureMethod::DRAW_AND_COPY;
-
-        // LUMINANCE, LUMINANCE_ALPHA and ALPHA textures aren't
-        // renderable, so we don't need to pass in the luma emulation
-        // blitter to this point.
-        copier->DoCopySubTexture(decoder, GL_TEXTURE_2D, rgba8_texture_, 0,
-                                 GL_RGBA8, GL_TEXTURE_2D, source_texture, 0,
-                                 internal_format, 0, 0, 0, 0, width_, height_,
-                                 width_, height_, width_, height_, false, false,
-                                 false, false, method, nullptr);
-      } else {
-        ApplyCMAAEffectTexture(source_texture, source_texture, do_copy);
-      }
-
-      decoder->RestoreTextureState(source_texture);
-    }
-  }
-
-  // Restore state
-  decoder->RestoreAllAttributes();
-  decoder->RestoreTextureUnitBindings(0);
-  decoder->RestoreTextureUnitBindings(1);
-  decoder->RestoreActiveTexture();
-  decoder->RestoreProgramBindings();
-  decoder->RestoreBufferBindings();
-  decoder->RestoreFramebufferBindings();
-  decoder->RestoreGlobalState();
-}
-
-void ApplyFramebufferAttachmentCMAAINTELResourceManager::ApplyCMAAEffectTexture(
-    GLuint source_texture,
-    GLuint dest_texture,
-    bool do_copy) {
-  frame_id_++;
-
-  GLuint edge_texture_a;
-  GLuint edge_texture_b;
-
-  // Flip flop - One pass clears the texture that needs clearing for the other
-  // one (actually it's only important that it clears the highest bit)
-  if ((frame_id_ % 2) == 0) {
-    edge_texture_a = edges0_texture_;
-    edge_texture_b = edges1_texture_;
-  } else {
-    edge_texture_a = edges1_texture_;
-    edge_texture_b = edges0_texture_;
-  }
-
-  // Setup the main fbo
-  glBindFramebufferEXT(GL_FRAMEBUFFER, cmaa_framebuffer_);
-  glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                            mini4_edge_texture_, 0);
-  glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
-                            mini4_edge_depth_texture_, 0);
-#if DCHECK_IS_ON()
-  GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
-  if (status != GL_FRAMEBUFFER_COMPLETE) {
-    DLOG(ERROR) << "ApplyFramebufferAttachmentCMAAINTEL: "
-                << "Incomplete framebuffer.";
-    Destroy();
-    return;
-  }
-#endif
-
-  // Setup the viewport to match the fbo
-  glViewport(0, 0, (width_ + 1) / 2, (height_ + 1) / 2);
-  glEnable(GL_DEPTH_TEST);
-
-  // Detect edges Pass 0
-  //   - For every pixel detect edges to the right and down and output depth
-  //   mask where edges detected (1 - far, for detected, 0-near for empty
-  //   pixels)
-
-  // Inputs
-  //  g_screenTexture                     source_texture               tex0
-  // Outputs
-  //  gl_FragDepth                        mini4_edge_depth_texture_    fbo.depth
-  //  out uvec4 outEdges                  mini4_edge_texture_          fbo.col
-  //  image2D g_resultRGBATextureSlot1    working_color_texture_       image1
-  GLenum edge_format = supports_r8_image_ ? GL_R8 : GL_R32F;
-
-  {
-    glUseProgram(edges0_shader_);
-    glUniform2f(0, 1.0f / width_, 1.0f / height_);
-    glDepthMask(GL_TRUE);
-    glDepthFunc(GL_ALWAYS);
-    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-
-    if (!is_gles31_compatible_) {
-      glUniform1i(edges0_shader_result_rgba_texture_slot1_, 1);
-      glUniform1i(edges0_shader_target_texture_slot2_, 2);
-    }
-    glBindImageTextureEXT(1, working_color_texture_, 0, GL_FALSE, 0,
-                          GL_WRITE_ONLY, GL_RGBA8);
-    if (do_copy) {
-      glUniform1i(2, GL_TRUE);
-      glBindImageTextureEXT(2, dest_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY,
-                            GL_RGBA8);
-    } else {
-      glUniform1i(2, GL_FALSE);
-    }
-
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, source_texture);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
-    glDrawArrays(GL_TRIANGLES, 0, 3);
-  }
-
-  // Detect edges Pass 1 (finish the previous pass edge processing).
-  // Do the culling of non-dominant local edges (leave mainly locally dominant
-  // edges) and merge Right and Bottom edges into TopRightBottomLeft
-
-  // Inputs
-  //  g_src0Texture4Uint                  mini4_edge_texture_          tex1
-  // Outputs
-  //  image2D g_resultEdgeTexture         edge_texture_b               image0
-  {
-    glUseProgram(edges1_shader_);
-    glUniform2f(0, 1.0f / width_, 1.0f / height_);
-    glDepthMask(GL_FALSE);
-    glDepthFunc(GL_LESS);
-    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
-
-    if (!is_gles31_compatible_) {
-      glUniform1i(edges1_shader_result_edge_texture_, 0);
-    }
-    glBindImageTextureEXT(0, edge_texture_b, 0, GL_FALSE, 0, GL_WRITE_ONLY,
-                          edge_format);
-
-    glActiveTexture(GL_TEXTURE1);
-    glBindTexture(GL_TEXTURE_2D, mini4_edge_texture_);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
-    glDrawArrays(GL_TRIANGLES, 0, 3);
-  }
-
-  //  - Combine RightBottom (.xy) edges from previous pass into
-  //    RightBottomLeftTop (.xyzw) edges and output it into the mask.
-  //  - On all pixels with any edge, input buffer into a temporary color buffer
-  //    needed for correct blending in the next pass (other pixels not needed
-  //    so not copied to avoid bandwidth use).
-  //  - On all pixels with 2 or more edges output positive depth mask for the
-  //    next pass.
-
-  // Inputs
-  //  g_src0TextureFlt                    edge_texture_b               tex1 //ps
-  // Outputs
-  //  image2D g_resultEdgeTexture         edge_texture_a               image2
-  //  gl_FragDepth                        mini4_edge_texture_          fbo.depth
-  {
-    // Combine edges: each pixel will now contain info on all (top, right,
-    // bottom, left) edges; also mark depth 1 value on all pixels with any edge
-    // and also copy source color data but only on edge pixels
-    glUseProgram(edges_combine_shader_);
-    glUniform2f(0, 1.0f / width_, 1.0f / height_);
-    glDepthMask(GL_TRUE);
-    glDepthFunc(GL_LESS);
-    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
-
-    if (!is_gles31_compatible_) {
-      glUniform1i(edges_combine_shader_result_edge_texture_, 0);
-    }
-    glBindImageTextureEXT(0, edge_texture_a, 0, GL_FALSE, 0, GL_WRITE_ONLY,
-                          edge_format);
-
-    glActiveTexture(GL_TEXTURE1);
-    glBindTexture(GL_TEXTURE_2D, edge_texture_b);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
-    glDrawArrays(GL_TRIANGLES, 0, 3);
-  }
-
-  // Using depth mask and [earlydepthstencil] to work on pixels with 2, 3, 4
-  // edges:
-  //    - First blend simple blur map for 2,3,4 edge pixels
-  //    - Then do the lines (line length counter -should- guarantee no overlap
-  //      with other pixels - pixels with 1 edge are excluded in the previous
-  //      pass and the pixels with 2 parallel edges are excluded in the simple
-  //      blur)
-
-  // Inputs
-  //  g_screenTexture                      working_color_texture_      tex0
-  //  g_src0TextureFlt                     edge_texture_a              tex1 //ps
-  //  sampled
-  // Outputs
-  //  g_resultRGBATextureSlot1             dest_texture                image1
-  //  gl_FragDepth                         mini4_edge_texture_         fbo.depth
-  {
-    glUseProgram(process_and_apply_shader_);
-    glUniform2f(0, 1.0f / width_, 1.0f / height_);
-    glDepthMask(GL_FALSE);
-    glDepthFunc(GL_LESS);
-    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
-
-    if (!is_gles31_compatible_) {
-      glUniform1i(process_and_apply_shader_result_rgba_texture_slot1_, 1);
-    }
-    glBindImageTextureEXT(1, dest_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY,
-                          GL_RGBA8);
-
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_2D, working_color_texture_);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
-    glActiveTexture(GL_TEXTURE1);
-    glBindTexture(GL_TEXTURE_2D, edge_texture_a);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
-    glDrawArrays(GL_TRIANGLES, 0, 3);
-  }
-
-  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-  glDisable(GL_DEPTH_TEST);
-  glDepthMask(GL_FALSE);
-  glActiveTexture(GL_TEXTURE0);
-}
-
-void ApplyFramebufferAttachmentCMAAINTELResourceManager::OnSize(GLint width,
-                                                                GLint height) {
-  if (height_ == height && width_ == width)
-    return;
-
-  ReleaseTextures();
-
-  height_ = height;
-  width_ = width;
-
-  glGenTextures(1, &rgba8_texture_);
-  glBindTexture(GL_TEXTURE_2D, rgba8_texture_);
-  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
-
-  // Edges texture - R8
-  // OpenGLES has no single component 8/16-bit image support, so needs to be R32
-  // Although CHT does support R8.
-  GLenum edge_format = supports_r8_image_ ? GL_R8 : GL_R32F;
-  glGenTextures(1, &edges0_texture_);
-  glBindTexture(GL_TEXTURE_2D, edges0_texture_);
-  glTexStorage2DEXT(GL_TEXTURE_2D, 1, edge_format, width, height);
-
-  glGenTextures(1, &edges1_texture_);
-  glBindTexture(GL_TEXTURE_2D, edges1_texture_);
-  glTexStorage2DEXT(GL_TEXTURE_2D, 1, edge_format, width, height);
-
-  // Color working texture - RGBA8
-  glGenTextures(1, &working_color_texture_);
-  glBindTexture(GL_TEXTURE_2D, working_color_texture_);
-  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
-
-  // Half*half compressed 4-edge-per-pixel texture - RGBA8
-  glGenTextures(1, &mini4_edge_texture_);
-  glBindTexture(GL_TEXTURE_2D, mini4_edge_texture_);
-  GLenum format = GL_RGBA8UI;
-  if (!supports_usampler_) {
-    format = GL_RGBA8;
-  }
-  glTexStorage2DEXT(GL_TEXTURE_2D, 1, format, (width + 1) / 2,
-                    (height + 1) / 2);
-
-  // Depth
-  glGenTextures(1, &mini4_edge_depth_texture_);
-  glBindTexture(GL_TEXTURE_2D, mini4_edge_depth_texture_);
-  glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT16, (width + 1) / 2,
-                    (height + 1) / 2);
-
-  // Create the FBO
-  glGenFramebuffersEXT(1, &cmaa_framebuffer_);
-  glBindFramebufferEXT(GL_FRAMEBUFFER, cmaa_framebuffer_);
-
-  // We need to clear the textures before they are first used.
-  // The algorithm self-clears them later.
-  glViewport(0, 0, width_, height_);
-  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
-  glBindFramebufferEXT(GL_FRAMEBUFFER, cmaa_framebuffer_);
-  glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                            edges0_texture_, 0);
-  glClear(GL_COLOR_BUFFER_BIT);
-
-  glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                            edges1_texture_, 0);
-  glClear(GL_COLOR_BUFFER_BIT);
-
-  textures_initialized_ = true;
-}
-
-void ApplyFramebufferAttachmentCMAAINTELResourceManager::ReleaseTextures() {
-  if (textures_initialized_) {
-    glDeleteFramebuffersEXT(1, &cmaa_framebuffer_);
-    glDeleteTextures(1, &rgba8_texture_);
-    glDeleteTextures(1, &edges0_texture_);
-    glDeleteTextures(1, &edges1_texture_);
-    glDeleteTextures(1, &mini4_edge_texture_);
-    glDeleteTextures(1, &mini4_edge_depth_texture_);
-    glDeleteTextures(1, &working_color_texture_);
-  }
-  textures_initialized_ = false;
-}
-
-GLuint ApplyFramebufferAttachmentCMAAINTELResourceManager::CreateProgram(
-    const char* defines,
-    const char* vs_source,
-    const char* fs_source) {
-  GLuint program = glCreateProgram();
-
-  GLuint vs = CreateShader(GL_VERTEX_SHADER, defines, vs_source);
-  GLuint fs = CreateShader(GL_FRAGMENT_SHADER, defines, fs_source);
-
-  glAttachShader(program, vs);
-  glDeleteShader(vs);
-  glAttachShader(program, fs);
-  glDeleteShader(fs);
-
-  glLinkProgram(program);
-  GLint link_status;
-  glGetProgramiv(program, GL_LINK_STATUS, &link_status);
-
-  if (link_status == 0) {
-#if DCHECK_IS_ON()
-    GLint info_log_length;
-    glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);
-    std::vector<GLchar> info_log(info_log_length);
-    glGetProgramInfoLog(program, static_cast<GLsizei>(info_log.size()), nullptr,
-                        &info_log[0]);
-    DLOG(ERROR) << "ApplyFramebufferAttachmentCMAAINTEL: "
-                << "program link failed: " << &info_log[0];
-#endif
-    glDeleteProgram(program);
-    program = 0;
-  }
-
-  return program;
-}
-
-GLuint ApplyFramebufferAttachmentCMAAINTELResourceManager::CreateShader(
-    GLenum type,
-    const char* defines,
-    const char* source) {
-  GLuint shader = glCreateShader(type);
-
-  const char header_es31[] =
-      "#version 310 es                                                      \n";
-  const char header_gl130[] =
-      "#version 130                                                         \n"
-      "#extension GL_ARB_shading_language_420pack  : require                \n"
-      "#extension GL_ARB_texture_gather            : require                \n"
-      "#extension GL_ARB_explicit_uniform_location : require                \n"
-      "#extension GL_ARB_explicit_attrib_location  : require                \n"
-      "#extension GL_ARB_shader_image_load_store   : require                \n";
-
-  std::ostringstream header;
-  if (is_gles31_compatible_) {
-    header << header_es31;
-    if (supports_r8_image_)
-      header << "#extension GL_NV_image_formats : require\n";
-  } else {
-    header << header_gl130;
-  }
-
-  std::string header_str = header.str();
-  const char* source_array[4] = {header_str.c_str(), defines, "\n", source};
-  glShaderSource(shader, 4, source_array, nullptr);
-
-  glCompileShader(shader);
-
-  GLint compile_result;
-  glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_result);
-  if (compile_result == 0) {
-#if DCHECK_IS_ON()
-    GLint info_log_length;
-    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
-    std::vector<GLchar> info_log(info_log_length);
-    glGetShaderInfoLog(shader, static_cast<GLsizei>(info_log.size()), nullptr,
-                       &info_log[0]);
-    DLOG(ERROR) << "ApplyFramebufferAttachmentCMAAINTEL: "
-                << "shader compilation failed: "
-                << (type == GL_VERTEX_SHADER
-                        ? "GL_VERTEX_SHADER"
-                        : (type == GL_FRAGMENT_SHADER ? "GL_FRAGMENT_SHADER"
-                                                      : "UNKNOWN_SHADER"))
-                << " shader compilation failed: " << &info_log[0];
-#endif
-    glDeleteShader(shader);
-    shader = 0;
-  }
-
-  return shader;
-}
-
-/* clang-format off */
-// Shaders used in the CMAA algorithm.
-const char ApplyFramebufferAttachmentCMAAINTELResourceManager::vert_str_[] =
-  SHADER(
-    precision highp float;
-    // No input data.
-    // Verts are autogenerated.
-    //
-    // vertexID 0,1,2 should generate
-    // POS: (-1,-1), (+3,-1), (-1,+3)
-    //
-    // This generates a triangle that completely covers the -1->1 viewport
-    //
-    void main()
-    {
-       float x = -1.0 + float((gl_VertexID & 1) << 2);
-       float y = -1.0 + float((gl_VertexID & 2) << 1);
-       gl_Position = vec4(x, y, 0.0, 1.0);
-    }
-  );
-
-const char ApplyFramebufferAttachmentCMAAINTELResourceManager::cmaa_frag_s1_[] =
-  SHADER(
-    precision highp float;
-    precision highp int;
-
-    \n#define SETTINGS_ALLOW_SHORT_Zs 1\n
-    \n#define EDGE_DETECT_THRESHOLD 13.0f\n
-    \n#define saturate(x) clamp((x), 0.0, 1.0)\n
-
-    // bind to a uniform buffer bind point 0
-    layout(location = 0) uniform vec2 g_OneOverScreenSize;
-    \n#ifndef EDGE_DETECT_THRESHOLD\n
-    layout(location = 1) uniform float g_ColorThreshold;
-    \n#endif\n
-    \n#ifdef DETECT_EDGES1\n
-    layout(location = 2) uniform int g_DoCopy;
-    \n#endif\n
-
-    \n#ifdef SUPPORTS_USAMPLER2D\n
-    \n#define USAMPLER usampler2D\n
-    \n#define UVEC4 uvec4\n
-    \n#define LOAD_UINT(arg) arg\n
-    \n#define STORE_UVEC4(arg) arg\n
-    \n#else\n
-    \n#define USAMPLER sampler2D\n
-    \n#define UVEC4 vec4\n
-    \n#define LOAD_UINT(arg) uint(arg * 255.0f)\n
-    \n#define STORE_UVEC4(arg) vec4(float(arg.x) / 255.0f,
-                                    float(arg.y) / 255.0f,
-                                    float(arg.z) / 255.0f,
-                                    float(arg.w) / 255.0f)\n
-    \n#endif\n
-
-    // bind to texture stage 0/1
-    layout(binding = 0) uniform highp sampler2D g_screenTexture;
-    layout(binding = 1) uniform highp sampler2D g_src0TextureFlt;
-    layout(binding = 1) uniform highp USAMPLER g_src0Texture4Uint;
-
-    // bind to image stage 0/1/2
-    \n#ifdef GL_ES\n
-    layout(binding = 0, EDGE_READ_FORMAT) restrict writeonly uniform highp
-        image2D g_resultEdgeTexture;
-    layout(binding = 1, rgba8) restrict writeonly uniform highp
-        image2D g_resultRGBATextureSlot1;
-    layout(binding = 2, rgba8) restrict writeonly uniform highp
-        image2D g_targetTextureSlot2;
-    \n#else\n
-    layout(EDGE_READ_FORMAT) restrict writeonly uniform highp
-        image2D g_resultEdgeTexture;
-    layout(rgba8) restrict writeonly uniform highp
-        image2D g_resultRGBATextureSlot1;
-    layout(rgba8) restrict writeonly uniform highp
-        image2D g_targetTextureSlot2;
-    \n#endif\n
-
-    // Constants
-    const vec4 c_lumWeights = vec4(0.2126f, 0.7152f, 0.0722f, 0.0000f);
-
-    \n#ifdef EDGE_DETECT_THRESHOLD\n
-    const float c_ColorThreshold = 1.0f / EDGE_DETECT_THRESHOLD;
-    \n#endif\n
-
-    // Must be even number; Will work with ~16 pretty good too for
-    // additional performance, or with ~64 for highest quality.
-    const int c_maxLineLength = 64;
-
-    const vec4 c_edgeDebugColours[5] = vec4[5](vec4(0.5, 0.5, 0.5, 0.4),
-                                               vec4(1.0, 0.1, 1.0, 0.8),
-                                               vec4(0.9, 0.0, 0.0, 0.8),
-                                               vec4(0.0, 0.9, 0.0, 0.8),
-                                               vec4(0.0, 0.0, 0.9, 0.8));
-
-    // this isn't needed if colour UAV is _SRGB but that doesn't work
-    // everywhere
-    \n#ifdef IN_GAMMA_CORRECT_MODE\n
-    ///////////////////////////////////////////////////////////////////////
-    //
-    // SRGB Helper Functions taken from D3DX_DXGIFormatConvert.inl
-    float D3DX_FLOAT_to_SRGB(float val) {
-      if (val < 0.0031308f)
-        val *= 12.92f;
-      else {
-        val = 1.055f * pow(val, 1.0f / 2.4f) - 0.055f;
-      }
-      return val;
-    }
-    //
-    vec3 D3DX_FLOAT3_to_SRGB(vec3 val) {
-      vec3 outVal;
-      outVal.x = D3DX_FLOAT_to_SRGB(val.x);
-      outVal.y = D3DX_FLOAT_to_SRGB(val.y);
-      outVal.z = D3DX_FLOAT_to_SRGB(val.z);
-      return outVal;
-    }
-    ///////////////////////////////////////////////////////////////////////
-    \n#endif\n  // IN_GAMMA_CORRECT_MODE
-
-    // how .rgba channels from the edge texture maps to pixel edges:
-    //
-    //                   A - 0x02
-    //              |¯¯¯¯¯¯¯¯¯|
-    //              |         |
-    //     0x04 - B |  pixel  | R - 0x01
-    //              |         |
-    //              |_________|
-    //                   G - 0x08
-    //
-    // (A - there's an edge between us and a pixel at the bottom)
-    // (R - there's an edge between us and a pixel to the right)
-    // (G - there's an edge between us and a pixel above us)
-    // (B - there's an edge between us and a pixel to the left)
-
-    // Expecting values of 1 and 0 only!
-    uint PackEdge(uvec4 edges) {
-      return (edges.x << 0u) | (edges.y << 1u) | (edges.z << 2u) |
-             (edges.w << 3u);
-    }
-
-    uvec4 UnpackEdge(uint value) {
-      uvec4 ret;
-      ret.x = (value & 0x01u) != 0u ? 1u : 0u;
-      ret.y = (value & 0x02u) != 0u ? 1u : 0u;
-      ret.z = (value & 0x04u) != 0u ? 1u : 0u;
-      ret.w = (value & 0x08u) != 0u ? 1u : 0u;
-      return ret;
-    }
-
-    vec4 PackBlurAAInfo(ivec2 pixelPos, uint shapeType) {
-      uint packedEdges = uint(
-          texelFetch(g_src0TextureFlt, pixelPos, 0).r * 255.5);
-
-      float retval = float(packedEdges + (shapeType << 4u));
-
-      return vec4(retval / 255.0);
-    }
-
-    void UnpackBlurAAInfo(float packedValue, out uint edges,
-                          out uint shapeType) {
-      uint packedValueInt = uint(packedValue * 255.5);
-      edges = packedValueInt & 0xFu;
-      shapeType = packedValueInt >> 4u;
-    }
-
-    float EdgeDetectColorCalcDiff(vec3 colorA, vec3 colorB) {
-    \n#ifdef IN_BGR_MODE\n
-      vec3 LumWeights = c_lumWeights.bgr;
-    \n#else\n
-      vec3 LumWeights = c_lumWeights.rgb;
-    \n#endif\n
-
-      return dot(abs(colorA.rgb - colorB.rgb), LumWeights);
-    }
-
-    bool EdgeDetectColor(vec3 colorA, vec3 colorB) {
-    \n#ifdef EDGE_DETECT_THRESHOLD\n
-      return EdgeDetectColorCalcDiff(colorA, colorB) > c_ColorThreshold;
-    \n#else\n
-      return EdgeDetectColorCalcDiff(colorA, colorB) > g_ColorThreshold;
-    \n#endif\n
-    }
-
-    void FindLineLength(out int lineLengthLeft,
-                        out int lineLengthRight,
-                        ivec2 screenPos,
-                        const bool horizontal,
-                        const bool invertedZShape,
-                        const ivec2 stepRight) {
-      // TODO: there must be a cleaner and faster way to get to these -
-      // a precalculated array indexing maybe?
-      uint maskLeft = uint(0);
-      uint bitsContinueLeft = uint(0);
-      uint maskRight = uint(0);
-      uint bitsContinueRight = uint(0);
-      {
-      // Horizontal (vertical is the same, just rotated 90º counter-clockwise)
-      // Inverted Z case:              // Normal Z case:
-      //   __                          // __
-      //  X|                           //  X|
-      // --                            //    --
-      //
-      // Vertical
-      // Inverted Z case:              // Normal Z case:
-      //  |                            //   |
-      //   --                          //  --
-      //   X|                          // |X
-        uint maskTraceLeft = uint(0);
-        uint maskTraceRight = uint(0);
-        uint maskStopLeft = uint(0);
-        uint maskStopRight = uint(0);
-        if (horizontal) {
-          if (invertedZShape) {
-            maskTraceLeft = 0x08u;  // tracing bottom edge
-            maskTraceRight = 0x02u; // tracing top edge
-          } else {
-            maskTraceLeft = 0x02u;  // tracing top edge
-            maskTraceRight = 0x08u; // tracing bottom edge
-          }
-          maskStopLeft = 0x01u;  // stop on right edge
-          maskStopRight = 0x04u; // stop on left edge
-        } else {
-          if (invertedZShape) {
-            maskTraceLeft = 0x01u;  // tracing right edge
-            maskTraceRight = 0x04u; // tracing left edge
-          } else {
-            maskTraceLeft = 0x04u;  // tracing left edge
-            maskTraceRight = 0x01u; // tracing right edge
-          }
-          maskStopLeft = 0x02u;  // stop on top edge
-          maskStopRight = 0x08u; // stop on bottom edge
-        }
-
-        maskLeft = maskTraceLeft | maskStopLeft;
-        bitsContinueLeft = maskTraceLeft;
-        maskRight = maskTraceRight | maskStopRight;
-        bitsContinueRight = maskTraceRight;
-      }
-    ///////////////////////////////////////////////////////////////////////
-
-    \n#ifdef SETTINGS_ALLOW_SHORT_Zs\n
-      int i = 1;
-    \n#else\n
-      int i = 2; // starting from 2 because we already know it's at least 2
-    \n#endif\n
-      for (; i < c_maxLineLength; i++) {
-        uint edgeLeft = uint(
-            texelFetch(g_src0TextureFlt,
-                       ivec2(screenPos.xy - stepRight * i), 0).r * 255.5);
-        uint edgeRight = uint(
-            texelFetch(g_src0TextureFlt,
-                       ivec2(screenPos.xy + stepRight * (i + 1)),
-                       0).r * 255.5);
-
-        // stop on encountering 'stopping' edge (as defined by masks)
-        int stopLeft = (edgeLeft & maskLeft) != bitsContinueLeft ? 1 : 0;
-        int stopRight =
-            (edgeRight & maskRight) != bitsContinueRight ? 1 : 0;
-
-        if (bool(stopLeft) || bool(stopRight)) {
-          lineLengthLeft = 1 + i - stopLeft;
-          lineLengthRight = 1 + i - stopRight;
-          return;
-        }
-      }
-      lineLengthLeft = lineLengthRight = i;
-      return;
-    }
-
-    void ProcessDetectedZ(ivec2 screenPos, bool horizontal,
-                          bool invertedZShape) {
-      int lineLengthLeft = 0;
-      int lineLengthRight = 0;
-
-      ivec2 stepRight = (horizontal) ? (ivec2(1, 0)) : (ivec2(0, 1));
-      vec2 blendDir = (horizontal) ? (vec2(0, -1)) : (vec2(1, 0));
-
-      FindLineLength(lineLengthLeft, lineLengthRight, screenPos,
-                     horizontal, invertedZShape, stepRight);
-
-      vec2 pixelSize = g_OneOverScreenSize;
-
-      float leftOdd = 0.15 * float(lineLengthLeft % 2);
-      float rightOdd = 0.15 * float(lineLengthRight % 2);
-
-      int loopFrom = -int((lineLengthLeft + 1) / 2) + 1;
-      int loopTo = int((lineLengthRight + 1) / 2);
-
-      float totalLength = float(loopTo - loopFrom) + 1.0 - leftOdd -
-                          rightOdd;
-
-      for (int i = loopFrom; i <= loopTo; i++) {
-        highp ivec2 pixelPos = screenPos + stepRight * i;
-        vec2 pixelPosFlt = vec2(float(pixelPos.x) + 0.5,
-                                float(pixelPos.y) + 0.5);
-
-    \n#ifdef DEBUG_OUTPUT_AAINFO\n
-        imageStore(g_resultEdgeTexture, pixelPos,
-                   PackBlurAAInfo(pixelPos, 1u));
-    \n#endif\n
-
-        float m = (float(i) + 0.5 - leftOdd - float(loopFrom)) /
-                   totalLength;
-        m = saturate(m);
-        float k = m - ((i > 0) ? 1.0 : 0.0);
-        k = (invertedZShape) ? (k) : (-k);
-
-        vec4 color = textureLod(g_screenTexture,
-                                (pixelPosFlt + blendDir * k) * pixelSize,
-                                0.0);
-
-    \n#ifdef IN_GAMMA_CORRECT_MODE\n
-        color.rgb = D3DX_FLOAT3_to_SRGB(color.rgb);
-    \n#endif\n
-        imageStore(g_resultRGBATextureSlot1, pixelPos, color);
-      }
-    }
-
-    vec4 CalcDbgDisplayColor(const vec4 blurMap) {
-      vec3 pixelC = vec3(0.0, 0.0, 0.0);
-      vec3 pixelL = vec3(0.0, 0.0, 1.0);
-      vec3 pixelT = vec3(1.0, 0.0, 0.0);
-      vec3 pixelR = vec3(0.0, 1.0, 0.0);
-      vec3 pixelB = vec3(0.8, 0.8, 0.0);
-
-      const float centerWeight = 1.0;
-      float fromBelowWeight = (1.0 / (1.0 - blurMap.x)) - 1.0;
-      float fromAboveWeight = (1.0 / (1.0 - blurMap.y)) - 1.0;
-      float fromRightWeight = (1.0 / (1.0 - blurMap.z)) - 1.0;
-      float fromLeftWeight = (1.0 / (1.0 - blurMap.w)) - 1.0;
-
-      float weightSum = centerWeight + dot(vec4(fromBelowWeight,
-                                                fromAboveWeight,
-                                                fromRightWeight,
-                                                fromLeftWeight),
-                                           vec4(1, 1, 1, 1));
-
-      vec4 pixel;
-
-      pixel.rgb = pixelC.rgb + fromAboveWeight * pixelT +
-                               fromBelowWeight * pixelB +
-                               fromLeftWeight * pixelL +
-                               fromRightWeight * pixelR;
-      pixel.rgb /= weightSum;
-
-      pixel.a = dot(pixel.rgb, vec3(1, 1, 1)) * 100.0;
-
-      return saturate(pixel);
-    }
-
-    \n#ifdef DETECT_EDGES1\n
-    layout(location = 0) out UVEC4 outEdges;
-    void DetectEdges1() {
-      uvec4 outputEdges;
-      ivec2 screenPosI = ivec2(gl_FragCoord.xy) * ivec2(2, 2);
-
-      // .rgb contains colour, .a contains flag whether to output it to
-      // working colour texture
-      vec4 pixel00 = texelFetch(g_screenTexture, screenPosI.xy, 0);
-      vec4 pixel10 =
-          texelFetchOffset(g_screenTexture, screenPosI.xy, 0, ivec2(1, 0));
-      vec4 pixel20 =
-          texelFetchOffset(g_screenTexture, screenPosI.xy, 0, ivec2(2, 0));
-      vec4 pixel01 =
-          texelFetchOffset(g_screenTexture, screenPosI.xy, 0, ivec2(0, 1));
-      vec4 pixel11 =
-          texelFetchOffset(g_screenTexture, screenPosI.xy, 0, ivec2(1, 1));
-      vec4 pixel21 =
-          texelFetchOffset(g_screenTexture, screenPosI.xy, 0, ivec2(2, 1));
-      vec4 pixel02 =
-          texelFetchOffset(g_screenTexture, screenPosI.xy, 0, ivec2(0, 2));
-      vec4 pixel12 =
-          texelFetchOffset(g_screenTexture, screenPosI.xy, 0, ivec2(1, 2));
-
-      if (g_DoCopy == 1) {
-        imageStore(g_targetTextureSlot2, screenPosI.xy + ivec2(0, 0), pixel00);
-        imageStore(g_targetTextureSlot2, screenPosI.xy + ivec2(1, 0), pixel10);
-        imageStore(g_targetTextureSlot2, screenPosI.xy + ivec2(0, 1), pixel01);
-        imageStore(g_targetTextureSlot2, screenPosI.xy + ivec2(1, 1), pixel11);
-      }
-
-      float storeFlagPixel00 = 0.0;
-      float storeFlagPixel10 = 0.0;
-      float storeFlagPixel20 = 0.0;
-      float storeFlagPixel01 = 0.0;
-      float storeFlagPixel11 = 0.0;
-      float storeFlagPixel21 = 0.0;
-      float storeFlagPixel02 = 0.0;
-      float storeFlagPixel12 = 0.0;
-
-      vec2 et;
-
-    \n#ifdef EDGE_DETECT_THRESHOLD\n
-      float threshold = c_ColorThreshold;
-    \n#else\n
-      float threshold = g_ColorThreshold;
-    \n#endif\n
-
-      {
-        et.x = EdgeDetectColorCalcDiff(pixel00.rgb, pixel10.rgb);
-        et.y = EdgeDetectColorCalcDiff(pixel00.rgb, pixel01.rgb);
-        et = saturate(et - threshold);
-        ivec2 eti = ivec2(et * 15.0 + 0.99);
-        outputEdges.x = uint(eti.x | (eti.y << 4));
-
-        storeFlagPixel00 += et.x;
-        storeFlagPixel00 += et.y;
-        storeFlagPixel10 += et.x;
-        storeFlagPixel01 += et.y;
-      }
-
-      {
-        et.x = EdgeDetectColorCalcDiff(pixel10.rgb, pixel20.rgb);
-        et.y = EdgeDetectColorCalcDiff(pixel10.rgb, pixel11.rgb);
-        et = saturate(et - threshold);
-        ivec2 eti = ivec2(et * 15.0 + 0.99);
-        outputEdges.y = uint(eti.x | (eti.y << 4));
-
-        storeFlagPixel10 += et.x;
-        storeFlagPixel10 += et.y;
-        storeFlagPixel20 += et.x;
-        storeFlagPixel11 += et.y;
-      }
-
-      {
-        et.x = EdgeDetectColorCalcDiff(pixel01.rgb, pixel11.rgb);
-        et.y = EdgeDetectColorCalcDiff(pixel01.rgb, pixel02.rgb);
-        et = saturate(et - threshold);
-        ivec2 eti = ivec2(et * 15.0 + 0.99);
-        outputEdges.z = uint(eti.x | (eti.y << 4));
-
-        storeFlagPixel01 += et.x;
-        storeFlagPixel01 += et.y;
-        storeFlagPixel11 += et.x;
-        storeFlagPixel02 += et.y;
-      }
-
-      {
-        et.x = EdgeDetectColorCalcDiff(pixel11.rgb, pixel21.rgb);
-        et.y = EdgeDetectColorCalcDiff(pixel11.rgb, pixel12.rgb);
-        et = saturate(et - threshold);
-        ivec2 eti = ivec2(et * 15.0 + 0.99);
-        outputEdges.w = uint(eti.x | (eti.y << 4));
-
-        storeFlagPixel11 += et.x;
-        storeFlagPixel11 += et.y;
-        storeFlagPixel21 += et.x;
-        storeFlagPixel12 += et.y;
-      }
-
-      gl_FragDepth = any(bvec4(outputEdges)) ? 1.0 : 0.0;
-
-      if (gl_FragDepth != 0.0) {
-        if (storeFlagPixel00 != 0.0)
-          imageStore(g_resultRGBATextureSlot1, screenPosI.xy + ivec2(0, 0),
-                     pixel00);
-        if (storeFlagPixel10 != 0.0)
-          imageStore(g_resultRGBATextureSlot1, screenPosI.xy + ivec2(1, 0),
-                     pixel10);
-        if (storeFlagPixel20 != 0.0)
-          imageStore(g_resultRGBATextureSlot1, screenPosI.xy + ivec2(2, 0),
-                     pixel20);
-        if (storeFlagPixel01 != 0.0)
-          imageStore(g_resultRGBATextureSlot1, screenPosI.xy + ivec2(0, 1),
-                     pixel01);
-        if (storeFlagPixel02 != 0.0)
-          imageStore(g_resultRGBATextureSlot1, screenPosI.xy + ivec2(0, 2),
-                     pixel02);
-        if (storeFlagPixel11 != 0.0)
-          imageStore(g_resultRGBATextureSlot1, screenPosI.xy + ivec2(1, 1),
-                     pixel11);
-        if (storeFlagPixel21 != 0.0)
-          imageStore(g_resultRGBATextureSlot1, screenPosI.xy + ivec2(2, 1),
-                     pixel21);
-        if (storeFlagPixel12 != 0.0)
-          imageStore(g_resultRGBATextureSlot1, screenPosI.xy + ivec2(1, 2),
-                     pixel12);
-      }
-      outEdges = STORE_UVEC4(outputEdges);
-    }
-    \n#endif\n  // DETECT_EDGES1
-
-    vec2 UnpackThresholds(uint val) {
-      return vec2(val & 0x0Fu, val >> 4u) / 15.0f;
-    }
-
-    uvec4 PruneNonDominantEdges(vec4 edges[3]) {
-      vec4 maxE4 = vec4(0.0, 0.0, 0.0, 0.0);
-
-      float avg = 0.0;
-
-      for (int i = 0; i < 3; i++) {
-        maxE4 = max(maxE4, edges[i]);
-
-        avg = dot(edges[i], vec4(1, 1, 1, 1) / (3.0 * 4.0));
-      }
-
-      vec2 maxE2 = max(maxE4.xy, maxE4.zw);
-      float maxE = max(maxE2.x, maxE2.y);
-
-      float threshold = avg * 0.65 + maxE * 0.35;
-
-      // threshold = 0.0001; // this disables non-dominant edge pruning!
-
-      uint cx = edges[0].x >= threshold ? 1u : 0u;
-      uint cy = edges[0].y >= threshold ? 1u : 0u;
-      return uvec4(cx, cy, 0, 0);
-    }
-
-    void CollectEdges(int offX,
-                      int offY,
-                      out vec4 edges[3],
-                      const uint packedVals[6 * 6]) {
-      vec2 pixelP0P0 = UnpackThresholds(packedVals[(offX)*6+(offY)]);
-      vec2 pixelP1P0 = UnpackThresholds(packedVals[(offX+1)*6+(offY)]);
-      vec2 pixelP0P1 = UnpackThresholds(packedVals[(offX)*6+(offY+1)]);
-      vec2 pixelM1P0 = UnpackThresholds(packedVals[(offX-1)*6 +(offY)]);
-      vec2 pixelP0M1 = UnpackThresholds(packedVals[(offX)*6+(offY-1)]);
-      vec2 pixelP1M1 = UnpackThresholds(packedVals[(offX+1)*6 +(offY-1)]);
-      vec2 pixelM1P1 = UnpackThresholds(packedVals[(offX-1)*6+(offY+1)]);
-
-      edges[0].x = pixelP0P0.x;
-      edges[0].y = pixelP0P0.y;
-      edges[0].z = pixelP1P0.x;
-      edges[0].w = pixelP1P0.y;
-      edges[1].x = pixelP0P1.x;
-      edges[1].y = pixelP0P1.y;
-      edges[1].z = pixelM1P0.x;
-      edges[1].w = pixelM1P0.y;
-      edges[2].x = pixelP0M1.x;
-      edges[2].y = pixelP0M1.y;
-      edges[2].z = pixelP1M1.y;
-      edges[2].w = pixelM1P1.x;
-    }
-  );
-
-const char ApplyFramebufferAttachmentCMAAINTELResourceManager::cmaa_frag_s2_[] =
-  SHADER(
-    \n#ifdef DETECT_EDGES2\n
-    layout(early_fragment_tests) in;
-    void DetectEdges2() {
-      ivec2 screenPosI = ivec2(gl_FragCoord.xy);
-      uvec2 notTopRight =
-          uvec2(notEqual((screenPosI + 1), textureSize(g_src0Texture4Uint, 0)));
-
-      // source : edge differences from previous pass
-      uint packedVals[6 * 6];
-
-      // center pixel (our output)
-      UVEC4 packedQ4 = texelFetch(g_src0Texture4Uint, screenPosI.xy, 0);
-      packedVals[(2) * 6 + (2)] = LOAD_UINT(packedQ4.x);
-      packedVals[(3) * 6 + (2)] = LOAD_UINT(packedQ4.y);
-      packedVals[(2) * 6 + (3)] = LOAD_UINT(packedQ4.z);
-      packedVals[(3) * 6 + (3)] = LOAD_UINT(packedQ4.w);
-
-      vec4 edges[3];
-      if (bool(packedVals[(2) * 6 + (2)]) ||
-          bool(packedVals[(3) * 6 + (2)])) {
-        UVEC4 packedQ1 = texelFetchOffset(g_src0Texture4Uint,
-                                          screenPosI.xy, 0, ivec2(0, -1));
-        packedVals[(2) * 6 + (0)] = LOAD_UINT(packedQ1.x);
-        packedVals[(3) * 6 + (0)] = LOAD_UINT(packedQ1.y);
-        packedVals[(2) * 6 + (1)] = LOAD_UINT(packedQ1.z);
-        packedVals[(3) * 6 + (1)] = LOAD_UINT(packedQ1.w);
-      }
-
-      if (bool(packedVals[(2) * 6 + (2)]) ||
-          bool(packedVals[(2) * 6 + (3)])) {
-        UVEC4 packedQ3 = texelFetchOffset(g_src0Texture4Uint,
-                                          screenPosI.xy, 0, ivec2(-1, 0));
-        packedVals[(0) * 6 + (2)] = LOAD_UINT(packedQ3.x);
-        packedVals[(1) * 6 + (2)] = LOAD_UINT(packedQ3.y);
-        packedVals[(0) * 6 + (3)] = LOAD_UINT(packedQ3.z);
-        packedVals[(1) * 6 + (3)] = LOAD_UINT(packedQ3.w);
-      }
-
-      if (bool(packedVals[(2) * 6 + (2)])) {
-        CollectEdges(2, 2, edges, packedVals);
-        uint pe = PackEdge(PruneNonDominantEdges(edges));
-        if (pe != 0u) {
-          imageStore(g_resultEdgeTexture, 2 * screenPosI.xy + ivec2(0, 0),
-                     vec4(float(0x80u | pe) / 255.0, 0, 0, 0));
-        }
-      }
-
-      if (bool(packedVals[(3) * 6 + (2)]) ||
-          bool(packedVals[(3) * 6 + (3)])) {
-        UVEC4 packedQ5 = texelFetchOffset(g_src0Texture4Uint,
-                                          screenPosI.xy, 0, ivec2(1, 0));
-        packedVals[(4) * 6 + (2)] = LOAD_UINT(packedQ5.x);
-        packedVals[(5) * 6 + (2)] = LOAD_UINT(packedQ5.y);
-        packedVals[(4) * 6 + (3)] = LOAD_UINT(packedQ5.z);
-        packedVals[(5) * 6 + (3)] = LOAD_UINT(packedQ5.w);
-      }
-
-      if (bool(packedVals[(3) * 6 + (2)])) {
-        UVEC4 packedQ2 = texelFetchOffset(g_src0Texture4Uint,
-                                          screenPosI.xy, 0, ivec2(1, -1));
-        packedVals[(4) * 6 + (0)] = LOAD_UINT(packedQ2.x);
-        packedVals[(5) * 6 + (0)] = LOAD_UINT(packedQ2.y);
-        packedVals[(4) * 6 + (1)] = LOAD_UINT(packedQ2.z);
-        packedVals[(5) * 6 + (1)] = LOAD_UINT(packedQ2.w);
-
-        CollectEdges(3, 2, edges, packedVals);
-        uvec4 dominant_edges = PruneNonDominantEdges(edges);
-        // The rightmost edge of the texture is not edge.
-        // Note: texelFetch() on out of range gives an undefined value.
-        uint pe = PackEdge(dominant_edges * uvec4(notTopRight.x, 1, 1, 1));
-        if (pe != 0u) {
-          imageStore(g_resultEdgeTexture, 2 * screenPosI.xy + ivec2(1, 0),
-                     vec4(float(0x80u | pe) / 255.0, 0, 0, 0));
-        }
-      }
-
-      if (bool(packedVals[(2) * 6 + (3)]) ||
-          bool(packedVals[(3) * 6 + (3)])) {
-        UVEC4 packedQ7 = texelFetchOffset(g_src0Texture4Uint,
-                                          screenPosI.xy, 0, ivec2(0, 1));
-        packedVals[(2) * 6 + (4)] = LOAD_UINT(packedQ7.x);
-        packedVals[(3) * 6 + (4)] = LOAD_UINT(packedQ7.y);
-        packedVals[(2) * 6 + (5)] = LOAD_UINT(packedQ7.z);
-        packedVals[(3) * 6 + (5)] = LOAD_UINT(packedQ7.w);
-      }
-
-      if (bool(packedVals[(2) * 6 + (3)])) {
-        UVEC4 packedQ6 = texelFetchOffset(g_src0Texture4Uint,
-                                          screenPosI.xy, 0, ivec2(-1, -1));
-        packedVals[(0) * 6 + (4)] = LOAD_UINT(packedQ6.x);
-        packedVals[(1) * 6 + (4)] = LOAD_UINT(packedQ6.y);
-        packedVals[(0) * 6 + (5)] = LOAD_UINT(packedQ6.z);
-        packedVals[(1) * 6 + (5)] = LOAD_UINT(packedQ6.w);
-
-        CollectEdges(2, 3, edges, packedVals);
-        uvec4 dominant_edges = PruneNonDominantEdges(edges);
-        uint pe = PackEdge(dominant_edges * uvec4(1, notTopRight.y, 1, 1));
-        if (pe != 0u) {
-          imageStore(g_resultEdgeTexture, 2 * screenPosI.xy + ivec2(0, 1),
-                     vec4(float(0x80u | pe) / 255.0, 0, 0, 0));
-        }
-      }
-
-      if (bool(packedVals[(3) * 6 + (3)])) {
-        CollectEdges(3, 3, edges, packedVals);
-        uvec4 dominant_edges = PruneNonDominantEdges(edges);
-        uint pe = PackEdge(dominant_edges * uvec4(notTopRight, 1, 1));
-        if (pe != 0u) {
-          imageStore(g_resultEdgeTexture, 2 * screenPosI.xy + ivec2(1, 1),
-                     vec4(float(0x80u | pe) / 255.0, 0, 0, 0));
-        }
-      }
-    }
-    \n#endif\n  // DETECT_EDGES2
-
-    \n#ifdef COMBINE_EDGES\n
-    void CombineEdges() {
-      ivec3 screenPosIBase = ivec3(ivec2(gl_FragCoord.xy) * 2, 0);
-      vec3 screenPosBase = vec3(screenPosIBase);
-      uvec2 notBottomLeft = uvec2(notEqual(screenPosIBase.xy, ivec2(0, 0)));
-      uint packedEdgesArray[3 * 3];
-
-      // use only if it has the 'prev frame' flag:[sample * 255.0 - 127.5]
-      //-> if it has the last bit flag (128), it's going to stay above 0
-      uvec4 sampA = uvec4(
-          textureGatherOffset(g_src0TextureFlt,
-                              screenPosBase.xy * g_OneOverScreenSize,
-                              ivec2(1, 0)) * 255.0 - 127.5);
-      uvec4 sampB = uvec4(
-          textureGatherOffset(g_src0TextureFlt,
-                              screenPosBase.xy * g_OneOverScreenSize,
-                              ivec2(0, 1)) * 255.0 - 127.5);
-      uint sampC = uint(
-          texelFetchOffset(g_src0TextureFlt, screenPosIBase.xy, 0,
-                           ivec2(1, 1)).r * 255.0 - 127.5);
-
-      packedEdgesArray[(0) * 3 + (0)] = 0u;
-      // The bottom-most edge of the texture is not edge.
-      // Note: texelFetch() on out of range gives an undefined value.
-      packedEdgesArray[(1) * 3 + (0)] = sampA.w * notBottomLeft.y;
-      packedEdgesArray[(2) * 3 + (0)] = sampA.z * notBottomLeft.y;
-      packedEdgesArray[(1) * 3 + (1)] = sampA.x;
-      packedEdgesArray[(2) * 3 + (1)] = sampA.y;
-      // The left-most edge of the texture is not edge.
-      packedEdgesArray[(0) * 3 + (1)] = sampB.w * notBottomLeft.x;
-      packedEdgesArray[(0) * 3 + (2)] = sampB.x * notBottomLeft.x;
-      packedEdgesArray[(1) * 3 + (2)] = sampB.y;
-      packedEdgesArray[(2) * 3 + (2)] = sampC;
-
-      uvec4 pixelsC = uvec4(packedEdgesArray[(1 + 0) * 3 + (1 + 0)],
-                            packedEdgesArray[(1 + 1) * 3 + (1 + 0)],
-                            packedEdgesArray[(1 + 0) * 3 + (1 + 1)],
-                            packedEdgesArray[(1 + 1) * 3 + (1 + 1)]);
-      uvec4 pixelsL = uvec4(packedEdgesArray[(0 + 0) * 3 + (1 + 0)],
-                            packedEdgesArray[(0 + 1) * 3 + (1 + 0)],
-                            packedEdgesArray[(0 + 0) * 3 + (1 + 1)],
-                            packedEdgesArray[(0 + 1) * 3 + (1 + 1)]);
-      uvec4 pixelsU = uvec4(packedEdgesArray[(1 + 0) * 3 + (0 + 0)],
-                            packedEdgesArray[(1 + 1) * 3 + (0 + 0)],
-                            packedEdgesArray[(1 + 0) * 3 + (0 + 1)],
-                            packedEdgesArray[(1 + 1) * 3 + (0 + 1)]);
-
-      uvec4 outEdge4 =
-          pixelsC | ((pixelsL & 0x01u) << 2u) | ((pixelsU & 0x02u) << 2u);
-      vec4 outEdge4Flt = vec4(outEdge4) / 255.0;
-
-      imageStore(g_resultEdgeTexture, screenPosIBase.xy + ivec2(0, 0),
-                 outEdge4Flt.xxxx);
-      imageStore(g_resultEdgeTexture, screenPosIBase.xy + ivec2(1, 0),
-                 outEdge4Flt.yyyy);
-      imageStore(g_resultEdgeTexture, screenPosIBase.xy + ivec2(0, 1),
-                 outEdge4Flt.zzzz);
-      imageStore(g_resultEdgeTexture, screenPosIBase.xy + ivec2(1, 1),
-                 outEdge4Flt.wwww);
-
-      // uvec4 numberOfEdges4 = uvec4(bitCount(outEdge4));
-      // gl_FragDepth =
-      //     any(greaterThan(numberOfEdges4, uvec4(1))) ? 1.0 : 0.0;
-
-      gl_FragDepth =
-          any(greaterThan(outEdge4, uvec4(1))) ? 1.0 : 0.0;
-    }
-    \n#endif\n  // COMBINE_EDGES
-
-    \n#ifdef BLUR_EDGES\n
-    layout(early_fragment_tests) in;
-    void BlurEdges() {
-      // Each |gl_FragCoord| updates 4 texels of the original texture, which are
-      // 2x|gl_FragCoord| + (-1 or 0, -1 or 0) in the unnormalized texture
-      // coordinate, which is the coordinate used by texelFetch().
-      // e.g. when gl_FragCoord == (3.5, 3.5), this fragment shader covers
-      // (6,6) (6,7) (7,6) (7,7) texels.
-      // Note: gl_FragCoord == (0.5, 0.5) (i.e. left-bottom-most fragment)
-      // covers (0,0) (0,1) (1,0) (1,1) texels
-      // gl_FragCoord == ((w/2)-0.5, (h/2)-0.5) (i.e. right-top-most fragment)
-      // covers (w-2,h-2) (w-2,h-1) (w-1,h-2) (w-1,h-1)
-      ivec3 screenPosIBase = ivec3(ivec2(gl_FragCoord.xy) * 2, 0);
-      vec3 screenPosBase = vec3(screenPosIBase);
-
-      // When gl_FragCoord == (0.5, 0.5) (i.e. left-bottom-most fragment),
-      // |sampA| textureGatherOffset() looks up (-1,-1), (-1,0), (0,-1), (0,0).
-      // (-1,-1), (-1,0), (0,-1) must be handled.
-      // Note: textureGatherOffset() on out of range gives an undefined value.
-      uvec2 notBottomLeft = uvec2(notEqual(screenPosIBase.xy, ivec2(0, 0)));
-      // When gl_FragCoord == ((w/2)-0.5, (h/2)-0.5) (i.e. right-top-most
-      // fragment), |sampD| looks up (w-1, h-1), (w-1, h), (w, h-1), (w, h).
-      // (w-1, h), (w, h-1), (w, h) must be handled.
-      uvec2 notTopRight = uvec2(
-          notEqual((screenPosIBase.xy + 2), textureSize(g_src0TextureFlt, 0)));
-
-      uint forFollowUpCount = 0u;
-      ivec4 forFollowUpCoords[4];
-
-      uint packedEdgesArray[4 * 4];
-
-      uvec4 sampA = uvec4(
-          textureGatherOffset(g_src0TextureFlt,
-                              screenPosBase.xy * g_OneOverScreenSize,
-                              ivec2(0, 0)) *255.5);
-      uvec4 sampB = uvec4(
-          textureGatherOffset(g_src0TextureFlt,
-                              screenPosBase.xy * g_OneOverScreenSize,
-                              ivec2(2, 0)) *255.5);
-      uvec4 sampC = uvec4(
-          textureGatherOffset(g_src0TextureFlt,
-                              screenPosBase.xy * g_OneOverScreenSize,
-                              ivec2(0, 2)) *255.5);
-      uvec4 sampD = uvec4(
-          textureGatherOffset(g_src0TextureFlt,
-                              screenPosBase.xy * g_OneOverScreenSize,
-                              ivec2(2, 2)) *255.5);
-
-      packedEdgesArray[(0) * 4 + (0)] =
-          sampA.w * notBottomLeft.x * notBottomLeft.y;
-      packedEdgesArray[(1) * 4 + (0)] = sampA.z * notBottomLeft.y;
-      packedEdgesArray[(0) * 4 + (1)] = sampA.x * notBottomLeft.x;
-      packedEdgesArray[(1) * 4 + (1)] = sampA.y;
-      packedEdgesArray[(2) * 4 + (0)] = sampB.w * notBottomLeft.y;
-      packedEdgesArray[(3) * 4 + (0)] =
-          sampB.z * notBottomLeft.y * notTopRight.x;
-      packedEdgesArray[(2) * 4 + (1)] = sampB.x;
-      packedEdgesArray[(3) * 4 + (1)] = sampB.y * notTopRight.x;
-      packedEdgesArray[(0) * 4 + (2)] = sampC.w * notBottomLeft.x;
-      packedEdgesArray[(1) * 4 + (2)] = sampC.z;
-      packedEdgesArray[(0) * 4 + (3)] =
-          sampC.x * notBottomLeft.x * notTopRight.y;
-      packedEdgesArray[(1) * 4 + (3)] = sampC.y * notTopRight.y;
-      packedEdgesArray[(2) * 4 + (2)] = sampD.w;
-      packedEdgesArray[(3) * 4 + (2)] = sampD.z * notTopRight.x;
-      packedEdgesArray[(2) * 4 + (3)] = sampD.x * notTopRight.y;
-      packedEdgesArray[(3) * 4 + (3)] = sampD.y * notTopRight.x * notTopRight.y;
-
-      for (int _i = 0; _i < 4; _i++) {
-        int _x = _i % 2;
-        int _y = _i / 2;
-
-        ivec3 screenPosI = screenPosIBase + ivec3(_x, _y, 0);
-
-        uint packedEdgesC = packedEdgesArray[(1 + _x) * 4 + (1 + _y)];
-
-        uvec4 edges = UnpackEdge(packedEdgesC);
-        uint numberOfEdges = edges.x + edges.y + edges.z + edges.w;
-        if (numberOfEdges <= 1u)
-          continue;
-
-        vec4 edgesFlt = vec4(edges);
-        float fromRight = edgesFlt.r;
-        float fromAbove = edgesFlt.g;
-        float fromLeft = edgesFlt.b;
-        float fromBelow = edgesFlt.a;
-
-        vec4 xFroms = vec4(fromBelow, fromAbove, fromRight, fromLeft);
-
-        float blurCoeff = 0.0;
-
-        // These are additional blurs that complement the main line-based
-        // blurring; Unlike line-based, these do not necessarily preserve
-        // the total amount of screen colour as they will take
-        // neighbouring pixel colours and apply them to the one currently
-        // processed.
-
-        // 1.) L-like shape.
-        // For this shape, the total amount of screen colour will be
-        // preserved when this is a part of a (zigzag) diagonal line as the
-        // corners from the other side  will do the same and take some of
-        // the current pixel's colour in return.
-        // However, in the case when this is an actual corner, the pixel's
-        // colour will be partially overwritten by it's 2 neighbours.
-        if (numberOfEdges == 2u)
-        {
-          // with value of 0.15, the pixel will retain approx 77% of its
-          // colour and the remaining 23% will come from its 2 neighbours
-          // (which are likely to be blurred too in the opposite direction)
-          blurCoeff = 0.15;
-
-          // Only do blending if it's L shape - if we're between two
-          // parallel edges, don't do anything
-          blurCoeff *= (1.0 - fromBelow * fromAbove) *
-                       (1.0 - fromRight * fromLeft);
-
-          if (blurCoeff == 0.0)
-            continue;
-
-          uint packedEdgesL = packedEdgesArray[(0 + _x) * 4 + (1 + _y)];
-          uint packedEdgesB = packedEdgesArray[(1 + _x) * 4 + (0 + _y)];
-          uint packedEdgesR = packedEdgesArray[(2 + _x) * 4 + (1 + _y)];
-          uint packedEdgesT = packedEdgesArray[(1 + _x) * 4 + (2 + _y)];
-
-          // Don't blend large L shape because it would be the intended shape
-          // with high probability. e.g. rectangle
-          // large_l1 large_l2 large_l3 large_l4
-          // _ _         |     |         _ _
-          //   X|       X|     |X       |X
-          //    |     ¯¯¯¯     ¯¯¯¯     |
-          bool large_l1 = (packedEdgesC == (0x01u | 0x02u)) &&
-                           bool(packedEdgesL & 0x02u) &&
-                           bool(packedEdgesB & 0x01u);
-          bool large_l2 = (packedEdgesC == (0x01u | 0x08u)) &&
-                           bool(packedEdgesL & 0x08u) &&
-                           bool(packedEdgesT & 0x01u);
-          bool large_l3 = (packedEdgesC == (0x04u | 0x08u)) &&
-                           bool(packedEdgesR & 0x08u) &&
-                           bool(packedEdgesT & 0x04u);
-          bool large_l4 = (packedEdgesC == (0x02u | 0x04u)) &&
-                           bool(packedEdgesR & 0x02u) &&
-                           bool(packedEdgesB & 0x04u);
-          if (large_l1 || large_l2 || large_l3 || large_l4)
-            continue;
-
-          // Don't blend isolated L shape because it's not a closed geometry.
-          // isolated_l1 isolated_l2 isolated_l3 isolated_l4
-          //   _                                   _
-          //   X|          X|         |X          |X
-          //               ¯¯         ¯¯
-          bool isolated_l1 = (packedEdgesC == (0x01u | 0x02u)) &&
-                           bool((packedEdgesL & 0x02u) == 0x00u) &&
-                           bool((packedEdgesT & 0x04u) == 0x00u) &&
-                           bool((packedEdgesR & 0x08u) == 0x00u) &&
-                           bool((packedEdgesB & 0x01u) == 0x00u);
-          bool isolated_l2 = (packedEdgesC == (0x01u | 0x08u)) &&
-                           bool((packedEdgesL & 0x08u) == 0x00u) &&
-                           bool((packedEdgesT & 0x01u) == 0x00u) &&
-                           bool((packedEdgesR & 0x02u) == 0x00u) &&
-                           bool((packedEdgesB & 0x04u) == 0x00u);
-          bool isolated_l3 = (packedEdgesC == (0x04u | 0x08u)) &&
-                           bool((packedEdgesL & 0x02u) == 0x00u) &&
-                           bool((packedEdgesT & 0x04u) == 0x00u) &&
-                           bool((packedEdgesR & 0x08u) == 0x00u) &&
-                           bool((packedEdgesB & 0x01u) == 0x00u);
-          bool isolated_l4 = (packedEdgesC == (0x02u | 0x04u)) &&
-                           bool((packedEdgesL & 0x08u) == 0x00u) &&
-                           bool((packedEdgesT & 0x01u) == 0x00u) &&
-                           bool((packedEdgesR & 0x02u) == 0x00u) &&
-                           bool((packedEdgesB & 0x04u) == 0x00u);
-          if (isolated_l1 || isolated_l2 || isolated_l3 || isolated_l4)
-            continue;
-        }
-
-        // 2.) U-like shape (surrounded with edges from 3 sides)
-        if (numberOfEdges == 3u) {
-          // with value of 0.13, the pixel will retain approx 72% of its
-          // colour and the remaining 28% will be picked from its 3
-          // neighbours (which are unlikely to be blurred too but could be)
-          blurCoeff = 0.13;
-        }
-
-        // 3.) Completely surrounded with edges from all 4 sides
-        if (numberOfEdges == 4u) {
-          // with value of 0.07, the pixel will retain 78% of its colour
-          // and the remaining 22% will come from its 4 neighbours (which
-          // are unlikely to be blurred)
-          blurCoeff = 0.07;
-        }
-
-        // |blurCoeff| must be not zero at this point.
-        vec4 blurMap = xFroms * blurCoeff;
-
-        vec4 pixelC = texelFetch(g_screenTexture, screenPosI.xy, 0);
-
-        const float centerWeight = 1.0;
-        float fromBelowWeight = blurMap.x;
-        float fromAboveWeight = blurMap.y;
-        float fromRightWeight = blurMap.z;
-        float fromLeftWeight  = blurMap.w;
-
-        // this would be the proper math for blending if we were handling
-        // lines (Zs) and mini kernel smoothing here, but since we're doing
-        // lines separately, no need to complicate, just tweak the settings
-        // float fromBelowWeight = (1.0 / (1.0 - blurMap.x)) - 1.0;
-        // float fromAboveWeight = (1.0 / (1.0 - blurMap.y)) - 1.0;
-        // float fromRightWeight = (1.0 / (1.0 - blurMap.z)) - 1.0;
-        // float fromLeftWeight  = (1.0 / (1.0 - blurMap.w)) - 1.0;
-
-        float fourWeightSum = dot(blurMap, vec4(1, 1, 1, 1));
-        float allWeightSum = centerWeight + fourWeightSum;
-
-        vec4 color = vec4(0, 0, 0, 0);
-        if (fromLeftWeight > 0.0) {
-          vec4 pixelL = texelFetchOffset(g_screenTexture, screenPosI.xy, 0,
-                                         ivec2(-1, 0));
-          color += fromLeftWeight * pixelL;
-        }
-        if (fromAboveWeight > 0.0) {
-          vec4 pixelT = texelFetchOffset(g_screenTexture, screenPosI.xy, 0,
-                                         ivec2(0, 1));
-          color += fromAboveWeight * pixelT;
-        }
-        if (fromRightWeight > 0.0) {
-          vec4 pixelR = texelFetchOffset(g_screenTexture, screenPosI.xy, 0,
-                                         ivec2(1, 0));
-          color += fromRightWeight * pixelR;
-        }
-        if (fromBelowWeight > 0.0) {
-          vec4 pixelB = texelFetchOffset(g_screenTexture, screenPosI.xy, 0,
-                                         ivec2(0, -1));
-          color += fromBelowWeight * pixelB;
-        }
-
-        color /= fourWeightSum + 0.0001;
-
-        color = mix(color, pixelC, centerWeight / allWeightSum);
-    \n#ifdef IN_GAMMA_CORRECT_MODE\n
-        color.rgb = D3DX_FLOAT3_to_SRGB(color.rgb);
-    \n#endif\n
-
-    \n#ifdef DEBUG_OUTPUT_AAINFO\n
-        imageStore(g_resultEdgeTexture, screenPosI.xy,
-                   PackBlurAAInfo(screenPosI.xy, numberOfEdges));
-    \n#endif\n
-        imageStore(g_resultRGBATextureSlot1, screenPosI.xy, color);
-
-        if (numberOfEdges == 2u) {
-          uint packedEdgesL = packedEdgesArray[(0 + _x) * 4 + (1 + _y)];
-          uint packedEdgesB = packedEdgesArray[(1 + _x) * 4 + (0 + _y)];
-          uint packedEdgesR = packedEdgesArray[(2 + _x) * 4 + (1 + _y)];
-          uint packedEdgesT = packedEdgesArray[(1 + _x) * 4 + (2 + _y)];
-
-          bool isHorizontalA = ((packedEdgesC) == (0x01u | 0x02u)) &&
-             ((packedEdgesR & 0x08u) == 0x08u);
-          bool isHorizontalB = ((packedEdgesC) == (0x01u | 0x08u)) &&
-             ((packedEdgesR & 0x02u) == 0x02u);
-
-          bool isHCandidate = isHorizontalA || isHorizontalB;
-
-          bool isVerticalA = ((packedEdgesC) == (0x02u | 0x04u)) &&
-             ((packedEdgesT & 0x01u) == 0x01u);
-          bool isVerticalB = ((packedEdgesC) == (0x01u | 0x02u)) &&
-             ((packedEdgesT & 0x04u) == 0x04u);
-          bool isVCandidate = isVerticalA || isVerticalB;
-
-          bool isCandidate = isHCandidate || isVCandidate;
-
-          if (!isCandidate)
-            continue;
-
-          bool horizontal = isHCandidate;
-
-          // what if both are candidates? do additional pruning (still not
-          // 100% but gets rid of worst case errors)
-          if (isHCandidate && isVCandidate)
-            horizontal =
-               (isHorizontalA && ((packedEdgesL & 0x02u) == 0x02u)) ||
-               (isHorizontalB && ((packedEdgesL & 0x08u) == 0x08u));
-
-          ivec2 offsetC;
-          uint packedEdgesM1P0;
-          uint packedEdgesP1P0;
-          if (horizontal) {
-            packedEdgesM1P0 = packedEdgesL;
-            packedEdgesP1P0 = packedEdgesR;
-            offsetC = ivec2(2, 0);
-          } else {
-            packedEdgesM1P0 = packedEdgesB;
-            packedEdgesP1P0 = packedEdgesT;
-            offsetC = ivec2(0, 2);
-          }
-
-          uvec4 edgesM1P0 = UnpackEdge(packedEdgesM1P0);
-          uvec4 edgesP1P0 = UnpackEdge(packedEdgesP1P0);
-          uvec4 edgesP2P0 = UnpackEdge(uint(texelFetch(
-             g_src0TextureFlt, screenPosI.xy + offsetC, 0).r * 255.5));
-
-          uvec4 arg0;
-          uvec4 arg1;
-          uvec4 arg2;
-          uvec4 arg3;
-          bool arg4;
-
-          if (horizontal) {
-            arg0 = uvec4(edges);
-            arg1 = edgesM1P0;
-            arg2 = edgesP1P0;
-            arg3 = edgesP2P0;
-            arg4 = true;
-          } else {
-            // Reuse the same code for vertical (used for horizontal above)
-            // but rotate input data 90º counter-clockwise. See FindLineLength()
-            // e.g. arg0.r (new top) must be mapped to edges.g (old top)
-            arg0 = uvec4(edges.gbar);
-            arg1 = edgesM1P0.gbar;
-            arg2 = edgesP1P0.gbar;
-            arg3 = edgesP2P0.gbar;
-            arg4 = false;
-          }
-
-          {
-            ivec2 screenPos = screenPosI.xy;
-            uvec4 _edges = arg0;
-            uvec4 _edgesM1P0 = arg1;
-            uvec4 _edgesP1P0 = arg2;
-            uvec4 _edgesP2P0 = arg3;
-            bool horizontal = arg4;
-
-            // Normal Z case:
-            // __
-            //  X|
-            //   ¯¯
-            bool isInvertedZ = false;
-            bool isNormalZ = false;
-            {
-    \n#ifndef SETTINGS_ALLOW_SHORT_Zs\n
-              // (1u-_edges.a) constraint can be removed; it was added for
-              // some rare cases
-              uint isZShape = _edges.r * _edges.g * _edgesM1P0.g *
-                 _edgesP1P0.a *_edgesP2P0.a * (1u - _edges.b) *
-                  (1u - _edgesP1P0.r) * (1u - _edges.a) *
-                  (1u - _edgesP1P0.g);
-    \n#else\n
-              uint isZShape = _edges.r * _edges.g * _edgesP1P0.a *
-                  (1u - _edges.b) * (1u - _edgesP1P0.r) * (1u - _edges.a) *
-                              (1u - _edgesP1P0.g);
-              isZShape *= (_edgesM1P0.g + _edgesP2P0.a);
-                              // and at least one of these need to be there
-    \n#endif\n
-              if (isZShape > 0u) {
-                isNormalZ = true;
-              }
-            }
-
-            // Inverted Z case:
-            //   __
-            //  X|
-            // ¯¯
-            {
-    \n#ifndef SETTINGS_ALLOW_SHORT_Zs\n
-              uint isZShape = _edges.r * _edges.a * _edgesM1P0.a *
-                  _edgesP1P0.g * _edgesP2P0.g * (1u - _edges.b) *
-                  (1u - _edgesP1P0.r) * (1u - _edges.g) *
-                  (1u - _edgesP1P0.a);
-    \n#else\n
-              uint isZShape = _edges.r * _edges.a * _edgesP1P0.g *
-                  (1u - _edges.b) * (1u - _edgesP1P0.r) * (1u - _edges.g) *
-                  (1u - _edgesP1P0.a);
-              isZShape *=
-                  (_edgesM1P0.a + _edgesP2P0.g);
-                              // and at least one of these need to be there
-    \n#endif\n
-
-              if (isZShape > 0u) {
-                isInvertedZ = true;
-              }
-            }
-
-            bool isZ = isInvertedZ || isNormalZ;
-            if (isZ) {
-              forFollowUpCoords[forFollowUpCount++] =
-                  ivec4(screenPosI.xy, horizontal, isInvertedZ);
-            }
-          }
-        }
-      }
-
-      // This code below is the only potential bug with this algorithm :
-      // it HAS to be executed after the simple shapes above. It used to be
-      // executed as separate compute shader (by storing the packed
-      // 'forFollowUpCoords' in an append buffer and  consuming it later)
-      // but the whole thing (append/consume buffers, using CS) appears to
-      // be too inefficient on most hardware.
-      // However, it seems to execute fairly efficiently here and without
-      // any issues, although there is no 100% guarantee that this code
-      // below will execute across all pixels (it has a c_maxLineLength
-      // wide kernel) after other shaders processing same pixels have done
-      // solving simple shapes. It appears to work regardless, across all
-      // hardware; pixels with 1-edge or two opposing edges are ignored by
-      // simple  shapes anyway and other shapes stop the long line
-      // algorithm from executing the only danger appears to be simple
-      // shape L's colliding with Z shapes from neighbouring pixels but I
-      // couldn't reproduce any problems on any hardware.
-      for (uint _i = 0u; _i < forFollowUpCount; _i++) {
-        ivec4 data = forFollowUpCoords[_i];
-        ProcessDetectedZ(data.xy, bool(data.z), bool(data.w));
-      }
-    }
-    \n#endif\n  // BLUR_EDGES
-
-    \n#ifdef DISPLAY_EDGES\n
-    layout(location = 0) out vec4 color;
-    layout(location = 1) out vec4 hasEdges;
-    void DisplayEdges() {
-      ivec2 screenPosI = ivec2(gl_FragCoord.xy);
-
-      uint packedEdges = uint(0);
-      uint shapeType = uint(0);
-      UnpackBlurAAInfo(texelFetch(g_src0TextureFlt, screenPosI, 0).r,
-                       packedEdges, shapeType);
-
-      vec4 edges = vec4(UnpackEdge(packedEdges));
-      if (any(greaterThan(edges.xyzw, vec4(0)))) {
-    \n#ifdef IN_BGR_MODE\n
-        color = c_edgeDebugColours[shapeType].bgra;
-    \n#else\n
-        color = c_edgeDebugColours[shapeType];
-    \n#endif\n
-        hasEdges = vec4(1.0);
-      } else {
-        color = vec4(0);
-        hasEdges = vec4(0.0);
-      }
-    }
-    \n#endif\n  // DISPLAY_EDGES
-
-    void main() {
-    \n#ifdef DETECT_EDGES1\n
-      DetectEdges1();
-    \n#endif\n
-    \n#if defined DETECT_EDGES2\n
-      DetectEdges2();
-    \n#endif\n
-    \n#if defined COMBINE_EDGES\n
-      CombineEdges();
-    \n#endif\n
-    \n#if defined BLUR_EDGES\n
-      BlurEdges();
-    \n#endif\n
-    \n#if defined DISPLAY_EDGES\n
-      DisplayEdges();
-    \n#endif\n
-    }
-  );
-/* clang-format on */
-
-}  // namespace gles2
-}  // namespace gpu
-
-#undef SHADER
diff --git a/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h b/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h
deleted file mode 100644
index 33bade3..0000000
--- a/gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_APPLY_FRAMEBUFFER_ATTACHMENT_CMAA_INTEL_H_
-#define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_APPLY_FRAMEBUFFER_ATTACHMENT_CMAA_INTEL_H_
-
-#include "gpu/command_buffer/service/gl_utils.h"
-#include "gpu/gpu_gles2_export.h"
-
-namespace gpu {
-namespace gles2 {
-class CopyTextureCHROMIUMResourceManager;
-class GLES2Decoder;
-class Framebuffer;
-class TextureManager;
-
-// This class encapsulates the resources required to implement the
-// GL_INTEL_framebuffer_CMAA extension via shaders.
-//
-// The CMAA Conservative Morphological Anti-Aliasing) algorithm is applied to
-// all color attachments of the currently bound draw framebuffer.
-//
-// Reference GL_INTEL_framebuffer_CMAA for details.
-class GPU_GLES2_EXPORT ApplyFramebufferAttachmentCMAAINTELResourceManager {
- public:
-  ApplyFramebufferAttachmentCMAAINTELResourceManager();
-  ~ApplyFramebufferAttachmentCMAAINTELResourceManager();
-
-  void Initialize(gles2::GLES2Decoder* decoder);
-  void Destroy();
-
-  // Applies the algorithm to the color attachments of the currently bound draw
-  // framebuffer.
-  void ApplyFramebufferAttachmentCMAAINTEL(
-      GLES2Decoder* decoder,
-      Framebuffer* framebuffer,
-      CopyTextureCHROMIUMResourceManager* copier,
-      TextureManager* texture_manager);
-
- private:
-  // Applies the CMAA algorithm to a texture.
-  void ApplyCMAAEffectTexture(GLuint source_texture,
-                              GLuint dest_texture,
-                              bool do_copy);
-
-  void OnSize(GLint width, GLint height);
-  void ReleaseTextures();
-
-  GLuint CreateProgram(const char* defines,
-                       const char* vs_source,
-                       const char* fs_source);
-  GLuint CreateShader(GLenum type, const char* defines, const char* source);
-
-  bool initialized_;
-  bool textures_initialized_;
-  bool is_in_gamma_correct_mode_;
-  bool supports_usampler_;
-  bool supports_r8_image_;
-  bool is_gles31_compatible_;
-
-  int frame_id_;
-
-  GLint width_;
-  GLint height_;
-
-  GLuint edges0_shader_;
-  GLuint edges1_shader_;
-  GLuint edges_combine_shader_;
-  GLuint process_and_apply_shader_;
-  GLuint debug_display_edges_shader_;
-
-  GLuint cmaa_framebuffer_;
-
-  GLuint rgba8_texture_;
-  GLuint working_color_texture_;
-  GLuint edges0_texture_;
-  GLuint edges1_texture_;
-  GLuint mini4_edge_texture_;
-  GLuint mini4_edge_depth_texture_;
-
-  GLuint edges0_shader_result_rgba_texture_slot1_;
-  GLuint edges0_shader_target_texture_slot2_;
-  GLuint edges1_shader_result_edge_texture_;
-  GLuint process_and_apply_shader_result_rgba_texture_slot1_;
-  GLuint edges_combine_shader_result_edge_texture_;
-
-  static const char vert_str_[];
-  static const char cmaa_frag_s1_[];
-  static const char cmaa_frag_s2_[];
-
-  DISALLOW_COPY_AND_ASSIGN(ApplyFramebufferAttachmentCMAAINTELResourceManager);
-};
-
-}  // namespace gles2
-}  // namespace gpu
-
-#endif  // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_APPLY_FRAMEBUFFER_ATTACHMENT_CMAA_INTEL_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 4145584..604c03f 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -54,7 +54,6 @@
 #include "gpu/command_buffer/service/framebuffer_manager.h"
 #include "gpu/command_buffer/service/gl_stream_texture_image.h"
 #include "gpu/command_buffer/service/gl_utils.h"
-#include "gpu/command_buffer/service/gles2_cmd_apply_framebuffer_attachment_cmaa_intel.h"
 #include "gpu/command_buffer/service/gles2_cmd_clear_framebuffer.h"
 #include "gpu/command_buffer/service/gles2_cmd_copy_tex_image.h"
 #include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
@@ -1179,7 +1178,6 @@
       const volatile GLbyte* mailbox);
   void DoBeginSharedImageAccessDirectCHROMIUM(GLuint client_id, GLenum mode);
   void DoEndSharedImageAccessDirectCHROMIUM(GLuint client_id);
-  void DoApplyScreenSpaceAntialiasingCHROMIUM();
 
   void BindImage(uint32_t client_texture_id,
                  uint32_t texture_target,
@@ -2706,8 +2704,6 @@
   // Log extra info.
   bool service_logging_;
 
-  std::unique_ptr<ApplyFramebufferAttachmentCMAAINTELResourceManager>
-      apply_framebuffer_attachment_cmaa_intel_;
   std::unique_ptr<CopyTexImageResourceManager> copy_tex_image_blit_;
   std::unique_ptr<CopyTextureCHROMIUMResourceManager> copy_texture_chromium_;
   std::unique_ptr<SRGBConverter> srgb_converter_;
@@ -5316,11 +5312,6 @@
 
   ReleaseAllBackTextures(have_context);
   if (have_context) {
-    if (apply_framebuffer_attachment_cmaa_intel_.get()) {
-      apply_framebuffer_attachment_cmaa_intel_->Destroy();
-      apply_framebuffer_attachment_cmaa_intel_.reset();
-    }
-
     if (copy_tex_image_blit_.get()) {
       copy_tex_image_blit_->Destroy();
       copy_tex_image_blit_.reset();
@@ -5437,7 +5428,6 @@
   // state_.current_program object.
   state_.current_program = nullptr;
 
-  apply_framebuffer_attachment_cmaa_intel_.reset();
   copy_tex_image_blit_.reset();
   copy_texture_chromium_.reset();
   srgb_converter_.reset();
@@ -18609,63 +18599,6 @@
   texture_ref->EndAccessSharedImage();
 }
 
-void GLES2DecoderImpl::DoApplyScreenSpaceAntialiasingCHROMIUM() {
-  Framebuffer* bound_framebuffer =
-      GetFramebufferInfoForTarget(GL_DRAW_FRAMEBUFFER);
-  // TODO(dshwang): support it even after glBindFrameBuffer(GL_FRAMEBUFFER, 0).
-  // skia will need to render to the window. crbug.com/656618
-  if (!bound_framebuffer) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION,
-                       "glApplyScreenSpaceAntialiasingCHROMIUM",
-                       "no bound framebuffer object");
-    return;
-  }
-
-  // Apply CMAA(Conservative Morphological Anti-Aliasing) algorithm to the
-  // color attachments of currently bound draw framebuffer.
-  // Reference GL_INTEL_framebuffer_CMAA for details.
-  // Use platform version if available.
-  if (!feature_info_->feature_flags()
-           .use_chromium_screen_space_antialiasing_via_shaders) {
-    api()->glApplyFramebufferAttachmentCMAAINTELFn();
-  } else {
-    // Defer initializing the CopyTextureCHROMIUMResourceManager until it is
-    // needed because it takes ??s of milliseconds to initialize.
-    if (!apply_framebuffer_attachment_cmaa_intel_.get()) {
-      LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER(
-          "glApplyFramebufferAttachmentCMAAINTEL");
-      apply_framebuffer_attachment_cmaa_intel_.reset(
-          new ApplyFramebufferAttachmentCMAAINTELResourceManager());
-      apply_framebuffer_attachment_cmaa_intel_->Initialize(this);
-      if (LOCAL_PEEK_GL_ERROR("glApplyFramebufferAttachmentCMAAINTEL") !=
-          GL_NO_ERROR)
-        return;
-    }
-    static const char kFunctionName[] =
-        "glApplyScreenSpaceAntialiasingCHROMIUM";
-    if (!InitializeCopyTextureCHROMIUM(kFunctionName))
-      return;
-    for (uint32_t i = 0; i < group_->max_draw_buffers(); ++i) {
-      const Framebuffer::Attachment* attachment =
-          bound_framebuffer->GetAttachment(GL_COLOR_ATTACHMENT0 + i);
-      if (attachment && attachment->IsTextureAttachment()) {
-        GLenum internal_format = attachment->internal_format();
-        if (!CanUseCopyTextureCHROMIUMInternalFormat(internal_format)) {
-          LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, kFunctionName,
-                             "Apply CMAA on framebuffer with attachment in "
-                             "invalid internalformat.");
-          return;
-        }
-      }
-    }
-
-    apply_framebuffer_attachment_cmaa_intel_
-        ->ApplyFramebufferAttachmentCMAAINTEL(this, bound_framebuffer,
-                                              copy_texture_chromium_.get(),
-                                              texture_manager());
-  }
-}
-
 void GLES2DecoderImpl::DoInsertEventMarkerEXT(
     GLsizei length, const GLchar* marker) {
   if (!marker) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
index 47e756ea9..7cccbf5 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h
@@ -5407,17 +5407,6 @@
   return error::kNoError;
 }
 
-error::Error GLES2DecoderImpl::HandleApplyScreenSpaceAntialiasingCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  if (!features().chromium_screen_space_antialiasing) {
-    return error::kUnknownCommand;
-  }
-
-  DoApplyScreenSpaceAntialiasingCHROMIUM();
-  return error::kNoError;
-}
-
 error::Error
 GLES2DecoderImpl::HandleUniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate(
     uint32_t immediate_data_size,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
index 3e6179a5..629f771e 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
@@ -1050,7 +1050,6 @@
                                                    GLsizei coeffsBufsize);
 error::Error DoCoverageModulationCHROMIUM(GLenum components);
 error::Error DoBlendBarrierKHR();
-error::Error DoApplyScreenSpaceAntialiasingCHROMIUM();
 error::Error DoBindFragDataLocationIndexedEXT(GLuint program,
                                               GLuint colorNumber,
                                               GLuint index,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index e76e674..2f9a161 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -5161,12 +5161,6 @@
   return error::kNoError;
 }
 
-error::Error
-GLES2DecoderPassthroughImpl::DoApplyScreenSpaceAntialiasingCHROMIUM() {
-  NOTIMPLEMENTED();
-  return error::kNoError;
-}
-
 error::Error GLES2DecoderPassthroughImpl::DoBindFragDataLocationIndexedEXT(
     GLuint program,
     GLuint colorNumber,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
index 019dfec..caac9cfa 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers_autogen.cc
@@ -4659,21 +4659,6 @@
   return error::kNoError;
 }
 
-error::Error
-GLES2DecoderPassthroughImpl::HandleApplyScreenSpaceAntialiasingCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  if (!features().chromium_screen_space_antialiasing) {
-    return error::kUnknownCommand;
-  }
-
-  error::Error error = DoApplyScreenSpaceAntialiasingCHROMIUM();
-  if (error != error::kNoError) {
-    return error;
-  }
-  return error::kNoError;
-}
-
 error::Error GLES2DecoderPassthroughImpl::
     HandleUniformMatrix4fvStreamTextureMatrixCHROMIUMImmediate(
         uint32_t immediate_data_size,
diff --git a/gpu/command_buffer/tests/gl_apply_screen_space_antialiasing_CHROMIUM_unittest.cc b/gpu/command_buffer/tests/gl_apply_screen_space_antialiasing_CHROMIUM_unittest.cc
deleted file mode 100644
index 2f33475..0000000
--- a/gpu/command_buffer/tests/gl_apply_screen_space_antialiasing_CHROMIUM_unittest.cc
+++ /dev/null
@@ -1,512 +0,0 @@
-// Copyright (c) 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef GL_GLEXT_PROTOTYPES
-#define GL_GLEXT_PROTOTYPES
-#endif
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GLES2/gl2extchromium.h>
-#include <GLES3/gl3.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/stl_util.h"
-#include "gpu/command_buffer/tests/gl_manager.h"
-#include "gpu/command_buffer/tests/gl_test_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace gpu {
-
-// A collection of tests that exercise the GL_CHROMIUM_copy_texture extension.
-class GLApplyScreenSpaceAntialiasingCHROMIUMTest : public testing::Test {
- protected:
-  void CreateAndBindDestinationTextureAndFBO(GLenum target) {
-    glGenTextures(1, &textures_);
-    glBindTexture(target, textures_);
-
-    // Some drivers (NVidia/SGX) require texture settings to be a certain way or
-    // they won't report FRAMEBUFFER_COMPLETE.
-    glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
-    glGenFramebuffers(1, &framebuffer_id_);
-    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id_);
-    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target,
-                           textures_, 0);
-  }
-
-  void CheckStatus() {
-    available_ =
-        GLTestHelper::HasExtension("GL_CHROMIUM_screen_space_antialiasing");
-    if (!available_) {
-      LOG(INFO) << "GL_CHROMIUM_screen_space_antialiasing not supported. "
-                   "Skipping test...";
-      return;
-    }
-
-    // Antialiasing will be enabled if framebuffer CMAA is enabled via GPU
-    // driver workarounds 'use_framebuffer_cmaa'.
-    available_ = gl_.workarounds().use_framebuffer_cmaa;
-    if (!available_) {
-      LOG(INFO) << "'use_framebuffer_cmaa' workaround not applied. "
-                   "Skipping test...";
-      return;
-    }
-
-    CreateAndBindDestinationTextureAndFBO(GL_TEXTURE_2D);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
-                 nullptr);
-    EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
-              glCheckFramebufferStatus(GL_FRAMEBUFFER));
-
-    glClearColor(0, 1, 0, 1);
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    glApplyScreenSpaceAntialiasingCHROMIUM();
-    if (glGetError() == GL_NO_ERROR)
-      return;
-
-    // For example, linux NVidia fails with this log.
-    // ApplyFramebufferAttachmentCMAAINTEL: shader compilation failed:
-    // GL_FRAGMENT_SHADER shader compilation failed: 0(98) : error C3013:
-    // input/output layout qualifiers supported above GL version 130
-    available_ = false;
-    LOG(ERROR) << "GL_CHROMIUM_screen_space_antialiasing maybe not supported "
-                  "in non-Intel GPU.";
-    DeleteResources();
-  }
-
-  void SetUp() override {
-    GLManager::Options options;
-    gl_.Initialize(options);
-    CheckStatus();
-  }
-
-  void TearDown() override {
-    if (available_)
-      DeleteResources();
-    gl_.Destroy();
-  }
-
-  void DeleteResources() {
-    glDeleteTextures(1, &textures_);
-    glDeleteFramebuffers(1, &framebuffer_id_);
-  }
-
-  GLManager gl_;
-  GLuint textures_ = 0;
-  GLuint framebuffer_id_ = 0;
-  bool available_ = false;
-};
-
-class GLApplyScreenSpaceAntialiasingCHROMIUMES3Test
-    : public GLApplyScreenSpaceAntialiasingCHROMIUMTest {
- protected:
-  void SetUp() override {
-    GLManager::Options options;
-    options.context_type = CONTEXT_TYPE_OPENGLES3;
-    gl_.Initialize(options);
-    CheckStatus();
-  }
-  void CheckStatus() {
-    // Not applicable for devices not supporting OpenGLES3.
-    if (!gl_.IsInitialized()) {
-      LOG(INFO) << "CONTEXT_TYPE_OPENGLES3 not supported. "
-                   "Skipping test...";
-      return;
-    }
-    GLApplyScreenSpaceAntialiasingCHROMIUMTest::CheckStatus();
-  }
-};
-
-// TODO(dongseong.hwang): This test fails on the Nexus 9 GPU fyi bot.
-// crbug.com/659638
-#if defined(OS_ANDROID)
-#define MAYBE_Basic DISABLED_Basic
-#else
-#define MAYBE_Basic Basic
-#endif
-// Test to ensure that the basic functionality of the extension works.
-TEST_F(GLApplyScreenSpaceAntialiasingCHROMIUMTest, MAYBE_Basic) {
-  if (!available_)
-    return;
-
-  glApplyScreenSpaceAntialiasingCHROMIUM();
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  // Check the FB is still bound.
-  GLint value = 0;
-  glGetIntegerv(GL_FRAMEBUFFER_BINDING, &value);
-  GLuint fb_id = value;
-  EXPECT_EQ(framebuffer_id_, fb_id);
-
-  // Check that FB is complete.
-  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
-            glCheckFramebufferStatus(GL_FRAMEBUFFER));
-
-  uint8_t pixels[1 * 4] = {0u, 255u, 0u, 255u};
-  GLTestHelper::CheckPixels(0, 0, 1, 1, 0, pixels, nullptr);
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-}
-
-TEST_F(GLApplyScreenSpaceAntialiasingCHROMIUMTest, DefaultFBO) {
-  if (!available_)
-    return;
-
-  glBindFramebuffer(GL_FRAMEBUFFER, 0);
-  glApplyScreenSpaceAntialiasingCHROMIUM();
-  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
-}
-
-TEST_F(GLApplyScreenSpaceAntialiasingCHROMIUMTest, InternalFormat) {
-  if (!available_)
-    return;
-
-  GLint formats[] = {GL_RGB, GL_RGBA, GL_ALPHA, GL_LUMINANCE,
-                     GL_LUMINANCE_ALPHA};
-  for (size_t index = 0; index < base::size(formats); index++) {
-    glTexImage2D(GL_TEXTURE_2D, 0, formats[index], 1, 1, 0, formats[index],
-                 GL_UNSIGNED_BYTE, nullptr);
-    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                           textures_, 0);
-
-    // Only if the format is supported by FBO, supported by this extension.
-    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
-      continue;
-
-    glClear(GL_COLOR_BUFFER_BIT);
-    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-    glApplyScreenSpaceAntialiasingCHROMIUM();
-    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()) << "index:"
-                                                              << index;
-
-    uint8_t pixels[1 * 4] = {0u, 255u, 0u, 255u};
-    GLTestHelper::CheckPixels(0, 0, 1, 1, 0, pixels, nullptr);
-    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-  }
-}
-
-struct FormatType {
-  GLenum internal_format;
-  GLenum format;
-  GLenum type;
-};
-
-TEST_F(GLApplyScreenSpaceAntialiasingCHROMIUMES3Test, InternalFormat) {
-  if (!available_)
-    return;
-
-  FormatType format_type = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
-
-  uint8_t pixels[1 * 4] = {0u, 255u, 0u, 255u};
-  glTexImage2D(GL_TEXTURE_2D, 0, format_type.internal_format, 1, 1, 0,
-               format_type.format, format_type.type, pixels);
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                         textures_, 0);
-
-  // Only if the format is supported by FBO, supported by this extension.
-  if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
-    return;
-
-  glApplyScreenSpaceAntialiasingCHROMIUM();
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError())
-      << "internal_format: "
-      << gles2::GLES2Util::GetStringEnum(format_type.internal_format);
-
-  GLTestHelper::CheckPixels(0, 0, 1, 1, 0, pixels, nullptr);
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-}
-
-TEST_F(GLApplyScreenSpaceAntialiasingCHROMIUMES3Test,
-       InternalFormatNotSupported) {
-  if (!available_)
-    return;
-
-  FormatType format_type = {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT};
-
-  uint32_t pixels[1 * 4] = {0u, 255u, 0u, 255u};
-  glTexImage2D(GL_TEXTURE_2D, 0, format_type.internal_format, 1, 1, 0,
-               format_type.format, format_type.type, pixels);
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                         textures_, 0);
-
-  // Only if the format is supported by FBO, supported by this extension.
-  if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
-    return;
-
-  glApplyScreenSpaceAntialiasingCHROMIUM();
-  EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError())
-      << "internal_format: "
-      << gles2::GLES2Util::GetStringEnum(format_type.internal_format);
-}
-
-TEST_F(GLApplyScreenSpaceAntialiasingCHROMIUMTest, ImmutableTexture) {
-  if (!available_)
-    return;
-
-  if (!GLTestHelper::HasExtension("GL_EXT_texture_storage")) {
-    LOG(INFO) << "GL_EXT_texture_storage not supported. Skipping test...";
-    return;
-  }
-  GLenum internal_formats[] = {GL_RGB8_OES, GL_RGBA8_OES, GL_BGRA8_EXT};
-  for (auto internal_format : internal_formats) {
-    glDeleteTextures(1, &textures_);
-    glDeleteFramebuffers(1, &framebuffer_id_);
-    CreateAndBindDestinationTextureAndFBO(GL_TEXTURE_2D);
-    glTexStorage2DEXT(GL_TEXTURE_2D, 1, internal_format, 1, 1);
-    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-    EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
-              glCheckFramebufferStatus(GL_FRAMEBUFFER));
-
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    glApplyScreenSpaceAntialiasingCHROMIUM();
-    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-    uint8_t pixels[1 * 4] = {0u, 255u, 0u, 255u};
-    GLTestHelper::CheckPixels(0, 0, 1, 1, 0, pixels, nullptr);
-    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-  }
-}
-
-// Similar to webgl conformance test 'testAntialias(true)' in
-// context-attributes-alpha-depth-stencil-antialias.html
-TEST_F(GLApplyScreenSpaceAntialiasingCHROMIUMTest, AntiAliasing) {
-  if (!available_)
-    return;
-
-  // Fill colors in the FBO as follows
-  //  +-----+
-  //  |R|R| |
-  //  +-----+
-  //  |R| | |
-  //  +-----+
-  //  | | | |
-  //  +-+-+-+
-  const int length = 3;
-  uint8_t rgba_pixels[4 * length * length] = {
-      255u, 0u, 0u, 255u, 255u, 0u, 0u, 255u, 0u, 0u, 0u, 0u,
-      255u, 0u, 0u, 255u, 0u,   0u, 0u, 0u,   0u, 0u, 0u, 0u,
-      0u,   0u, 0u, 0u,   0u,   0u, 0u, 0u,   0u, 0u, 0u, 0u,
-  };
-  glBindTexture(GL_TEXTURE_2D, textures_);
-  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, length, length, 0, GL_RGBA,
-               GL_UNSIGNED_BYTE, rgba_pixels);
-
-  uint8_t transparent[4] = {0u, 0u, 0u, 0u};
-  uint8_t red[4] = {255u, 0u, 0u, 255u};
-  GLTestHelper::CheckPixels(0, 0, 1, 1, 0, red, nullptr);
-  GLTestHelper::CheckPixels(0, 1, 1, 1, 0, red, nullptr);
-  GLTestHelper::CheckPixels(0, 2, 1, 1, 0, transparent, nullptr);
-  GLTestHelper::CheckPixels(1, 0, 1, 1, 0, red, nullptr);
-  GLTestHelper::CheckPixels(1, 1, 1, 1, 0, transparent, nullptr);
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  glApplyScreenSpaceAntialiasingCHROMIUM();
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  GLTestHelper::CheckPixels(0, 0, 1, 1, 0, red, nullptr);
-  GLTestHelper::CheckPixels(2, 2, 1, 1, 0, transparent, nullptr);
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  // Check if middle pixel is anti-aliased.
-  uint8_t pixels[4] = {0u};
-  glReadPixels(1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixels);
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-  EXPECT_NE(transparent, pixels);
-  EXPECT_NE(red, pixels);
-  EXPECT_TRUE(pixels[0] > transparent[0] && pixels[0] < red[0]);
-  EXPECT_EQ(0u, pixels[1]);
-  EXPECT_EQ(0u, pixels[2]);
-  EXPECT_TRUE(pixels[3] > transparent[3] && pixels[3] < red[3]);
-}
-
-namespace {
-
-void glEnableDisable(GLint param, GLboolean value) {
-  if (value)
-    glEnable(param);
-  else
-    glDisable(param);
-}
-
-}  // unnamed namespace
-
-// Validate that some basic GL state is not touched upon execution of
-// the extension.
-TEST_F(GLApplyScreenSpaceAntialiasingCHROMIUMTest, BasicStatePreservation) {
-  if (!available_)
-    return;
-
-  GLboolean reference_settings[2] = {GL_TRUE, GL_FALSE};
-  for (int x = 0; x < 2; ++x) {
-    GLboolean setting = reference_settings[x];
-    glEnableDisable(GL_DEPTH_TEST, setting);
-    glEnableDisable(GL_SCISSOR_TEST, setting);
-    glEnableDisable(GL_STENCIL_TEST, setting);
-    glEnableDisable(GL_CULL_FACE, setting);
-    glEnableDisable(GL_BLEND, setting);
-    glColorMask(setting, setting, setting, setting);
-    glDepthMask(setting);
-
-    glActiveTexture(GL_TEXTURE1 + x);
-
-    glApplyScreenSpaceAntialiasingCHROMIUM();
-    EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-    EXPECT_EQ(setting, glIsEnabled(GL_DEPTH_TEST));
-    EXPECT_EQ(setting, glIsEnabled(GL_SCISSOR_TEST));
-    EXPECT_EQ(setting, glIsEnabled(GL_STENCIL_TEST));
-    EXPECT_EQ(setting, glIsEnabled(GL_CULL_FACE));
-    EXPECT_EQ(setting, glIsEnabled(GL_BLEND));
-
-    GLboolean bool_array[4] = {GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE};
-    glGetBooleanv(GL_DEPTH_WRITEMASK, bool_array);
-    EXPECT_EQ(setting, bool_array[0]);
-
-    bool_array[0] = GL_FALSE;
-    glGetBooleanv(GL_COLOR_WRITEMASK, bool_array);
-    EXPECT_EQ(setting, bool_array[0]);
-    EXPECT_EQ(setting, bool_array[1]);
-    EXPECT_EQ(setting, bool_array[2]);
-    EXPECT_EQ(setting, bool_array[3]);
-
-    GLint active_texture = 0;
-    glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
-    EXPECT_EQ(GL_TEXTURE1 + x, active_texture);
-  }
-
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-}
-
-// Verify that invocation of the extension does not modify the bound
-// texture state.
-TEST_F(GLApplyScreenSpaceAntialiasingCHROMIUMTest, TextureStatePreserved) {
-  if (!available_)
-    return;
-
-  GLuint texture_ids[2];
-  glGenTextures(2, texture_ids);
-
-  glActiveTexture(GL_TEXTURE0);
-  glBindTexture(GL_TEXTURE_2D, texture_ids[0]);
-
-  glActiveTexture(GL_TEXTURE1);
-  glBindTexture(GL_TEXTURE_2D, texture_ids[1]);
-
-  glApplyScreenSpaceAntialiasingCHROMIUM();
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  GLint active_texture = 0;
-  glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
-  EXPECT_EQ(GL_TEXTURE1, active_texture);
-
-  GLint bound_texture = 0;
-  glGetIntegerv(GL_TEXTURE_BINDING_2D, &bound_texture);
-  EXPECT_EQ(texture_ids[1], static_cast<GLuint>(bound_texture));
-  glBindTexture(GL_TEXTURE_2D, 0);
-
-  bound_texture = 0;
-  glActiveTexture(GL_TEXTURE0);
-  glGetIntegerv(GL_TEXTURE_BINDING_2D, &bound_texture);
-  EXPECT_EQ(texture_ids[0], static_cast<GLuint>(bound_texture));
-  glBindTexture(GL_TEXTURE_2D, 0);
-
-  glDeleteTextures(2, texture_ids);
-
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-}
-
-TEST_F(GLApplyScreenSpaceAntialiasingCHROMIUMTest, ProgramStatePreservation) {
-  if (!available_)
-    return;
-
-  // unbind the one created in setup.
-  glBindFramebuffer(GL_FRAMEBUFFER, 0);
-  glBindTexture(GL_TEXTURE_2D, 0);
-
-  GLManager gl2;
-  GLManager::Options options;
-  options.size = gfx::Size(16, 16);
-  options.share_group_manager = &gl_;
-  gl2.Initialize(options);
-  gl_.MakeCurrent();
-
-  static const char* v_shader_str =
-      "attribute vec4 g_Position;\n"
-      "void main()\n"
-      "{\n"
-      "   gl_Position = g_Position;\n"
-      "}\n";
-  static const char* f_shader_str =
-      "precision mediump float;\n"
-      "void main()\n"
-      "{\n"
-      "  gl_FragColor = vec4(0,1,0,1);\n"
-      "}\n";
-
-  GLuint program = GLTestHelper::LoadProgram(v_shader_str, f_shader_str);
-  glUseProgram(program);
-  GLuint position_loc = glGetAttribLocation(program, "g_Position");
-  glFlush();
-
-  // Delete program from other context.
-  gl2.MakeCurrent();
-  glDeleteProgram(program);
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-  glFlush();
-
-  // Program should still be usable on this context.
-  gl_.MakeCurrent();
-
-  GLTestHelper::SetupUnitQuad(position_loc);
-
-  // test using program before
-  uint8_t expected[] = {
-      0, 255, 0, 255,
-  };
-  uint8_t zero[] = {
-      0, 0, 0, 0,
-  };
-  glClearColor(0, 0, 0, 0);
-  glClear(GL_COLOR_BUFFER_BIT);
-  EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0, zero, nullptr));
-  glDrawArrays(GL_TRIANGLES, 0, 6);
-  EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0, expected, nullptr));
-
-  // Call copyTextureCHROMIUM
-  uint8_t pixels[1 * 4] = {255u, 0u, 0u, 255u};
-  glBindTexture(GL_TEXTURE_2D, textures_);
-  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
-               pixels);
-  glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id_);
-  glApplyScreenSpaceAntialiasingCHROMIUM();
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  // test using program after
-  glClear(GL_COLOR_BUFFER_BIT);
-  EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0, zero, nullptr));
-  glDrawArrays(GL_TRIANGLES, 0, 6);
-  EXPECT_TRUE(GLTestHelper::CheckPixels(0, 0, 1, 1, 0, expected, nullptr));
-
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  gl2.MakeCurrent();
-  gl2.Destroy();
-  gl_.MakeCurrent();
-}
-
-}  // namespace gpu
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 2ff94d58a..547ad19 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -1596,25 +1596,6 @@
       ]
     },
     {
-      "id": 172,
-      "description": "Use GL_INTEL_framebuffer_CMAA on ChromeOS",
-      "cr_bugs": [535198],
-      "os": {
-        "type" : "chromeos"
-      },
-      "vendor_id": "0x8086",
-      "driver_vendor": "Mesa",
-      "gl_vendor": "Intel.*",
-      "gl_type": "gles",
-      "gl_version": {
-        "op": ">=",
-        "value": "3.1"
-      },
-      "features": [
-        "use_framebuffer_cmaa"
-      ]
-    },
-    {
       "id": 174,
       "description": "Adreno 4xx support for EXT_multisampled_render_to_texture is buggy on Android 7.0",
       "cr_bugs": [612474],
@@ -3362,6 +3343,40 @@
       "disabled_extensions": [
         "GL_MESA_framebuffer_flip_y"
       ]
+    },
+    {
+      "id": 316,
+      "cr_bugs": [1003860],
+      "description": "Limit MSAA to 4x on ChromeOS for Intel",
+      "os": {
+        "type": "chromeos"
+      },
+      "intel_gpu_generation": {
+        "op": ">=",
+        "value": "9"
+      },
+      "driver_vendor": "Mesa",
+      "gl_vendor": "Intel.*",
+      "features": [
+        "max_msaa_sample_count_4"
+      ]
+    },
+    {
+      "id": 317,
+      "cr_bugs": [1003860],
+      "description": "Limit MSAA to 2x on older Intel GPU generations on ChromeOS",
+      "os": {
+        "type": "chromeos"
+      },
+      "intel_gpu_generation": {
+        "op": "<",
+        "value": "9"
+      },
+      "driver_vendor": "Mesa",
+      "gl_vendor": "Intel.*",
+      "features": [
+        "max_msaa_sample_count_2"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
index 5d5beda..c78c544 100644
--- a/gpu/config/gpu_workaround_list.txt
+++ b/gpu/config/gpu_workaround_list.txt
@@ -73,6 +73,7 @@
 init_vertex_attributes
 max_copy_texture_chromium_size_1048576
 max_copy_texture_chromium_size_262144
+max_msaa_sample_count_2
 max_msaa_sample_count_4
 max_texture_size_limit_4096
 msaa_is_slow
@@ -103,7 +104,6 @@
 unpack_overlapping_rows_separately_unpack_buffer
 use_client_side_arrays_for_stream_buffers
 use_es2_for_oopr
-use_framebuffer_cmaa
 use_gpu_driver_workaround_for_testing
 use_intermediary_for_copy_texture_image
 use_non_zero_size_for_client_side_stream_buffers
diff --git a/infra/config/consoles/android.packager.star b/infra/config/consoles/android.packager.star
new file mode 100644
index 0000000..cbbf6ac8
--- /dev/null
+++ b/infra/config/consoles/android.packager.star
@@ -0,0 +1,15 @@
+luci.console_view(
+    name = 'android.packager',
+    header = '//consoles/chromium-header.textpb',
+    repo = 'https://chromium.googlesource.com/chromium/src',
+    entries = [
+        luci.console_view_entry(
+            builder = 'ci/android-avd-packager',
+            short_name = 'avd',
+        ),
+        luci.console_view_entry(
+            builder = 'ci/android-sdk-packager',
+            short_name = 'sdk',
+        ),
+    ],
+)
diff --git a/infra/config/consoles/chromium.fyi.star b/infra/config/consoles/chromium.fyi.star
index b423f87..408e547 100644
--- a/infra/config/consoles/chromium.fyi.star
+++ b/infra/config/consoles/chromium.fyi.star
@@ -306,6 +306,10 @@
             category = 'win10|1803',
         ),
         luci.console_view_entry(
+            builder = 'ci/win32-arm64-rel',
+            category = 'win32|arm64',
+        ),
+        luci.console_view_entry(
             builder = 'ci/win-celab-builder-rel',
             category = 'celab',
         ),
diff --git a/infra/config/consoles/chromium.star b/infra/config/consoles/chromium.star
index d34cf1b..89fb2f657 100644
--- a/infra/config/consoles/chromium.star
+++ b/infra/config/consoles/chromium.star
@@ -36,12 +36,22 @@
         ),
         luci.console_view_entry(
             builder = 'ci/win32-archive-rel',
-            category = 'win-rel',
+            category = 'win|rel',
             short_name = '32',
         ),
         luci.console_view_entry(
             builder = 'ci/win-archive-rel',
-            category = 'win-rel',
+            category = 'win|rel',
+            short_name = '64',
+        ),
+        luci.console_view_entry(
+            builder = 'ci/win32-archive-dbg',
+            category = 'win|dbg',
+            short_name = '32',
+        ),
+        luci.console_view_entry(
+            builder = 'ci/win-archive-dbg',
+            category = 'win|dbg',
             short_name = '64',
         ),
     ],
diff --git a/infra/config/consoles/findit.star b/infra/config/consoles/findit.star
new file mode 100644
index 0000000..0d597fe
--- /dev/null
+++ b/infra/config/consoles/findit.star
@@ -0,0 +1,8 @@
+luci.list_view(
+    name = 'findit',
+    entries = [
+        'findit/findit-rerun',
+        'findit/findit_variable',
+        'findit/linux_chromium_bot_db_exporter',
+    ],
+)
diff --git a/infra/config/consoles/tryserver.chromium.dawn.star b/infra/config/consoles/tryserver.chromium.dawn.star
new file mode 100644
index 0000000..3aa79dc
--- /dev/null
+++ b/infra/config/consoles/tryserver.chromium.dawn.star
@@ -0,0 +1,12 @@
+luci.list_view(
+    name = 'tryserver.chromium.dawn',
+    entries = [
+        'try/dawn-linux-x64-deps-rel',
+        'try/dawn-mac-x64-deps-rel',
+        'try/dawn-win10-x64-deps-rel',
+        'try/dawn-win10-x86-deps-rel',
+        'try/linux-dawn-rel',
+        'try/mac-dawn-rel',
+        'try/win-dawn-rel',
+    ],
+)
diff --git a/infra/config/consoles/tryserver.chromium.linux.star b/infra/config/consoles/tryserver.chromium.linux.star
index cb67065..1be61ea0 100644
--- a/infra/config/consoles/tryserver.chromium.linux.star
+++ b/infra/config/consoles/tryserver.chromium.linux.star
@@ -28,6 +28,7 @@
         'try/layout_test_leak_detection',
         'try/leak_detection_linux',
         'try/linux-annotator-rel',
+        'try/linux-dcheck-off-rel',
         'try/linux-gcc-rel',
         'try/linux-libfuzzer-asan-rel',
         'try/linux-ozone-rel',
@@ -35,6 +36,7 @@
         'try/linux-clang-tidy-dbg',
         'try/linux-clang-tidy-rel',
         'try/linux-trusty-rel',
+        'try/linux-webkit-msan-rel',
         'try/linux_arm',
         'try/linux_chromium_analysis',
         'try/linux_chromium_archive_rel_ng',
diff --git a/infra/config/consoles/tryserver.chromium.win.star b/infra/config/consoles/tryserver.chromium.win.star
index 4c59559..0ed185a 100644
--- a/infra/config/consoles/tryserver.chromium.win.star
+++ b/infra/config/consoles/tryserver.chromium.win.star
@@ -17,6 +17,8 @@
         'try/gpu-fyi-try-win10-nvidia-rel-32',
         'try/gpu-fyi-try-win10-nvidia-rel-64',
         'try/gpu-fyi-try-win10-nvidia-skgl-64',
+        'try/gpu-try-win10-nvidia-rel',
+        'try/win-asan',
         'try/win-libfuzzer-asan-rel',
         'try/win7-rel',
         'try/win10_chromium_x64_dbg_ng',
diff --git a/infra/config/dev.star b/infra/config/dev.star
index faf5459..a7da408 100755
--- a/infra/config/dev.star
+++ b/infra/config/dev.star
@@ -14,16 +14,11 @@
     fail_on_warnings = True,
 )
 
-# Copy the not-yet migrated files to the generated outputs
-# TODO(https://crbug.com/1011908) Migrate the configuration in these files to starlark
-[lucicfg.emit(dest = f, data = io.read_file(f)) for f in (
-    'luci-milo-dev.cfg',
-)]
-
 luci.project(
     name = 'chromium',
     buildbucket = 'cr-buildbucket-dev.appspot.com',
     logdog = 'luci-logdog-dev.appspot.com',
+    milo = 'luci-milo-dev.appspot.com',
     scheduler = 'luci-scheduler-dev.appspot.com',
     swarming = 'chromium-swarm-dev.appspot.com',
     acls = [
@@ -50,6 +45,13 @@
     gs_bucket = 'chromium-luci-logdog',
 )
 
+luci.milo(
+    logo = 'https://storage.googleapis.com/chrome-infra-public/logo/chromium.svg',
+)
+
 exec('//dev/buckets/ci.star')
 exec('//dev/buckets/cron.star')
 exec('//dev/buckets/try.star')
+
+exec('//dev/consoles/chromium.swarm.star')
+exec('//dev/consoles/snapshots.star')
diff --git a/infra/config/dev/consoles/chromium-header.textpb b/infra/config/dev/consoles/chromium-header.textpb
new file mode 100644
index 0000000..070905e
--- /dev/null
+++ b/infra/config/dev/consoles/chromium-header.textpb
@@ -0,0 +1,145 @@
+oncalls: {
+  name: "Chromium"
+  url: "https://build.chromium.org/p/chromium/sheriff.json"
+}
+oncalls: {
+  name: "Android"
+  url: "https://build.chromium.org/p/chromium/sheriff_android.json"
+}
+oncalls: {
+  name: "iOS"
+  url: "https://build.chromium.org/p/chromium/sheriff_ios.json"
+}
+oncalls: {
+  name: "CrOS MTV"
+  url: "https://build.chromium.org/p/chromium/sheriff_cros_mtv.json"
+}
+oncalls: {
+  name: "CrOS non-MTV"
+  url: "https://build.chromium.org/p/chromium/sheriff_cros_nonmtv.json"
+}
+oncalls: {
+  name: "ChromeOS Gardener"
+  url: "https://build.chromium.org/p/chromium/sheriff_cr_cros_gardeners.json"
+}
+oncalls: {
+  name: "GPU"
+  url: "https://build.chromium.org/p/chromium/sheriff_gpu.json"
+}
+oncalls: {
+  name: "Angle"
+  url: "https://build.chromium.org/p/chromium/sheriff_angle.json"
+}
+oncalls: {
+  name: "Perf"
+  url: "https://build.chromium.org/p/chromium/sheriff_perf.json"
+}
+oncalls: {
+  name: "Perfbot"
+  url: "https://build.chromium.org/p/chromium/sheriff_perfbot.json"
+}
+oncalls: {
+  name: "V8"
+  url: "https://build.chromium.org/p/chromium/sheriff_v8.json"
+}
+oncalls: {
+  name: "Trooper"
+  url: "https://build.chromium.org/p/chromium/trooper.json"
+}
+
+links: {
+  name: "Builds"
+  links: {
+    text: "continuous"
+    url: "https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html"
+    alt: "Continuous browser snapshots"
+  }
+  links: {
+    text: "symbols"
+    url: "https://www.chromium.org/developers/how-tos/debugging-on-windows"
+    alt: "Windows Symbols"
+  }
+  links: {
+    text: "status"
+    url: "https://chromium-status.appspot.com/"
+    alt: "Current tree status"
+  }
+}
+
+links: {
+  name: "Dashboards"
+  links: {
+    text: "perf"
+    url: "https://chromeperf.appspot.com/"
+    alt: "Chrome perf dashboard"
+  }
+  links {
+    text: "flake-portal"
+    url: "https://analysis.chromium.org/p/chromium/flake-portal"
+    alt: "New flake portal"
+  }
+  links {
+    text: "legacy-flakiness"
+    url: "https://test-results.appspot.com/dashboards/flakiness_dashboard.html"
+    alt: "Legacy flakiness dashboard"
+  }
+}
+
+links: {
+  name: "Chromium"
+  links: {
+    text: "source"
+    url: "https://chromium.googlesource.com/chromium/src"
+    alt: "Chromium source code repository"
+  }
+  links: {
+    text: "reviews"
+    url: "https://chromium-review.googlesource.com"
+    alt: "Chromium code review tool"
+  }
+  links: {
+    text: "bugs"
+    url: "https://crbug.com"
+    alt: "Chromium bug tracker"
+  }
+  links: {
+    text: "dev"
+    url: "https://dev.chromium.org/Home"
+    alt: "Chromium developer home page"
+  }
+  links: {
+    text: "support"
+    url: "https://support.google.com/chrome/#topic=7438008"
+    alt: "Google Chrome help center"
+  }
+}
+
+links: {
+  name: "Consoles"
+  links: {
+    text: "swarm"
+    url: "/p/chromium/g/chromium.swarm"
+    alt: "Chromium Swarm console"
+  }
+  links: {
+    text: "prod"
+    url: "https://luci-milo.appspot.com/p/chromium/g/main/console"
+    alt: "Main luci-milo console"
+  }
+}
+
+links: {
+  name: "Navigate"
+  links: {
+    text: "about"
+    url: "http://dev.chromium.org/developers/testing/chromium-build-infrastructure/tour-of-the-chromium-buildbot"
+    alt: "Tour of the console"
+  }
+  links: {
+    text: "customize"
+    url: "https://chromium.googlesource.com/chromium/src/+/master/infra/config/luci-milo-dev.cfg"
+      alt: "Customize this console"
+  }
+}
+
+tree_status_host: "chromium-status.appspot.com"
diff --git a/infra/config/dev/consoles/chromium.swarm.star b/infra/config/dev/consoles/chromium.swarm.star
new file mode 100644
index 0000000..61f27ed2
--- /dev/null
+++ b/infra/config/dev/consoles/chromium.swarm.star
@@ -0,0 +1,13 @@
+luci.console_view(
+    name = 'chromium.swarm',
+    header = '//dev/consoles/chromium-header.textpb',
+    repo = 'https://chromium.googlesource.com/chromium/src',
+    entries = [
+        luci.console_view_entry(builder = 'ci/Android N5 Swarm'),
+        luci.console_view_entry(builder = 'ci/Android N5X Swarm'),
+        luci.console_view_entry(builder = 'ci/ChromeOS Swarm'),
+        luci.console_view_entry(builder = 'ci/Linux Swarm'),
+        luci.console_view_entry(builder = 'ci/Mac Swarm'),
+        luci.console_view_entry(builder = 'ci/Windows Swarm'),
+   ],
+)
diff --git a/infra/config/dev/consoles/snapshots.star b/infra/config/dev/consoles/snapshots.star
new file mode 100644
index 0000000..d06e164c
--- /dev/null
+++ b/infra/config/dev/consoles/snapshots.star
@@ -0,0 +1,8 @@
+luci.list_view(
+    name = 'snapshots',
+    title = 'Snapshot Builder',
+    entries = [
+        'cron/Snapshot Builder',
+        'cron/Snapshots',
+    ],
+)
diff --git a/infra/config/generated/luci-milo-dev.cfg b/infra/config/generated/luci-milo-dev.cfg
index 340e802d..d805e00 100644
--- a/infra/config/generated/luci-milo-dev.cfg
+++ b/infra/config/generated/luci-milo-dev.cfg
@@ -1,189 +1,184 @@
-logo_url: "https://storage.googleapis.com/chrome-infra-public/logo/chromium.svg"
+# Auto-generated by lucicfg.
+# Do not modify manually.
+#
+# For the schema of this file, see Project message:
+#   https://luci-config.appspot.com/schemas/projects:luci-milo.cfg
 
-headers: {
-  id: "chromium"
-  oncalls: {
-    name: "Chromium"
-    url: "https://build.chromium.org/p/chromium/sheriff.json"
-  }
-  oncalls: {
-    name: "Android"
-    url: "https://build.chromium.org/p/chromium/sheriff_android.json"
-  }
-  oncalls: {
-    name: "iOS"
-    url: "https://build.chromium.org/p/chromium/sheriff_ios.json"
-  }
-  oncalls: {
-    name: "CrOS MTV"
-    url: "https://build.chromium.org/p/chromium/sheriff_cros_mtv.json"
-  }
-  oncalls: {
-    name: "CrOS non-MTV"
-    url: "https://build.chromium.org/p/chromium/sheriff_cros_nonmtv.json"
-  }
-  oncalls: {
-    name: "ChromeOS Gardener"
-    url: "https://build.chromium.org/p/chromium/sheriff_cr_cros_gardeners.json"
-  }
-  oncalls: {
-    name: "GPU"
-    url: "https://build.chromium.org/p/chromium/sheriff_gpu.json"
-  }
-  oncalls: {
-    name: "Angle"
-    url: "https://build.chromium.org/p/chromium/sheriff_angle.json"
-  }
-  oncalls: {
-    name: "Perf"
-    url: "https://build.chromium.org/p/chromium/sheriff_perf.json"
-  }
-  oncalls: {
-    name: "Perfbot"
-    url: "https://build.chromium.org/p/chromium/sheriff_perfbot.json"
-  }
-  oncalls: {
-    name: "V8"
-    url: "https://build.chromium.org/p/chromium/sheriff_v8.json"
-  }
-  oncalls: {
-    name: "Trooper"
-    url: "https://build.chromium.org/p/chromium/trooper.json"
-  }
-
-  links: {
-    name: "Builds"
-    links: {
-      text: "continuous"
-      url: "https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html"
-      alt: "Continuous browser snapshots"
-    }
-    links: {
-      text: "symbols"
-      url: "https://www.chromium.org/developers/how-tos/debugging-on-windows"
-      alt: "Windows Symbols"
-    }
-    links: {
-      text: "status"
-      url: "https://chromium-status.appspot.com/"
-      alt: "Current tree status"
-    }
-  }
-
-  links: {
-    name: "Dashboards"
-    links: {
-      text: "perf"
-      url: "https://chromeperf.appspot.com/"
-      alt: "Chrome perf dashboard"
-    }
-    links {
-      text: "flake-portal"
-      url: "https://analysis.chromium.org/p/chromium/flake-portal"
-      alt: "New flake portal"
-    }
-    links {
-      text: "legacy-flakiness"
-      url: "https://test-results.appspot.com/dashboards/flakiness_dashboard.html"
-      alt: "Legacy flakiness dashboard"
-    }
-  }
-
-  links: {
-    name: "Chromium"
-    links: {
-      text: "source"
-      url: "https://chromium.googlesource.com/chromium/src"
-      alt: "Chromium source code repository"
-    }
-    links: {
-      text: "reviews"
-      url: "https://chromium-review.googlesource.com"
-      alt: "Chromium code review tool"
-    }
-    links: {
-      text: "bugs"
-      url: "https://crbug.com"
-      alt: "Chromium bug tracker"
-    }
-    links: {
-      text: "dev"
-      url: "https://dev.chromium.org/Home"
-      alt: "Chromium developer home page"
-    }
-    links: {
-      text: "support"
-      url: "https://support.google.com/chrome/#topic=7438008"
-      alt: "Google Chrome help center"
-    }
-  }
-
-  links: {
-    name: "Consoles"
-    links: {
-      text: "swarm"
-      url: "/p/chromium/g/chromium.swarm"
-      alt: "Chromium Swarm console"
-    }
-    links: {
-      text: "prod"
-      url: "https://luci-milo.appspot.com/p/chromium/g/main/console"
-      alt: "Main luci-milo console"
-    }
-  }
-
-  links: {
-    name: "Navigate"
-    links: {
-      text: "about"
-      url: "http://dev.chromium.org/developers/testing/chromium-build-infrastructure/tour-of-the-chromium-buildbot"
-      alt: "Tour of the console"
-    }
-    links: {
-      text: "customize"
-      url: "https://chromium.googlesource.com/chromium/src/+/master/infra/config/luci-milo-dev.cfg"
-      alt: "Customize this console"
-    }
-  }
-
-  tree_status_host: "chromium-status.appspot.com"
-}
-
-consoles {
-  header_id: "chromium"
+consoles: <
   id: "chromium.swarm"
   name: "chromium.swarm"
   repo_url: "https://chromium.googlesource.com/chromium/src"
-  refs: "refs/heads/master"
+  refs: "regexp:refs/heads/master"
   manifest_name: "REVISION"
-  builders {
+  builders: <
     name: "buildbucket/luci.chromium.ci/Android N5 Swarm"
-  }
-  builders {
+  >
+  builders: <
     name: "buildbucket/luci.chromium.ci/Android N5X Swarm"
-  }
-  builders {
+  >
+  builders: <
     name: "buildbucket/luci.chromium.ci/ChromeOS Swarm"
-  }
-  builders {
+  >
+  builders: <
     name: "buildbucket/luci.chromium.ci/Linux Swarm"
-  }
-  builders {
+  >
+  builders: <
     name: "buildbucket/luci.chromium.ci/Mac Swarm"
-  }
-  builders {
+  >
+  builders: <
     name: "buildbucket/luci.chromium.ci/Windows Swarm"
-  }
-}
-
-consoles {
+  >
+  header: <
+    oncalls: <
+      name: "Chromium"
+      url: "https://build.chromium.org/p/chromium/sheriff.json"
+    >
+    oncalls: <
+      name: "Android"
+      url: "https://build.chromium.org/p/chromium/sheriff_android.json"
+    >
+    oncalls: <
+      name: "iOS"
+      url: "https://build.chromium.org/p/chromium/sheriff_ios.json"
+    >
+    oncalls: <
+      name: "CrOS MTV"
+      url: "https://build.chromium.org/p/chromium/sheriff_cros_mtv.json"
+    >
+    oncalls: <
+      name: "CrOS non-MTV"
+      url: "https://build.chromium.org/p/chromium/sheriff_cros_nonmtv.json"
+    >
+    oncalls: <
+      name: "ChromeOS Gardener"
+      url: "https://build.chromium.org/p/chromium/sheriff_cr_cros_gardeners.json"
+    >
+    oncalls: <
+      name: "GPU"
+      url: "https://build.chromium.org/p/chromium/sheriff_gpu.json"
+    >
+    oncalls: <
+      name: "Angle"
+      url: "https://build.chromium.org/p/chromium/sheriff_angle.json"
+    >
+    oncalls: <
+      name: "Perf"
+      url: "https://build.chromium.org/p/chromium/sheriff_perf.json"
+    >
+    oncalls: <
+      name: "Perfbot"
+      url: "https://build.chromium.org/p/chromium/sheriff_perfbot.json"
+    >
+    oncalls: <
+      name: "V8"
+      url: "https://build.chromium.org/p/chromium/sheriff_v8.json"
+    >
+    oncalls: <
+      name: "Trooper"
+      url: "https://build.chromium.org/p/chromium/trooper.json"
+    >
+    links: <
+      name: "Builds"
+      links: <
+        text: "continuous"
+        url: "https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html"
+        alt: "Continuous browser snapshots"
+      >
+      links: <
+        text: "symbols"
+        url: "https://www.chromium.org/developers/how-tos/debugging-on-windows"
+        alt: "Windows Symbols"
+      >
+      links: <
+        text: "status"
+        url: "https://chromium-status.appspot.com/"
+        alt: "Current tree status"
+      >
+    >
+    links: <
+      name: "Dashboards"
+      links: <
+        text: "perf"
+        url: "https://chromeperf.appspot.com/"
+        alt: "Chrome perf dashboard"
+      >
+      links: <
+        text: "flake-portal"
+        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        alt: "New flake portal"
+      >
+      links: <
+        text: "legacy-flakiness"
+        url: "https://test-results.appspot.com/dashboards/flakiness_dashboard.html"
+        alt: "Legacy flakiness dashboard"
+      >
+    >
+    links: <
+      name: "Chromium"
+      links: <
+        text: "source"
+        url: "https://chromium.googlesource.com/chromium/src"
+        alt: "Chromium source code repository"
+      >
+      links: <
+        text: "reviews"
+        url: "https://chromium-review.googlesource.com"
+        alt: "Chromium code review tool"
+      >
+      links: <
+        text: "bugs"
+        url: "https://crbug.com"
+        alt: "Chromium bug tracker"
+      >
+      links: <
+        text: "dev"
+        url: "https://dev.chromium.org/Home"
+        alt: "Chromium developer home page"
+      >
+      links: <
+        text: "support"
+        url: "https://support.google.com/chrome/#topic=7438008"
+        alt: "Google Chrome help center"
+      >
+    >
+    links: <
+      name: "Consoles"
+      links: <
+        text: "swarm"
+        url: "/p/chromium/g/chromium.swarm"
+        alt: "Chromium Swarm console"
+      >
+      links: <
+        text: "prod"
+        url: "https://luci-milo.appspot.com/p/chromium/g/main/console"
+        alt: "Main luci-milo console"
+      >
+    >
+    links: <
+      name: "Navigate"
+      links: <
+        text: "about"
+        url: "http://dev.chromium.org/developers/testing/chromium-build-infrastructure/tour-of-the-chromium-buildbot"
+        alt: "Tour of the console"
+      >
+      links: <
+        text: "customize"
+        url: "https://chromium.googlesource.com/chromium/src/+/master/infra/config/luci-milo-dev.cfg"
+        alt: "Customize this console"
+      >
+    >
+    tree_status_host: "chromium-status.appspot.com"
+  >
+>
+consoles: <
   id: "snapshots"
-  builder_view_only: true
   name: "Snapshot Builder"
-  builders {
+  builders: <
     name: "buildbucket/luci.chromium.cron/Snapshot Builder"
-  }
-  builders {
+  >
+  builders: <
     name: "buildbucket/luci.chromium.cron/Snapshots"
-  }
-}
+  >
+  builder_view_only: true
+>
+logo_url: "https://storage.googleapis.com/chrome-infra-public/logo/chromium.svg"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index c942378..f3d951f 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -5,6 +5,258 @@
 #   https://luci-config.appspot.com/schemas/projects:luci-milo.cfg
 
 consoles: <
+  id: "android.packager"
+  name: "android.packager"
+  repo_url: "https://chromium.googlesource.com/chromium/src"
+  refs: "regexp:refs/heads/master"
+  manifest_name: "REVISION"
+  builders: <
+    name: "buildbucket/luci.chromium.ci/android-avd-packager"
+    short_name: "avd"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/android-sdk-packager"
+    short_name: "sdk"
+  >
+  header: <
+    oncalls: <
+      name: "Chromium"
+      url: "https://rota-ng.appspot.com/legacy/sheriff.json"
+    >
+    oncalls: <
+      name: "Android"
+      url: "https://rota-ng.appspot.com/legacy/sheriff_android.json"
+    >
+    oncalls: <
+      name: "iOS"
+      url: "https://rota-ng.appspot.com/legacy/sheriff_ios.json"
+    >
+    oncalls: <
+      name: "GPU"
+      url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
+    >
+    oncalls: <
+      name: "Angle"
+      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+    >
+    oncalls: <
+      name: "Perf"
+      url: "https://rota-ng.appspot.com/legacy/sheriff_perf.json"
+    >
+    oncalls: <
+      name: "Perfbot"
+      url: "https://rota-ng.appspot.com/legacy/sheriff_perfbot.json"
+    >
+    oncalls: <
+      name: "V8"
+      url: "https://rota-ng.appspot.com/legacy/sheriff_v8.json"
+    >
+    oncalls: <
+      name: "Trooper"
+      url: "https://rota-ng.appspot.com/legacy/trooper.json"
+    >
+    links: <
+      name: "Builds"
+      links: <
+        text: "continuous"
+        url: "https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html"
+        alt: "Continuous browser snapshots"
+      >
+      links: <
+        text: "symbols"
+        url: "https://www.chromium.org/developers/how-tos/debugging-on-windows"
+        alt: "Windows Symbols"
+      >
+      links: <
+        text: "status"
+        url: "https://chromium-status.appspot.com/"
+        alt: "Current tree status"
+      >
+    >
+    links: <
+      name: "Dashboards"
+      links: <
+        text: "perf"
+        url: "https://chromeperf.appspot.com/"
+        alt: "Chrome perf dashboard"
+      >
+      links: <
+        text: "flake-portal"
+        url: "https://analysis.chromium.org/p/chromium/flake-portal"
+        alt: "New flake portal"
+      >
+      links: <
+        text: "legacy-flakiness"
+        url: "https://test-results.appspot.com/dashboards/flakiness_dashboard.html"
+        alt: "Legacy flakiness dashboard"
+      >
+    >
+    links: <
+      name: "Chromium"
+      links: <
+        text: "source"
+        url: "https://chromium.googlesource.com/chromium/src"
+        alt: "Chromium source code repository"
+      >
+      links: <
+        text: "reviews"
+        url: "https://chromium-review.googlesource.com"
+        alt: "Chromium code review tool"
+      >
+      links: <
+        text: "bugs"
+        url: "https://crbug.com"
+        alt: "Chromium bug tracker"
+      >
+      links: <
+        text: "coverage"
+        url: "https://analysis.chromium.org/p/chromium/coverage"
+        alt: "Chromium code coverage dashboard"
+      >
+      links: <
+        text: "dev"
+        url: "https://dev.chromium.org/Home"
+        alt: "Chromium developer home page"
+      >
+      links: <
+        text: "support"
+        url: "https://support.google.com/chrome/#topic=7438008"
+        alt: "Google Chrome help center"
+      >
+    >
+    links: <
+      name: "Consoles"
+      links: <
+        text: "android"
+        url: "/p/chromium/g/chromium.android"
+        alt: "Chromium Android console"
+      >
+      links: <
+        text: "clang"
+        url: "/p/chromium/g/chromium.clang"
+        alt: "Chromium Clang console"
+      >
+      links: <
+        text: "dawn"
+        url: "/p/chromium/g/chromium.dawn"
+        alt: "Chromium Dawn console"
+      >
+      links: <
+        text: "fuzz"
+        url: "/p/chromium/g/chromium.fuzz"
+        alt: "Chromium Fuzz console"
+      >
+      links: <
+        text: "fyi"
+        url: "/p/chromium/g/chromium.fyi"
+        alt: "Chromium FYI console"
+      >
+      links: <
+        text: "gpu"
+        url: "/p/chromium/g/chromium.gpu"
+        alt: "Chromium GPU console"
+      >
+      links: <
+        text: "perf"
+        url: "/p/chrome/g/chrome.perf/console"
+        alt: "Chromium Perf console"
+      >
+      links: <
+        text: "perf.fyi"
+        url: "/p/chrome/g/chrome.perf.fyi/console"
+        alt: "Chromium Perf FYI console"
+      >
+      links: <
+        text: "webrtc"
+        url: "/p/chromium/g/chromium.webrtc"
+        alt: "Chromium WebRTC console"
+      >
+      links: <
+        text: "chromiumos"
+        url: "/p/chromium/g/chromium.chromiumos"
+        alt: "ChromiumOS console"
+      >
+    >
+    links: <
+      name: "Tryservers"
+      links: <
+        text: "android"
+        url: "/p/chromium/g/tryserver.chromium.android/builders"
+        alt: "Android"
+      >
+      links: <
+        text: "angle"
+        url: "/p/chromium/g/angle.try/builders"
+        alt: "Angle"
+      >
+      links: <
+        text: "blink"
+        url: "/p/chromium/g/tryserver.blink/builders"
+        alt: "Blink"
+      >
+      links: <
+        text: "chrome"
+        url: "/p/chrome/g/tryserver.chrome/builders"
+        alt: "Chrome"
+      >
+      links: <
+        text: "chromiumos"
+        url: "/p/chromium/g/tryserver.chromium.chromiumos/builders"
+        alt: "ChromiumOS"
+      >
+      links: <
+        text: "linux"
+        url: "/p/chromium/g/tryserver.chromium.linux/builders"
+        alt: "Linux"
+      >
+      links: <
+        text: "mac"
+        url: "/p/chromium/g/tryserver.chromium.mac/builders"
+        alt: "Mac"
+      >
+      links: <
+        text: "win"
+        url: "/p/chromium/g/tryserver.chromium.win/builders"
+        alt: "Win"
+      >
+    >
+    links: <
+      name: "Navigate"
+      links: <
+        text: "about"
+        url: "http://dev.chromium.org/developers/testing/chromium-build-infrastructure/tour-of-the-chromium-buildbot"
+        alt: "Tour of the console"
+      >
+      links: <
+        text: "customize"
+        url: "https://chromium.googlesource.com/chromium/src/+/master/infra/config/luci-milo.cfg"
+        alt: "Customize this console"
+      >
+    >
+    console_groups: <
+      title: <
+        text: "Tree Closers"
+        url: "https://chromium-status.appspot.com/"
+      >
+      console_ids: "chromium/chromium"
+      console_ids: "chromium/chromium.win"
+      console_ids: "chromium/chromium.mac"
+      console_ids: "chromium/chromium.linux"
+      console_ids: "chromium/chromium.chromiumos"
+      console_ids: "chrome/chrome"
+      console_ids: "chromium/chromium.memory"
+      console_ids: "chromium/chromium.gpu"
+    >
+    console_groups: <
+      console_ids: "chromium/chromium.android"
+      console_ids: "chrome/chrome.perf"
+      console_ids: "chromium/chromium.gpu.fyi"
+      console_ids: "chromium/chromium.fuzz"
+    >
+    tree_status_host: "chromium-status.appspot.com"
+  >
+>
+consoles: <
   id: "angle.try"
   name: "angle.try"
   builders: <
@@ -92,12 +344,22 @@
   >
   builders: <
     name: "buildbucket/luci.chromium.ci/win32-archive-rel"
-    category: "win-rel"
+    category: "win|rel"
     short_name: "32"
   >
   builders: <
     name: "buildbucket/luci.chromium.ci/win-archive-rel"
-    category: "win-rel"
+    category: "win|rel"
+    short_name: "64"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/win32-archive-dbg"
+    category: "win|dbg"
+    short_name: "32"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/win-archive-dbg"
+    category: "win|dbg"
     short_name: "64"
   >
   header: <
@@ -2856,6 +3118,10 @@
     category: "win10|1803"
   >
   builders: <
+    name: "buildbucket/luci.chromium.ci/win32-arm64-rel"
+    category: "win32|arm64"
+  >
+  builders: <
     name: "buildbucket/luci.chromium.ci/win-celab-builder-rel"
     category: "celab"
   >
@@ -7911,6 +8177,20 @@
   >
 >
 consoles: <
+  id: "findit"
+  name: "findit"
+  builders: <
+    name: "buildbucket/luci.chromium.findit/findit-rerun"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.findit/findit_variable"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.findit/linux_chromium_bot_db_exporter"
+  >
+  builder_view_only: true
+>
+consoles: <
   id: "goma.latest"
   name: "goma.latest"
   repo_url: "https://chromium.googlesource.com/chromium/src"
@@ -10283,6 +10563,32 @@
   builder_view_only: true
 >
 consoles: <
+  id: "tryserver.chromium.dawn"
+  name: "tryserver.chromium.dawn"
+  builders: <
+    name: "buildbucket/luci.chromium.try/dawn-linux-x64-deps-rel"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.try/dawn-mac-x64-deps-rel"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.try/dawn-win10-x64-deps-rel"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.try/dawn-win10-x86-deps-rel"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.try/linux-dawn-rel"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.try/mac-dawn-rel"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.try/win-dawn-rel"
+  >
+  builder_view_only: true
+>
+consoles: <
   id: "tryserver.chromium.linux"
   name: "tryserver.chromium.linux"
   builders: <
@@ -10367,6 +10673,9 @@
     name: "buildbucket/luci.chromium.try/linux-annotator-rel"
   >
   builders: <
+    name: "buildbucket/luci.chromium.try/linux-dcheck-off-rel"
+  >
+  builders: <
     name: "buildbucket/luci.chromium.try/linux-gcc-rel"
   >
   builders: <
@@ -10388,6 +10697,9 @@
     name: "buildbucket/luci.chromium.try/linux-trusty-rel"
   >
   builders: <
+    name: "buildbucket/luci.chromium.try/linux-webkit-msan-rel"
+  >
+  builders: <
     name: "buildbucket/luci.chromium.try/linux_arm"
   >
   builders: <
@@ -10620,6 +10932,12 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-skgl-64"
   >
   builders: <
+    name: "buildbucket/luci.chromium.try/gpu-try-win10-nvidia-rel"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.try/win-asan"
+  >
+  builders: <
     name: "buildbucket/luci.chromium.try/win-libfuzzer-asan-rel"
   >
   builders: <
diff --git a/infra/config/cq-builders-md.star b/infra/config/generators/cq-builders-md.star
similarity index 100%
rename from infra/config/cq-builders-md.star
rename to infra/config/generators/cq-builders-md.star
diff --git a/infra/config/luci-milo-dev.cfg b/infra/config/luci-milo-dev.cfg
deleted file mode 100644
index 340e802d..0000000
--- a/infra/config/luci-milo-dev.cfg
+++ /dev/null
@@ -1,189 +0,0 @@
-logo_url: "https://storage.googleapis.com/chrome-infra-public/logo/chromium.svg"
-
-headers: {
-  id: "chromium"
-  oncalls: {
-    name: "Chromium"
-    url: "https://build.chromium.org/p/chromium/sheriff.json"
-  }
-  oncalls: {
-    name: "Android"
-    url: "https://build.chromium.org/p/chromium/sheriff_android.json"
-  }
-  oncalls: {
-    name: "iOS"
-    url: "https://build.chromium.org/p/chromium/sheriff_ios.json"
-  }
-  oncalls: {
-    name: "CrOS MTV"
-    url: "https://build.chromium.org/p/chromium/sheriff_cros_mtv.json"
-  }
-  oncalls: {
-    name: "CrOS non-MTV"
-    url: "https://build.chromium.org/p/chromium/sheriff_cros_nonmtv.json"
-  }
-  oncalls: {
-    name: "ChromeOS Gardener"
-    url: "https://build.chromium.org/p/chromium/sheriff_cr_cros_gardeners.json"
-  }
-  oncalls: {
-    name: "GPU"
-    url: "https://build.chromium.org/p/chromium/sheriff_gpu.json"
-  }
-  oncalls: {
-    name: "Angle"
-    url: "https://build.chromium.org/p/chromium/sheriff_angle.json"
-  }
-  oncalls: {
-    name: "Perf"
-    url: "https://build.chromium.org/p/chromium/sheriff_perf.json"
-  }
-  oncalls: {
-    name: "Perfbot"
-    url: "https://build.chromium.org/p/chromium/sheriff_perfbot.json"
-  }
-  oncalls: {
-    name: "V8"
-    url: "https://build.chromium.org/p/chromium/sheriff_v8.json"
-  }
-  oncalls: {
-    name: "Trooper"
-    url: "https://build.chromium.org/p/chromium/trooper.json"
-  }
-
-  links: {
-    name: "Builds"
-    links: {
-      text: "continuous"
-      url: "https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html"
-      alt: "Continuous browser snapshots"
-    }
-    links: {
-      text: "symbols"
-      url: "https://www.chromium.org/developers/how-tos/debugging-on-windows"
-      alt: "Windows Symbols"
-    }
-    links: {
-      text: "status"
-      url: "https://chromium-status.appspot.com/"
-      alt: "Current tree status"
-    }
-  }
-
-  links: {
-    name: "Dashboards"
-    links: {
-      text: "perf"
-      url: "https://chromeperf.appspot.com/"
-      alt: "Chrome perf dashboard"
-    }
-    links {
-      text: "flake-portal"
-      url: "https://analysis.chromium.org/p/chromium/flake-portal"
-      alt: "New flake portal"
-    }
-    links {
-      text: "legacy-flakiness"
-      url: "https://test-results.appspot.com/dashboards/flakiness_dashboard.html"
-      alt: "Legacy flakiness dashboard"
-    }
-  }
-
-  links: {
-    name: "Chromium"
-    links: {
-      text: "source"
-      url: "https://chromium.googlesource.com/chromium/src"
-      alt: "Chromium source code repository"
-    }
-    links: {
-      text: "reviews"
-      url: "https://chromium-review.googlesource.com"
-      alt: "Chromium code review tool"
-    }
-    links: {
-      text: "bugs"
-      url: "https://crbug.com"
-      alt: "Chromium bug tracker"
-    }
-    links: {
-      text: "dev"
-      url: "https://dev.chromium.org/Home"
-      alt: "Chromium developer home page"
-    }
-    links: {
-      text: "support"
-      url: "https://support.google.com/chrome/#topic=7438008"
-      alt: "Google Chrome help center"
-    }
-  }
-
-  links: {
-    name: "Consoles"
-    links: {
-      text: "swarm"
-      url: "/p/chromium/g/chromium.swarm"
-      alt: "Chromium Swarm console"
-    }
-    links: {
-      text: "prod"
-      url: "https://luci-milo.appspot.com/p/chromium/g/main/console"
-      alt: "Main luci-milo console"
-    }
-  }
-
-  links: {
-    name: "Navigate"
-    links: {
-      text: "about"
-      url: "http://dev.chromium.org/developers/testing/chromium-build-infrastructure/tour-of-the-chromium-buildbot"
-      alt: "Tour of the console"
-    }
-    links: {
-      text: "customize"
-      url: "https://chromium.googlesource.com/chromium/src/+/master/infra/config/luci-milo-dev.cfg"
-      alt: "Customize this console"
-    }
-  }
-
-  tree_status_host: "chromium-status.appspot.com"
-}
-
-consoles {
-  header_id: "chromium"
-  id: "chromium.swarm"
-  name: "chromium.swarm"
-  repo_url: "https://chromium.googlesource.com/chromium/src"
-  refs: "refs/heads/master"
-  manifest_name: "REVISION"
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android N5 Swarm"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Android N5X Swarm"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/ChromeOS Swarm"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Linux Swarm"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Mac Swarm"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.ci/Windows Swarm"
-  }
-}
-
-consoles {
-  id: "snapshots"
-  builder_view_only: true
-  name: "Snapshot Builder"
-  builders {
-    name: "buildbucket/luci.chromium.cron/Snapshot Builder"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.cron/Snapshots"
-  }
-}
diff --git a/infra/config/main.star b/infra/config/main.star
index 72ce009..56b78c5 100755
--- a/infra/config/main.star
+++ b/infra/config/main.star
@@ -81,6 +81,7 @@
 exec('//buckets/webrtc.star')
 exec('//buckets/webrtc.fyi.star')
 
+exec('//consoles/android.packager.star')
 exec('//consoles/angle.try.star')
 exec('//consoles/chromium.star')
 exec('//consoles/chromium.android.star')
@@ -102,6 +103,7 @@
 exec('//consoles/chromium.webrtc.star')
 exec('//consoles/chromium.webrtc.fyi.star')
 exec('//consoles/chromium.win.star')
+exec('//consoles/findit.star')
 exec('//consoles/goma.latest.star')
 exec('//consoles/luci.chromium.goma.star')
 exec('//consoles/luci.chromium.try.star')
@@ -110,8 +112,11 @@
 exec('//consoles/tryserver.blink.star')
 exec('//consoles/tryserver.chromium.android.star')
 exec('//consoles/tryserver.chromium.chromiumos.star')
+exec('//consoles/tryserver.chromium.dawn.star')
 exec('//consoles/tryserver.chromium.linux.star')
 exec('//consoles/tryserver.chromium.mac.star')
 exec('//consoles/tryserver.chromium.win.star')
 
-exec('//cq-builders-md.star')
+exec('//generators/cq-builders-md.star')
+
+exec('//validators/builders-in-consoles.star')
diff --git a/infra/config/validators/builders-in-consoles.star b/infra/config/validators/builders-in-consoles.star
new file mode 100644
index 0000000..0f45eaa
--- /dev/null
+++ b/infra/config/validators/builders-in-consoles.star
@@ -0,0 +1,24 @@
+def _validate_builders_in_console(ctx):
+  builders = {}
+
+  for console in ctx.output['luci-milo.cfg'].consoles:
+    for builder in console.builders:
+      for name in builder.name:
+        _, long_bucket, builder_name = name.split('/')
+        _, _, bucket = long_bucket.split('.', 2)
+        builders.setdefault(bucket, {})[builder_name] = True
+
+  builders_without_console = []
+
+  for bucket in ctx.output['cr-buildbucket.cfg'].buckets:
+    bucket_builders = builders.get(bucket.name, {})
+    for builder in bucket.swarming.builders:
+      if builder.name not in bucket_builders:
+        builders_without_console.append(
+            '{}/{}'.format(bucket.name, builder.name))
+
+  if builders_without_console:
+    fail('The following builders do not appear in any console:\n  '
+         + '\n  '.join([repr(b) for b in builders_without_console]))
+
+lucicfg.generator(_validate_builders_in_console)
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm
index 663448ea..2f8c353dd 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_save_card_coordinator.mm
@@ -11,6 +11,7 @@
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.h"
 #import "ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator_implementation.h"
 #import "ios/chrome/browser/ui/infobars/infobar_container.h"
+#import "ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h"
 #include "ui/gfx/image/image.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -21,9 +22,9 @@
 
 // InfobarBannerViewController owned by this Coordinator.
 @property(nonatomic, strong) InfobarBannerViewController* bannerViewController;
-// ModalViewController owned by this Coordinator.
-// TODO(crbug.com/1014652): Replace with CustomVC later on.
-@property(nonatomic, strong) UIViewController* modalViewController;
+// InfobarSaveCardTableViewController owned by this Coordinator.
+@property(nonatomic, strong)
+    InfobarSaveCardTableViewController* modalViewController;
 // Delegate that holds the Infobar information and actions.
 @property(nonatomic, readonly)
     autofill::AutofillSaveCardInfoBarDelegateMobile* saveCardInfoBarDelegate;
@@ -133,17 +134,41 @@
 #pragma mark Modal
 
 - (BOOL)configureModalViewController {
-  // TODO(crbug.com/1014652): Continue implementation.
-  return NO;
+  // Return early if there's no delegate. e.g. A Modal presentation has been
+  // triggered after the Infobar was destroyed, but before the badge/banner
+  // were dismissed.
+  if (!self.saveCardInfoBarDelegate)
+    return NO;
+
+  self.modalViewController =
+      [[InfobarSaveCardTableViewController alloc] initWithModalDelegate:self];
+  // TODO(crbug.com/1014652): Replace with Modal specific text.
+  self.modalViewController.title =
+      base::SysUTF16ToNSString(self.saveCardInfoBarDelegate->GetMessageText());
+
+  return YES;
 }
 
 - (void)infobarModalPresentedFromBanner:(BOOL)presentedFromBanner {
-  // TODO(crbug.com/1014652): Continue implementation.
+  // TODO(crbug.com/1014652): Check if there's a metric that should be recorded
+  // here, or if there's a need to keep track of the presented state of the
+  // Infobar for recording metrics on de-alloc. (See equivalent method on
+  // InfobarPasswordCoordinator).
 }
 
 - (CGFloat)infobarModalHeightForWidth:(CGFloat)width {
-  // TODO(crbug.com/1014652): Continue implementation.
-  return 0.0;
+  UITableView* tableView = self.modalViewController.tableView;
+  // Update the tableView frame to then layout its content for |width|.
+  tableView.frame = CGRectMake(0, 0, width, tableView.frame.size.height);
+  [tableView setNeedsLayout];
+  [tableView layoutIfNeeded];
+
+  // Since the TableView is contained in a NavigationController get the
+  // navigation bar height.
+  CGFloat navigationBarHeight = self.modalViewController.navigationController
+                                    .navigationBar.frame.size.height;
+
+  return tableView.contentSize.height + navigationBarHeight;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/infobars/modals/BUILD.gn b/ios/chrome/browser/ui/infobars/modals/BUILD.gn
index 0c4a7390..3c46fad 100644
--- a/ios/chrome/browser/ui/infobars/modals/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/modals/BUILD.gn
@@ -11,6 +11,8 @@
     "infobar_password_modal_delegate.h",
     "infobar_password_table_view_controller.h",
     "infobar_password_table_view_controller.mm",
+    "infobar_save_card_table_view_controller.h",
+    "infobar_save_card_table_view_controller.mm",
   ]
   deps = [
     ":public",
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h
new file mode 100644
index 0000000..eaf8a1b
--- /dev/null
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_INFOBARS_MODALS_INFOBAR_SAVE_CARD_TABLE_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_INFOBARS_MODALS_INFOBAR_SAVE_CARD_TABLE_VIEW_CONTROLLER_H_
+
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
+
+@protocol InfobarModalDelegate;
+
+// InfobarSaveCardTableViewController represents the content for the Save Card
+// InfobarModal.
+@interface InfobarSaveCardTableViewController : ChromeTableViewController
+
+- (instancetype)initWithModalDelegate:(id<InfobarModalDelegate>)modalDelegate
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithTableViewStyle:(UITableViewStyle)style
+                           appBarStyle:
+                               (ChromeTableViewControllerStyle)appBarStyle
+    NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_INFOBARS_MODALS_INFOBAR_SAVE_CARD_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
new file mode 100644
index 0000000..84564e6
--- /dev/null
+++ b/ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.mm
@@ -0,0 +1,73 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h"
+
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "ios/chrome/browser/infobars/infobar_metrics_recorder.h"
+#import "ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h"
+#import "ios/chrome/browser/ui/infobars/modals/infobar_modal_delegate.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#import "ios/chrome/common/colors/semantic_color_names.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface InfobarSaveCardTableViewController ()
+
+// InfobarPasswordModalDelegate for this ViewController.
+@property(nonatomic, strong) id<InfobarModalDelegate> infobarModalDelegate;
+// Used to build and record metrics.
+@property(nonatomic, strong) InfobarMetricsRecorder* metricsRecorder;
+
+@end
+
+@implementation InfobarSaveCardTableViewController
+
+- (instancetype)initWithModalDelegate:(id<InfobarModalDelegate>)modalDelegate {
+  self = [super initWithTableViewStyle:UITableViewStylePlain
+                           appBarStyle:ChromeTableViewControllerStyleNoAppBar];
+  if (self) {
+    _infobarModalDelegate = modalDelegate;
+    _metricsRecorder = [[InfobarMetricsRecorder alloc]
+        initWithType:InfobarType::kInfobarTypeSaveCard];
+  }
+  return self;
+}
+
+#pragma mark - ViewController Lifecycle
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+  self.view.backgroundColor = [UIColor colorNamed:kBackgroundColor];
+  self.styler.cellBackgroundColor = [UIColor colorNamed:kBackgroundColor];
+  self.tableView.sectionHeaderHeight = 0;
+  [self.tableView
+      setSeparatorInset:UIEdgeInsetsMake(0, kTableViewHorizontalSpacing, 0, 0)];
+
+  // Configure the NavigationBar.
+  UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc]
+      initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
+                           target:self
+                           action:@selector(dismissInfobarModal:)];
+  cancelButton.accessibilityIdentifier = kInfobarModalCancelButton;
+  self.navigationItem.leftBarButtonItem = cancelButton;
+  self.navigationController.navigationBar.prefersLargeTitles = NO;
+}
+
+#pragma mark - Private Methods
+
+- (void)dismissInfobarModal:(UIButton*)sender {
+  base::RecordAction(
+      base::UserMetricsAction("MobileMessagesModalCancelledTapped"));
+  [self.metricsRecorder recordModalEvent:MobileMessagesModalEvent::Canceled];
+  [self.infobarModalDelegate dismissInfobarModal:sender
+                                        animated:YES
+                                      completion:nil];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/page_info/page_info_egtest.mm b/ios/chrome/browser/ui/page_info/page_info_egtest.mm
index 57ed9c4..a7ead44 100644
--- a/ios/chrome/browser/ui/page_info/page_info_egtest.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_egtest.mm
@@ -30,14 +30,8 @@
   }
 
   if ([[UIDevice currentDevice] orientation] != UIDeviceOrientationPortrait) {
-#if defined(CHROME_EARL_GREY_1)
-    [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
-                             errorOrNil:nil];
-#elif defined(CHROME_EARL_GREY_2)
-    [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait error:nil];
-#else
-#error Neither CHROME_EARL_GREY_1 nor CHROME_EARL_GREY_2 are defined
-#endif
+    [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
+                                        error:nil];
   }
 
   [ChromeEarlGrey loadURL:GURL("https://invalid")];
@@ -54,16 +48,8 @@
   [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                           kPageInfoViewAccessibilityIdentifier)]
       assertWithMatcher:grey_sufficientlyVisible()];
-
-#if defined(CHROME_EARL_GREY_1)
-  [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeRight
-                           errorOrNil:nil];
-#elif defined(CHROME_EARL_GREY_2)
-  [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeRight
-                                error:nil];
-#else
-#error Neither CHROME_EARL_GREY_1 nor CHROME_EARL_GREY_2 are defined
-#endif
+  [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeRight
+                                      error:nil];
 
   // Expect that the page info view has disappeared.
   [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_transition_egtest.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_transition_egtest.mm
index f64dc83..43aa31c3 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_transition_egtest.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_transition_egtest.mm
@@ -30,17 +30,6 @@
 
 namespace {
 
-// Rotates the device to the given orientation.
-void RotateDevice(UIDeviceOrientation orientation) {
-#if defined(CHROME_EARL_GREY_1)
-  [EarlGrey rotateDeviceToOrientation:orientation errorOrNil:nil];
-#elif defined(CHROME_EARL_GREY_2)
-  [EarlGrey rotateDeviceToOrientation:orientation error:nil];
-#else
-#error
-#endif
-}
-
 // Shows the tab switcher by tapping the switcher button.  Works on both phone
 // and tablet.
 void ShowTabSwitcher() {
@@ -108,7 +97,8 @@
 // Rotate the device back to portrait if needed, since some tests attempt to run
 // in landscape.
 - (void)tearDown {
-  RotateDevice(UIDeviceOrientationPortrait);
+  [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
+                                      error:nil];
   [super tearDown];
 }
 
@@ -370,21 +360,24 @@
   [ChromeEarlGrey loadURL:[self makeURLForTitle:tab_title]];
 
   // Show the tab switcher and return to the BVC, in portrait.
-  RotateDevice(UIDeviceOrientationPortrait);
+  [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
+                                      error:nil];
   ShowTabSwitcher();
   SelectTab(tab_title);
   [ChromeEarlGrey
       waitForWebStateContainingText:base::SysNSStringToUTF8(tab_title)];
 
   // Show the tab switcher and return to the BVC, in landscape.
-  RotateDevice(UIDeviceOrientationLandscapeLeft);
+  [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeLeft
+                                      error:nil];
   ShowTabSwitcher();
   SelectTab(tab_title);
   [ChromeEarlGrey
       waitForWebStateContainingText:base::SysNSStringToUTF8(tab_title)];
 
   // Show the tab switcher and return to the BVC, in portrait.
-  RotateDevice(UIDeviceOrientationPortrait);
+  [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
+                                      error:nil];
   ShowTabSwitcher();
   SelectTab(tab_title);
   [ChromeEarlGrey
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
index 9f262b6..06b185d 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -429,9 +429,7 @@
   }
   // The content inset of the tab grids must be modified so that the toolbars
   // do not obscure the tabs. This may change depending on orientation.
-  CGFloat bottomInset = self.configuration == TabGridConfigurationBottomToolbar
-                            ? self.bottomToolbar.intrinsicContentSize.height
-                            : 0;
+  CGFloat bottomInset = self.bottomToolbar.intrinsicContentSize.height;
   UIEdgeInsets inset = UIEdgeInsetsMake(
       self.topToolbar.intrinsicContentSize.height, 0, bottomInset, 0);
   // Left and right side could be missing correct safe area
diff --git a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm
index 868e108..8e26b62 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_egtest.mm
@@ -202,12 +202,8 @@
             forViewController:topViewController];
   } else {
     // On iPhone rotate to test the the landscape orientation.
-    [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeLeft
-#if defined(CHROME_EARL_GREY_1)
-                             errorOrNil:nil];
-#elif defined(CHROME_EARL_GREY_2)
-                                  error:nil];
-#endif
+    [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeLeft
+                                        error:nil];
     return topViewController.traitCollection;
   }
 }
@@ -496,12 +492,8 @@
     }
   } else {
     // Cancel the rotation.
-    [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
-#if defined(CHROME_EARL_GREY_1)
-                             errorOrNil:nil];
-#elif defined(CHROME_EARL_GREY_2)
-                                  error:nil];
-#endif
+    [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
+                                        error:nil];
   }
 
   // Check the visiblity after a rotation.
@@ -539,12 +531,8 @@
     }
   } else {
     // Cancel the rotation.
-    [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
-#if defined(CHROME_EARL_GREY_1)
-                             errorOrNil:nil];
-#elif defined(CHROME_EARL_GREY_2)
-                                  error:nil];
-#endif
+    [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
+                                        error:nil];
   }
 
   // Check the visiblity after a size class change. This should let the trait
@@ -721,12 +709,8 @@
 - (void)testShareButton {
   if (![ChromeEarlGrey isIPadIdiom]) {
     // If this test is run on an iPhone, rotate it to have the unsplit toolbar.
-    [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeLeft
-#if defined(CHROME_EARL_GREY_1)
-                             errorOrNil:nil];
-#elif defined(CHROME_EARL_GREY_2)
-                                  error:nil];
-#endif
+    [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationLandscapeLeft
+                                        error:nil];
   }
 
   // Setup the server.
@@ -742,12 +726,8 @@
 
   if (![ChromeEarlGrey isIPadIdiom]) {
     // Cancel rotation.
-    [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
-#if defined(CHROME_EARL_GREY_1)
-                             errorOrNil:nil];
-#elif defined(CHROME_EARL_GREY_2)
-                                  error:nil];
-#endif
+    [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
+                                        error:nil];
   }
 }
 
@@ -808,12 +788,8 @@
     }
   } else {
     // Cancel the rotation.
-    [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
-#if defined(CHROME_EARL_GREY_1)
-                             errorOrNil:nil];
-#elif defined(CHROME_EARL_GREY_2)
-                                  error:nil];
-#endif
+    [ChromeEarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait
+                                        error:nil];
   }
 }
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 12625096..8a19271 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -5,7 +5,7 @@
 #ifndef IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_H_
 #define IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_H_
 
-#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
 
 #include <string>
 
@@ -40,6 +40,11 @@
 
 #pragma mark - Device Utilities
 
+// Simulate the user action to rotate the device to a certain orientation.
+// TODO(crbug.com/1017265): Remove along EG1 support.
+- (void)rotateDeviceToOrientation:(UIDeviceOrientation)deviceOrientation
+                            error:(NSError**)error;
+
 // Returns YES if running on an iPad.
 - (BOOL)isIPadIdiom;
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 0dcd3a1..67abe355 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -66,6 +66,21 @@
 
 #pragma mark - Device Utilities
 
+- (void)rotateDeviceToOrientation:(UIDeviceOrientation)deviceOrientation
+                            error:(NSError**)error {
+#if defined(CHROME_EARL_GREY_1)
+  NSError* strongErrorReference = nil;
+  [EarlGrey rotateDeviceToOrientation:deviceOrientation
+                           errorOrNil:&strongErrorReference];
+  if (error)
+    *error = strongErrorReference;
+#elif defined(CHROME_EARL_GREY_2)
+  [EarlGrey rotateDeviceToOrientation:deviceOrientation error:error];
+#else
+#error Neither CHROME_EARL_GREY_1 nor CHROME_EARL_GREY_2 are defined
+#endif
+}
+
 - (BOOL)isIPadIdiom {
 #if defined(CHROME_EARL_GREY_1)
   UIUserInterfaceIdiom idiom = [[UIDevice currentDevice] userInterfaceIdiom];
diff --git a/ios/chrome/test/earl_grey/chrome_test_case.mm b/ios/chrome/test/earl_grey/chrome_test_case.mm
index c314f30..8cdd7241 100644
--- a/ios/chrome/test/earl_grey/chrome_test_case.mm
+++ b/ios/chrome/test/earl_grey/chrome_test_case.mm
@@ -271,13 +271,7 @@
   if (GetCurrentDeviceOrientation() != _originalOrientation) {
     // Rotate the device back to the original orientation, since some tests
     // attempt to run in other orientations.
-#if defined(CHROME_EARL_GREY_1)
-    [EarlGrey rotateDeviceToOrientation:_originalOrientation errorOrNil:nil];
-#elif defined(CHROME_EARL_GREY_2)
-    [EarlGrey rotateDeviceToOrientation:_originalOrientation error:nil];
-#else
-#error Neither CHROME_EARL_GREY_1 nor CHROME_EARL_GREY_2 are defined
-#endif
+    [ChromeEarlGrey rotateDeviceToOrientation:_originalOrientation error:nil];
   }
   [super tearDown];
   _executedTestMethodSetUp = NO;
diff --git a/ios/testing/earl_grey/base_earl_grey_test_case.mm b/ios/testing/earl_grey/base_earl_grey_test_case.mm
index 2a4e85654..d49b63b3 100644
--- a/ios/testing/earl_grey/base_earl_grey_test_case.mm
+++ b/ios/testing/earl_grey/base_earl_grey_test_case.mm
@@ -114,6 +114,8 @@
       forceRestart:NO];
 }
 
+#pragma mark - Private
+
 // Prevents tests inheriting from this class from putting logic in +setUp.
 // +setUp will be called before the application is launched,
 // and thus is not suitable for most test case setup. Inheriting tests should
diff --git a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
index 621f27ea..ea16664 100644
--- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
+++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
@@ -43,7 +43,7 @@
 
 @JNINamespace("media")
 class AudioManagerAndroid {
-    private static final String TAG = "cr.media";
+    private static final String TAG = "media";
 
     // Set to true to enable debug logs. Avoid in production builds.
     // NOTE: always check in as false.
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
index 8543b7e..41c5bab2 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
@@ -33,7 +33,7 @@
  */
 @JNINamespace("media")
 class MediaCodecBridge {
-    private static final String TAG = "cr_MediaCodecBridge";
+    private static final String TAG = "MediaCodecBridge";
 
     // After a flush(), dequeueOutputBuffer() can often produce empty presentation timestamps
     // for several frames. As a result, the player may find that the time does not increase
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java
index 464ebb9..59aea2a 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridgeBuilder.java
@@ -19,7 +19,7 @@
 @JNINamespace("media")
 @MainDex
 class MediaCodecBridgeBuilder {
-    private static final String TAG = "cr_MediaCodecBridge";
+    private static final String TAG = "MediaCodecBridge";
 
     @CalledByNative
     static MediaCodecBridge createVideoDecoder(String mime, @CodecType int codecType,
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecEncoder.java b/media/base/android/java/src/org/chromium/media/MediaCodecEncoder.java
index 7f25696..9a7d398 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecEncoder.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecEncoder.java
@@ -21,7 +21,7 @@
  */
 @JNINamespace("media")
 class MediaCodecEncoder extends MediaCodecBridge {
-    private static final String TAG = "cr_MediaCodecEncoder";
+    private static final String TAG = "MediaCodecEncoder";
 
     // Output buffers mapping with MediaCodec output buffers for the possible frame-merging.
     private SparseArray<ByteBuffer> mOutputBuffers = new SparseArray<>();
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
index a8d66d5..e36c7df 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -40,7 +40,7 @@
 @JNINamespace("media")
 @MainDex
 class MediaCodecUtil {
-    private static final String TAG = "cr_MediaCodecUtil";
+    private static final String TAG = "MediaCodecUtil";
 
     /**
      * Information returned by createDecoder()
diff --git a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
index daeeea7a..4a5e340 100644
--- a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
@@ -61,7 +61,7 @@
 @SuppressLint("WrongConstant")
 @TargetApi(Build.VERSION_CODES.KITKAT)
 public class MediaDrmBridge {
-    private static final String TAG = "cr_media";
+    private static final String TAG = "media";
     private static final String SECURITY_LEVEL = "securityLevel";
     private static final String SERVER_CERTIFICATE = "serviceCertificate";
     private static final String ORIGIN = "origin";
diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
index 79b54374..e6c2da48 100644
--- a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
@@ -38,8 +38,7 @@
 */
 @JNINamespace("media")
 public class MediaPlayerBridge {
-
-    private static final String TAG = "cr.media";
+    private static final String TAG = "media";
 
     // Local player to forward this to. We don't initialize it here since the subclass might not
     // want it.
diff --git a/media/blink/resource_multibuffer_data_provider.cc b/media/blink/resource_multibuffer_data_provider.cc
index aa58a92d..5fd96c9 100644
--- a/media/blink/resource_multibuffer_data_provider.cc
+++ b/media/blink/resource_multibuffer_data_provider.cc
@@ -387,8 +387,6 @@
   DCHECK(active_loader_);
   DCHECK_GT(data_length, 0);
 
-  url_data_->AddBytesReadFromNetwork(data_length);
-
   if (bytes_to_discard_) {
     uint64_t tmp = std::min<uint64_t>(bytes_to_discard_, data_length);
     data_length -= tmp;
diff --git a/media/blink/url_index.cc b/media/blink/url_index.cc
index fb940a4..9f2ddfb 100644
--- a/media/blink/url_index.cc
+++ b/media/blink/url_index.cc
@@ -10,7 +10,6 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/location.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -57,12 +56,7 @@
       last_used_(),
       multibuffer_(this, url_index_->block_shift_) {}
 
-UrlData::~UrlData() {
-  UMA_HISTOGRAM_MEMORY_KB("Media.BytesReadFromCache",
-                          BytesReadFromCache() >> 10);
-  UMA_HISTOGRAM_MEMORY_KB("Media.BytesReadFromNetwork",
-                          BytesReadFromNetwork() >> 10);
-}
+UrlData::~UrlData() = default;
 
 std::pair<GURL, UrlData::CorsMode> UrlData::key() const {
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -123,12 +117,6 @@
   // Copy any cached data over to the new location.
   url_data->multibuffer()->MergeFrom(multibuffer());
 
-  // All |bytes_received_callbacks_| should also listen for bytes on the
-  // redirect UrlData.
-  for (const auto& cb : bytes_received_callbacks_) {
-    url_data->AddBytesReceivedCallback(cb);
-  }
-
   std::vector<RedirectCB> redirect_callbacks;
   redirect_callbacks.swap(redirect_callbacks_);
   for (const RedirectCB& cb : redirect_callbacks) {
@@ -217,18 +205,6 @@
   return &multibuffer_;
 }
 
-void UrlData::AddBytesReceivedCallback(BytesReceivedCB bytes_received_cb) {
-  bytes_received_callbacks_.emplace_back(std::move(bytes_received_cb));
-}
-
-void UrlData::AddBytesReadFromNetwork(int64_t b) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  bytes_read_from_network_ += b;
-  for (const auto& cb : bytes_received_callbacks_) {
-    cb.Run(b);
-  }
-}
-
 size_t UrlData::CachedSize() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return multibuffer()->map().size();
diff --git a/media/blink/url_index.h b/media/blink/url_index.h
index 797b9e82..2b6d2cd 100644
--- a/media/blink/url_index.h
+++ b/media/blink/url_index.h
@@ -149,17 +149,8 @@
   // Virtual so we can override it for testing.
   virtual ResourceMultiBuffer* multibuffer();
 
-  // Callback for reporting number of bytes received by the network.
-  using BytesReceivedCB = base::RepeatingCallback<void(uint64_t)>;
-
-  // Register a BytesReceivedCallback for this UrlData. These callbacks will be
-  // copied to another UrlData if there is a redirect.
-  void AddBytesReceivedCallback(BytesReceivedCB bytes_received_cb);
-
   void AddBytesRead(int64_t b) { bytes_read_from_cache_ += b; }
   int64_t BytesReadFromCache() const { return bytes_read_from_cache_; }
-  void AddBytesReadFromNetwork(int64_t b);
-  int64_t BytesReadFromNetwork() const { return bytes_read_from_network_; }
 
  protected:
   UrlData(const GURL& url, CorsMode cors_mode, UrlIndex* url_index);
@@ -195,9 +186,6 @@
   // Number of bytes read from this resource.
   int64_t bytes_read_from_cache_ = 0;
 
-  // Number of bytes read from network into the cache for this resource.
-  int64_t bytes_read_from_network_ = 0;
-
   // Does the server support ranges?
   bool range_supported_;
 
@@ -228,8 +216,6 @@
   ResourceMultiBuffer multibuffer_;
   std::vector<RedirectCB> redirect_callbacks_;
 
-  std::vector<BytesReceivedCB> bytes_received_callbacks_;
-
   std::vector<base::OnceClosure> waiting_load_callbacks_;
 
   base::ThreadChecker thread_checker_;
diff --git a/media/blink/watch_time_reporter_unittest.cc b/media/blink/watch_time_reporter_unittest.cc
index 9244054..cdeaa3e 100644
--- a/media/blink/watch_time_reporter_unittest.cc
+++ b/media/blink/watch_time_reporter_unittest.cc
@@ -245,14 +245,12 @@
             receiver) override {}
     void Initialize(bool is_mse, mojom::MediaURLScheme url_scheme) override {}
     void OnError(PipelineStatus status) override {}
-    void SetIsAdMedia() override {}
     void SetIsEME() override {}
     void SetTimeToMetadata(base::TimeDelta elapsed) override {}
     void SetTimeToFirstFrame(base::TimeDelta elapsed) override {}
     void SetTimeToPlayReady(base::TimeDelta elapsed) override {}
     void SetContainerName(
         container_names::MediaContainerName container_name) override {}
-    void AddBytesReceived(uint64_t bytes_received) override {}
     void SetHasPlayed() override {}
     void SetHaveEnough() override {}
     void SetHasAudio(AudioCodec audio_codec) override {}
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 9209c0ba..0ac10db4 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -409,9 +409,6 @@
   memory_usage_reporting_timer_.SetTaskRunner(
       frame_->GetTaskRunner(blink::TaskType::kInternalMedia));
 
-  if (frame_->IsAdSubframe())
-    media_metrics_provider_->SetIsAdMedia();
-
 #if defined(OS_ANDROID)
   renderer_factory_selector_->SetRemotePlayStateChangeCB(
       BindToCurrentLoop(base::BindRepeating(
@@ -749,9 +746,6 @@
 
     auto url_data =
         url_index_->GetByUrl(url, static_cast<UrlData::CorsMode>(cors_mode));
-    // Notify |this| of bytes received by the network.
-    url_data->AddBytesReceivedCallback(BindToCurrentLoop(
-        base::BindRepeating(&WebMediaPlayerImpl::OnBytesReceived, weak_this_)));
     mb_data_source_ = new MultibufferDataSource(
         main_task_runner_, std::move(url_data), media_log_.get(),
         buffered_data_source_host_.get(),
@@ -2435,29 +2429,6 @@
   MaybeSendOverlayInfoToDecoder();
 }
 
-void WebMediaPlayerImpl::SendBytesReceivedUpdate() {
-  media_metrics_provider_->AddBytesReceived(bytes_received_since_last_update_);
-  bytes_received_since_last_update_ = 0;
-}
-
-void WebMediaPlayerImpl::OnBytesReceived(uint64_t data_length) {
-  bytes_received_since_last_update_ += data_length;
-  constexpr base::TimeDelta kBytesReceivedUpdateInterval =
-      base::TimeDelta::FromMilliseconds(500);
-  auto current_time = base::TimeTicks::Now();
-  if (earliest_time_next_bytes_received_update_.is_null() ||
-      earliest_time_next_bytes_received_update_ <= current_time) {
-    report_bytes_received_timer_.Stop();
-    SendBytesReceivedUpdate();
-    earliest_time_next_bytes_received_update_ =
-        current_time + kBytesReceivedUpdateInterval;
-  } else {
-    report_bytes_received_timer_.Start(
-        FROM_HERE, kBytesReceivedUpdateInterval, this,
-        &WebMediaPlayerImpl::SendBytesReceivedUpdate);
-  }
-}
-
 void WebMediaPlayerImpl::ScheduleRestart() {
   // TODO(watk): All restart logic should be moved into PipelineController.
   if (pipeline_controller_->IsPipelineRunning() &&
@@ -2717,9 +2688,6 @@
                          BindToCurrentLoop(base::Bind(
                              &WebMediaPlayerImpl::OnProgress, weak_this_)),
                          encrypted_media_init_data_cb, media_log_.get());
-    // Notify |this| of bytes that are received via MSE.
-    chunk_demuxer_->AddBytesReceivedCallback(BindToCurrentLoop(
-        base::BindRepeating(&WebMediaPlayerImpl::OnBytesReceived, weak_this_)));
     demuxer_.reset(chunk_demuxer_);
 
     if (base::FeatureList::IsEnabled(kMemoryPressureBasedSourceBufferGC)) {
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 6da80820..d14865d 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -239,11 +239,6 @@
   void OnSeekBackward(double seconds) override;
   void OnVolumeMultiplierUpdate(double multiplier) override;
   void OnBecamePersistentVideo(bool value) override;
-
-  // Callback for when bytes are received by |chunk_demuxer_| or the UrlData
-  // being loaded.
-  void OnBytesReceived(uint64_t data_length);
-
   void RequestRemotePlaybackDisabled(bool disabled) override;
 
 #if defined(OS_ANDROID)
@@ -611,8 +606,6 @@
   // Switch to SurfaceLayer, either initially or from VideoLayer.
   void ActivateSurfaceLayerForVideo();
 
-  void SendBytesReceivedUpdate();
-
   // Notifies |mb_data_source_| of playback and rate changes which may increase
   // the amount of data the DataSource buffers. Does nothing prior to reaching
   // kReadyStateHaveEnoughData for the first time.
@@ -857,16 +850,6 @@
   // devices we might use MediaPlayerRenderer for non HLS playback.
   bool demuxer_found_hls_ = false;
 
-  // Bytes received since the last update was sent to |media_metrics_provider_|.
-  uint64_t bytes_received_since_last_update_ = 0;
-
-  // The time that a bytes received update should be sent.
-  base::TimeTicks earliest_time_next_bytes_received_update_;
-
-  // Ensures that all bytes received will eventually be reported, even if
-  // updates stop being received.
-  base::OneShotTimer report_bytes_received_timer_;
-
   // Called sometime after the media is suspended in a playing state in
   // OnFrameHidden(), causing the state to change to paused.
   base::OneShotTimer background_pause_timer_;
diff --git a/media/capture/content/android/java/src/org/chromium/media/ScreenCapture.java b/media/capture/content/android/java/src/org/chromium/media/ScreenCapture.java
index 5b53ede5..d948440 100644
--- a/media/capture/content/android/java/src/org/chromium/media/ScreenCapture.java
+++ b/media/capture/content/android/java/src/org/chromium/media/ScreenCapture.java
@@ -48,7 +48,7 @@
 @JNINamespace("media")
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class ScreenCapture extends Fragment {
-    private static final String TAG = "cr_ScreenCapture";
+    private static final String TAG = "ScreenCapture";
 
     private static final int REQUEST_MEDIA_PROJECTION = 1;
 
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureFactory.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureFactory.java
index 98b9bde..6fb1e26 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureFactory.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureFactory.java
@@ -26,7 +26,7 @@
     // Internal class to encapsulate camera device id manipulations.
     static class ChromiumCameraInfo {
         private static int sNumberOfSystemCameras = -1;
-        private static final String TAG = "cr.media";
+        private static final String TAG = "media";
 
         private static int getNumberOfCameras() {
             if (sNumberOfSystemCameras == -1) {
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 6edb274..44e589d3 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -828,10 +828,6 @@
   return itr->second->EvictCodedFrames(currentMediaTime, newDataSize);
 }
 
-void ChunkDemuxer::AddBytesReceivedCallback(BytesReceivedCB bytes_received_cb) {
-  bytes_received_cb_ = bytes_received_cb;
-}
-
 bool ChunkDemuxer::AppendData(const std::string& id,
                               const uint8_t* data,
                               size_t length,
@@ -856,9 +852,6 @@
     if (length == 0u)
       return true;
 
-    if (bytes_received_cb_)
-      bytes_received_cb_.Run(length);
-
     DCHECK(data);
 
     switch (state_) {
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 4b4ac61..214f700 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -273,12 +273,6 @@
                                    base::TimeDelta curr_time,
                                    TrackChangeCB change_completed_cb) override;
 
-  // Callback for reporting bytes appended to a SourceBuffer.
-  using BytesReceivedCB = base::RepeatingCallback<void(uint64_t)>;
-
-  // Register a BytesReceivedCB.
-  void AddBytesReceivedCallback(BytesReceivedCB bytes_received_cb);
-
   // Appends media data to the source buffer associated with |id|, applying
   // and possibly updating |*timestamp_offset| during coded frame processing.
   // |append_window_start| and |append_window_end| correspond to the MSE spec's
@@ -523,9 +517,6 @@
   // in a shut down state, so reading from them will return EOS.
   std::vector<std::unique_ptr<ChunkDemuxerStream>> removed_streams_;
 
-  // Callback for reporting the number of bytes appended to this ChunkDemuxer.
-  BytesReceivedCB bytes_received_cb_;
-
   std::map<MediaTrack::Id, ChunkDemuxerStream*> track_id_to_demux_stream_map_;
 
   DISALLOW_COPY_AND_ASSIGN(ChunkDemuxer);
diff --git a/media/gpu/windows/d3d11_vp9_accelerator.cc b/media/gpu/windows/d3d11_vp9_accelerator.cc
index f185a93..a8ef7df 100644
--- a/media/gpu/windows/d3d11_vp9_accelerator.cc
+++ b/media/gpu/windows/d3d11_vp9_accelerator.cc
@@ -128,8 +128,10 @@
   COPY_PARAM(frame_context_idx);
   COPY_PARAM(allow_high_precision_mv);
 
-  // extra_plane, BitDepthMinus8Luma, and BitDepthMinus8Chroma are initialized
-  // at 0 already.
+  // extra_plane is initialized to zero.
+
+  pic_params->BitDepthMinus8Luma = pic_params->BitDepthMinus8Chroma =
+      pic.frame_hdr->bit_depth - 8;
 
   pic_params->CurrPic.Index7Bits = pic.level();
   pic_params->frame_type = !pic.frame_hdr->IsKeyframe();
diff --git a/media/mojo/mojom/media_metrics_provider.mojom b/media/mojo/mojom/media_metrics_provider.mojom
index e8b76cf..3d91c64 100644
--- a/media/mojo/mojom/media_metrics_provider.mojom
+++ b/media/mojo/mojom/media_metrics_provider.mojom
@@ -45,7 +45,6 @@
   // Setters for various one-time lazily generated metrics or properties.
   SetHasPlayed();
   SetHaveEnough();
-  SetIsAdMedia();
   SetIsEME();
   SetTimeToMetadata(mojo_base.mojom.TimeDelta elapsed);
   SetTimeToFirstFrame(mojo_base.mojom.TimeDelta elapsed);
@@ -67,9 +66,6 @@
     string taskName,
     pending_receiver<media.learning.mojom.LearningTaskController> controller);
 
-  // Reports that bytes have been received by the media player.
-  AddBytesReceived(uint64 bytes_received);
-
   // Can be called multiple times to set properties about a playback.
   SetHasAudio(AudioCodec codec);
   SetHasVideo(VideoCodec codec);
diff --git a/media/mojo/services/media_metrics_provider.cc b/media/mojo/services/media_metrics_provider.cc
index 46097e5..2c23287 100644
--- a/media/mojo/services/media_metrics_provider.cc
+++ b/media/mojo/services/media_metrics_provider.cc
@@ -77,24 +77,6 @@
     builder.SetTimeToPlayReady(time_to_play_ready_.InMilliseconds());
 
   builder.Record(ukm_recorder);
-
-  // Buffered bytes are reported from a different source for EME/MSE.
-  std::string suffix;
-  if (uma_info_.is_eme)
-    suffix = "EME";
-  else if (is_mse_)
-    suffix = "MSE";
-  else
-    suffix = "SRC";
-  base::UmaHistogramMemoryKB("Media.BytesReceived." + suffix,
-                             total_bytes_received_ >> 10);
-  if (is_ad_media_) {
-    base::UmaHistogramMemoryKB("Ads.Media.BytesReceived",
-                               total_bytes_received_ >> 10);
-    base::UmaHistogramMemoryKB("Ads.Media.BytesReceived." + suffix,
-                               total_bytes_received_ >> 10);
-  }
-
   ReportPipelineUMA();
 }
 
@@ -241,11 +223,6 @@
   uma_info_.last_pipeline_status = status;
 }
 
-void MediaMetricsProvider::SetIsAdMedia() {
-  // This may be called before Initialize().
-  is_ad_media_ = true;
-}
-
 void MediaMetricsProvider::SetIsEME() {
   // This may be called before Initialize().
   uma_info_.is_eme = true;
@@ -332,8 +309,4 @@
       std::move(receiver));
 }
 
-void MediaMetricsProvider::AddBytesReceived(uint64_t bytes_received) {
-  total_bytes_received_ += bytes_received;
-}
-
 }  // namespace media
diff --git a/media/mojo/services/media_metrics_provider.h b/media/mojo/services/media_metrics_provider.h
index c63816f2..8db443f 100644
--- a/media/mojo/services/media_metrics_provider.h
+++ b/media/mojo/services/media_metrics_provider.h
@@ -94,7 +94,6 @@
   void SetHasPlayed() override;
   void SetHasVideo(VideoCodec video_codec) override;
   void SetHaveEnough() override;
-  void SetIsAdMedia() override;
   void SetIsEME() override;
   void SetTimeToMetadata(base::TimeDelta elapsed) override;
   void SetTimeToFirstFrame(base::TimeDelta elapsed) override;
@@ -110,7 +109,6 @@
       const std::string& taskName,
       mojo::PendingReceiver<media::learning::mojom::LearningTaskController>
           receiver) override;
-  void AddBytesReceived(uint64_t bytes_received) override;
 
   void ReportPipelineUMA();
   std::string GetUMANameForAVStream(const PipelineInfo& player_info);
@@ -131,17 +129,11 @@
   // UMA pipeline packaged data
   PipelineInfo uma_info_;
 
-  // These values are not always sent but have known defaults.
-  bool is_ad_media_ = false;
-
   // The values below are only set if |initialized_| is true.
   bool initialized_ = false;
   bool is_mse_;
   mojom::MediaURLScheme url_scheme_;
 
-  // Total number of bytes received by the media player so far.
-  uint64_t total_bytes_received_ = 0;
-
   base::TimeDelta time_to_metadata_ = kNoTimestamp;
   base::TimeDelta time_to_first_frame_ = kNoTimestamp;
   base::TimeDelta time_to_play_ready_ = kNoTimestamp;
diff --git a/media/mojo/services/media_metrics_provider_unittest.cc b/media/mojo/services/media_metrics_provider_unittest.cc
index 0b497bb..745b4d4 100644
--- a/media/mojo/services/media_metrics_provider_unittest.cc
+++ b/media/mojo/services/media_metrics_provider_unittest.cc
@@ -145,43 +145,6 @@
   }
 }
 
-TEST_F(MediaMetricsProviderTest, TestBytesReceivedUMA) {
-  base::HistogramTester histogram_tester;
-  Initialize(false, false, false, kTestOrigin, mojom::MediaURLScheme::kHttp);
-  provider_->AddBytesReceived(1 << 10);
-  provider_.reset();
-  base::RunLoop().RunUntilIdle();
-
-  histogram_tester.ExpectBucketCount("Media.BytesReceived.SRC", 1, 1);
-  histogram_tester.ExpectTotalCount("Media.BytesReceived.MSE", 0);
-  histogram_tester.ExpectTotalCount("Media.BytesReceived.EME", 0);
-  histogram_tester.ExpectTotalCount("Ads.Media.BytesReceived", 0);
-
-  // EME is recorded in before MSE/SRC.
-  Initialize(true, false, false, kTestOrigin, mojom::MediaURLScheme::kHttp);
-  provider_->AddBytesReceived(1 << 10);
-  provider_->SetIsEME();
-  provider_->SetIsAdMedia();
-  provider_.reset();
-  base::RunLoop().RunUntilIdle();
-
-  histogram_tester.ExpectBucketCount("Media.BytesReceived.EME", 1, 1);
-  histogram_tester.ExpectTotalCount("Media.BytesReceived.MSE", 0);
-  histogram_tester.ExpectBucketCount("Ads.Media.BytesReceived", 1, 1);
-  histogram_tester.ExpectBucketCount("Ads.Media.BytesReceived.EME", 1, 1);
-  histogram_tester.ExpectTotalCount("Ads.Media.BytesReceived.MSE", 0);
-
-  Initialize(true, false, false, kTestOrigin, mojom::MediaURLScheme::kHttp);
-  provider_->AddBytesReceived(1 << 10);
-  provider_->SetIsAdMedia();
-  provider_.reset();
-  base::RunLoop().RunUntilIdle();
-
-  histogram_tester.ExpectBucketCount("Media.BytesReceived.MSE", 1, 1);
-  histogram_tester.ExpectBucketCount("Ads.Media.BytesReceived.MSE", 1, 1);
-  histogram_tester.ExpectBucketCount("Ads.Media.BytesReceived", 1, 2);
-}
-
 TEST_F(MediaMetricsProviderTest, TestPipelineUMA) {
   base::HistogramTester histogram_tester;
   Initialize(false, false, false, kTestOrigin, mojom::MediaURLScheme::kHttps);
diff --git a/net/BUILD.gn b/net/BUILD.gn
index f280971..bc5600d5 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1622,7 +1622,6 @@
       "third_party/quiche/src/quic/core/quic_packet_writer.h",
       "third_party/quiche/src/quic/core/quic_packets.cc",
       "third_party/quiche/src/quic/core/quic_packets.h",
-      "third_party/quiche/src/quic/core/quic_pending_retransmission.h",
       "third_party/quiche/src/quic/core/quic_received_packet_manager.cc",
       "third_party/quiche/src/quic/core/quic_received_packet_manager.h",
       "third_party/quiche/src/quic/core/quic_sent_packet_manager.cc",
@@ -1731,6 +1730,8 @@
       "third_party/quiche/src/quic/quic_transport/quic_transport_server_session.cc",
       "third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h",
       "third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h",
+      "third_party/quiche/src/quic/quic_transport/quic_transport_stream.cc",
+      "third_party/quiche/src/quic/quic_transport/quic_transport_stream.h",
       "third_party/quiche/src/spdy/core/fifo_write_scheduler.h",
       "third_party/quiche/src/spdy/core/hpack/hpack_constants.cc",
       "third_party/quiche/src/spdy/core/hpack/hpack_constants.h",
@@ -3519,6 +3520,8 @@
     "third_party/quiche/src/quic/test_tools/simulator/queue.h",
     "third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.cc",
     "third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h",
+    "third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.cc",
+    "third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.h",
     "third_party/quiche/src/quic/test_tools/simulator/simulator.cc",
     "third_party/quiche/src/quic/test_tools/simulator/simulator.h",
     "third_party/quiche/src/quic/test_tools/simulator/switch.cc",
@@ -5640,6 +5643,7 @@
     "third_party/quiche/src/quic/platform/api/quic_text_utils_test.cc",
     "third_party/quiche/src/quic/quic_transport/quic_transport_client_session_test.cc",
     "third_party/quiche/src/quic/quic_transport/quic_transport_server_session_test.cc",
+    "third_party/quiche/src/quic/quic_transport/quic_transport_stream_test.cc",
     "third_party/quiche/src/quic/test_tools/crypto_test_utils_test.cc",
     "third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.cc",
     "third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.h",
diff --git a/net/cookies/OWNERS b/net/cookies/OWNERS
index fb0dd2d..9b5ffc36 100644
--- a/net/cookies/OWNERS
+++ b/net/cookies/OWNERS
@@ -1,3 +1,4 @@
+chlily@chromium.org
 estark@chromium.org
 mkwst@chromium.org
 mmenke@chromium.org
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index e5fb2f88..50c122c3 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -444,6 +444,7 @@
   return key;
 }
 
+// static
 std::string HttpCache::GenerateCacheKeyForTest(const HttpRequestInfo* request) {
   return GenerateCacheKey(request);
 }
@@ -520,6 +521,7 @@
   return ERR_IO_PENDING;
 }
 
+// static
 // Generate a key that can be used inside the cache.
 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
   std::string isolation_key;
@@ -540,7 +542,6 @@
   // concatenate with the network isolation key if we are splitting the cache.
   std::string url = isolation_key + HttpUtil::SpecForRequest(request->url);
 
-  DCHECK_NE(DISABLE, mode_);
   // No valid URL can begin with numerals, so we should not have to worry
   // about collisions with normal URLs.
   if (request->upload_data_stream &&
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index c08c410..353d4d7 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -268,7 +268,7 @@
   static std::string GetResourceURLFromHttpCacheKey(const std::string& key);
 
   // Function to generate cache key for testing.
-  std::string GenerateCacheKeyForTest(const HttpRequestInfo* request);
+  static std::string GenerateCacheKeyForTest(const HttpRequestInfo* request);
 
  private:
   // Types --------------------------------------------------------------------
@@ -417,7 +417,7 @@
   int GetBackendForTransaction(Transaction* transaction);
 
   // Generates the cache key for this request.
-  std::string GenerateCacheKey(const HttpRequestInfo*);
+  static std::string GenerateCacheKey(const HttpRequestInfo*);
 
   // Dooms the entry selected by |key|, if it is currently in the list of active
   // entries.
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 3838a85a..057b454 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -546,15 +546,25 @@
 }
 
 const MockTransaction kRangeGET_TransactionOK = {
-    "http://www.google.com/range", "GET", base::Time(),
-    "Range: bytes = 40-49\r\n" EXTRA_HEADER, LOAD_NORMAL,
+    "http://www.google.com/range",
+    "GET",
+    base::Time(),
+    "Range: bytes = 40-49\r\n" EXTRA_HEADER,
+    LOAD_NORMAL,
     "HTTP/1.1 206 Partial Content",
     "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
     "ETag: \"foo\"\n"
     "Accept-Ranges: bytes\n"
     "Content-Length: 10\n",
-    base::Time(), "rg: 40-49 ", TEST_MODE_NORMAL,
-    &RangeTransactionServer::RangeHandler, nullptr, nullptr, 0, 0, OK};
+    base::Time(),
+    "rg: 40-49 ",
+    TEST_MODE_NORMAL,
+    &RangeTransactionServer::RangeHandler,
+    nullptr,
+    nullptr,
+    0,
+    0,
+    OK};
 
 const char kFullRangeData[] =
     "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 "
@@ -583,8 +593,8 @@
 void CreateTruncatedEntry(std::string raw_headers, MockHttpCache* cache) {
   // Create a disk cache entry that stores an incomplete resource.
   disk_cache::Entry* entry;
-  ASSERT_TRUE(
-      cache->CreateBackendEntry(kRangeGET_TransactionOK.url, &entry, nullptr));
+  MockHttpRequest request(kRangeGET_TransactionOK);
+  ASSERT_TRUE(cache->CreateBackendEntry(request.CacheKey(), &entry, nullptr));
 
   HttpResponseInfo response;
   response.response_time = base::Time::Now();
@@ -958,7 +968,7 @@
 
   // We have to open the entry again to propagate the failure flag.
   disk_cache::Entry* en;
-  ASSERT_TRUE(cache.OpenBackendEntry(kSimpleGET_Transaction.url, &en));
+  ASSERT_TRUE(cache.OpenBackendEntry(request.CacheKey(), &en));
   en->Close();
 
   ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
@@ -1706,8 +1716,8 @@
   base::RunLoop().RunUntilIdle();
 
   // All requests are added to writers.
-  EXPECT_EQ(kNumTransactions,
-            cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(kNumTransactions, cache.GetCountWriterTransactions(cache_key));
 
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -1726,9 +1736,8 @@
     // After the 1st transaction has completed the response, all transactions
     // get added to readers.
     if (i > 0) {
-      EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
-      EXPECT_EQ(kNumTransactions - i,
-                cache.GetCountReaders(kSimpleGET_Transaction.url));
+      EXPECT_FALSE(cache.IsWriterPresent(cache_key));
+      EXPECT_EQ(kNumTransactions - i, cache.GetCountReaders(cache_key));
     }
 
     ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
@@ -1958,7 +1967,7 @@
 
   // First entry created is doomed due to 2nd transaction's validation leading
   // to restarting of the queued transactions.
-  EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
+  EXPECT_TRUE(cache.IsWriterPresent(request.CacheKey()));
 
   // TODO(shivanisha): The restarted transactions race for creating the entry
   // and thus instead of all 4 succeeding, 2 of them succeed. This is very
@@ -2030,19 +2039,18 @@
     // 3rd transaction will doom the entry.
     base::RunLoop().RunUntilIdle();
 
+    std::string cache_key = request.CacheKey();
     // Check status of the first and second entries after every transaction.
     switch (i) {
       case 0:
-        first_entry =
-            cache.disk_cache()->GetDiskEntryRef(kRangeGET_TransactionOK.url);
+        first_entry = cache.disk_cache()->GetDiskEntryRef(cache_key);
         break;
       case 1:
         EXPECT_FALSE(first_entry->is_doomed());
         break;
       case 2:
         EXPECT_TRUE(first_entry->is_doomed());
-        second_entry =
-            cache.disk_cache()->GetDiskEntryRef(kRangeGET_TransactionOK.url);
+        second_entry = cache.disk_cache()->GetDiskEntryRef(cache_key);
         EXPECT_FALSE(second_entry->is_doomed());
         break;
     }
@@ -2122,8 +2130,7 @@
     // Check status of the entry after every transaction.
     switch (i) {
       case 0:
-        first_entry =
-            cache.disk_cache()->GetDiskEntryRef(kRangeGET_TransactionOK.url);
+        first_entry = cache.disk_cache()->GetDiskEntryRef(request.CacheKey());
         break;
       case 1:
         EXPECT_FALSE(first_entry->is_doomed());
@@ -2224,8 +2231,9 @@
     EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
   }
 
-  EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
-  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
+  std::string cache_key = request2.CacheKey();
+  EXPECT_TRUE(cache.IsWriterPresent(cache_key));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(cache_key));
 
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -2294,7 +2302,8 @@
   context.result = context.trans->Start(&request, context.callback.callback(),
                                         NetLogWithSource());
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, cache.GetCountReaders(transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(1, cache.GetCountReaders(cache_key));
   RemoveMockTransaction(&transaction);
 
   // A range request should now "not" create Writers while readers is still
@@ -2310,9 +2319,9 @@
       &range_request, range_context.callback.callback(), NetLogWithSource());
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(1, cache.GetCountReaders(transaction.url));
-  EXPECT_FALSE(cache.IsWriterPresent(transaction.url));
-  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(transaction.url));
+  EXPECT_EQ(1, cache.GetCountReaders(cache_key));
+  EXPECT_FALSE(cache.IsWriterPresent(cache_key));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(cache_key));
 
   RemoveMockTransaction(&range_transaction);
 }
@@ -2379,7 +2388,7 @@
     EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
   }
 
-  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
+  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(request1.CacheKey()));
 
   EXPECT_EQ(3, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -2634,8 +2643,9 @@
     EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
   }
 
-  EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
-  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
+  std::string cache_key = request1.CacheKey();
+  EXPECT_TRUE(cache.IsWriterPresent(cache_key));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(cache_key));
 
   // Should have created another transaction for the uncached range.
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
@@ -2736,8 +2746,9 @@
     EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
   }
 
-  EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
-  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
+  std::string cache_key = request1.CacheKey();
+  EXPECT_TRUE(cache.IsWriterPresent(cache_key));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(cache_key));
 
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -2794,7 +2805,7 @@
   EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c1->trans->GetLoadState());
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.IsWriterPresent(request.CacheKey()));
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2865,7 +2876,7 @@
   // The first request should be a writer at this point, and the subsequent
   // requests should have passed the validation phase and created their own
   // entries since none of them matched the headers of the earlier one.
-  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.IsWriterPresent(request.CacheKey()));
 
   EXPECT_EQ(5, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -2990,7 +3001,7 @@
   // The new entry will have all the transactions except the first one which
   // will continue in the doomed entry.
   EXPECT_EQ(kNumTransactions - 1,
-            cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+            cache.GetCountWriterTransactions(validate_request.CacheKey()));
 
   EXPECT_EQ(1, cache.disk_cache()->doomed_count());
 
@@ -3056,8 +3067,7 @@
   // The first request should be a writer at this point, and the subsequent
   // request should have passed the validation phase and doomed the existing
   // entry.
-  EXPECT_TRUE(
-      cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(cache.disk_cache()->IsDiskEntryDoomed(request.CacheKey()));
 
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -3114,12 +3124,13 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_EQ(1, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
-  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(1, cache.GetCountWriterTransactions(cache_key));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(cache_key));
 
   context_list[1].reset();
 
-  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(cache_key));
 
   // Complete the rest of the transactions.
   for (auto& context : context_list) {
@@ -3162,13 +3173,12 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   // Both transactions would be added to writers.
-  EXPECT_EQ(kNumTransactions,
-            cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(kNumTransactions, cache.GetCountWriterTransactions(cache_key));
 
   context_list[1].reset();
 
-  EXPECT_EQ(kNumTransactions - 1,
-            cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  EXPECT_EQ(kNumTransactions - 1, cache.GetCountWriterTransactions(cache_key));
 
   // Complete the rest of the transactions.
   for (auto& context : context_list) {
@@ -3224,8 +3234,9 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
-  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_TRUE(cache.IsWriterPresent(cache_key));
+  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(cache_key));
 
   base::RunLoop().RunUntilIdle();
 
@@ -3274,20 +3285,20 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_EQ(kNumTransactions - 1,
-            cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
-  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+
+  EXPECT_EQ(kNumTransactions - 1, cache.GetCountWriterTransactions(cache_key));
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(cache_key));
 
   // Complete the response body.
   auto& c = context_list[0];
   ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
 
   // Rest of the transactions should move to readers.
-  EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
-  EXPECT_EQ(kNumTransactions - 2,
-            cache.GetCountReaders(kSimpleGET_Transaction.url));
-  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
-  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+  EXPECT_FALSE(cache.IsWriterPresent(cache_key));
+  EXPECT_EQ(kNumTransactions - 2, cache.GetCountReaders(cache_key));
+  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(cache_key));
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(cache_key));
 
   // Add 2 new transactions.
   kNumTransactions = 6;
@@ -3303,15 +3314,15 @@
         c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
   }
 
-  EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
+  EXPECT_EQ(2, cache.GetCountAddToEntryQueue(cache_key));
 
   // Delete a reader.
   context_list[1].reset();
 
   // Deleting the reader did not impact any other transaction.
-  EXPECT_EQ(1, cache.GetCountReaders(kSimpleGET_Transaction.url));
-  EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
-  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+  EXPECT_EQ(1, cache.GetCountReaders(cache_key));
+  EXPECT_EQ(2, cache.GetCountAddToEntryQueue(cache_key));
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(cache_key));
 
   // Resume network start for headers_transaction. It will doom the entry as it
   // will be a 200 and will go to network for the response body.
@@ -3321,7 +3332,7 @@
   // The pending transactions will be added to a new entry as writers.
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(3, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  EXPECT_EQ(3, cache.GetCountWriterTransactions(cache_key));
 
   // Complete the rest of the transactions.
   for (int i = 2; i < kNumTransactions; ++i) {
@@ -3356,8 +3367,9 @@
   EXPECT_EQ(1, buffer_callback.GetResult(result));
 
   // Read the second byte, but leave the cache write hanging.
+  std::string cache_key = request.CacheKey();
   scoped_refptr<MockDiskEntry> entry =
-      mock_cache.disk_cache()->GetDiskEntryRef(kSimpleGET_Transaction.url);
+      mock_cache.disk_cache()->GetDiskEntryRef(cache_key);
   entry->SetDefer(MockDiskEntry::DEFER_WRITE);
 
   buffer = base::MakeRefCounted<IOBuffer>(1);
@@ -3365,13 +3377,13 @@
   result = transaction->Read(buffer.get(), 1, buffer_callback2.callback());
   EXPECT_EQ(ERR_IO_PENDING, result);
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(mock_cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_TRUE(mock_cache.IsWriterPresent(cache_key));
 
   // At this point the next byte should have been read from the network but is
   // waiting to be written to the cache. Destroy the transaction and make sure
   // that everything has been cleaned up.
   transaction = nullptr;
-  EXPECT_FALSE(mock_cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_FALSE(mock_cache.IsWriterPresent(cache_key));
   EXPECT_FALSE(mock_cache.network_layer()->last_transaction());
 }
 
@@ -3414,8 +3426,9 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
-  EXPECT_EQ(2, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  std::string cache_key = validate_request.CacheKey();
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(cache_key));
+  EXPECT_EQ(2, cache.GetCountWriterTransactions(cache_key));
 
   // Initiate Read from both writers and kill 1 of them mid-read.
   std::string first_read;
@@ -3445,7 +3458,7 @@
 
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(1, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  EXPECT_EQ(1, cache.GetCountWriterTransactions(cache_key));
 
   // Complete the rest of the transactions.
   for (int i = 0; i < kNumTransactions; i++) {
@@ -3506,8 +3519,9 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_EQ(3, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
-  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  std::string cache_key = read_request.CacheKey();
+  EXPECT_EQ(3, cache.GetCountWriterTransactions(cache_key));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(cache_key));
 
   // Initiate Read from two writers and let the first get a network failure.
   for (int i = 0; i < 2; i++) {
@@ -3533,7 +3547,7 @@
   read_only->result = read_only->callback.WaitForResult();
   EXPECT_EQ(ERR_CACHE_MISS, read_only->result);
 
-  EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_FALSE(cache.IsWriterPresent(cache_key));
 
   // Invoke Read on the 3rd transaction and it should get the error code back.
   auto& c = context_list[2];
@@ -3579,14 +3593,15 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_EQ(3, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
-  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  std::string cache_key = read_request.CacheKey();
+  EXPECT_EQ(3, cache.GetCountWriterTransactions(cache_key));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(cache_key));
 
   // Initiate Read from two writers and let the first get a cache write failure.
   cache.disk_cache()->set_soft_failures_mask(MockDiskEntry::FAIL_ALL);
   // We have to open the entry again to propagate the failure flag.
   disk_cache::Entry* en;
-  cache.OpenBackendEntry(kSimpleGET_Transaction.url, &en);
+  cache.OpenBackendEntry(cache_key, &en);
   en->Close();
   const int kBufferSize = 5;
   std::vector<scoped_refptr<IOBuffer>> buffer(
@@ -3618,7 +3633,7 @@
   read_only->result = read_only->callback.WaitForResult();
   EXPECT_EQ(ERR_CACHE_MISS, read_only->result);
 
-  EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+  EXPECT_FALSE(cache.IsWriterPresent(cache_key));
 
   // Invoke Read on the 3rd transaction and it should get the error code back.
   auto& c = context_list[2];
@@ -3679,9 +3694,7 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  std::string cache_key =
-      base::StringPrintf("1/%s", kSimplePOST_Transaction.url);
-
+  std::string cache_key = request.CacheKey();
   // Only the 1st transaction gets added to writers.
   EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(cache_key));
   EXPECT_EQ(1, cache.GetCountWriterTransactions(cache_key));
@@ -3747,8 +3760,9 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_EQ(3, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
-  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(3, cache.GetCountWriterTransactions(cache_key));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(cache_key));
 
   // Initiate Read from two writers.
   const int kBufferSize = 5;
@@ -3780,7 +3794,7 @@
                                       kSimpleGET_Transaction);
     if (i == 0) {
       // Remaining transactions should now be readers.
-      EXPECT_EQ(3, cache.GetCountReaders(kSimpleGET_Transaction.url));
+      EXPECT_EQ(3, cache.GetCountReaders(cache_key));
     }
   }
 
@@ -3839,9 +3853,9 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_EQ(1, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
-  EXPECT_EQ(kNumTransactions - 1,
-            cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(1, cache.GetCountWriterTransactions(cache_key));
+  EXPECT_EQ(kNumTransactions - 1, cache.GetCountDoneHeadersQueue(cache_key));
 
   // Initiate Read from first transaction.
   const int kBufferSize = 5;
@@ -3913,8 +3927,9 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_EQ(2, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
-  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(2, cache.GetCountWriterTransactions(cache_key));
+  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(cache_key));
 
   // Get the network bytes read by the first transaction.
   int total_received_bytes = context_list[0]->trans->GetTotalReceivedBytes();
@@ -3925,7 +3940,7 @@
   ReadAndVerifyTransaction(context_list[1]->trans.get(),
                            kSimpleGET_Transaction);
 
-  EXPECT_EQ(1, cache.GetCountReaders(kSimpleGET_Transaction.url));
+  EXPECT_EQ(1, cache.GetCountReaders(cache_key));
 
   // Verify that the network bytes read are not attributed to the 2nd
   // transaction but to the 1st.
@@ -3956,8 +3971,9 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_EQ(1, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
-  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(1, cache.GetCountWriterTransactions(cache_key));
+  EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(cache_key));
 
   ReadAndVerifyTransaction(c.trans.get(), kSimpleGET_Transaction);
 
@@ -4001,8 +4017,8 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_EQ(kNumTransactions,
-            cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(kNumTransactions, cache.GetCountWriterTransactions(cache_key));
 
   // Let first transaction read some bytes.
   {
@@ -4070,9 +4086,9 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_EQ(kNumTransactions - 1,
-            cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
-  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(kNumTransactions - 1, cache.GetCountWriterTransactions(cache_key));
+  EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(cache_key));
 
   // Invoking StopCaching on the writer will lead to dooming the entry and
   // restarting the validated transactions. Since it is a read-only transaction
@@ -4126,9 +4142,9 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
-  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
-  EXPECT_EQ(kNumTransactions - 1,
-            cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(cache_key));
+  EXPECT_EQ(kNumTransactions - 1, cache.GetCountWriterTransactions(cache_key));
 
   // Invoking StopCaching on the writer will be a no-op since there are multiple
   // transaction in writers.
@@ -4140,7 +4156,7 @@
   base::RunLoop().RunUntilIdle();
   // After validation old entry will be doomed and headers_transaction will be
   // added to the new entry.
-  EXPECT_EQ(1, cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  EXPECT_EQ(1, cache.GetCountWriterTransactions(cache_key));
 
   // Complete the rest of the transactions.
   for (auto& context : context_list) {
@@ -4180,8 +4196,9 @@
 
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
-  EXPECT_EQ(1, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_TRUE(cache.IsHeadersTransactionPresent(cache_key));
+  EXPECT_EQ(1, cache.GetCountAddToEntryQueue(cache_key));
 
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -4235,8 +4252,8 @@
   base::RunLoop().RunUntilIdle();
 
   // All transactions become writers.
-  EXPECT_EQ(kNumTransactions,
-            cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  std::string cache_key = request.CacheKey();
+  EXPECT_EQ(kNumTransactions, cache.GetCountWriterTransactions(cache_key));
 
   // All requests depend on the writer, and the writer is between Start and
   // Read, i.e. idle.
@@ -4252,7 +4269,7 @@
   cache.disk_cache()->set_soft_failures_mask(MockDiskEntry::FAIL_ALL);
   // We have to open the entry again to propagate the failure flag.
   disk_cache::Entry* en;
-  cache.OpenBackendEntry(kSimpleGET_Transaction.url, &en);
+  cache.OpenBackendEntry(cache_key, &en);
   en->Close();
 
   for (int i = 0; i < kNumTransactions; ++i) {
@@ -4261,8 +4278,7 @@
       c->result = c->callback.WaitForResult();
     if (i == 1) {
       // The earlier entry must be destroyed and its disk entry doomed.
-      EXPECT_TRUE(
-          cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+      EXPECT_TRUE(cache.disk_cache()->IsDiskEntryDoomed(cache_key));
     }
 
     if (i == 0) {
@@ -4558,8 +4574,8 @@
   // Allow all requests to move from the Create queue to the active entry.
   // All would have been added to writers.
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kNumTransactions,
-            cache.GetCountWriterTransactions(kSimpleGET_Transaction.url));
+  std::string cache_key = cache.http_cache()->GenerateCacheKeyForTest(&request);
+  EXPECT_EQ(kNumTransactions, cache.GetCountWriterTransactions(cache_key));
 
   // The second transaction skipped validation, thus only one network
   // transaction is created.
@@ -7373,7 +7389,9 @@
 
   // Simulate a previous transaction being cancelled.
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
+  MockHttpRequest request(transaction);
+  std::string cache_key = cache.http_cache()->GenerateCacheKeyForTest(&request);
+  ASSERT_TRUE(cache.OpenBackendEntry(cache_key, &entry));
   entry->CancelSparseIO();
 
   // Test with a range request.
@@ -8041,10 +8059,10 @@
 TEST_F(HttpCacheTest, GET_Previous206_NotSparse) {
   MockHttpCache cache;
 
+  MockHttpRequest request(kSimpleGET_Transaction);
   // Create a disk cache entry that stores 206 headers while not being sparse.
   disk_cache::Entry* entry;
-  ASSERT_TRUE(
-      cache.CreateBackendEntry(kSimpleGET_Transaction.url, &entry, nullptr));
+  ASSERT_TRUE(cache.CreateBackendEntry(request.CacheKey(), &entry, nullptr));
 
   std::string raw_headers(kRangeGET_TransactionOK.status);
   raw_headers.append("\n");
@@ -8089,9 +8107,9 @@
   AddMockTransaction(&kRangeGET_TransactionOK);
 
   // Create a disk cache entry that stores 206 headers while not being sparse.
+  MockHttpRequest request(kRangeGET_TransactionOK);
   disk_cache::Entry* entry;
-  ASSERT_TRUE(
-      cache.CreateBackendEntry(kRangeGET_TransactionOK.url, &entry, nullptr));
+  ASSERT_TRUE(cache.CreateBackendEntry(request.CacheKey(), &entry, nullptr));
 
   std::string raw_headers(kRangeGET_TransactionOK.status);
   raw_headers.append("\n");
@@ -8128,10 +8146,10 @@
 TEST_F(HttpCacheTest, GET_Previous206_NotValidation) {
   MockHttpCache cache;
 
+  MockHttpRequest request(kSimpleGET_Transaction);
   // Create a disk cache entry that stores 206 headers.
   disk_cache::Entry* entry;
-  ASSERT_TRUE(
-      cache.CreateBackendEntry(kSimpleGET_Transaction.url, &entry, nullptr));
+  ASSERT_TRUE(cache.CreateBackendEntry(request.CacheKey(), &entry, nullptr));
 
   // Make sure that the headers cannot be validated with the server.
   std::string raw_headers(kRangeGET_TransactionOK.status);
@@ -8340,7 +8358,7 @@
 
   // Verify that the entry has not been deleted.
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
+  ASSERT_TRUE(cache.OpenBackendEntry(request.CacheKey(), &entry));
   entry->Close();
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 }
@@ -8380,7 +8398,7 @@
   base::RunLoop().RunUntilIdle();
 
   // Verify that the entry has not been marked as truncated.
-  VerifyTruncatedFlag(&cache, kRangeGET_TransactionOK.url, false, 0);
+  VerifyTruncatedFlag(&cache, request.CacheKey(), false, 0);
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 }
 
@@ -8514,7 +8532,8 @@
 
   // Verify that we don't have a cached entry.
   disk_cache::Entry* entry;
-  EXPECT_FALSE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
+  MockHttpRequest request(transaction);
+  EXPECT_FALSE(cache.OpenBackendEntry(request.CacheKey(), &entry));
 
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 }
@@ -8542,7 +8561,8 @@
 
   // Verify that we don't have a cached entry.
   disk_cache::Entry* entry;
-  EXPECT_FALSE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
+  MockHttpRequest request(transaction);
+  EXPECT_FALSE(cache.OpenBackendEntry(request.CacheKey(), &entry));
 
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 }
@@ -8613,7 +8633,8 @@
 
   // Verify that we have a cached entry.
   disk_cache::Entry* en;
-  ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &en));
+  MockHttpRequest request(transaction);
+  ASSERT_TRUE(cache.OpenBackendEntry(request.CacheKey(), &en));
   en->Close();
 
   RemoveMockTransaction(&kRangeGET_TransactionOK);
@@ -8973,7 +8994,7 @@
   EXPECT_FALSE(c->callback.have_result());
 
   base::RunLoop().RunUntilIdle();
-  VerifyTruncatedFlag(&cache, kSimpleGET_Transaction.url, true, 0);
+  VerifyTruncatedFlag(&cache, request.CacheKey(), true, 0);
 }
 
 // Tests that we do not mark an entry as truncated when the request is
@@ -9032,7 +9053,7 @@
   // complete.
   base::RunLoop().RunUntilIdle();
   disk_cache::Entry* entry;
-  ASSERT_FALSE(cache.OpenBackendEntry(kSimpleGET_Transaction.url, &entry));
+  ASSERT_FALSE(cache.OpenBackendEntry(request.CacheKey(), &entry));
 }
 
 // Tests that we don't mark an entry as truncated when we read everything.
@@ -9063,7 +9084,7 @@
   c->trans.reset();
 
   // Verify that the entry is not marked as truncated.
-  VerifyTruncatedFlag(&cache, kSimpleGET_Transaction.url, false, 0);
+  VerifyTruncatedFlag(&cache, request.CacheKey(), false, 0);
 }
 
 // Tests that sparse entries don't set the truncate flag.
@@ -9089,7 +9110,7 @@
 
   // Should not trigger any DCHECK.
   trans.reset();
-  VerifyTruncatedFlag(&cache, kRangeGET_TransactionOK.url, false, 0);
+  VerifyTruncatedFlag(&cache, request->CacheKey(), false, 0);
 }
 
 // Tests that sparse entries don't set the truncate flag (when the byte range
@@ -9116,7 +9137,7 @@
 
   // Should not trigger any DCHECK.
   trans.reset();
-  VerifyTruncatedFlag(&cache, kRangeGET_TransactionOK.url, false, 0);
+  VerifyTruncatedFlag(&cache, request->CacheKey(), false, 0);
 }
 
 // Tests that we can continue with a request that was interrupted.
@@ -9151,7 +9172,8 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   // Verify that the disk entry was updated.
-  VerifyTruncatedFlag(&cache, kRangeGET_TransactionOK.url, false, 80);
+  MockHttpRequest request(transaction);
+  VerifyTruncatedFlag(&cache, request.CacheKey(), false, 80);
 }
 
 // Tests the handling of no-store when revalidating a truncated entry.
@@ -9195,7 +9217,8 @@
 
   // Verify that the disk entry was deleted.
   disk_cache::Entry* entry;
-  EXPECT_FALSE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
+  MockHttpRequest request(transaction);
+  EXPECT_FALSE(cache.OpenBackendEntry(request.CacheKey(), &entry));
   RemoveMockTransaction(&transaction);
 }
 
@@ -9292,7 +9315,8 @@
 
   // Verify that the disk entry was deleted.
   disk_cache::Entry* entry;
-  ASSERT_FALSE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
+  MockHttpRequest request(transaction);
+  ASSERT_FALSE(cache.OpenBackendEntry(request.CacheKey(), &entry));
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 }
 
@@ -9374,7 +9398,7 @@
 
   // Verify that the entry was deleted.
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
+  ASSERT_TRUE(cache.OpenBackendEntry(request.CacheKey(), &entry));
   entry->Close();
 
   RemoveMockTransaction(&kRangeGET_TransactionOK);
@@ -9441,7 +9465,8 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   // Verify that the disk entry was updated.
-  VerifyTruncatedFlag(&cache, kRangeGET_TransactionOK.url, false, 11);
+  MockHttpRequest request(transaction);
+  VerifyTruncatedFlag(&cache, request.CacheKey(), false, 11);
 }
 
 // Tests that when we cancel a request that was interrupted, we mark it again
@@ -9484,7 +9509,7 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   // Verify that the disk entry was updated: now we have 30 bytes.
-  VerifyTruncatedFlag(&cache, kRangeGET_TransactionOK.url, true, 30);
+  VerifyTruncatedFlag(&cache, request.CacheKey(), true, 30);
 }
 
 // Tests that we can handle range requests when we have a truncated entry.
@@ -9673,7 +9698,8 @@
     }
 
     disk_cache::Entry* entry;
-    EXPECT_TRUE(cache.OpenBackendEntry(transaction.url, &entry));
+    MockHttpRequest request(transaction);
+    EXPECT_TRUE(cache.OpenBackendEntry(request.CacheKey(), &entry));
     entry->Close();
   }
 }
@@ -9702,7 +9728,8 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   disk_cache::Entry* entry;
-  EXPECT_TRUE(cache.OpenBackendEntry(transaction.url, &entry));
+  MockHttpRequest request(transaction);
+  EXPECT_TRUE(cache.OpenBackendEntry(request.CacheKey(), &entry));
   entry->Close();
 }
 
@@ -9727,7 +9754,8 @@
   EXPECT_EQ(2, cache.disk_cache()->create_count());
 
   disk_cache::Entry* entry;
-  EXPECT_FALSE(cache.OpenBackendEntry(transaction.url, &entry));
+  MockHttpRequest request(transaction);
+  EXPECT_FALSE(cache.OpenBackendEntry(request.CacheKey(), &entry));
 }
 
 TEST_F(HttpCacheTest, CacheControlNoStore2) {
@@ -9755,7 +9783,8 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   disk_cache::Entry* entry;
-  EXPECT_FALSE(cache.OpenBackendEntry(transaction.url, &entry));
+  MockHttpRequest request(transaction);
+  EXPECT_FALSE(cache.OpenBackendEntry(request.CacheKey(), &entry));
 }
 
 TEST_F(HttpCacheTest, CacheControlNoStore3) {
@@ -9784,7 +9813,8 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   disk_cache::Entry* entry;
-  EXPECT_FALSE(cache.OpenBackendEntry(transaction.url, &entry));
+  MockHttpRequest request(transaction);
+  EXPECT_FALSE(cache.OpenBackendEntry(request.CacheKey(), &entry));
 }
 
 // Ensure that we don't cache requests served over bad HTTPS.
@@ -9914,6 +9944,7 @@
 
   RemoveMockTransaction(&mock_network_response);
 }
+
 TEST_F(HttpCacheTest, SplitCacheWithFrameOrigin) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
@@ -10116,6 +10147,7 @@
 
   // A request without a top frame origin is not cached at all.
   MockHttpRequest trans_info = MockHttpRequest(kSimpleGET_Transaction);
+  trans_info.network_isolation_key = net::NetworkIsolationKey();
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
   EXPECT_FALSE(response.was_cached);
@@ -10205,6 +10237,7 @@
 
   // A request without a top frame is cached normally.
   MockHttpRequest trans_info = MockHttpRequest(kSimpleGET_Transaction);
+  trans_info.network_isolation_key = NetworkIsolationKey();
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
   EXPECT_FALSE(response.was_cached);
@@ -10527,7 +10560,7 @@
   // Verify that the entry is marked as incomplete.
   // VerifyTruncatedFlag(&cache, kSimpleGET_Transaction.url, true, 0);
   // Verify that the entry is doomed.
-  cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url);
+  cache.disk_cache()->IsDiskEntryDoomed(request.CacheKey());
 }
 
 // Tests that we handle truncated enries when StopCaching is called.
@@ -10571,7 +10604,7 @@
   }
 
   // Verify that the disk entry was updated.
-  VerifyTruncatedFlag(&cache, kRangeGET_TransactionOK.url, false, 80);
+  VerifyTruncatedFlag(&cache, request.CacheKey(), false, 80);
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 }
 
@@ -10884,7 +10917,8 @@
   RemoveMockTransaction(&transaction);
 
   // Verify that the entry is marked as incomplete.
-  VerifyTruncatedFlag(&cache, kSimpleGET_Transaction.url, true, 0);
+  MockHttpRequest request(transaction);
+  VerifyTruncatedFlag(&cache, request.CacheKey(), true, 0);
 }
 
 // Make sure that calling SetPriority on a cache transaction passes on
diff --git a/net/http/http_transaction_test_util.cc b/net/http/http_transaction_test_util.cc
index 5f75d668..43bb1e6e 100644
--- a/net/http/http_transaction_test_util.cc
+++ b/net/http/http_transaction_test_util.cc
@@ -20,6 +20,7 @@
 #include "net/base/load_flags.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_isolation_key.h"
 #include "net/cert/x509_certificate.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/http_cache.h"
@@ -174,6 +175,12 @@
   method = t.method;
   extra_headers.AddHeadersFromString(t.request_headers);
   load_flags = t.load_flags;
+  url::Origin origin = url::Origin::Create(url);
+  network_isolation_key = NetworkIsolationKey(origin, origin);
+}
+
+std::string MockHttpRequest::CacheKey() {
+  return HttpCache::GenerateCacheKeyForTest(this);
 }
 
 //-----------------------------------------------------------------------------
diff --git a/net/http/http_transaction_test_util.h b/net/http/http_transaction_test_util.h
index 513d87150..acb3274 100644
--- a/net/http/http_transaction_test_util.h
+++ b/net/http/http_transaction_test_util.h
@@ -122,6 +122,7 @@
 class MockHttpRequest : public HttpRequestInfo {
  public:
   explicit MockHttpRequest(const MockTransaction& t);
+  std::string CacheKey();
 };
 
 //-----------------------------------------------------------------------------
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index bfb434b9..5af24c6 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -821,7 +821,7 @@
   connect_timing_.dns_start = dns_resolution_start_time;
   connect_timing_.dns_end = dns_resolution_end_time;
   if (!retransmittable_on_wire_timeout.IsZero()) {
-    connection->set_retransmittable_on_wire_timeout(
+    connection->set_initial_retransmittable_on_wire_timeout(
         retransmittable_on_wire_timeout);
   }
 }
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index 1ccaa5d..8285f01 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -1897,7 +1897,7 @@
   CompleteCryptoHandshake();
 
   EXPECT_EQ(quic::QuicTime::Delta::FromMilliseconds(200),
-            session_->connection()->retransmittable_on_wire_timeout());
+            session_->connection()->initial_retransmittable_on_wire_timeout());
 
   // Open a stream since the connection only sends PINGs to keep a
   // retransmittable packet on the wire if there's an open stream.
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index f02720b8..aaca2a2 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -63,17 +63,6 @@
   return dict;
 }
 
-base::Value NetLogQuicPacketRetransmittedParams(
-    quic::QuicPacketNumber old_packet_number,
-    quic::QuicPacketNumber new_packet_number) {
-  base::Value dict(base::Value::Type::DICTIONARY);
-  dict.SetKey("old_packet_number",
-              NetLogNumberValue(old_packet_number.ToUint64()));
-  dict.SetKey("new_packet_number",
-              NetLogNumberValue(new_packet_number.ToUint64()));
-  return dict;
-}
-
 base::Value NetLogQuicPacketLostParams(quic::QuicPacketNumber packet_number,
                                        quic::TransmissionType transmission_type,
                                        quic::QuicTime detection_time) {
@@ -605,22 +594,14 @@
 
 void QuicConnectionLogger::OnPacketSent(
     const quic::SerializedPacket& serialized_packet,
-    quic::QuicPacketNumber original_packet_number,
     quic::TransmissionType transmission_type,
     quic::QuicTime sent_time) {
   if (!net_log_.IsCapturing())
     return;
-  if (!original_packet_number.IsInitialized()) {
-    net_log_.AddEvent(NetLogEventType::QUIC_SESSION_PACKET_SENT, [&] {
-      return NetLogQuicPacketSentParams(serialized_packet, transmission_type,
-                                        sent_time);
-    });
-  } else {
-    net_log_.AddEvent(NetLogEventType::QUIC_SESSION_PACKET_RETRANSMITTED, [&] {
-      return NetLogQuicPacketRetransmittedParams(
-          original_packet_number, serialized_packet.packet_number);
-    });
-  }
+  net_log_.AddEvent(NetLogEventType::QUIC_SESSION_PACKET_SENT, [&] {
+    return NetLogQuicPacketSentParams(serialized_packet, transmission_type,
+                                      sent_time);
+  });
 }
 
 void QuicConnectionLogger::OnPacketLoss(
diff --git a/net/quic/quic_connection_logger.h b/net/quic/quic_connection_logger.h
index 4eb35cd96..4620c07 100644
--- a/net/quic/quic_connection_logger.h
+++ b/net/quic/quic_connection_logger.h
@@ -47,7 +47,6 @@
 
   // QuicConnectionDebugVisitorInterface
   void OnPacketSent(const quic::SerializedPacket& serialized_packet,
-                    quic::QuicPacketNumber original_packet_number,
                     quic::TransmissionType transmission_type,
                     quic::QuicTime sent_time) override;
   void OnIncomingAck(quic::QuicPacketNumber ack_packet_number,
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 9501613..b155b62 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -284,12 +284,6 @@
           FLAGS_quic_reloadable_flag_quic_sent_packet_manager_cleanup,
           true)
 
-// If true, QuicSession::ShouldKeepConnectionAlive() will not consider locally
-// closed streams whose highest byte offset is not received yet.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_aggressive_connection_aliveness,
-          true)
-
 // If true, QuicStreamSequencer will not take in new data if the stream is
 // reset.
 QUIC_FLAG(bool,
@@ -316,9 +310,6 @@
           FLAGS_quic_reloadable_flag_quic_reply_to_old_android_conformance_test,
           true)
 
-// If true, no SPDY SETTINGS will be sent after handshake is confirmed.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_do_not_send_settings, true)
-
 // The maximum amount of CRYPTO frame data that can be buffered.
 QUIC_FLAG(int32_t, FLAGS_quic_max_buffered_crypto_bytes, 16 * 1024)
 
@@ -334,13 +325,6 @@
 // instead.
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_detect_spurious_loss, true)
 
-// If true, a stream will reset itself if it receives a stream frame that
-// includes a data beyond the close offset.
-QUIC_FLAG(
-    bool,
-    FLAGS_quic_reloadable_flag_quic_rst_if_stream_frame_beyond_close_offset,
-    true)
-
 // If true, enable IETF loss detection as described in
 // https://tools.ietf.org/html/draft-ietf-quic-recovery-22#section-6.1.
 QUIC_FLAG(bool,
@@ -404,3 +388,26 @@
 // bandwidth * this flag), consider the current aggregation completed
 // and starts a new one.
 QUIC_FLAG(double, FLAGS_quic_ack_aggregation_bandwidth_threshold, 1.0)
+
+// If set to non-zero, the maximum number of consecutive pings that can be sent
+// with aggressive initial retransmittable on wire timeout if there is no new
+// data received. After which, the timeout will be exponentially back off until
+// exceeds the default ping timeout.
+QUIC_FLAG(int32_t,
+          FLAGS_quic_max_aggressive_retransmittable_on_wire_ping_count,
+          0)
+
+// If true, Adjacent stream frames will be combined into one stream frame before
+// the packet is serialized.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_coalesce_stream_frames, false)
+
+// If true, populate nonretransmittable frames in SerializedPacket.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_populate_nonretransmittable_frames,
+          false)
+
+// If true, a stream will be reset if it receives fin that has offset less than
+// its highest offset.
+QUIC_FLAG(bool,
+          FLAGS_quic_reloadable_flag_quic_no_decrease_in_final_offset,
+          false)
diff --git a/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java b/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java
index 815524c..22a7a11 100644
--- a/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java
+++ b/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServer.java
@@ -39,7 +39,7 @@
  * Note that this runs net::test_server::EmbeddedTestServer in a service in a separate APK.
  */
 public class EmbeddedTestServer {
-    private static final String TAG = "cr_TestServer";
+    private static final String TAG = "TestServer";
 
     private static final String EMBEDDED_TEST_SERVER_SERVICE =
             "org.chromium.net.test.EMBEDDED_TEST_SERVER_SERVICE";
diff --git a/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java b/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java
index 6fabece..513766ff 100644
--- a/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java
+++ b/net/test/android/javatests/src/org/chromium/net/test/EmbeddedTestServerImpl.java
@@ -30,7 +30,7 @@
  */
 @JNINamespace("net::test_server")
 public class EmbeddedTestServerImpl extends IEmbeddedTestServerImpl.Stub {
-    private static final String TAG = "cr_TestServer";
+    private static final String TAG = "TestServer";
 
     private static AtomicInteger sCount = new AtomicInteger();
 
diff --git a/pdf/pdfium/fuzzers/BUILD.gn b/pdf/pdfium/fuzzers/BUILD.gn
index 705f377..b025753b 100644
--- a/pdf/pdfium/fuzzers/BUILD.gn
+++ b/pdf/pdfium/fuzzers/BUILD.gn
@@ -66,6 +66,9 @@
       }
     }
   }
+  if (is_clang) {
+    deps += [ ":pdf_nametree_fuzzer" ]
+  }
 }
 
 fuzzer_test("pdfium_fuzzer") {
@@ -358,6 +361,15 @@
   }
 }
 
+if (is_clang) {
+  fuzzer_test("pdf_nametree_fuzzer") {
+    sources = []
+    deps = [
+      "//third_party/pdfium/testing/fuzzers:pdf_nametree_fuzzer_src",
+    ]
+  }
+}
+
 proto_library("xfa_proto") {
   sources = [
     "pdfium_xfa_lpm_fuzzer/xfa.proto",
diff --git a/printing/android/java/src/org/chromium/printing/PrintManagerDelegateImpl.java b/printing/android/java/src/org/chromium/printing/PrintManagerDelegateImpl.java
index 15a15df..2f03cee 100644
--- a/printing/android/java/src/org/chromium/printing/PrintManagerDelegateImpl.java
+++ b/printing/android/java/src/org/chromium/printing/PrintManagerDelegateImpl.java
@@ -25,7 +25,7 @@
  */
 @TargetApi(Build.VERSION_CODES.KITKAT)
 public class PrintManagerDelegateImpl implements PrintManagerDelegate {
-    private static final String TAG = "cr.printing";
+    private static final String TAG = "printing";
     private final PrintManager mPrintManager;
 
     public PrintManagerDelegateImpl(Activity activity) {
diff --git a/remoting/host/mac/host_service_main.cc b/remoting/host/mac/host_service_main.cc
index 61ea34e..d00e765 100644
--- a/remoting/host/mac/host_service_main.cc
+++ b/remoting/host/mac/host_service_main.cc
@@ -29,8 +29,6 @@
 #include "remoting/host/mac/constants_mac.h"
 #include "remoting/host/username.h"
 
-using namespace remoting;
-
 namespace remoting {
 namespace {
 
@@ -325,34 +323,34 @@
 int main(int argc, char const* argv[]) {
   base::AtExitManager exitManager;
   base::CommandLine::Init(argc, argv);
-  InitHostLogging();
+  remoting::InitHostLogging();
 
-  HostService service;
+  remoting::HostService service;
   auto* current_cmdline = base::CommandLine::ForCurrentProcess();
   std::string pid = base::NumberToString(base::Process::Current().Pid());
-  if (current_cmdline->HasSwitch(kSwitchDisable)) {
+  if (current_cmdline->HasSwitch(remoting::kSwitchDisable)) {
     service.PrintPid();
     if (!service.Disable()) {
       LOG(ERROR) << "Failed to disable";
       return 1;
     }
-  } else if (current_cmdline->HasSwitch(kSwitchEnable)) {
+  } else if (current_cmdline->HasSwitch(remoting::kSwitchEnable)) {
     service.PrintPid();
     if (!service.Enable()) {
       LOG(ERROR) << "Failed to enable";
       return 1;
     }
-  } else if (current_cmdline->HasSwitch(kSwitchSaveConfig)) {
+  } else if (current_cmdline->HasSwitch(remoting::kSwitchSaveConfig)) {
     service.PrintPid();
     if (!service.WriteStdinToConfig()) {
       LOG(ERROR) << "Failed to save config";
       return 1;
     }
-  } else if (current_cmdline->HasSwitch(kSwitchHostVersion)) {
+  } else if (current_cmdline->HasSwitch(remoting::kSwitchHostVersion)) {
     service.PrintHostVersion();
-  } else if (current_cmdline->HasSwitch(kSwitchHostRunFromLaunchd)) {
-    RegisterSignalHandler();
-    HOST_LOG << "Host started for user " << GetUsername() << " at "
+  } else if (current_cmdline->HasSwitch(remoting::kSwitchHostRunFromLaunchd)) {
+    remoting::RegisterSignalHandler();
+    HOST_LOG << "Host started for user " << remoting::GetUsername() << " at "
              << base::Time::Now();
     return service.RunHost();
   } else {
diff --git a/services/data_decoder/public/cpp/decode_image.cc b/services/data_decoder/public/cpp/decode_image.cc
index 0f67295..ccf49f37 100644
--- a/services/data_decoder/public/cpp/decode_image.cc
+++ b/services/data_decoder/public/cpp/decode_image.cc
@@ -20,13 +20,15 @@
 // Helper callback which owns a mojo::Remote<ImageDecoder> until invoked. This
 // keeps the ImageDecoder pipe open just long enough to dispatch a reply, at
 // which point the reply is forwarded to the wrapped |callback|.
-void OnDecodeImage(mojo::Remote<mojom::ImageDecoder> decoder,
+void OnDecodeImage(mojo::Remote<mojom::DataDecoderService> service,
+                   mojo::Remote<mojom::ImageDecoder> decoder,
                    mojom::ImageDecoder::DecodeImageCallback callback,
                    const SkBitmap& bitmap) {
   std::move(callback).Run(bitmap);
 }
 
-void OnDecodeImages(mojo::Remote<mojom::ImageDecoder> decoder,
+void OnDecodeImages(mojo::Remote<mojom::DataDecoderService> service,
+                    mojo::Remote<mojom::ImageDecoder> decoder,
                     mojom::ImageDecoder::DecodeAnimationCallback callback,
                     std::vector<mojom::AnimationFramePtr> bitmaps) {
   std::move(callback).Run(std::move(bitmaps));
@@ -41,37 +43,95 @@
                  uint64_t max_size_in_bytes,
                  const gfx::Size& desired_image_frame_size,
                  mojom::ImageDecoder::DecodeImageCallback callback) {
-  mojo::Remote<mojom::ImageDecoder> decoder;
-  connector->Connect(mojom::kServiceName, decoder.BindNewPipeAndPassReceiver());
+  mojo::PendingRemote<mojom::ImageDecoder> pending_decoder;
+  connector->Connect(mojom::kServiceName,
+                     pending_decoder.InitWithNewPipeAndPassReceiver());
+  return DecodeImage(std::move(pending_decoder), encoded_bytes, codec,
+                     shrink_to_fit, max_size_in_bytes, desired_image_frame_size,
+                     std::move(callback));
+}
+
+void DecodeImage(mojo::PendingRemote<mojom::ImageDecoder> pending_decoder,
+                 const std::vector<uint8_t>& encoded_bytes,
+                 mojom::ImageCodec codec,
+                 bool shrink_to_fit,
+                 uint64_t max_size_in_bytes,
+                 const gfx::Size& desired_image_frame_size,
+                 mojom::ImageDecoder::DecodeImageCallback callback) {
+  mojo::Remote<mojom::DataDecoderService> null_service;
+  mojo::Remote<mojom::ImageDecoder> decoder(std::move(pending_decoder));
 
   // |call_once| runs |callback| on its first invocation.
   auto call_once = base::AdaptCallbackForRepeating(std::move(callback));
-  decoder.set_disconnect_handler(base::Bind(call_once, SkBitmap()));
+  decoder.set_disconnect_handler(base::BindOnce(call_once, SkBitmap()));
 
   mojom::ImageDecoder* raw_decoder = decoder.get();
   raw_decoder->DecodeImage(
       encoded_bytes, codec, shrink_to_fit, max_size_in_bytes,
       desired_image_frame_size,
-      base::BindOnce(&OnDecodeImage, std::move(decoder), std::move(call_once)));
+      base::BindOnce(&OnDecodeImage, std::move(null_service),
+                     std::move(decoder), std::move(call_once)));
 }
 
-void DecodeAnimation(service_manager::Connector* connector,
+void DecodeImage(mojo::Remote<mojom::DataDecoderService> service,
+                 const std::vector<uint8_t>& encoded_bytes,
+                 mojom::ImageCodec codec,
+                 bool shrink_to_fit,
+                 uint64_t max_size_in_bytes,
+                 const gfx::Size& desired_image_frame_size,
+                 mojom::ImageDecoder::DecodeImageCallback callback) {
+  mojo::Remote<mojom::ImageDecoder> decoder;
+  service->BindImageDecoder(decoder.BindNewPipeAndPassReceiver());
+
+  // |call_once| runs |callback| on its first invocation.
+  auto call_once = base::AdaptCallbackForRepeating(std::move(callback));
+  decoder.set_disconnect_handler(base::BindOnce(call_once, SkBitmap()));
+
+  mojom::ImageDecoder* raw_decoder = decoder.get();
+  raw_decoder->DecodeImage(
+      encoded_bytes, codec, shrink_to_fit, max_size_in_bytes,
+      desired_image_frame_size,
+      base::BindOnce(&OnDecodeImage, std::move(service), std::move(decoder),
+                     std::move(call_once)));
+}
+
+void DecodeAnimation(mojo::PendingRemote<mojom::ImageDecoder> pending_decoder,
                      const std::vector<uint8_t>& encoded_bytes,
                      bool shrink_to_fit,
                      uint64_t max_size_in_bytes,
                      mojom::ImageDecoder::DecodeAnimationCallback callback) {
-  mojo::Remote<mojom::ImageDecoder> decoder;
-  connector->Connect(mojom::kServiceName, decoder.BindNewPipeAndPassReceiver());
+  mojo::Remote<mojom::DataDecoderService> null_service;
+  mojo::Remote<mojom::ImageDecoder> decoder(std::move(pending_decoder));
 
   // |call_once| runs |callback| on its first invocation.
   auto call_once = base::AdaptCallbackForRepeating(std::move(callback));
-  decoder.set_disconnect_handler(base::Bind(
+  decoder.set_disconnect_handler(base::BindOnce(
       call_once, base::Passed(std::vector<mojom::AnimationFramePtr>())));
 
   mojom::ImageDecoder* raw_decoder = decoder.get();
   raw_decoder->DecodeAnimation(
       encoded_bytes, shrink_to_fit, max_size_in_bytes,
-      base::BindOnce(&OnDecodeImages, std::move(decoder),
+      base::BindOnce(&OnDecodeImages, std::move(null_service),
+                     std::move(decoder), std::move(call_once)));
+}
+
+void DecodeAnimation(mojo::Remote<mojom::DataDecoderService> service,
+                     const std::vector<uint8_t>& encoded_bytes,
+                     bool shrink_to_fit,
+                     uint64_t max_size_in_bytes,
+                     mojom::ImageDecoder::DecodeAnimationCallback callback) {
+  mojo::Remote<mojom::ImageDecoder> decoder;
+  service->BindImageDecoder(decoder.BindNewPipeAndPassReceiver());
+
+  // |call_once| runs |callback| on its first invocation.
+  auto call_once = base::AdaptCallbackForRepeating(std::move(callback));
+  decoder.set_disconnect_handler(base::BindOnce(
+      call_once, base::Passed(std::vector<mojom::AnimationFramePtr>())));
+
+  mojom::ImageDecoder* raw_decoder = decoder.get();
+  raw_decoder->DecodeAnimation(
+      encoded_bytes, shrink_to_fit, max_size_in_bytes,
+      base::BindOnce(&OnDecodeImages, std::move(service), std::move(decoder),
                      std::move(call_once)));
 }
 
diff --git a/services/data_decoder/public/cpp/decode_image.h b/services/data_decoder/public/cpp/decode_image.h
index 886e44b..c220a2f 100644
--- a/services/data_decoder/public/cpp/decode_image.h
+++ b/services/data_decoder/public/cpp/decode_image.h
@@ -9,6 +9,9 @@
 
 #include <vector>
 
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/data_decoder/public/mojom/data_decoder_service.mojom.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
 
 namespace gfx {
@@ -40,10 +43,38 @@
                  const gfx::Size& desired_image_frame_size,
                  mojom::ImageDecoder::DecodeImageCallback callback);
 
+// Same as above but uses an ImageDecoder interface directly instead of going
+// through the Service Manager.
+void DecodeImage(mojo::PendingRemote<mojom::ImageDecoder> pending_decoder,
+                 const std::vector<uint8_t>& encoded_bytes,
+                 mojom::ImageCodec codec,
+                 bool shrink_to_fit,
+                 uint64_t max_size_in_bytes,
+                 const gfx::Size& desired_image_frame_size,
+                 mojom::ImageDecoder::DecodeImageCallback callback);
+
+// Same as above but uses a dedicated DataDecoderService instance for the
+// operation.
+void DecodeImage(mojo::Remote<mojom::DataDecoderService> service,
+                 const std::vector<uint8_t>& encoded_bytes,
+                 mojom::ImageCodec codec,
+                 bool shrink_to_fit,
+                 uint64_t max_size_in_bytes,
+                 const gfx::Size& desired_image_frame_size,
+                 mojom::ImageDecoder::DecodeImageCallback callback);
+
 // Helper function to decode an animation via the data_decoder service. Any
 // image with multiple frames is considered an animation, so long as the frames
 // are all the same size.
-void DecodeAnimation(service_manager::Connector* connector,
+void DecodeAnimation(mojo::PendingRemote<mojom::ImageDecoder> pending_decoder,
+                     const std::vector<uint8_t>& encoded_bytes,
+                     bool shrink_to_fit,
+                     uint64_t max_size_in_bytes,
+                     mojom::ImageDecoder::DecodeAnimationCallback callback);
+
+// Same as above but uses a dedicated DataDecoderService instance for the
+// operation.
+void DecodeAnimation(mojo::Remote<mojom::DataDecoderService> service,
                      const std::vector<uint8_t>& encoded_bytes,
                      bool shrink_to_fit,
                      uint64_t max_size_in_bytes,
diff --git a/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java b/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java
index 52c501c..2cc8f3e0 100644
--- a/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java
+++ b/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java
@@ -24,7 +24,7 @@
  */
 @VisibleForTesting
 public class LocationProviderAdapter {
-    private static final String TAG = "cr_LocationProvider";
+    private static final String TAG = "LocationProvider";
 
     // Delegate handling the real work in the main thread.
     private LocationProvider mImpl;
diff --git a/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAndroid.java b/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAndroid.java
index 8c3b62b8..1afc1cb 100644
--- a/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAndroid.java
+++ b/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAndroid.java
@@ -26,7 +26,7 @@
  * [1] https://developer.android.com/reference/android/location/package-summary.html
  */
 public class LocationProviderAndroid implements LocationListener, LocationProvider {
-    private static final String TAG = "cr_LocationProvider";
+    private static final String TAG = "LocationProvider";
 
     private LocationManager mLocationManager;
     private boolean mIsRunning;
diff --git a/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderGmsCore.java b/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderGmsCore.java
index 9f88da9..2dad2ba 100644
--- a/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderGmsCore.java
+++ b/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderGmsCore.java
@@ -29,7 +29,7 @@
  */
 public class LocationProviderGmsCore implements ConnectionCallbacks, OnConnectionFailedListener,
                                                 LocationListener, LocationProvider {
-    private static final String TAG = "cr_LocationProvider";
+    private static final String TAG = "LocationProvider";
 
     // Values for the LocationRequest's setInterval for normal and high accuracy, respectively.
     private static final long UPDATE_INTERVAL_MS = 1000;
diff --git a/services/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java b/services/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java
index 2214843..ee4d93c 100644
--- a/services/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java
+++ b/services/device/time_zone_monitor/android/java/src/org/chromium/device/time_zone_monitor/TimeZoneMonitor.java
@@ -20,7 +20,7 @@
  */
 @JNINamespace("device")
 class TimeZoneMonitor {
-    private static final String TAG = "cr_TimeZoneMonitor";
+    private static final String TAG = "TimeZoneMonitor";
 
     private final IntentFilter mFilter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/services/network/cookie_access_delegate_impl.cc b/services/network/cookie_access_delegate_impl.cc
index 1b00b1d8..1adb96c 100644
--- a/services/network/cookie_access_delegate_impl.cc
+++ b/services/network/cookie_access_delegate_impl.cc
@@ -9,13 +9,20 @@
 namespace network {
 
 CookieAccessDelegateImpl::CookieAccessDelegateImpl(
+    mojom::CookieAccessDelegateType type,
     const CookieSettings* cookie_settings)
-    : cookie_settings_(cookie_settings) {}
+    : type_(type), cookie_settings_(cookie_settings) {
+  if (type == mojom::CookieAccessDelegateType::USE_CONTENT_SETTINGS) {
+    DCHECK(cookie_settings);
+  }
+}
 
 CookieAccessDelegateImpl::~CookieAccessDelegateImpl() = default;
 
 net::CookieAccessSemantics CookieAccessDelegateImpl::GetAccessSemantics(
     const net::CanonicalCookie& cookie) const {
+  if (type_ == mojom::CookieAccessDelegateType::ALWAYS_LEGACY)
+    return net::CookieAccessSemantics::LEGACY;
   return cookie_settings_->GetCookieAccessSemanticsForDomain(cookie.Domain());
 }
 
diff --git a/services/network/cookie_access_delegate_impl.h b/services/network/cookie_access_delegate_impl.h
index 840dfcc8..50b0a08 100644
--- a/services/network/cookie_access_delegate_impl.h
+++ b/services/network/cookie_access_delegate_impl.h
@@ -8,16 +8,19 @@
 #include "base/component_export.h"
 #include "net/cookies/cookie_access_delegate.h"
 #include "services/network/cookie_settings.h"
+#include "services/network/public/mojom/cookie_manager.mojom.h"
 
 namespace network {
 
 class COMPONENT_EXPORT(NETWORK_SERVICE) CookieAccessDelegateImpl
     : public net::CookieAccessDelegate {
  public:
-  // |cookie_settings| contains the set of content settings that describes which
-  // cookies should be subject to legacy access rules.
-  // |cookie_settings| is expected to outlive this class.
-  explicit CookieAccessDelegateImpl(const CookieSettings* cookie_settings);
+  // If |type| is USE_CONTENT_SETTINGS, a non-null |cookie_settings| is
+  // expected. |cookie_settings| contains the set of content settings that
+  // describes which cookies should be subject to legacy access rules.
+  // If non-null, |cookie_settings| is expected to outlive this class.
+  CookieAccessDelegateImpl(mojom::CookieAccessDelegateType type,
+                           const CookieSettings* cookie_settings = nullptr);
 
   ~CookieAccessDelegateImpl() override;
 
@@ -26,6 +29,7 @@
       const net::CanonicalCookie& cookie) const override;
 
  private:
+  const mojom::CookieAccessDelegateType type_;
   const CookieSettings* const cookie_settings_;
 };
 
diff --git a/services/network/cookie_manager.cc b/services/network/cookie_manager.cc
index 79f5342..5dbc9d2 100644
--- a/services/network/cookie_manager.cc
+++ b/services/network/cookie_manager.cc
@@ -18,6 +18,7 @@
 #include "net/cookies/cookie_options.h"
 #include "net/cookies/cookie_store.h"
 #include "net/cookies/cookie_util.h"
+#include "services/network/cookie_access_delegate_impl.h"
 #include "services/network/session_cleanup_cookie_store.h"
 #include "url/gurl.h"
 
@@ -47,12 +48,18 @@
     mojom::CookieManagerParamsPtr params)
     : cookie_store_(cookie_store),
       session_cleanup_cookie_store_(std::move(session_cleanup_cookie_store)) {
+  mojom::CookieAccessDelegateType cookie_access_delegate_type =
+      mojom::CookieAccessDelegateType::USE_CONTENT_SETTINGS;
   if (params) {
     ConfigureCookieSettings(*params, &cookie_settings_);
+    cookie_access_delegate_type = params->cookie_access_delegate_type;
     // Don't wait for callback, the work happens synchronously.
     AllowFileSchemeCookies(params->allow_file_scheme_cookies,
                            base::DoNothing());
   }
+  cookie_store_->SetCookieAccessDelegate(
+      std::make_unique<CookieAccessDelegateImpl>(cookie_access_delegate_type,
+                                                 &cookie_settings_));
 }
 
 CookieManager::~CookieManager() {
@@ -60,6 +67,10 @@
     session_cleanup_cookie_store_->DeleteSessionCookies(
         cookie_settings_.CreateDeleteCookieOnExitPredicate());
   }
+  // Make sure we destroy the CookieStore's CookieAccessDelegate, because it
+  // holds a pointer to this CookieManager's CookieSettings, which is about to
+  // be destroyed.
+  cookie_store_->SetCookieAccessDelegate(nullptr);
 }
 
 void CookieManager::AddReceiver(
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index d9c92c4..f4111b7 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -64,7 +64,6 @@
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_builder.h"
-#include "services/network/cookie_access_delegate_impl.h"
 #include "services/network/cookie_manager.h"
 #include "services/network/cors/cors_url_loader_factory.h"
 #include "services/network/host_resolver.h"
@@ -429,12 +428,6 @@
   // shutdown would be unnecessary as the reports would be thrown out.
   domain_reliability_monitor_.reset();
 
-  // The cookie store's CookieAccessDelegate holds a pointer to the
-  // CookieManager's CookieSettings. Since the CookieManager is being destroyed,
-  // first destroy the CookieAccessDelegate.
-  if (url_request_context_ && url_request_context_->cookie_store())
-    url_request_context_->cookie_store()->SetCookieAccessDelegate(nullptr);
-
   if (url_request_context_ &&
       url_request_context_->transport_security_state()) {
     if (certificate_report_sender_) {
@@ -1984,10 +1977,6 @@
       std::move(session_cleanup_cookie_store),
       std::move(params_->cookie_manager_params));
 
-  result.url_request_context->cookie_store()->SetCookieAccessDelegate(
-      std::make_unique<CookieAccessDelegateImpl>(
-          &cookie_manager_->cookie_settings()));
-
   if (cert_net_fetcher_)
     cert_net_fetcher_->SetURLRequestContext(result.url_request_context.get());
 
diff --git a/services/network/public/mojom/cookie_manager.mojom b/services/network/public/mojom/cookie_manager.mojom
index 775fbcc..c604337 100644
--- a/services/network/public/mojom/cookie_manager.mojom
+++ b/services/network/public/mojom/cookie_manager.mojom
@@ -33,6 +33,19 @@
   // access rules.
   array<content_settings.mojom.ContentSettingPatternSource>
       settings_for_legacy_cookie_access;
+
+  // The type of CookieAccessDelegate to pass to the underlying CookieStore.
+  // If these params are not present, CookieManager defaults to using
+  // USE_CONTENT_SETTINGS.
+  CookieAccessDelegateType cookie_access_delegate_type = USE_CONTENT_SETTINGS;
+};
+
+enum CookieAccessDelegateType {
+  // Decides access semantics based on the content settings it was constructed
+  // with.
+  USE_CONTENT_SETTINGS,
+  // Always returns Legacy access semantics.
+  ALWAYS_LEGACY,
 };
 
 enum CookiePriority {
diff --git a/testing/android/driver/java/src/org/chromium/test/driver/OnDeviceInstrumentationDriver.java b/testing/android/driver/java/src/org/chromium/test/driver/OnDeviceInstrumentationDriver.java
index a3c4693..2c67d517 100644
--- a/testing/android/driver/java/src/org/chromium/test/driver/OnDeviceInstrumentationDriver.java
+++ b/testing/android/driver/java/src/org/chromium/test/driver/OnDeviceInstrumentationDriver.java
@@ -155,8 +155,7 @@
     }
 
     private class Driver implements Runnable {
-
-        private static final String TAG = OnDeviceInstrumentationDriver.TAG + ".Driver";
+        private static final String TAG = OnDeviceInstrumentationDriver.TAG + "_Driver";
 
         private Bundle mTargetArgs;
         private String mTargetClass;
diff --git a/testing/android/native_test/java/src/org/chromium/native_test/NativeBrowserTestActivity.java b/testing/android/native_test/java/src/org/chromium/native_test/NativeBrowserTestActivity.java
index 437cd8c2..04021c9f 100644
--- a/testing/android/native_test/java/src/org/chromium/native_test/NativeBrowserTestActivity.java
+++ b/testing/android/native_test/java/src/org/chromium/native_test/NativeBrowserTestActivity.java
@@ -13,7 +13,7 @@
  * An {@link android.app.Activity} for running native browser tests.
  */
 public abstract class NativeBrowserTestActivity extends FragmentActivity {
-    private static final String TAG = "cr_NativeTest";
+    private static final String TAG = "NativeTest";
 
     private NativeTest mTest = new NativeTest();
     private boolean mStarted;
diff --git a/testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java b/testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java
index 6ab21577..1e546a7 100644
--- a/testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java
+++ b/testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java
@@ -41,7 +41,7 @@
     public static final String EXTRA_COVERAGE_DEVICE_FILE =
             "org.chromium.native_test.NativeTest.CoverageDeviceFile";
 
-    private static final String TAG = "cr_NativeTest";
+    private static final String TAG = "NativeTest";
 
     private String mCommandLineFilePath;
     private StringBuilder mCommandLineFlags = new StringBuilder();
diff --git a/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java b/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java
index b881bcc81..124c179 100644
--- a/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java
+++ b/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java
@@ -47,7 +47,7 @@
     public static final String EXTRA_TEST =
             "org.chromium.native_test.NativeTestInstrumentationTestRunner.Test";
 
-    private static final String TAG = "cr_NativeTest";
+    private static final String TAG = "NativeTest";
 
     private static final long DEFAULT_SHARD_NANO_TIMEOUT = 60 * 1000000000L;
     // Default to no size limit.
diff --git a/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTest.java b/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTest.java
index 89b6a56..f2ee76b 100644
--- a/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTest.java
+++ b/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTest.java
@@ -18,7 +18,7 @@
  * A helper for running native unit tests (i.e., not browser tests)
  */
 public class NativeUnitTest extends NativeTest {
-    private static final String TAG = "cr_NativeTest";
+    private static final String TAG = "NativeTest";
 
     @Override
     public void preCreate(Activity activity) {
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 78deecfa..9d31b404 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -78,11 +78,10 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "android_webview_unittests_oor_cors"
+            "android_webview_unittests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "android_webview_unittests_oor_cors",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
@@ -125,11 +124,10 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "system_webview_shell_layout_test_apk_oor_cors"
+            "system_webview_shell_layout_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "system_webview_shell_layout_test_apk_oor_cors",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
@@ -172,11 +170,10 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webview_cts_tests_oor_cors"
+            "webview_cts_tests"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "webview_cts_tests_oor_cors",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
@@ -225,11 +222,10 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webview_instrumentation_test_apk_oor_cors"
+            "webview_instrumentation_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "webview_instrumentation_test_apk_oor_cors",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
@@ -273,11 +269,10 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "webview_ui_test_app_test_apk_oor_cors"
+            "webview_ui_test_app_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "webview_ui_test_app_test_apk_oor_cors",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 21d7682..c6956aa 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4805,15 +4805,6 @@
       },
     },
 
-    'system_webview_shell_instrumentation_tests_oor_cors': {
-      'system_webview_shell_layout_test_apk_oor_cors': {
-        'args': [
-          '--enable-features=OutOfBlinkCors',
-        ],
-        'test': 'system_webview_shell_layout_test_apk',
-      },
-    },
-
     'system_webview_wpt': {
       'system_webview_wpt': {
         'swarming': {
@@ -4993,27 +4984,10 @@
       },
     },
 
-    'webview_bot_instrumentation_test_apk_oor_cors_gtest': {
-      'webview_instrumentation_test_apk_oor_cors': {
-        'args': ['--enable-features=OutOfBlinkCors'],
-        'swarming': {
-          'shards': 12,
-        },
-        'test': 'webview_instrumentation_test_apk',
-      },
-    },
-
     'webview_bot_unittests_gtest': {
       'android_webview_unittests': {},
     },
 
-    'webview_bot_unittests_oor_cors_gtest': {
-      'android_webview_unittests_oor_cors': {
-        'args': ['--enable-features=OutOfBlinkCors'],
-        'test': 'android_webview_unittests',
-      },
-    },
-
     'webview_cts_tests_gtest': {
       'webview_cts_tests': {
         'swarming': {
@@ -5048,25 +5022,6 @@
       },
     },
 
-    'webview_cts_tests_gtest_oor_cors': {
-      'webview_cts_tests_oor_cors': {
-        'args': [
-          '--enable-features=OutOfBlinkCors',
-        ],
-        'swarming': {
-          'shards': 3,
-          'cipd_packages': [
-            {
-              "cipd_package": 'chromium/android_webview/tools/cts_archive',
-              'location': 'android_webview/tools/cts_archive',
-              'revision': '4kDr36wBuZtvbfaEin4U4oeFD7oAuN0flkWDImKBts4C',
-            }
-          ]
-        },
-        'test': 'webview_cts_tests',
-      },
-    },
-
     'webview_ui_instrumentation_tests': {
       'webview_ui_test_app_test_apk': {},
     },
@@ -5080,15 +5035,6 @@
       },
     },
 
-    'webview_ui_instrumentation_tests_oor_cors': {
-      'webview_ui_test_app_test_apk_oor_cors': {
-        'args': [
-          '--enable-features=OutOfBlinkCors',
-        ],
-        'test': 'webview_ui_test_app_test_apk',
-      },
-    },
-
     'win7_32_bit_gtests': {
       'base_unittests': {},
       'browser_tests': {
@@ -5985,11 +5931,11 @@
     ],
 
     'webview_bot_oor_cors_gtests': [
-      'system_webview_shell_instrumentation_tests_oor_cors',
-      'webview_bot_instrumentation_test_apk_oor_cors_gtest',
-      'webview_bot_unittests_oor_cors_gtest',
-      'webview_cts_tests_gtest_oor_cors',
-      'webview_ui_instrumentation_tests_oor_cors',
+      'system_webview_shell_instrumentation_tests',
+      'webview_bot_instrumentation_test_apk_gtest',
+      'webview_bot_unittests_gtest',
+      'webview_cts_tests_gtest',
+      'webview_ui_instrumentation_tests',
     ],
 
     'webview_bot_system_gtests': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index cc59a6a0..0d49e3c 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -677,6 +677,9 @@
         'test_suites': {
           'gtest_tests': 'webview_bot_oor_cors_gtests',
         },
+        'args': [
+          '--enable-features=OutOfBlinkCors',
+        ],
         'use_swarming': True,
         'os_type': 'android',
       },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index f7f4c3e..7d9ff44 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1560,6 +1560,21 @@
             ]
         }
     ],
+    "ChromeOSParentalControlsSettings": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ChromeOSParentalControlsSettings"
+                    ]
+                }
+            ]
+        }
+    ],
     "ChromeSmartSelection": [
         {
             "platforms": [
diff --git a/third_party/blink/common/loader/mime_sniffing_throttle.cc b/third_party/blink/common/loader/mime_sniffing_throttle.cc
index 96ca9af..0d3ecfa 100644
--- a/third_party/blink/common/loader/mime_sniffing_throttle.cc
+++ b/third_party/blink/common/loader/mime_sniffing_throttle.cc
@@ -7,7 +7,7 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/mime_sniffer.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/common/loader/mime_sniffing_url_loader.h"
 
 namespace blink {
@@ -20,7 +20,7 @@
 
 void MimeSniffingThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {
   // No need to do mime sniffing again.
   if (response_head->did_mime_sniff)
@@ -46,9 +46,9 @@
     network::mojom::URLLoaderClientRequest source_client_request;
     MimeSniffingURLLoader* mime_sniffing_loader;
     std::tie(new_remote, new_receiver, mime_sniffing_loader) =
-        MimeSniffingURLLoader::CreateLoader(weak_factory_.GetWeakPtr(),
-                                            response_url, *response_head,
-                                            task_runner_);
+        MimeSniffingURLLoader::CreateLoader(
+            weak_factory_.GetWeakPtr(), response_url, response_head->Clone(),
+            task_runner_);
     delegate_->InterceptResponse(
         network::mojom::URLLoaderPtr(std::move(new_remote)),
         std::move(new_receiver), &source_loader, &source_client_request);
diff --git a/third_party/blink/common/loader/mime_sniffing_throttle_unittest.cc b/third_party/blink/common/loader/mime_sniffing_throttle_unittest.cc
index 0286cce..bb76dd1e 100644
--- a/third_party/blink/common/loader/mime_sniffing_throttle_unittest.cc
+++ b/third_party/blink/common/loader/mime_sniffing_throttle_unittest.cc
@@ -13,7 +13,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
-#include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
 #include "services/network/test/test_url_loader_client.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -202,10 +202,10 @@
   auto delegate = std::make_unique<MockDelegate>();
   throttle->set_delegate(delegate.get());
 
-  network::ResourceResponseHead response_head;
+  auto response_head = network::mojom::URLResponseHead::New();
   bool defer = false;
-  throttle->WillProcessResponse(GURL("https://example.com"), &response_head,
-                                &defer);
+  throttle->WillProcessResponse(GURL("https://example.com"),
+                                response_head.get(), &defer);
   EXPECT_TRUE(defer);
   EXPECT_TRUE(delegate->is_intercepted());
 }
@@ -216,11 +216,11 @@
   auto delegate = std::make_unique<MockDelegate>();
   throttle->set_delegate(delegate.get());
 
-  network::ResourceResponseHead response_head;
-  response_head.mime_type = "text/plain";
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->mime_type = "text/plain";
   bool defer = false;
-  throttle->WillProcessResponse(GURL("https://example.com"), &response_head,
-                                &defer);
+  throttle->WillProcessResponse(GURL("https://example.com"),
+                                response_head.get(), &defer);
   EXPECT_TRUE(defer);
   EXPECT_TRUE(delegate->is_intercepted());
 }
@@ -231,11 +231,11 @@
   auto delegate = std::make_unique<MockDelegate>();
   throttle->set_delegate(delegate.get());
 
-  network::ResourceResponseHead response_head;
-  response_head.mime_type = "text/javascript";
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->mime_type = "text/javascript";
   bool defer = false;
-  throttle->WillProcessResponse(GURL("https://example.com"), &response_head,
-                                &defer);
+  throttle->WillProcessResponse(GURL("https://example.com"),
+                                response_head.get(), &defer);
   EXPECT_FALSE(defer);
   EXPECT_FALSE(delegate->is_intercepted());
 }
@@ -246,9 +246,9 @@
   auto delegate = std::make_unique<MockDelegate>();
   throttle->set_delegate(delegate.get());
 
-  network::ResourceResponseHead response_head;
+  auto response_head = network::mojom::URLResponseHead::New();
   bool defer = false;
-  throttle->WillProcessResponse(GURL("wss://example.com"), &response_head,
+  throttle->WillProcessResponse(GURL("wss://example.com"), response_head.get(),
                                 &defer);
   EXPECT_FALSE(defer);
   EXPECT_FALSE(delegate->is_intercepted());
@@ -260,10 +260,10 @@
   auto delegate = std::make_unique<MockDelegate>();
   throttle->set_delegate(delegate.get());
 
-  network::ResourceResponseHead response_head;
-  response_head.mime_type = "text/plain";
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->mime_type = "text/plain";
   bool defer = false;
-  throttle->WillProcessResponse(GURL("wss://example.com"), &response_head,
+  throttle->WillProcessResponse(GURL("wss://example.com"), response_head.get(),
                                 &defer);
   EXPECT_FALSE(defer);
   EXPECT_FALSE(delegate->is_intercepted());
@@ -275,10 +275,10 @@
   auto delegate = std::make_unique<MockDelegate>();
   throttle->set_delegate(delegate.get());
 
-  network::ResourceResponseHead response_head;
-  response_head.mime_type = "text/javascript";
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->mime_type = "text/javascript";
   bool defer = false;
-  throttle->WillProcessResponse(GURL("wss://example.com"), &response_head,
+  throttle->WillProcessResponse(GURL("wss://example.com"), response_head.get(),
                                 &defer);
   EXPECT_FALSE(defer);
   EXPECT_FALSE(delegate->is_intercepted());
@@ -290,12 +290,12 @@
   auto delegate = std::make_unique<MockDelegate>();
   throttle->set_delegate(delegate.get());
 
-  network::ResourceResponseHead response_head;
-  response_head.mime_type = "text/plain";
-  response_head.did_mime_sniff = true;
+  auto response_head = network::mojom::URLResponseHead::New();
+  response_head->mime_type = "text/plain";
+  response_head->did_mime_sniff = true;
   bool defer = false;
-  throttle->WillProcessResponse(GURL("https://example.com"), &response_head,
-                                &defer);
+  throttle->WillProcessResponse(GURL("https://example.com"),
+                                response_head.get(), &defer);
   EXPECT_FALSE(defer);
   EXPECT_FALSE(delegate->is_intercepted());
 }
@@ -307,9 +307,9 @@
   throttle->set_delegate(delegate.get());
 
   GURL response_url("https://example.com");
-  network::ResourceResponseHead response_head;
+  auto response_head = network::mojom::URLResponseHead::New();
   bool defer = false;
-  throttle->WillProcessResponse(response_url, &response_head, &defer);
+  throttle->WillProcessResponse(response_url, response_head.get(), &defer);
   EXPECT_TRUE(defer);
   EXPECT_TRUE(delegate->is_intercepted());
 
@@ -331,9 +331,9 @@
   throttle->set_delegate(delegate.get());
 
   GURL response_url("https://example.com");
-  network::ResourceResponseHead response_head;
+  auto response_head = network::mojom::URLResponseHead::New();
   bool defer = false;
-  throttle->WillProcessResponse(response_url, &response_head, &defer);
+  throttle->WillProcessResponse(response_url, response_head.get(), &defer);
   EXPECT_TRUE(defer);
   EXPECT_TRUE(delegate->is_intercepted());
 
@@ -361,9 +361,9 @@
   throttle->set_delegate(delegate.get());
 
   GURL response_url("https://example.com");
-  network::ResourceResponseHead response_head;
+  auto response_head = network::mojom::URLResponseHead::New();
   bool defer = false;
-  throttle->WillProcessResponse(response_url, &response_head, &defer);
+  throttle->WillProcessResponse(response_url, response_head.get(), &defer);
   EXPECT_TRUE(defer);
   EXPECT_TRUE(delegate->is_intercepted());
 
@@ -385,9 +385,9 @@
   throttle->set_delegate(delegate.get());
 
   GURL response_url("https://example.com/hogehoge.docx");
-  network::ResourceResponseHead response_head;
+  auto response_head = network::mojom::URLResponseHead::New();
   bool defer = false;
-  throttle->WillProcessResponse(response_url, &response_head, &defer);
+  throttle->WillProcessResponse(response_url, response_head.get(), &defer);
   EXPECT_TRUE(defer);
   EXPECT_TRUE(delegate->is_intercepted());
 
@@ -409,9 +409,9 @@
   throttle->set_delegate(delegate.get());
 
   GURL response_url("https://example.com/hogehoge.docx");
-  network::ResourceResponseHead response_head;
+  auto response_head = network::mojom::URLResponseHead::New();
   bool defer = false;
-  throttle->WillProcessResponse(response_url, &response_head, &defer);
+  throttle->WillProcessResponse(response_url, response_head.get(), &defer);
   EXPECT_TRUE(defer);
   EXPECT_TRUE(delegate->is_intercepted());
 
@@ -433,9 +433,9 @@
   throttle->set_delegate(delegate.get());
 
   GURL response_url("https://example.com");
-  network::ResourceResponseHead response_head;
+  auto response_head = network::mojom::URLResponseHead::New();
   bool defer = false;
-  throttle->WillProcessResponse(response_url, &response_head, &defer);
+  throttle->WillProcessResponse(response_url, response_head.get(), &defer);
   EXPECT_TRUE(defer);
   EXPECT_TRUE(delegate->is_intercepted());
 
@@ -489,9 +489,9 @@
   throttle->set_delegate(delegate.get());
 
   GURL response_url("https://example.com");
-  network::ResourceResponseHead response_head;
+  auto response_head = network::mojom::URLResponseHead::New();
   bool defer = false;
-  throttle->WillProcessResponse(response_url, &response_head, &defer);
+  throttle->WillProcessResponse(response_url, response_head.get(), &defer);
   EXPECT_TRUE(defer);
   EXPECT_TRUE(delegate->is_intercepted());
 
diff --git a/third_party/blink/common/loader/url_loader_throttle.cc b/third_party/blink/common/loader/url_loader_throttle.cc
index bb691fb..41a42c41 100644
--- a/third_party/blink/common/loader/url_loader_throttle.cc
+++ b/third_party/blink/common/loader/url_loader_throttle.cc
@@ -49,19 +49,19 @@
 
 void URLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& response_head,
+    const network::mojom::URLResponseHead& response_head,
     bool* defer,
     std::vector<std::string>* to_be_removed_request_headers,
     net::HttpRequestHeaders* modified_request_headers) {}
 
 void URLLoaderThrottle::WillProcessResponse(
     const GURL& response_url,
-    network::ResourceResponseHead* response_head,
+    network::mojom::URLResponseHead* response_head,
     bool* defer) {}
 
 void URLLoaderThrottle::BeforeWillProcessResponse(
     const GURL& response_url,
-    const network::ResourceResponseHead& response_head,
+    const network::mojom::URLResponseHead& response_head,
     bool* defer) {}
 
 void URLLoaderThrottle::WillOnCompleteWithError(
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 4d72a2f5..202b06e7 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -525,6 +525,7 @@
   ]
 
   public_deps = [
+    ":resources_grit",
     "//net",
     "//services/service_manager/public/cpp",
     "//skia",
diff --git a/third_party/blink/public/common/loader/mime_sniffing_throttle.h b/third_party/blink/public/common/loader/mime_sniffing_throttle.h
index 6d714c8..335dc6d 100644
--- a/third_party/blink/public/common/loader/mime_sniffing_throttle.h
+++ b/third_party/blink/public/common/loader/mime_sniffing_throttle.h
@@ -25,7 +25,7 @@
 
   // Implements blink::URLLoaderThrottle.
   void WillProcessResponse(const GURL& response_url,
-                           network::ResourceResponseHead* response_head,
+                           network::mojom::URLResponseHead* response_head,
                            bool* defer) override;
 
   // Called from MimeSniffingURLLoader once mime type is ready.
diff --git a/third_party/blink/public/common/loader/url_loader_throttle.h b/third_party/blink/public/common/loader/url_loader_throttle.h
index 85ef3074..9e1195a9 100644
--- a/third_party/blink/public/common/loader/url_loader_throttle.h
+++ b/third_party/blink/public/common/loader/url_loader_throttle.h
@@ -22,7 +22,6 @@
 
 namespace network {
 struct ResourceRequest;
-struct ResourceResponseHead;
 }  // namespace network
 
 namespace blink {
@@ -160,16 +159,17 @@
   // WillStartRequest on possible side-effects.
   virtual void WillRedirectRequest(
       net::RedirectInfo* redirect_info,
-      const network::ResourceResponseHead& response_head,
+      const network::mojom::URLResponseHead& response_head,
       bool* defer,
       std::vector<std::string>* to_be_removed_request_headers,
       net::HttpRequestHeaders* modified_request_headers);
 
   // Called when the response headers and meta data are available.
-  // TODO(776312): Migrate this URL to ResourceResponseHead.
-  virtual void WillProcessResponse(const GURL& response_url,
-                                   network::ResourceResponseHead* response_head,
-                                   bool* defer);
+  // TODO(776312): Migrate this URL to URLResponseHead.
+  virtual void WillProcessResponse(
+      const GURL& response_url,
+      network::mojom::URLResponseHead* response_head,
+      bool* defer);
 
   // Called prior WillProcessResponse() to allow throttles to restart the URL
   // load by calling delegate_->RestartWithFlags().
@@ -179,7 +179,7 @@
   // restarts.
   virtual void BeforeWillProcessResponse(
       const GURL& response_url,
-      const network::ResourceResponseHead& response_head,
+      const network::mojom::URLResponseHead& response_head,
       bool* defer);
 
   // Called if there is a non-OK net::Error in the completion status.
diff --git a/third_party/blink/public/mojom/contacts/contacts_manager.mojom b/third_party/blink/public/mojom/contacts/contacts_manager.mojom
index 37fdadeb..a07d7029 100644
--- a/third_party/blink/public/mojom/contacts/contacts_manager.mojom
+++ b/third_party/blink/public/mojom/contacts/contacts_manager.mojom
@@ -4,11 +4,14 @@
 
 module blink.mojom;
 
+import "components/payments/mojom/payment_request_data.mojom";
+
 // As per https://wicg.github.io/contact-api/spec/.
 struct ContactInfo {
   array<string>? name;
   array<string>? email;
   array<string>? tel;
+  array<payments.mojom.PaymentAddress>? address;
 };
 
 // The Contacts Manager lives in the browser process and can be initiated by the
@@ -18,6 +21,6 @@
   // website. The array of contacts returned can be null, in case of an error,
   // for example if the dialog cannot be shown. The array is empty if the user
   // does not select any contacts (e.g. cancels selection).
-  Select(bool multiple, bool include_names, bool include_emails, bool include_tel)
+  Select(bool multiple, bool include_names, bool include_emails, bool include_tel, bool include_addresses)
       => (array<ContactInfo>? contacts);
 };
diff --git a/third_party/blink/public/platform/web_graphics_context_3d_provider.h b/third_party/blink/public/platform/web_graphics_context_3d_provider.h
index 2d7b7db..a4b8456 100644
--- a/third_party/blink/public/platform/web_graphics_context_3d_provider.h
+++ b/third_party/blink/public/platform/web_graphics_context_3d_provider.h
@@ -71,7 +71,6 @@
   kAntialiasingModeNone,
   kAntialiasingModeMSAAImplicitResolve,
   kAntialiasingModeMSAAExplicitResolve,
-  kAntialiasingModeScreenSpaceAntialiasing,
 };
 
 struct WebglPreferences {
diff --git a/third_party/blink/renderer/build/scripts/templates/feature_policy_helper.cc.tmpl b/third_party/blink/renderer/build/scripts/templates/feature_policy_helper.cc.tmpl
index 4d4284d..13202e6c 100644
--- a/third_party/blink/renderer/build/scripts/templates/feature_policy_helper.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/feature_policy_helper.cc.tmpl
@@ -37,10 +37,6 @@
                                  mojom::FeaturePolicyFeature::k{{feature.name}});
     {% endif %}
     {% endfor %}
-    // TODO(crbug.com/960132): WebVR manually added here for compatibility.
-    // Remove this when WebVR is removed.
-    ASSERT_ORIGIN_TRIAL(WebVR);
-    default_feature_name_map.Set("vr", mojom::FeaturePolicyFeature::kWebVr);
     {% for runtime_feature_name, FP_features in runtime_to_feature_policy_map.items() %}
     if (RuntimeEnabledFeatures::{{runtime_feature_name}}Enabled()) {
       {% for feature in FP_features %}
diff --git a/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
index 986db9d9..25afc795 100644
--- a/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
@@ -115,7 +115,7 @@
 CSSTransformInterpolationType::PreInterpolationCompositeIfNeeded(
     InterpolationValue value,
     const InterpolationValue& underlying,
-    EffectModel::CompositeOperation,
+    EffectModel::CompositeOperation composite,
     ConversionCheckers& conversion_checkers) const {
   // Due to the post-interpolation composite optimization, the interpolation
   // stack aggressively caches interpolated values. When we are doing
@@ -126,8 +126,20 @@
   // caching composited values.
   conversion_checkers.push_back(std::make_unique<AlwaysInvalidateChecker>());
 
-  To<InterpolableTransformList>(*value.interpolable_value)
-      .PreConcat(To<InterpolableTransformList>(*underlying.interpolable_value));
+  InterpolableTransformList& transform_list =
+      To<InterpolableTransformList>(*value.interpolable_value);
+  const InterpolableTransformList& underlying_transform_list =
+      To<InterpolableTransformList>(*underlying.interpolable_value);
+
+  // Addition of transform lists uses concatenation, whilst accumulation
+  // performs a similar matching to interpolation but then adds the components.
+  // See https://drafts.csswg.org/css-transforms-2/#combining-transform-lists
+  if (composite == EffectModel::CompositeOperation::kCompositeAdd) {
+    transform_list.PreConcat(underlying_transform_list);
+  } else {
+    DCHECK_EQ(composite, EffectModel::CompositeOperation::kCompositeAccumulate);
+    transform_list.AccumulateOnto(underlying_transform_list);
+  }
   return value;
 }
 
diff --git a/third_party/blink/renderer/core/animation/interpolable_transform_list.cc b/third_party/blink/renderer/core/animation/interpolable_transform_list.cc
index 948ea14d..92c9fba 100644
--- a/third_party/blink/renderer/core/animation/interpolable_transform_list.cc
+++ b/third_party/blink/renderer/core/animation/interpolable_transform_list.cc
@@ -25,14 +25,19 @@
 }
 
 void InterpolableTransformList::PreConcat(
-    const InterpolableTransformList& other) {
+    const InterpolableTransformList& underlying) {
   Vector<scoped_refptr<TransformOperation>> result;
-  result.ReserveCapacity(other.operations_.size() + operations_.size());
-  result.AppendVector(other.operations_.Operations());
+  result.ReserveCapacity(underlying.operations_.size() + operations_.size());
+  result.AppendVector(underlying.operations_.Operations());
   result.AppendVector(operations_.Operations());
   operations_.Operations() = result;
 }
 
+void InterpolableTransformList::AccumulateOnto(
+    const InterpolableTransformList& underlying) {
+  operations_ = underlying.operations_.Accumulate(operations_);
+}
+
 void InterpolableTransformList::Interpolate(const InterpolableValue& to,
                                             const double progress,
                                             InterpolableValue& result) const {
diff --git a/third_party/blink/renderer/core/animation/interpolable_transform_list.h b/third_party/blink/renderer/core/animation/interpolable_transform_list.h
index d0ab330..9275a2e 100644
--- a/third_party/blink/renderer/core/animation/interpolable_transform_list.h
+++ b/third_party/blink/renderer/core/animation/interpolable_transform_list.h
@@ -32,7 +32,8 @@
   // and interpolation, to apply the results back to the style.
   TransformOperations operations() const { return operations_; }
 
-  void PreConcat(const InterpolableTransformList&);
+  void PreConcat(const InterpolableTransformList& underlying);
+  void AccumulateOnto(const InterpolableTransformList& underlying);
 
   // InterpolableValue implementation:
   void Interpolate(const InterpolableValue& to,
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 6a62f2c..7d152f1 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -1170,15 +1170,14 @@
  public:
   OrderedNamedLinesCollectorInGridLayout(const ComputedStyle& style,
                                          bool is_row_axis,
-                                         size_t auto_repeat_tracks_count)
+                                         size_t auto_repeat_tracks_count,
+                                         size_t auto_repeat_track_list_length)
       : OrderedNamedLinesCollector(style, is_row_axis),
         insertion_point_(is_row_axis
                              ? style.GridAutoRepeatColumnsInsertionPoint()
                              : style.GridAutoRepeatRowsInsertionPoint()),
         auto_repeat_total_tracks_(auto_repeat_tracks_count),
-        auto_repeat_track_list_length_(
-            is_row_axis ? style.GridAutoRepeatColumns().size()
-                        : style.GridAutoRepeatRows().size()) {}
+        auto_repeat_track_list_length_(auto_repeat_track_list_length) {}
   void CollectLineNamesForIndex(cssvalue::CSSGridLineNamesValue&,
                                 size_t index) const override;
 
@@ -1227,7 +1226,7 @@
     cssvalue::CSSGridLineNamesValue& line_names_value,
     size_t i) const {
   DCHECK(!IsEmpty());
-  if (ordered_named_auto_repeat_grid_lines_.IsEmpty() || i < insertion_point_) {
+  if (auto_repeat_track_list_length_ == 0LU || i < insertion_point_) {
     AppendLines(line_names_value, i, kNamedLines);
     return;
   }
@@ -1346,7 +1345,8 @@
   if (is_layout_grid) {
     const auto* grid = ToLayoutGrid(layout_object);
     OrderedNamedLinesCollectorInGridLayout collector(
-        style, is_row_axis, grid->AutoRepeatCountForDirection(direction));
+        style, is_row_axis, grid->AutoRepeatCountForDirection(direction),
+        auto_repeat_track_sizes.size());
     PopulateGridTrackList(
         list, collector, grid->TrackSizesForComputedStyle(direction),
         [&](const LayoutUnit& v) { return ZoomAdjustedPixelValue(v, style); });
diff --git a/third_party/blink/renderer/core/dom/user_gesture_indicator.cc b/third_party/blink/renderer/core/dom/user_gesture_indicator.cc
index 02aa7cf5..0f6c436 100644
--- a/third_party/blink/renderer/core/dom/user_gesture_indicator.cc
+++ b/third_party/blink/renderer/core/dom/user_gesture_indicator.cc
@@ -19,8 +19,7 @@
 UserGestureToken::UserGestureToken(Status status)
     : consumable_gestures_(0),
       clock_(base::DefaultClock::GetInstance()),
-      timestamp_(clock_->Now().ToDoubleT()),
-      timeout_policy_(kDefault) {
+      timestamp_(clock_->Now().ToDoubleT()) {
   if (status == kNewGesture || !UserGestureIndicator::CurrentTokenThreadSafe())
     consumable_gestures_++;
 }
@@ -43,28 +42,14 @@
   return true;
 }
 
-void UserGestureToken::SetTimeoutPolicy(TimeoutPolicy policy) {
-  if (HasGestures() && policy > timeout_policy_)
-    timeout_policy_ = policy;
-}
-
 void UserGestureToken::ResetTimestamp() {
   timestamp_ = clock_->Now().ToDoubleT();
 }
 
 bool UserGestureToken::HasTimedOut() const {
-  if (timeout_policy_ == kHasPaused)
-    return false;
   return clock_->Now().ToDoubleT() - timestamp_ > kUserGestureTimeout;
 }
 
-// This enum is used in a histogram, so its values shouldn't change.
-enum GestureMergeState {
-  kOldTokenHasGesture = 1 << 0,
-  kNewTokenHasGesture = 1 << 1,
-  kGestureMergeStateEnd = 1 << 2,
-};
-
 UserGestureToken* UserGestureIndicator::root_token_ = nullptr;
 
 void UserGestureIndicator::UpdateRootToken() {
@@ -132,11 +117,4 @@
   return IsMainThread() ? CurrentToken() : nullptr;
 }
 
-// static
-void UserGestureIndicator::SetTimeoutPolicy(
-    UserGestureToken::TimeoutPolicy policy) {
-  if (auto* token = CurrentTokenThreadSafe())
-    token->SetTimeoutPolicy(policy);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/user_gesture_indicator.h b/third_party/blink/renderer/core/dom/user_gesture_indicator.h
index 0b58bca3..ab869d6b 100644
--- a/third_party/blink/renderer/core/dom/user_gesture_indicator.h
+++ b/third_party/blink/renderer/core/dom/user_gesture_indicator.h
@@ -28,7 +28,6 @@
 
  public:
   enum Status { kNewGesture, kPossiblyExistingGesture };
-  enum TimeoutPolicy { kDefault, kHasPaused };
 
   ~UserGestureToken() = default;
 
@@ -41,7 +40,6 @@
 
   void TransferGestureTo(UserGestureToken*);
   bool ConsumeGesture();
-  void SetTimeoutPolicy(TimeoutPolicy);
   void ResetTimestamp();
   bool HasGestures() const;
   bool HasTimedOut() const;
@@ -49,7 +47,6 @@
   size_t consumable_gestures_;
   const base::Clock* clock_;
   double timestamp_;
-  TimeoutPolicy timeout_policy_;
   DISALLOW_COPY_AND_ASSIGN(UserGestureToken);
 };
 
@@ -76,8 +73,6 @@
   static UserGestureToken* CurrentToken();
   static UserGestureToken* CurrentTokenThreadSafe();
 
-  static void SetTimeoutPolicy(UserGestureToken::TimeoutPolicy);
-
   explicit UserGestureIndicator(scoped_refptr<UserGestureToken>);
 
   // Constructs a UserGestureIndicator with a new UserGestureToken of the given
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5 b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
index 9454fdef..08283c3f 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
@@ -308,11 +308,5 @@
       feature_policy_name: "xr-spatial-tracking",
       depends_on: ["WebXR"],
     },
-    // TODO(crbug.com/960132): WebVR added manually in feature_policy_helper.cc.
-    // {
-    //   name: "WebVr",
-    //   feature_policy_name: "vr",
-    //   depends_on: ["WebVR"],
-    // },
   ],
 }
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index b763d49..5266cc11 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1492,16 +1492,6 @@
 }
 
 // static
-std::unique_ptr<UserGestureIndicator> LocalFrame::NotifyUserActivation(
-    LocalFrame* frame,
-    UserGestureToken* token) {
-  DCHECK(!RuntimeEnabledFeatures::UserActivationV2Enabled());
-  if (frame)
-    frame->NotifyUserActivation(false);
-  return std::make_unique<UserGestureIndicator>(token);
-}
-
-// static
 bool LocalFrame::HasTransientUserActivation(LocalFrame* frame,
                                             bool check_if_main_thread) {
   if (RuntimeEnabledFeatures::UserActivationV2Enabled())
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 26f10434..40ebd1f 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -206,11 +206,6 @@
       UserGestureToken::Status = UserGestureToken::kPossiblyExistingGesture,
       bool need_browser_verification = false);
 
-  // Similar to above, but used only in old UAv1-specific code.
-  static std::unique_ptr<UserGestureIndicator> NotifyUserActivation(
-      LocalFrame*,
-      UserGestureToken*);
-
   // Returns the transient user activation state of the |LocalFrame|, provided
   // it is non-null.  Otherwise returns |false|.
   //
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.cc b/third_party/blink/renderer/core/inspector/devtools_session.cc
index 06cd9e3..7bc5f65 100644
--- a/third_party/blink/renderer/core/inspector/devtools_session.cc
+++ b/third_party/blink/renderer/core/inspector/devtools_session.cc
@@ -56,16 +56,6 @@
   return unwrap_message;
 }
 
-protocol::ProtocolMessage ToProtocolMessage(
-    std::unique_ptr<v8_inspector::StringBuffer> buffer) {
-  protocol::ProtocolMessage message;
-  const auto& string = buffer->string();
-  DCHECK(string.is8Bit());
-  // TODO: add StringBuffer::takeBytes().
-  message.binary = WebVector<uint8_t>(string.characters8(), string.length());
-  return message;
-}
-
 // Platform allows us to inject the string<->double conversion
 // routines from Blink into the inspector_protocol JSON parser / serializer.
 class JsonPlatform : public ::inspector_protocol_encoding::json::Platform {
@@ -92,6 +82,12 @@
   JsonPlatform platform;
   return ConvertCBORToJSON(platform, cbor, json);
 }
+
+std::vector<uint8_t> Get8BitStringFrom(v8_inspector::StringBuffer* msg) {
+  const v8_inspector::StringView& s = msg->string();
+  DCHECK(s.is8Bit());
+  return std::vector<uint8_t>(s.characters8(), s.characters8() + s.length());
+}
 }  // namespace
 
 // Created and stored in unique_ptr on UI.
@@ -285,10 +281,7 @@
 void DevToolsSession::sendProtocolResponse(
     int call_id,
     std::unique_ptr<protocol::Serializable> message) {
-  SendProtocolResponse(
-      call_id,
-      protocol::ProtocolMessage{
-          WTF::String(), WebVector<uint8_t>(message->serializeToBinary())});
+  SendProtocolResponse(call_id, message->serializeToBinary());
 }
 
 void DevToolsSession::fallThrough(int call_id,
@@ -301,15 +294,11 @@
 void DevToolsSession::sendResponse(
     int call_id,
     std::unique_ptr<v8_inspector::StringBuffer> message) {
-  // We can potentially avoid copies if WebString would convert to utf8 right
-  // from StringView, but it uses StringImpl itself, so we don't create any
-  // extra copies here.
-  SendProtocolResponse(call_id, ToProtocolMessage(std::move(message)));
+  SendProtocolResponse(call_id, Get8BitStringFrom(message.get()));
 }
 
-void DevToolsSession::SendProtocolResponse(
-    int call_id,
-    const protocol::ProtocolMessage& message) {
+void DevToolsSession::SendProtocolResponse(int call_id,
+                                           std::vector<uint8_t> message) {
   if (IsDetached())
     return;
   flushProtocolNotifications();
@@ -320,66 +309,30 @@
   if (WebTestSupport::IsRunningWebTest())
     agent_->FlushProtocolNotifications();
 
-  host_remote_->DispatchProtocolResponse(FinalizeMessage(message), call_id,
-                                         session_state_.TakeUpdates());
+  host_remote_->DispatchProtocolResponse(FinalizeMessage(std::move(message)),
+                                         call_id, session_state_.TakeUpdates());
 }
 
-class DevToolsSession::Notification {
- public:
-  static std::unique_ptr<Notification> CreateForBlink(
-      std::unique_ptr<protocol::Serializable> notification) {
-    return std::unique_ptr<Notification>(
-        new Notification(std::move(notification)));
-  }
-
-  static std::unique_ptr<Notification> CreateForV8(
-      std::unique_ptr<v8_inspector::StringBuffer> notification) {
-    return std::unique_ptr<Notification>(
-        new Notification(std::move(notification)));
-  }
-
-  explicit Notification(std::unique_ptr<protocol::Serializable> notification)
-      : blink_notification_(std::move(notification)) {}
-
-  explicit Notification(
-      std::unique_ptr<v8_inspector::StringBuffer> notification)
-      : v8_notification_(std::move(notification)) {}
-
-  protocol::ProtocolMessage Serialize() {
-    if (blink_notification_) {
-      auto result = protocol::ProtocolMessage{
-          WTF::String(),
-          WebVector<uint8_t>(blink_notification_->serializeToBinary())};
-      blink_notification_.reset();
-      return result;
-    }
-    if (v8_notification_) {
-      auto result = ToProtocolMessage(std::move(v8_notification_));
-      v8_notification_.reset();
-      return result;
-    }
-    return protocol::ProtocolMessage();
-  }
-
- private:
-  std::unique_ptr<protocol::Serializable> blink_notification_;
-  std::unique_ptr<v8_inspector::StringBuffer> v8_notification_;
-};
-
 void DevToolsSession::sendProtocolNotification(
     std::unique_ptr<protocol::Serializable> notification) {
   if (IsDetached())
     return;
-  notification_queue_.push_back(
-      Notification::CreateForBlink(std::move(notification)));
+  notification_queue_.push_back(WTF::Bind(
+      [](std::unique_ptr<protocol::Serializable> notification) {
+        return notification->serializeToBinary();
+      },
+      std::move(notification)));
 }
 
 void DevToolsSession::sendNotification(
     std::unique_ptr<v8_inspector::StringBuffer> notification) {
   if (IsDetached())
     return;
-  notification_queue_.push_back(
-      Notification::CreateForV8(std::move(notification)));
+  notification_queue_.push_back(WTF::Bind(
+      [](std::unique_ptr<v8_inspector::StringBuffer> notification) {
+        return Get8BitStringFrom(notification.get());
+      },
+      std::move(notification)));
 }
 
 void DevToolsSession::flushProtocolNotifications() {
@@ -393,7 +346,7 @@
     v8_session_state_cbor_.Set(v8_session_->state());
   for (wtf_size_t i = 0; i < notification_queue_.size(); ++i) {
     host_remote_->DispatchProtocolNotification(
-        FinalizeMessage(notification_queue_[i]->Serialize()),
+        FinalizeMessage(std::move(notification_queue_[i]).Run()),
         session_state_.TakeUpdates());
   }
   notification_queue_.clear();
@@ -405,8 +358,8 @@
 }
 
 blink::mojom::blink::DevToolsMessagePtr DevToolsSession::FinalizeMessage(
-    protocol::ProtocolMessage message) {
-  std::vector<uint8_t> message_to_send = message.binary.ReleaseVector();
+    std::vector<uint8_t> message) const {
+  std::vector<uint8_t> message_to_send = std::move(message);
   if (!client_expects_binary_responses_) {
     std::vector<uint8_t> json;
     IPEStatus status = ConvertCBORToJSON(SpanFrom(message_to_send), &json);
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.h b/third_party/blink/renderer/core/inspector/devtools_session.h
index 4cb447ca..2dd2349 100644
--- a/third_party/blink/renderer/core/inspector/devtools_session.h
+++ b/third_party/blink/renderer/core/inspector/devtools_session.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_DEVTOOLS_SESSION_H_
 
 #include <memory>
-
+#include "base/callback.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
@@ -90,12 +90,11 @@
       std::unique_ptr<v8_inspector::StringBuffer> message) override;
 
   bool IsDetached();
-  void SendProtocolResponse(int call_id,
-                            const protocol::ProtocolMessage& message);
+  void SendProtocolResponse(int call_id, std::vector<uint8_t> message);
 
   // Converts to JSON if requested by the client.
   blink::mojom::blink::DevToolsMessagePtr FinalizeMessage(
-      protocol::ProtocolMessage message);
+      std::vector<uint8_t> message) const;
 
   Member<DevToolsAgent> agent_;
   mojo::AssociatedReceiver<mojom::blink::DevToolsSession> receiver_;
@@ -105,8 +104,10 @@
   std::unique_ptr<protocol::UberDispatcher> inspector_backend_dispatcher_;
   InspectorSessionState session_state_;
   HeapVector<Member<InspectorAgent>> agents_;
-  class Notification;
-  Vector<std::unique_ptr<Notification>> notification_queue_;
+  // Notifications are lazily serialized to shift the overhead we spend away
+  // from Javascript code that generates many events (e.g., a loop logging to
+  // console on every iteration).
+  Vector<base::OnceCallback<std::vector<uint8_t>()>> notification_queue_;
   const bool client_expects_binary_responses_;
   InspectorAgentState v8_session_state_;
   InspectorAgentState::Bytes v8_session_state_cbor_;
diff --git a/third_party/blink/renderer/core/inspector/main_thread_debugger.cc b/third_party/blink/renderer/core/inspector/main_thread_debugger.cc
index 08425de..4b1e032 100644
--- a/third_party/blink/renderer/core/inspector/main_thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/main_thread_debugger.cc
@@ -247,8 +247,6 @@
   DCHECK(paused_frame == paused_frame->LocalFrameRoot());
   paused_ = true;
 
-  UserGestureIndicator::SetTimeoutPolicy(UserGestureToken::kHasPaused);
-
   // Wait for continue or step command.
   if (client_message_loop_)
     client_message_loop_->Run(paused_frame);
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index ce971d8..a46779d5 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -373,35 +373,27 @@
     web_view_->Client()->CloseWindowSoon();
 }
 
-// Although a LocalFrame is passed in, we don't actually use it, since we
-// already know our own m_webView.
 bool ChromeClientImpl::OpenJavaScriptAlertDelegate(LocalFrame* frame,
                                                    const String& message) {
   NotifyPopupOpeningObservers();
   WebLocalFrameImpl* webframe = WebLocalFrameImpl::FromFrame(frame);
   if (webframe->Client()) {
-    if (UserGestureIndicator::ProcessingUserGesture())
-      UserGestureIndicator::SetTimeoutPolicy(UserGestureToken::kHasPaused);
     webframe->Client()->RunModalAlertDialog(message);
     return true;
   }
   return false;
 }
 
-// See comments for openJavaScriptAlertDelegate().
 bool ChromeClientImpl::OpenJavaScriptConfirmDelegate(LocalFrame* frame,
                                                      const String& message) {
   NotifyPopupOpeningObservers();
   WebLocalFrameImpl* webframe = WebLocalFrameImpl::FromFrame(frame);
   if (webframe->Client()) {
-    if (UserGestureIndicator::ProcessingUserGesture())
-      UserGestureIndicator::SetTimeoutPolicy(UserGestureToken::kHasPaused);
     return webframe->Client()->RunModalConfirmDialog(message);
   }
   return false;
 }
 
-// See comments for openJavaScriptAlertDelegate().
 bool ChromeClientImpl::OpenJavaScriptPromptDelegate(LocalFrame* frame,
                                                     const String& message,
                                                     const String& default_value,
@@ -409,8 +401,6 @@
   NotifyPopupOpeningObservers();
   WebLocalFrameImpl* webframe = WebLocalFrameImpl::FromFrame(frame);
   if (webframe->Client()) {
-    if (UserGestureIndicator::ProcessingUserGesture())
-      UserGestureIndicator::SetTimeoutPolicy(UserGestureToken::kHasPaused);
     WebString actual_value;
     bool ok = webframe->Client()->RunModalPromptDialog(message, default_value,
                                                        &actual_value);
diff --git a/third_party/blink/renderer/modules/DEPS b/third_party/blink/renderer/modules/DEPS
index 04ac798..7ecc390 100644
--- a/third_party/blink/renderer/modules/DEPS
+++ b/third_party/blink/renderer/modules/DEPS
@@ -25,5 +25,8 @@
     "media_capabilities_fuzzer.cc": [
         "+testing/libfuzzer/proto/lpm_interface.h",
         "+third_party/protobuf/src/google/protobuf/repeated_field.h",
+    ],
+    "canvas_fuzzer.cc": [
+        "+base/test/bind_test_util.h",
     ]
 }
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/canvas/BUILD.gn b/third_party/blink/renderer/modules/canvas/BUILD.gn
index 4841cf9a..7fcd19b 100644
--- a/third_party/blink/renderer/modules/canvas/BUILD.gn
+++ b/third_party/blink/renderer/modules/canvas/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//testing/libfuzzer/fuzzer_test.gni")
 import("//third_party/blink/renderer/modules/modules.gni")
 
 blink_modules_sources("canvas") {
@@ -39,3 +40,14 @@
     "offscreencanvas2d/offscreen_canvas_rendering_context_2d.h",
   ]
 }
+
+fuzzer_test("canvas_fuzzer") {
+  sources = [
+    "canvas_fuzzer.cc",
+  ]
+  seed_corpuses = [ "//third_party/blink/web_tests/fast/canvas" ]
+  deps = [
+    "../../platform:blink_fuzzer_test_support",
+    "//third_party/blink/renderer/core",
+  ]
+}
diff --git a/third_party/blink/renderer/modules/canvas/canvas_fuzzer.cc b/third_party/blink/renderer/modules/canvas/canvas_fuzzer.cc
new file mode 100644
index 0000000..fd870c4
--- /dev/null
+++ b/third_party/blink/renderer/modules/canvas/canvas_fuzzer.cc
@@ -0,0 +1,103 @@
+// 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 <fuzzer/FuzzedDataProvider.h>
+#include <stddef.h>
+#include <stdint.h>
+#include "base/test/bind_test_util.h"
+#include "base/time/default_tick_clock.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+class PageHelper {
+ public:
+  PageHelper() = default;
+  ~PageHelper() = default;
+
+  void SetUp() {
+    DCHECK(!dummy_page_holder_) << "Page should be set up only once";
+    auto setter = base::BindLambdaForTesting([&](Settings& settings) {
+      if (enable_compositing_)
+        settings.SetAcceleratedCompositingEnabled(true);
+    });
+    EnablePlatform();
+    dummy_page_holder_ = std::make_unique<DummyPageHolder>(
+        IntSize(800, 600), nullptr, nullptr, std::move(setter), GetTickClock());
+
+    // Use no-quirks (ake "strict") mode by default.
+    GetDocument().SetCompatibilityMode(Document::kNoQuirksMode);
+
+    // Use desktop page scale limits by default.
+    GetPage().SetDefaultPageScaleLimits(1, 4);
+  }
+
+  Document& GetDocument() const { return dummy_page_holder_->GetDocument(); }
+
+  Page& GetPage() const { return dummy_page_holder_->GetPage(); }
+
+  void SetBodyContentFromFuzzer(const uint8_t* data, size_t size) {
+    FuzzedDataProvider provider(data, size);
+    std::string body_content = provider.ConsumeBytesAsString(size);
+    GetDocument().documentElement()->SetInnerHTMLFromString(
+        String::FromUTF8(body_content));
+    UpdateAllLifecyclePhasesForTest();
+  }
+
+  void UpdateAllLifecyclePhasesForTest() {
+    GetDocument().View()->UpdateAllLifecyclePhases(
+        DocumentLifecycle::LifecycleUpdateReason::kTest);
+    GetDocument().View()->RunPostLifecycleSteps();
+  }
+
+  void EnablePlatform() {
+    DCHECK(!platform_);
+    platform_ = std::make_unique<ScopedTestingPlatformSupport<
+        TestingPlatformSupportWithMockScheduler>>();
+  }
+  const base::TickClock* GetTickClock() {
+    return platform_ ? platform()->test_task_runner()->GetMockTickClock()
+                     : base::DefaultTickClock::GetInstance();
+  }
+
+ private:
+  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>&
+  platform() {
+    return *platform_;
+  }
+  // The order is important: |platform_| must be destroyed after
+  // |dummy_page_holder_| is destroyed.
+  std::unique_ptr<
+      ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>>
+      platform_;
+  std::unique_ptr<DummyPageHolder> dummy_page_holder_;
+  bool enable_compositing_ = true;
+};
+
+// Fuzzer for blink::ManifestParser
+int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // We are ignoring small tests
+  constexpr int minSizeHtml = 20;
+  if (size < minSizeHtml)
+    return 0;
+
+  static BlinkFuzzerTestSupport test_support = BlinkFuzzerTestSupport();
+
+  PageHelper page;
+  page.SetUp();
+  page.SetBodyContentFromFuzzer(data, size);
+  page.UpdateAllLifecyclePhasesForTest();
+
+  return 0;
+}
+
+}  // namespace blink
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  return blink::LLVMFuzzerTestOneInput(data, size);
+}
diff --git a/third_party/blink/renderer/modules/contacts_picker/BUILD.gn b/third_party/blink/renderer/modules/contacts_picker/BUILD.gn
index 7a543ada..a78fb4e6 100644
--- a/third_party/blink/renderer/modules/contacts_picker/BUILD.gn
+++ b/third_party/blink/renderer/modules/contacts_picker/BUILD.gn
@@ -6,6 +6,8 @@
 
 blink_modules_sources("contacts_picker") {
   sources = [
+    "contact_address.cc",
+    "contact_address.h",
     "contacts_manager.cc",
     "contacts_manager.h",
     "navigator_contacts.cc",
diff --git a/third_party/blink/renderer/modules/contacts_picker/DEPS b/third_party/blink/renderer/modules/contacts_picker/DEPS
index b618045..096633b 100644
--- a/third_party/blink/renderer/modules/contacts_picker/DEPS
+++ b/third_party/blink/renderer/modules/contacts_picker/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
     "-third_party/blink/renderer/modules",
     "+third_party/blink/renderer/modules/contacts_picker",
+    "+third_party/blink/renderer/modules/payments",
 ]
diff --git a/third_party/blink/renderer/modules/contacts_picker/contact_address.cc b/third_party/blink/renderer/modules/contacts_picker/contact_address.cc
new file mode 100644
index 0000000..46e44fd
--- /dev/null
+++ b/third_party/blink/renderer/modules/contacts_picker/contact_address.cc
@@ -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.
+
+#include "third_party/blink/renderer/modules/contacts_picker/contact_address.h"
+
+namespace blink {
+
+ContactAddress::ContactAddress(
+    payments::mojom::blink::PaymentAddressPtr payment_address)
+    : PaymentAddress(std::move(payment_address)) {}
+
+ContactAddress::~ContactAddress() = default;
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/contacts_picker/contact_address.h b/third_party/blink/renderer/modules/contacts_picker/contact_address.h
new file mode 100644
index 0000000..17ad200
--- /dev/null
+++ b/third_party/blink/renderer/modules/contacts_picker/contact_address.h
@@ -0,0 +1,24 @@
+// 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_MODULES_CONTACTS_PICKER_CONTACT_ADDRESS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_CONTACTS_PICKER_CONTACT_ADDRESS_H_
+
+#include "third_party/blink/public/mojom/payments/payment_request.mojom-blink.h"
+#include "third_party/blink/renderer/modules/payments/payment_address.h"
+
+namespace blink {
+
+class ContactAddress : public PaymentAddress {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  explicit ContactAddress(
+      payments::mojom::blink::PaymentAddressPtr payment_address);
+  ~ContactAddress() override;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_CONTACTS_PICKER_CONTACT_ADDRESS_H_
diff --git a/third_party/blink/renderer/modules/contacts_picker/contact_address.idl b/third_party/blink/renderer/modules/contacts_picker/contact_address.idl
new file mode 100644
index 0000000..449f02f
--- /dev/null
+++ b/third_party/blink/renderer/modules/contacts_picker/contact_address.idl
@@ -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.
+
+// https://wicg.github.io/contact-api/spec/#contactaddress
+
+[
+    SecureContext,
+    Exposed=Window,
+    RuntimeEnabled=ContactsManagerAddresses
+] interface ContactAddress : PaymentAddress {};
diff --git a/third_party/blink/renderer/modules/contacts_picker/contact_info.idl b/third_party/blink/renderer/modules/contacts_picker/contact_info.idl
index c3444d43..6ac1373 100644
--- a/third_party/blink/renderer/modules/contacts_picker/contact_info.idl
+++ b/third_party/blink/renderer/modules/contacts_picker/contact_info.idl
@@ -8,4 +8,5 @@
     sequence<USVString> name;
     sequence<USVString> email;
     sequence<USVString> tel;
+    sequence<ContactAddress> address;
 };
diff --git a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc
index 2240b7e..426ecf0 100644
--- a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc
+++ b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/modules/contacts_picker/contact_address.h"
 #include "third_party/blink/renderer/modules/contacts_picker/contact_info.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
@@ -29,7 +30,7 @@
     Convert(const blink::mojom::blink::ContactInfoPtr& contact) {
   blink::ContactInfo* contact_info = blink::ContactInfo::Create();
 
-  if (contact->name.has_value()) {
+  if (contact->name) {
     Vector<String> names;
     names.ReserveInitialCapacity(contact->name->size());
 
@@ -39,7 +40,7 @@
     contact_info->setName(names);
   }
 
-  if (contact->email.has_value()) {
+  if (contact->email) {
     Vector<String> emails;
     emails.ReserveInitialCapacity(contact->email->size());
 
@@ -49,7 +50,7 @@
     contact_info->setEmail(emails);
   }
 
-  if (contact->tel.has_value()) {
+  if (contact->tel) {
     Vector<String> numbers;
     numbers.ReserveInitialCapacity(contact->tel->size());
 
@@ -59,6 +60,17 @@
     contact_info->setTel(numbers);
   }
 
+  if (contact->address) {
+    blink::HeapVector<blink::Member<blink::ContactAddress>> addresses;
+    for (auto& address : *contact->address) {
+      auto* blink_address = blink::MakeGarbageCollected<blink::ContactAddress>(
+          std::move(address));
+      addresses.push_back(blink_address);
+    }
+
+    contact_info->setAddress(addresses);
+  }
+
   return contact_info;
 }
 
@@ -132,6 +144,7 @@
   bool include_names = false;
   bool include_emails = false;
   bool include_tel = false;
+  bool include_addresses = false;
 
   for (const String& property : properties) {
     if (!base::Contains(properties_, property)) {
@@ -149,6 +162,8 @@
       include_emails = true;
     else if (property == kTel)
       include_tel = true;
+    else if (property == kAddress)
+      include_addresses = true;
   }
 
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
@@ -157,6 +172,7 @@
   contact_picker_in_use_ = true;
   GetContactsManager(script_state)
       ->Select(options->multiple(), include_names, include_emails, include_tel,
+               include_addresses,
                WTF::Bind(&ContactsManager::OnContactsSelected,
                          WrapPersistent(this), WrapPersistent(resolver)));
 
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
index 054e30c..8dda7e5c 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
@@ -849,9 +849,7 @@
       media_stream_device_observer_for_testing_;
   if (frame_) {  // Can be null for tests.
     auto* web_frame = static_cast<WebLocalFrame*>(WebFrame::FromFrame(frame_));
-    DCHECK(web_frame);
-
-    if (!web_frame->Client())
+    if (!web_frame || !web_frame->Client())
       return nullptr;
 
     // TODO(704136): Move ownership of |WebMediaStreamDeviceObserver| out of
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.h b/third_party/blink/renderer/modules/mediastream/user_media_processor.h
index fd16054..c3771147 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_processor.h
+++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.h
@@ -294,7 +294,7 @@
   MediaDevicesDispatcherCallback media_devices_dispatcher_cb_;
   base::OnceClosure request_completed_cb_;
 
-  WeakMember<LocalFrame> frame_;
+  Member<LocalFrame> frame_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   THREAD_CHECKER(thread_checker_);
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index 658025d..ed7767ce 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -100,6 +100,7 @@
           "clipboard/clipboard_item.idl",
           "compression/compression_stream.idl",
           "compression/decompression_stream.idl",
+          "contacts_picker/contact_address.idl",
           "contacts_picker/contacts_manager.idl",
           "content_index/content_index.idl",
           "content_index/content_index_event.idl",
diff --git a/third_party/blink/renderer/modules/payments/payment_address.h b/third_party/blink/renderer/modules/payments/payment_address.h
index f2ace49f..ed6b7dae 100644
--- a/third_party/blink/renderer/modules/payments/payment_address.h
+++ b/third_party/blink/renderer/modules/payments/payment_address.h
@@ -16,7 +16,7 @@
 
 namespace blink {
 
-class MODULES_EXPORT PaymentAddress final : public ScriptWrappable {
+class MODULES_EXPORT PaymentAddress : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 65b672a..cd206a7 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -719,18 +719,11 @@
   bool supports_implicit_resolve =
       !UsingSwapChain() && extensions_util_->SupportsExtension(
                                "GL_EXT_multisampled_render_to_texture");
-  bool supports_screen_space_aa =
-      !UsingSwapChain() && extensions_util_->SupportsExtension(
-                               "GL_CHROMIUM_screen_space_antialiasing");
   if (webgl_preferences.anti_aliasing_mode == kAntialiasingModeUnspecified) {
     if (use_multisampling) {
       anti_aliasing_mode_ = kAntialiasingModeMSAAExplicitResolve;
       if (supports_implicit_resolve) {
         anti_aliasing_mode_ = kAntialiasingModeMSAAImplicitResolve;
-      } else if (supports_screen_space_aa &&
-                 ContextProvider()->GetGpuFeatureInfo().IsWorkaroundEnabled(
-                     gpu::USE_FRAMEBUFFER_CMAA)) {
-        anti_aliasing_mode_ = kAntialiasingModeScreenSpaceAntialiasing;
       }
     } else {
       anti_aliasing_mode_ = kAntialiasingModeNone;
@@ -738,26 +731,13 @@
   } else {
     bool prefer_implicit_resolve = (webgl_preferences.anti_aliasing_mode ==
                                     kAntialiasingModeMSAAImplicitResolve);
-    bool prefer_screen_space_aa = (webgl_preferences.anti_aliasing_mode ==
-                                   kAntialiasingModeScreenSpaceAntialiasing);
-    if ((prefer_implicit_resolve && !supports_implicit_resolve) ||
-        (prefer_screen_space_aa && !supports_screen_space_aa)) {
+    if (prefer_implicit_resolve && !supports_implicit_resolve) {
       DLOG(ERROR) << "Invalid anti-aliasing mode specified.";
       return false;
     }
     anti_aliasing_mode_ = webgl_preferences.anti_aliasing_mode;
   }
 
-  // TODO(dshwang): Enable storage textures on all platforms. crbug.com/557848
-  // The Linux ATI bot fails
-  // WebglConformance.conformance_textures_misc_tex_image_webgl, so use storage
-  // textures only if ScreenSpaceAntialiasing is enabled, because
-  // ScreenSpaceAntialiasing is much faster with storage textures.
-  storage_texture_supported_ =
-      (webgl_version_ > kWebGL1 ||
-       extensions_util_->SupportsExtension("GL_EXT_texture_storage")) &&
-      anti_aliasing_mode_ == kAntialiasingModeScreenSpaceAntialiasing;
-
   sample_count_ = std::min(
       static_cast<int>(webgl_preferences.msaa_sample_count), max_sample_count);
   eqaa_storage_sample_count_ = webgl_preferences.eqaa_storage_sample_count;
@@ -1235,8 +1215,6 @@
   }
 
   gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
-  if (anti_aliasing_mode_ == kAntialiasingModeScreenSpaceAntialiasing)
-    gl_->ApplyScreenSpaceAntialiasingCHROMIUM();
 }
 
 void DrawingBuffer::ResolveIfNeeded() {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
index 2aa58412..28908a7 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -503,7 +503,6 @@
   const bool using_gpu_compositing_;
   const bool using_swap_chain_;
   bool has_implicit_stencil_buffer_ = false;
-  bool storage_texture_supported_ = false;
 
   // The texture target (2D or RECTANGLE) for our allocations.
   GLenum texture_target_ = 0;
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
index 6450da3e..40c7e15 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
@@ -213,12 +213,6 @@
     if (extensions_util->SupportsExtension(
             "GL_EXT_multisampled_render_to_texture")) {
       anti_aliasing_mode_ = kMSAAImplicitResolve;
-    } else if (extensions_util->SupportsExtension(
-                   "GL_CHROMIUM_screen_space_antialiasing") &&
-               drawing_buffer_->ContextProvider()
-                   ->GetGpuFeatureInfo()
-                   .IsWorkaroundEnabled(gpu::USE_FRAMEBUFFER_CMAA)) {
-      anti_aliasing_mode_ = kScreenSpaceAntialiasing;
     }
   }
   DVLOG(2) << __FUNCTION__
@@ -575,12 +569,7 @@
     client->DrawingBufferClientRestoreScissorTest();
   } else {
     gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
-    if (anti_aliasing_mode_ == kScreenSpaceAntialiasing) {
-      DVLOG(3) << __FUNCTION__ << ": screen space antialiasing";
-      gl->ApplyScreenSpaceAntialiasingCHROMIUM();
-    } else {
-      DVLOG(3) << __FUNCTION__ << ": nothing to do";
-    }
+    DVLOG(3) << __FUNCTION__ << ": nothing to do";
   }
 
   // On exit, leaves the destination framebuffer active. Caller is responsible
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
index 6727432..5a265e18 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
@@ -167,7 +167,6 @@
     kNone,
     kMSAAImplicitResolve,
     kMSAAExplicitResolve,
-    kScreenSpaceAntialiasing,
   };
 
   AntialiasingMode anti_aliasing_mode_ = kNone;
diff --git a/third_party/blink/renderer/platform/heap/heap_test_utilities.cc b/third_party/blink/renderer/platform/heap/heap_test_utilities.cc
index 528bd25a..1cb66949 100644
--- a/third_party/blink/renderer/platform/heap/heap_test_utilities.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test_utilities.cc
@@ -49,8 +49,7 @@
 }
 
 void IncrementalMarkingTestDriver::Start() {
-  thread_state_->IncrementalMarkingStart(
-      BlinkGC::GCReason::kForcedGCForTesting);
+  thread_state_->IncrementalMarkingStartForTesting();
 }
 
 bool IncrementalMarkingTestDriver::SingleStep(BlinkGC::StackState stack_state) {
@@ -75,7 +74,7 @@
   FinishSteps(BlinkGC::StackState::kNoHeapPointersOnStack);
   CHECK_EQ(ThreadState::kIncrementalMarkingFinalizeScheduled,
            thread_state_->GetGCState());
-  thread_state_->RunScheduledGC(BlinkGC::StackState::kNoHeapPointersOnStack);
+  thread_state_->IncrementalMarkingFinalize();
   CHECK(!thread_state_->IsIncrementalMarking());
   if (complete_sweep) {
     thread_state_->CompleteSweep();
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index f424931..930944a0 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -98,11 +98,6 @@
 
 namespace {
 
-// Duration of one incremental marking step. Should be short enough that it
-// doesn't cause jank even though it is scheduled as a normal task.
-constexpr base::TimeDelta kDefaultIncrementalMarkingStepDuration =
-    base::TimeDelta::FromMilliseconds(2);
-
 // Concurrent marking should stop every once in a while to flush private
 // segments to v8 marking worklist. It should also stop to avoid priority
 // inversion.
@@ -133,6 +128,122 @@
 
 }  // namespace
 
+constexpr base::TimeDelta ThreadState::kDefaultIncrementalMarkingStepDuration;
+
+class ThreadState::IncrementalMarkingScheduler {
+ public:
+  explicit IncrementalMarkingScheduler(ThreadState* thread_state)
+      : thread_state_(thread_state) {}
+
+  // Starts incremental marking with further scheduled steps.
+  void Start(BlinkGC::GCReason reason) {
+    Init(reason);
+    thread_state_->IncrementalMarkingStart(reason_);
+    ScheduleTask();
+  }
+
+  // Cancels incremental marking task in case there is any pending.
+  void Cancel() {
+    if (!pending_task_) {
+      return;
+    }
+    pending_task_->Cancel();
+  }
+
+ private:
+  void Init(BlinkGC::GCReason reason) {
+    DCHECK(!pending_task_);
+    reason_ = reason;
+    next_incremental_marking_step_duration_ =
+        kDefaultIncrementalMarkingStepDuration;
+    previous_incremental_marking_time_left_ = base::TimeDelta::Max();
+  }
+
+  void ScheduleTask() {
+    if (pending_task_) {
+      return;
+    }
+    auto task = std::make_unique<Task>(this);
+    pending_task_ = task.get();
+    ThreadScheduler::Current()->V8TaskRunner()->PostNonNestableTask(
+        FROM_HERE, WTF::Bind(&Task::Run, std::move(task)));
+  }
+
+  // TODO(bikineev): Replace with PostNonNestableCancelableTask when
+  // implemented.
+  class Task {
+   public:
+    explicit Task(IncrementalMarkingScheduler* scheduler)
+        : scheduler_(scheduler) {}
+    ~Task() {
+      if (cancelled_) {
+        return;
+      }
+      Cancel();
+    }
+
+    void Run() {
+      // Bail out if the ThreadState has been cancelled/destroyed.
+      if (cancelled_) {
+        return;
+      }
+      Cancel();
+      scheduler_->Dispatch();
+    }
+
+    void Cancel() {
+      cancelled_ = true;
+      scheduler_->pending_task_ = nullptr;
+    }
+
+   private:
+    IncrementalMarkingScheduler* scheduler_;
+    bool cancelled_ = false;
+  };
+
+  void Dispatch() {
+    switch (thread_state_->GetGCState()) {
+      case ThreadState::kIncrementalGCScheduled:
+        thread_state_->IncrementalMarkingStart(reason_);
+        ScheduleTask();
+        break;
+      case ThreadState::kIncrementalMarkingStepScheduled:
+        thread_state_->IncrementalMarkingStep(
+            BlinkGC::kNoHeapPointersOnStack,
+            next_incremental_marking_step_duration_);
+        UpdateIncrementalMarkingStepDuration();
+        ScheduleTask();
+        break;
+      case ThreadState::kIncrementalMarkingFinalizeScheduled:
+        thread_state_->IncrementalMarkingFinalize();
+        break;
+      default:
+        break;
+    }
+  }
+
+  void UpdateIncrementalMarkingStepDuration() {
+    const ThreadHeap& heap = thread_state_->Heap();
+    base::TimeDelta time_left =
+        heap.stats_collector()->estimated_marking_time() -
+        heap.stats_collector()->marking_time_so_far();
+    // Increase step size if estimated time left is increasing.
+    if (previous_incremental_marking_time_left_ < time_left) {
+      constexpr double ratio = 2.0;
+      next_incremental_marking_step_duration_ *= ratio;
+    }
+    previous_incremental_marking_time_left_ = time_left;
+  }
+
+  ThreadState* thread_state_;
+  BlinkGC::GCReason reason_;
+  base::TimeDelta next_incremental_marking_step_duration_ =
+      kDefaultIncrementalMarkingStepDuration;
+  base::TimeDelta previous_incremental_marking_time_left_ =
+      base::TimeDelta::Max();
+  Task* pending_task_ = nullptr;
+};
+
 ThreadState::ThreadState()
     : thread_(CurrentThread()),
       persistent_region_(std::make_unique<PersistentRegion>()),
@@ -141,6 +252,8 @@
 #if defined(ADDRESS_SANITIZER)
       asan_fake_stack_(__asan_get_current_fake_stack()),
 #endif
+      incremental_marking_scheduler_(
+          std::make_unique<IncrementalMarkingScheduler>(this)),
       marker_scheduler_(std::make_unique<CancelableTaskScheduler>(
           base::MakeRefCounted<WorkerPoolTaskRunner>())),
       sweeper_scheduler_(std::make_unique<CancelableTaskScheduler>(
@@ -367,8 +480,6 @@
   VLOG(2) << "[state:" << this << "] ScheduleGCIfNeeded";
   DCHECK(CheckThread());
 
-  UpdateIncrementalMarkingStepDuration();
-
   // Allocation is allowed during sweeping, but those allocations should not
   // trigger nested GCs.
   if (IsGCForbidden() || SweepForbidden())
@@ -389,7 +500,7 @@
           blink::features::kBlinkHeapIncrementalMarkingStress)) {
     VLOG(2) << "[state:" << this << "] "
             << "ScheduleGCIfNeeded: Scheduled incremental marking for testing";
-    IncrementalMarkingStart(BlinkGC::GCReason::kForcedGCForTesting);
+    StartIncrementalMarking(BlinkGC::GCReason::kForcedGCForTesting);
     return;
   }
 }
@@ -466,14 +577,17 @@
   }
 }
 
-void ThreadState::ScheduleIncrementalMarkingStep() {
-  CHECK(!IsSweepingInProgress());
-  SetGCState(kIncrementalMarkingStepScheduled);
-}
-
-void ThreadState::ScheduleIncrementalMarkingFinalize() {
-  CHECK(!IsSweepingInProgress());
-  SetGCState(kIncrementalMarkingFinalizeScheduled);
+void ThreadState::StartIncrementalMarking(BlinkGC::GCReason reason) {
+  DCHECK(CheckThread());
+  // Schedule an incremental GC only when no GC is scheduled. Otherwise, already
+  // scheduled GCs should be prioritized.
+  if (GetGCState() != kNoGCScheduled) {
+    return;
+  }
+  CompleteSweep();
+  reason_for_scheduled_gc_ = reason;
+  SetGCState(kIncrementalGCScheduled);
+  incremental_marking_scheduler_->Start(reason);
 }
 
 void ThreadState::ScheduleIdleLazySweep() {
@@ -505,17 +619,6 @@
   SetGCState(kPreciseGCScheduled);
 }
 
-void ThreadState::ScheduleIncrementalGC(BlinkGC::GCReason reason) {
-  DCHECK(CheckThread());
-  // Schedule an incremental GC only when no GC is scheduled. Otherwise, already
-  // scheduled GCs should be prioritized.
-  if (GetGCState() == kNoGCScheduled) {
-    CompleteSweep();
-    reason_for_scheduled_gc_ = reason;
-    SetGCState(kIncrementalGCScheduled);
-  }
-}
-
 namespace {
 
 #define UNEXPECTED_GCSTATE(s)                                   \
@@ -635,15 +738,6 @@
                      BlinkGC::kConcurrentAndLazySweeping,
                      BlinkGC::GCReason::kPreciseGC);
       break;
-    case kIncrementalMarkingStepScheduled:
-      IncrementalMarkingStep(stack_state);
-      break;
-    case kIncrementalMarkingFinalizeScheduled:
-      IncrementalMarkingFinalize();
-      break;
-    case kIncrementalGCScheduled:
-      IncrementalMarkingStart(reason_for_scheduled_gc_);
-      break;
     default:
       break;
   }
@@ -689,6 +783,7 @@
   DCHECK(InAtomicMarkingPause());
   Heap().MakeConsistentForGC();
   Heap().ClearArenaAges();
+
   // AtomicPauseMarkPrologue is the common entry point for marking. The
   // requirement is to lock from roots marking to weakness processing which is
   // why the lock is taken at the end of the prologue.
@@ -1054,11 +1149,21 @@
   SetIncrementalMarking(false);
 }
 
+void ThreadState::IncrementalMarkingStartForTesting() {
+  // kIncrementalGCScheduled state requires sweeping to not be in progress.
+  CompleteSweep();
+  SetGCState(kIncrementalGCScheduled);
+  IncrementalMarkingStart(BlinkGC::GCReason::kForcedGCForTesting);
+}
+
 void ThreadState::IncrementalMarkingStart(BlinkGC::GCReason reason) {
+  DCHECK(!IsGCForbidden());
+  DCHECK_EQ(kIncrementalGCScheduled, GetGCState());
+
   VLOG(2) << "[state:" << this << "] "
           << "IncrementalMarking: Start";
   DCHECK(!IsMarkingInProgress());
-  CompleteSweep();
+  // Sweeping is performed in driver functions.
   DCHECK(!IsSweepingInProgress());
   Heap().stats_collector()->NotifyMarkingStarted(reason);
   {
@@ -1068,9 +1173,6 @@
         BlinkGC::ToString(reason));
     AtomicPauseScope atomic_pause_scope(this);
     ScriptForbiddenScope script_forbidden_scope;
-    next_incremental_marking_step_duration_ =
-        kDefaultIncrementalMarkingStepDuration;
-    previous_incremental_marking_time_left_ = base::TimeDelta::Max();
     MarkPhasePrologue(BlinkGC::kNoHeapPointersOnStack,
                       BlinkGC::kIncrementalAndConcurrentMarking, reason);
     {
@@ -1087,13 +1189,15 @@
       current_gc_data_.visitor->FlushMarkingWorklist();
       ScheduleConcurrentMarking();
     }
-    ScheduleIncrementalMarkingStep();
+    SetGCState(kIncrementalMarkingStepScheduled);
     DCHECK(IsMarkingInProgress());
   }
 }
 
-void ThreadState::IncrementalMarkingStep(BlinkGC::StackState stack_state) {
+void ThreadState::IncrementalMarkingStep(BlinkGC::StackState stack_state,
+                                         base::TimeDelta duration) {
   DCHECK(IsMarkingInProgress());
+  DCHECK_EQ(kIncrementalMarkingStepScheduled, GetGCState());
 
   ThreadHeapStatsCollector::EnabledScope stats_scope(
       Heap().stats_collector(),
@@ -1107,8 +1211,7 @@
     Heap().FlushNotFullyConstructedObjects();
   }
 
-  bool complete = MarkPhaseAdvanceMarking(
-      base::TimeTicks::Now() + next_incremental_marking_step_duration_);
+  bool complete = MarkPhaseAdvanceMarking(base::TimeTicks::Now() + duration);
 
   if (base::FeatureList::IsEnabled(
           blink::features::kBlinkHeapConcurrentMarking)) {
@@ -1124,10 +1227,10 @@
       DCHECK(IsUnifiedGCMarkingInProgress());
       SetGCState(kIncrementalMarkingStepPaused);
     } else {
-      ScheduleIncrementalMarkingFinalize();
+      SetGCState(kIncrementalMarkingFinalizeScheduled);
     }
   } else {
-    ScheduleIncrementalMarkingStep();
+    SetGCState(kIncrementalMarkingStepScheduled);
   }
   DCHECK(IsMarkingInProgress());
 }
@@ -1145,6 +1248,7 @@
 void ThreadState::IncrementalMarkingFinalize() {
   DCHECK(IsMarkingInProgress());
   DCHECK(!IsUnifiedGCMarkingInProgress());
+  DCHECK_EQ(kIncrementalMarkingFinalizeScheduled, GetGCState());
 
   ThreadHeapStatsCollector::EnabledScope stats_scope(
       Heap().stats_collector(),
@@ -1573,6 +1677,8 @@
   }
   Heap().DestroyMarkingWorklists(current_gc_data_.stack_state);
 
+  incremental_marking_scheduler_->Cancel();
+
   size_t marked_bytes = concurrently_marked_bytes_;
 
   current_gc_data_.visitor->FlushCompactionWorklists();
@@ -1617,20 +1723,6 @@
   Heap().Compaction()->EnableCompactionForNextGCForTesting();
 }
 
-void ThreadState::UpdateIncrementalMarkingStepDuration() {
-  if (!IsIncrementalMarking())
-    return;
-  base::TimeDelta time_left =
-      Heap().stats_collector()->estimated_marking_time() -
-      Heap().stats_collector()->marking_time_so_far();
-  // Increase step size if estimated time left is increasing.
-  if (previous_incremental_marking_time_left_ < time_left) {
-    constexpr double ratio = 2.0;
-    next_incremental_marking_step_duration_ *= ratio;
-  }
-  previous_incremental_marking_time_left_ = time_left;
-}
-
 void ThreadState::ScheduleConcurrentMarking() {
   base::AutoLock lock(active_concurrent_markers_lock_);
 
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 7fbff67..5e40b17 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -253,7 +253,6 @@
   void PerformConcurrentSweep();
 
   void SchedulePreciseGC();
-  void ScheduleIncrementalGC(BlinkGC::GCReason);
   void ScheduleForcedGCForTesting();
   void ScheduleGCIfNeeded();
   void WillStartV8GC(BlinkGC::V8GCType);
@@ -261,6 +260,10 @@
   GCState GetGCState() const { return gc_state_; }
   void SetGCPhase(GCPhase);
 
+  // Immediately starts incremental marking and schedules further steps if
+  // necessary.
+  void StartIncrementalMarking(BlinkGC::GCReason);
+
   // Returns true if marking is in progress.
   bool IsMarkingInProgress() const { return gc_phase_ == GCPhase::kMarking; }
 
@@ -286,9 +289,6 @@
 
   void EnableCompactionForNextGCForTesting();
 
-  void IncrementalMarkingStart(BlinkGC::GCReason);
-  void IncrementalMarkingStep(BlinkGC::StackState);
-  void IncrementalMarkingFinalize();
   bool FinishIncrementalMarkingIfRunning(BlinkGC::StackState,
                                          BlinkGC::MarkingType,
                                          BlinkGC::SweepingType,
@@ -382,6 +382,13 @@
   bool IsVerifyMarkingEnabled() const;
 
  private:
+  class IncrementalMarkingScheduler;
+
+  // Duration of one incremental marking step. Should be short enough that it
+  // doesn't cause jank even though it is scheduled as a normal task.
+  static constexpr base::TimeDelta kDefaultIncrementalMarkingStepDuration =
+      base::TimeDelta::FromMilliseconds(2);
+
   // Stores whether some ThreadState is currently in incremental marking.
   static AtomicEntryFlag incremental_marking_flag_;
 
@@ -482,9 +489,15 @@
   // Visit all DOM wrappers allocatd on this thread.
   void VisitDOMWrappers(Visitor*);
 
+  // Incremental marking implementation functions.
+  void IncrementalMarkingStartForTesting();
+  void IncrementalMarkingStart(BlinkGC::GCReason);
+  void IncrementalMarkingStep(
+      BlinkGC::StackState,
+      base::TimeDelta duration = kDefaultIncrementalMarkingStepDuration);
+  void IncrementalMarkingFinalize();
+
   // Schedule helpers.
-  void ScheduleIncrementalMarkingStep();
-  void ScheduleIncrementalMarkingFinalize();
   void ScheduleIdleLazySweep();
   void ScheduleConcurrentAndLazySweep();
 
@@ -493,8 +506,6 @@
 
   void RunScheduledGC(BlinkGC::StackState);
 
-  void UpdateIncrementalMarkingStepDuration();
-
   void SynchronizeAndFinishConcurrentSweeping();
 
   void InvokePreFinalizers();
@@ -540,9 +551,6 @@
   size_t gc_forbidden_count_ = 0;
   size_t static_persistent_registration_disabled_count_ = 0;
 
-  base::TimeDelta next_incremental_marking_step_duration_;
-  base::TimeDelta previous_incremental_marking_time_left_;
-
   GCState gc_state_ = GCState::kNoGCScheduled;
   GCPhase gc_phase_ = GCPhase::kNone;
   BlinkGC::GCReason reason_for_scheduled_gc_ =
@@ -583,6 +591,8 @@
   };
   GCData current_gc_data_;
 
+  std::unique_ptr<IncrementalMarkingScheduler> incremental_marking_scheduler_;
+
   std::unique_ptr<CancelableTaskScheduler> marker_scheduler_;
   uint8_t active_markers_ = 0;
   base::Lock active_concurrent_markers_lock_;
diff --git a/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc b/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc
index c8958ad..2b2bf36 100644
--- a/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc
@@ -2,15 +2,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
 #include "third_party/blink/renderer/platform/heap/thread_state_scopes.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
 
+namespace {
+
+void RunLoop() {
+  base::RunLoop rl;
+  // Push quit task.
+  ThreadScheduler::Current()->V8TaskRunner()->PostNonNestableTask(
+      FROM_HERE, WTF::Bind(rl.QuitWhenIdleClosure()));
+  rl.Run();
+}
+
+}  // namespace
+
 class ThreadStateSchedulingTest : public TestSupportingGC {
  public:
   void SetUp() override {
@@ -26,13 +41,8 @@
     EXPECT_FALSE(state_->IsSweepingInProgress());
   }
 
-  void StartIncrementalMarking() {
-    EXPECT_EQ(ThreadState::kNoGCScheduled, state_->GetGCState());
-    state_->ScheduleIncrementalGC(BlinkGC::GCReason::kForcedGCForTesting);
-    state_->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
-    EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
-              state_->GetGCState());
-    EXPECT_TRUE(state_->IsMarkingInProgress());
+  BlinkGC::GCReason LastReason() const {
+    return state_->reason_for_scheduled_gc_;
   }
 
   void StartLazySweepingForPreciseGC() {
@@ -58,21 +68,17 @@
   int initial_gc_age_;
 };
 
-TEST_F(ThreadStateSchedulingTest, ScheduleIncrementalV8FollowupGCAgain) {
+TEST_F(ThreadStateSchedulingTest, RunIncrementalGCForTesting) {
   ThreadStateSchedulingTest* test = this;
 
   EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
-  test->state()->ScheduleIncrementalGC(
-      BlinkGC::GCReason::kIncrementalV8FollowupGC);
-  EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
+  test->state()->StartIncrementalMarking(
+      BlinkGC::GCReason::kForcedGCForTesting);
+  EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
+            test->state()->GetGCState());
 
-  // Calling ScheduleIncrementalV8FollowupGC() while one is already scheduled
-  // will do nothing.
-  test->state()->ScheduleIncrementalGC(
-      BlinkGC::GCReason::kIncrementalV8FollowupGC);
-
-  EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
-  EXPECT_EQ(0, test->GCCount());
+  RunLoop();
+  EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
 }
 
 TEST_F(ThreadStateSchedulingTest, SchedulePreciseGCWhileLazySweeping) {
@@ -87,27 +93,13 @@
   EXPECT_EQ(ThreadState::kPreciseGCScheduled, test->state()->GetGCState());
 }
 
-TEST_F(ThreadStateSchedulingTest,
-       ScheduleIncrementalV8FollowupGCWhileLazySweeping) {
-  ThreadStateSchedulingTest* test = this;
-
-  test->StartLazySweepingForPreciseGC();
-
-  test->state()->ScheduleIncrementalGC(
-      BlinkGC::GCReason::kIncrementalV8FollowupGC);
-
-  // Scheduling a IncrementalV8FollowupGC should finish lazy sweeping.
-  EXPECT_FALSE(test->state()->IsSweepingInProgress());
-  EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
-}
-
 TEST_F(ThreadStateSchedulingTest, SchedulePreciseGCWhileIncrementalMarking) {
   ThreadStateSchedulingTest* test = this;
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(
       blink::features::kBlinkHeapIncrementalMarking);
-
-  test->StartIncrementalMarking();
+  test->state()->StartIncrementalMarking(
+      BlinkGC::GCReason::kForcedGCForTesting);
   test->state()->SchedulePreciseGC();
   // Scheduling a precise GC should cancel incremental marking tasks.
   EXPECT_EQ(ThreadState::kPreciseGCScheduled, test->state()->GetGCState());
@@ -122,54 +114,10 @@
   EXPECT_EQ(0, test->GCCount());
   test->state()->CompleteSweep();
   EXPECT_EQ(1, test->GCCount());
-}
 
-TEST_F(ThreadStateSchedulingTest,
-       ScheduleIncrementalV8FollowupGCWhileIncrementalMarking) {
-  ThreadStateSchedulingTest* test = this;
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      blink::features::kBlinkHeapIncrementalMarking);
-
-  test->StartIncrementalMarking();
-  test->state()->ScheduleIncrementalGC(
-      BlinkGC::GCReason::kIncrementalV8FollowupGC);
-  // Scheduling another incremental GC should not cancel incremental marking
-  // tasks.
-  EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
-            test->state()->GetGCState());
-}
-
-TEST_F(ThreadStateSchedulingTest,
-       ScheduleIncrementalV8FollowupGCWhileGCForbidden) {
-  ThreadStateSchedulingTest* test = this;
-
-  EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
-  test->state()->ScheduleIncrementalGC(
-      BlinkGC::GCReason::kIncrementalV8FollowupGC);
-  EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
-
-  ThreadState::GCForbiddenScope gc_forbidden_scope(test->state());
-  test->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
-
-  // Starting an IncrementalV8FollowupGC while GC is forbidden should do
-  // nothing.
-  EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
-  EXPECT_EQ(0, GCCount());
-}
-
-TEST_F(ThreadStateSchedulingTest, RunIncrementalV8FollowupGC) {
-  ThreadStateSchedulingTest* test = this;
-
-  EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
-  test->state()->ScheduleIncrementalGC(
-      BlinkGC::GCReason::kIncrementalV8FollowupGC);
-  EXPECT_EQ(ThreadState::kIncrementalGCScheduled, test->state()->GetGCState());
-
-  test->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
-
-  EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
-            test->state()->GetGCState());
+  // Check that incremental GC hasn't been run.
+  RunLoop();
+  EXPECT_EQ(1, test->GCCount());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/unified_heap_controller.cc b/third_party/blink/renderer/platform/heap/unified_heap_controller.cc
index 9df99b39..e9f8c191 100644
--- a/third_party/blink/renderer/platform/heap/unified_heap_controller.cc
+++ b/third_party/blink/renderer/platform/heap/unified_heap_controller.cc
@@ -50,13 +50,12 @@
       BlinkGC::kConcurrentAndLazySweeping,
       thread_state_->current_gc_data_.reason);
 
-  // Reset any previously scheduled garbage collections.
   thread_state_->SetGCState(ThreadState::kNoGCScheduled);
   BlinkGC::GCReason gc_reason =
       (v8_flags & v8::EmbedderHeapTracer::TraceFlags::kReduceMemory)
           ? BlinkGC::GCReason::kUnifiedHeapForMemoryReductionGC
           : BlinkGC::GCReason::kUnifiedHeapGC;
-  thread_state_->IncrementalMarkingStart(gc_reason);
+  thread_state_->StartIncrementalMarking(gc_reason);
 
   is_tracing_done_ = false;
 }
diff --git a/third_party/blink/renderer/platform/lifecycle_context_test.cc b/third_party/blink/renderer/platform/lifecycle_context_test.cc
index 3f45f763..94a9886 100644
--- a/third_party/blink/renderer/platform/lifecycle_context_test.cc
+++ b/third_party/blink/renderer/platform/lifecycle_context_test.cc
@@ -29,7 +29,9 @@
 #include "third_party/blink/public/common/features.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"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/lifecycle_notifier.h"
 #include "third_party/blink/renderer/platform/lifecycle_observer.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
@@ -149,8 +151,8 @@
       {blink::features::kBlinkHeapIncrementalMarking},
       {blink::features::kBlinkHeapConcurrentMarking,
        blink::features::kBlinkHeapConcurrentSweeping});
-  ThreadState* thread_state = ThreadState::Current();
-  thread_state->IncrementalMarkingStart(BlinkGC::GCReason::kForcedGCForTesting);
+  IncrementalMarkingTestDriver driver(ThreadState::Current());
+  driver.Start();
 
   auto* context = MakeGarbageCollected<DummyContext>();
 
@@ -163,10 +165,7 @@
   EXPECT_TRUE(observer->ContextDestroyedCalled());
   context = nullptr;
 
-  while (thread_state->GetGCState() ==
-         ThreadState::kIncrementalMarkingStepScheduled)
-    thread_state->IncrementalMarkingStep(BlinkGC::kNoHeapPointersOnStack);
-  thread_state->IncrementalMarkingFinalize();
+  driver.FinishGC();
 }
 
 TEST(LifecycleContextTest, ForEachObserver) {
diff --git a/third_party/blink/renderer/platform/mojo/bluetooth_mojom_traits.h b/third_party/blink/renderer/platform/mojo/bluetooth_mojom_traits.h
index cde9a60..b970277 100644
--- a/third_party/blink/renderer/platform/mojo/bluetooth_mojom_traits.h
+++ b/third_party/blink/renderer/platform/mojo/bluetooth_mojom_traits.h
@@ -7,12 +7,14 @@
 
 #include "device/bluetooth/public/mojom/uuid.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-blink.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace mojo {
 
 template <>
-struct StructTraits<::blink::mojom::WebBluetoothDeviceIdDataView, WTF::String> {
+struct PLATFORM_EXPORT
+    StructTraits<::blink::mojom::WebBluetoothDeviceIdDataView, WTF::String> {
   static const WTF::String& device_id(const WTF::String& input) {
     return input;
   }
@@ -22,7 +24,8 @@
 };
 
 template <>
-struct StructTraits<bluetooth::mojom::UUIDDataView, WTF::String> {
+struct PLATFORM_EXPORT
+    StructTraits<bluetooth::mojom::UUIDDataView, WTF::String> {
   static const WTF::String& uuid(const WTF::String& input) { return input; }
 
   static bool Read(bluetooth::mojom::UUIDDataView, WTF::String* output);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 4e48008..a28fc2b 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1774,7 +1774,6 @@
     },
     {
       name: "WebVR",
-      status: "test",
     },
     {
       name: "WebVTTRegions",
diff --git a/third_party/blink/renderer/platform/transforms/identity_transform_operation.h b/third_party/blink/renderer/platform/transforms/identity_transform_operation.h
index b3d2cab..f21acfb6 100644
--- a/third_party/blink/renderer/platform/transforms/identity_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/identity_transform_operation.h
@@ -49,6 +49,12 @@
 
   void Apply(TransformationMatrix&, const FloatSize&) const override {}
 
+  scoped_refptr<TransformOperation> Accumulate(
+      const TransformOperation& other) override {
+    NOTREACHED();
+    return this;
+  }
+
   scoped_refptr<TransformOperation> Blend(
       const TransformOperation*,
       double progress,
diff --git a/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h b/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h
index 5b82a7d3..341b2748 100644
--- a/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h
@@ -60,6 +60,12 @@
   void Apply(TransformationMatrix&,
              const FloatSize& border_box_size) const override;
 
+  scoped_refptr<TransformOperation> Accumulate(
+      const TransformOperation&) override {
+    NOTREACHED();
+    return this;
+  }
+
   scoped_refptr<TransformOperation> Blend(
       const TransformOperation* from,
       double progress,
diff --git a/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc b/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc
index 50bb4cf..1ee49fe 100644
--- a/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc
+++ b/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc
@@ -1,5 +1,3 @@
-
-
 /*
  * Copyright (C) 2009 Apple Inc. All rights reserved.
  *
@@ -27,10 +25,70 @@
 
 #include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
 
+#include "third_party/blink/renderer/platform/transforms/rotation.h"
+#include "ui/gfx/geometry/quaternion.h"
+
 #include <algorithm>
 
 namespace blink {
 
+scoped_refptr<TransformOperation> Matrix3DTransformOperation::Accumulate(
+    const TransformOperation& other_op) {
+  DCHECK(other_op.IsSameType(*this));
+  const auto& other = ToMatrix3DTransformOperation(other_op);
+
+  // If either matrix is non-invertible, fail and fallback to replace.
+  if (!matrix_.IsInvertible() || !other.matrix_.IsInvertible())
+    return nullptr;
+
+  // Similar to interpolation, accumulating 3D matrices is done by decomposing
+  // them, accumulating the individual functions, and then recomposing.
+
+  TransformationMatrix::DecomposedType from_decomp;
+  TransformationMatrix::DecomposedType to_decomp;
+  if (!matrix_.Decompose(from_decomp) || !other.matrix_.Decompose(to_decomp))
+    return nullptr;
+
+  // Scale is accumulated using 1-based addition.
+  from_decomp.scale_x += to_decomp.scale_x - 1;
+  from_decomp.scale_y += to_decomp.scale_y - 1;
+  from_decomp.scale_z += to_decomp.scale_z - 1;
+
+  // Skew can be added.
+  from_decomp.skew_xy += to_decomp.skew_xy;
+  from_decomp.skew_xz += to_decomp.skew_xz;
+  from_decomp.skew_yz += to_decomp.skew_yz;
+
+  // To accumulate quaternions, we multiply them. This is equivalent to 'adding'
+  // the rotations that they represent.
+  gfx::Quaternion from_quaternion(
+      from_decomp.quaternion_x, from_decomp.quaternion_y,
+      from_decomp.quaternion_z, from_decomp.quaternion_w);
+  gfx::Quaternion to_quaternion(to_decomp.quaternion_x, to_decomp.quaternion_y,
+                                to_decomp.quaternion_z, to_decomp.quaternion_w);
+
+  gfx::Quaternion result_quaternion = from_quaternion * to_quaternion;
+  from_decomp.quaternion_x = result_quaternion.x();
+  from_decomp.quaternion_y = result_quaternion.y();
+  from_decomp.quaternion_z = result_quaternion.z();
+  from_decomp.quaternion_w = result_quaternion.w();
+
+  // Translate is a simple addition.
+  from_decomp.translate_x += to_decomp.translate_x;
+  from_decomp.translate_y += to_decomp.translate_y;
+  from_decomp.translate_z += to_decomp.translate_z;
+
+  // We sum the perspective components; note that w is 1-based.
+  from_decomp.perspective_x += to_decomp.perspective_x;
+  from_decomp.perspective_y += to_decomp.perspective_y;
+  from_decomp.perspective_z += to_decomp.perspective_z;
+  from_decomp.perspective_w += to_decomp.perspective_w - 1;
+
+  TransformationMatrix result;
+  result.Recompose(from_decomp);
+  return Matrix3DTransformOperation::Create(result);
+}
+
 scoped_refptr<TransformOperation> Matrix3DTransformOperation::Blend(
     const TransformOperation* from,
     double progress,
diff --git a/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h b/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h
index c5fd803..9c61b126 100644
--- a/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h
@@ -63,6 +63,9 @@
     transform.Multiply(TransformationMatrix(matrix_));
   }
 
+  scoped_refptr<TransformOperation> Accumulate(
+      const TransformOperation& other) override;
+
   scoped_refptr<TransformOperation> Blend(
       const TransformOperation* from,
       double progress,
diff --git a/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc b/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc
index 8cf0648..7ac6a31 100644
--- a/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc
+++ b/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc
@@ -25,6 +25,41 @@
 
 namespace blink {
 
+scoped_refptr<TransformOperation> MatrixTransformOperation::Accumulate(
+    const TransformOperation& other_op) {
+  DCHECK(other_op.IsSameType(*this));
+  const MatrixTransformOperation& other = ToMatrixTransformOperation(other_op);
+
+  TransformationMatrix from_t(other.a_, other.b_, other.c_, other.d_, other.e_,
+                              other.f_);
+  TransformationMatrix to_t(a_, b_, c_, d_, e_, f_);
+
+  // If either matrix is non-invertible, fail and fallback to replace.
+  if (!from_t.IsInvertible() || !to_t.IsInvertible())
+    return nullptr;
+
+  // Similar to interpolation, accumulating matrices is done by decomposing
+  // them, accumulating the individual functions, and then recomposing.
+
+  TransformationMatrix::Decomposed2dType from_decomp;
+  TransformationMatrix::Decomposed2dType to_decomp;
+  if (!from_t.Decompose2D(from_decomp) || !to_t.Decompose2D(to_decomp)) {
+    return nullptr;
+  }
+
+  // For a 2D matrix, the components can just be naively summed, noting that
+  // scale uses 1-based addition.
+  from_decomp.scale_x += to_decomp.scale_x - 1;
+  from_decomp.scale_y += to_decomp.scale_y - 1;
+  from_decomp.skew_xy += to_decomp.skew_xy;
+  from_decomp.translate_x += to_decomp.translate_x;
+  from_decomp.translate_y += to_decomp.translate_y;
+  from_decomp.angle += to_decomp.angle;
+
+  from_t.Recompose2D(from_decomp);
+  return MatrixTransformOperation::Create(from_t);
+}
+
 scoped_refptr<TransformOperation> MatrixTransformOperation::Blend(
     const TransformOperation* from,
     double progress,
diff --git a/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h b/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h
index 2c3c43a3..bb943c2 100644
--- a/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h
@@ -77,6 +77,9 @@
     transform.Multiply(matrix);
   }
 
+  scoped_refptr<TransformOperation> Accumulate(
+      const TransformOperation&) override;
+
   scoped_refptr<TransformOperation> Blend(
       const TransformOperation* from,
       double progress,
diff --git a/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc b/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc
index a5753ed..7a491ab 100644
--- a/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc
+++ b/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc
@@ -30,6 +30,23 @@
 
 namespace blink {
 
+scoped_refptr<TransformOperation> PerspectiveTransformOperation::Accumulate(
+    const TransformOperation& other) {
+  DCHECK(other.IsSameType(*this));
+  double other_p = ToPerspectiveTransformOperation(other).p_;
+
+  if (p_ == 0 && other_p == 0)
+    return nullptr;
+
+  // We want to solve:
+  //   -1/p + -1/p' == -1/p'', where we know p and p'.
+  //
+  // This can be rewritten as:
+  //   p'' == (p * p') / (p + p')
+  double p = (p_ * other_p) / (p_ + other_p);
+  return PerspectiveTransformOperation::Create(p);
+}
+
 scoped_refptr<TransformOperation> PerspectiveTransformOperation::Blend(
     const TransformOperation* from,
     double progress,
@@ -62,7 +79,7 @@
 
   if (decomp.perspective_z) {
     double val = -1.0 / decomp.perspective_z;
-    return PerspectiveTransformOperation::Create(clampTo<int>(val, 0));
+    return PerspectiveTransformOperation::Create(clampTo<double>(val, 0));
   }
   return PerspectiveTransformOperation::Create(0);
 }
diff --git a/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h b/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h
index 9ae1a18..246286504 100644
--- a/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h
@@ -62,6 +62,8 @@
     transform.ApplyPerspective(p_);
   }
 
+  scoped_refptr<TransformOperation> Accumulate(
+      const TransformOperation& other) override;
   scoped_refptr<TransformOperation> Blend(
       const TransformOperation* from,
       double progress,
diff --git a/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc b/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc
index f02ea5f9..4910951 100644
--- a/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc
+++ b/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc
@@ -24,6 +24,20 @@
 #include "third_party/blink/renderer/platform/geometry/blend.h"
 
 namespace blink {
+namespace {
+TransformOperation::OperationType GetTypeForRotation(const Rotation& rotation) {
+  float x = rotation.axis.X();
+  float y = rotation.axis.Y();
+  float z = rotation.axis.Z();
+  if (x && !y && !z)
+    return TransformOperation::kRotateX;
+  if (y && !x && !z)
+    return TransformOperation::kRotateY;
+  if (z && !x && !y)
+    return TransformOperation::kRotateZ;
+  return TransformOperation::kRotate3D;
+}
+}  // namespace
 
 bool RotateTransformOperation::operator==(
     const TransformOperation& other) const {
@@ -44,6 +58,15 @@
                                  result_angle_a, result_angle_b);
 }
 
+scoped_refptr<TransformOperation> RotateTransformOperation::Accumulate(
+    const TransformOperation& other) {
+  DCHECK(IsMatchingOperationType(other.GetType()));
+  Rotation new_rotation =
+      Rotation::Add(rotation_, ToRotateTransformOperation(other).rotation_);
+  return RotateTransformOperation::Create(new_rotation,
+                                          GetTypeForRotation(new_rotation));
+}
+
 scoped_refptr<TransformOperation> RotateTransformOperation::Blend(
     const TransformOperation* from,
     double progress,
diff --git a/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h b/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h
index 2fa68f2..d5f7bdd8 100644
--- a/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h
@@ -90,6 +90,8 @@
     return Angle() && (X() || Y());
   }
 
+  scoped_refptr<TransformOperation> Accumulate(
+      const TransformOperation& other) override;
   scoped_refptr<TransformOperation> Blend(
       const TransformOperation* from,
       double progress,
diff --git a/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc b/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc
index 24a0884..0f326e4 100644
--- a/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc
+++ b/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc
@@ -24,6 +24,40 @@
 #include "third_party/blink/renderer/platform/geometry/blend.h"
 
 namespace blink {
+namespace {
+// Return the correct OperationType for a given scale.
+TransformOperation::OperationType GetTypeForScale(double x,
+                                                  double y,
+                                                  double z) {
+  // Note: purely due to ordering, we will convert scale(1, 1, 1) to kScaleX.
+  // This is fine; they are equivalent.
+
+  if (z != 1)
+    return TransformOperation::kScale3D;
+
+  if (y == 1)
+    return TransformOperation::kScaleX;
+
+  if (x == 1)
+    return TransformOperation::kScaleY;
+
+  // Both x and y are non-1, so a 2D scale.
+  return TransformOperation::kScale;
+}
+}  // namespace
+
+scoped_refptr<TransformOperation> ScaleTransformOperation::Accumulate(
+    const TransformOperation& other) {
+  DCHECK(other.CanBlendWith(*this));
+  const auto& other_op = ToScaleTransformOperation(other);
+  // Scale parameters are one in the identity transform function so use
+  // accumulation for one-based values.
+  double new_x = x_ + other_op.x_ - 1;
+  double new_y = y_ + other_op.y_ - 1;
+  double new_z = z_ + other_op.z_ - 1;
+  return ScaleTransformOperation::Create(new_x, new_y, new_z,
+                                         GetTypeForScale(new_x, new_y, new_z));
+}
 
 scoped_refptr<TransformOperation> ScaleTransformOperation::Blend(
     const TransformOperation* from,
diff --git a/third_party/blink/renderer/platform/transforms/scale_transform_operation.h b/third_party/blink/renderer/platform/transforms/scale_transform_operation.h
index 9ad90334..874e3ef 100644
--- a/third_party/blink/renderer/platform/transforms/scale_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/scale_transform_operation.h
@@ -58,6 +58,8 @@
   void Apply(TransformationMatrix& transform, const FloatSize&) const override {
     transform.Scale3d(x_, y_, z_);
   }
+  scoped_refptr<TransformOperation> Accumulate(
+      const TransformOperation& other) override;
   scoped_refptr<TransformOperation> Blend(
       const TransformOperation* from,
       double progress,
diff --git a/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc b/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc
index bf95584..15eeabb 100644
--- a/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc
+++ b/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc
@@ -25,6 +25,14 @@
 
 namespace blink {
 
+scoped_refptr<TransformOperation> SkewTransformOperation::Accumulate(
+    const TransformOperation& other) {
+  DCHECK(other.CanBlendWith(*this));
+  const SkewTransformOperation& skew_other = ToSkewTransformOperation(other);
+  return SkewTransformOperation::Create(angle_x_ + skew_other.angle_x_,
+                                        angle_y_ + skew_other.angle_y_, type_);
+}
+
 scoped_refptr<TransformOperation> SkewTransformOperation::Blend(
     const TransformOperation* from,
     double progress,
diff --git a/third_party/blink/renderer/platform/transforms/skew_transform_operation.h b/third_party/blink/renderer/platform/transforms/skew_transform_operation.h
index e638e60..368ddfc 100644
--- a/third_party/blink/renderer/platform/transforms/skew_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/skew_transform_operation.h
@@ -62,6 +62,8 @@
     transform.Skew(angle_x_, angle_y_);
   }
 
+  scoped_refptr<TransformOperation> Accumulate(
+      const TransformOperation& other) override;
   scoped_refptr<TransformOperation> Blend(
       const TransformOperation* from,
       double progress,
diff --git a/third_party/blink/renderer/platform/transforms/transform_operation.h b/third_party/blink/renderer/platform/transforms/transform_operation.h
index ad257d3..6ccfd1d7 100644
--- a/third_party/blink/renderer/platform/transforms/transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/transform_operation.h
@@ -74,6 +74,11 @@
   virtual void Apply(TransformationMatrix&,
                      const FloatSize& border_box_size) const = 0;
 
+  // Implements the accumulative behavior described in
+  // https://drafts.csswg.org/css-transforms-2/#combining-transform-lists
+  virtual scoped_refptr<TransformOperation> Accumulate(
+      const TransformOperation& other) = 0;
+
   virtual scoped_refptr<TransformOperation> Blend(
       const TransformOperation* from,
       double progress,
diff --git a/third_party/blink/renderer/platform/transforms/transform_operations.cc b/third_party/blink/renderer/platform/transforms/transform_operations.cc
index b511af3..8e5477f 100644
--- a/third_party/blink/renderer/platform/transforms/transform_operations.cc
+++ b/third_party/blink/renderer/platform/transforms/transform_operations.cc
@@ -28,9 +28,51 @@
 #include "third_party/blink/renderer/platform/transforms/interpolated_transform_operation.h"
 #include "third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h"
 #include "third_party/blink/renderer/platform/transforms/rotate_transform_operation.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
 
+namespace {
+using ApplyCallback = base::RepeatingCallback<scoped_refptr<TransformOperation>(
+    const scoped_refptr<TransformOperation>& from,
+    const scoped_refptr<TransformOperation>& to)>;
+
+// Applies a given function (|ApplyCallback|) to matching pairs of operations.
+TransformOperations ApplyFunctionToMatchingPrefix(
+    ApplyCallback apply_cb,
+    const TransformOperations& from,
+    const TransformOperations& to,
+    wtf_size_t matching_prefix_length,
+    bool* success) {
+  TransformOperations result;
+  wtf_size_t from_size = from.Operations().size();
+  wtf_size_t to_size = to.Operations().size();
+
+  // If the lists matched entirely but one was shorter, |matching_prefix_length|
+  // will be the length of the longer list and we implicitly consider the
+  // missing functions to be matching identity operations.
+  DCHECK(matching_prefix_length <= std::max(from_size, to_size));
+
+  for (wtf_size_t i = 0; i < matching_prefix_length; i++) {
+    scoped_refptr<TransformOperation> from_operation =
+        (i < from_size) ? from.Operations()[i].get() : nullptr;
+    scoped_refptr<TransformOperation> to_operation =
+        (i < to_size) ? to.Operations()[i].get() : nullptr;
+
+    scoped_refptr<TransformOperation> result_operation =
+        apply_cb.Run(from_operation, to_operation);
+
+    if (result_operation) {
+      result.Operations().push_back(result_operation);
+    } else {
+      *success = false;
+      return result;
+    }
+  }
+  return result;
+}
+}  // namespace
+
 TransformOperations::TransformOperations(bool make_identity) {
   if (make_identity)
     operations_.push_back(IdentityTransformOperation::Create());
@@ -75,36 +117,6 @@
   return std::max(Operations().size(), other.Operations().size());
 }
 
-TransformOperations TransformOperations::BlendPrefixByMatchingOperations(
-    const TransformOperations& from,
-    wtf_size_t matching_prefix_length,
-    double progress,
-    bool* success) const {
-  TransformOperations result;
-  wtf_size_t from_size = from.Operations().size();
-  wtf_size_t to_size = Operations().size();
-  for (wtf_size_t i = 0; i < matching_prefix_length; i++) {
-    scoped_refptr<TransformOperation> from_operation =
-        (i < from_size) ? from.Operations()[i].get() : nullptr;
-    scoped_refptr<TransformOperation> to_operation =
-        (i < to_size) ? Operations()[i].get() : nullptr;
-
-    scoped_refptr<TransformOperation> blended_operation =
-        to_operation
-            ? to_operation->Blend(from_operation.get(), progress)
-            : (from_operation ? from_operation->Blend(nullptr, progress, true)
-                              : nullptr);
-
-    if (blended_operation)
-      result.Operations().push_back(blended_operation);
-    else {
-      *success = false;
-      return result;
-    }
-  }
-  return result;
-}
-
 scoped_refptr<TransformOperation>
 TransformOperations::BlendRemainingByUsingMatrixInterpolation(
     const TransformOperations& from,
@@ -146,8 +158,17 @@
       std::max(Operations().size(), from.Operations().size());
 
   bool success = true;
-  TransformOperations result = BlendPrefixByMatchingOperations(
-      from, matching_prefix_length, progress, &success);
+  TransformOperations result = ApplyFunctionToMatchingPrefix(
+      WTF::BindRepeating(
+          [](double progress, const scoped_refptr<TransformOperation>& from,
+             const scoped_refptr<TransformOperation>& to) {
+            // Where the lists matched but one was longer, the shorter list is
+            // padded with nullptr that represent matching identity operations.
+            return to ? to->Blend(from.get(), progress)
+                      : (from ? from->Blend(nullptr, progress, true) : nullptr);
+          },
+          progress),
+      from, *this, matching_prefix_length, &success);
   if (success && matching_prefix_length < max_path_length) {
     scoped_refptr<TransformOperation> matrix_op =
         BlendRemainingByUsingMatrixInterpolation(from, matching_prefix_length,
@@ -163,6 +184,54 @@
   return result;
 }
 
+TransformOperations TransformOperations::Accumulate(
+    const TransformOperations& to) const {
+  if (!to.size() && !size())
+    return *this;
+
+  bool success = true;
+  wtf_size_t matching_prefix_length = MatchingPrefixLength(to);
+  wtf_size_t max_path_length =
+      std::max(Operations().size(), to.Operations().size());
+
+  // Accumulate matching pairs of transform functions.
+  TransformOperations result = ApplyFunctionToMatchingPrefix(
+      WTF::BindRepeating([](const scoped_refptr<TransformOperation>& from,
+                            const scoped_refptr<TransformOperation>& to) {
+        if (to && from)
+          return from->Accumulate(*to);
+        // Where the lists matched but one was longer, the shorter list is
+        // padded with nullptr that represent matching identity operations. For
+        // any function, accumulate(f, identity) == f, so just return f.
+        return to ? to : from;
+      }),
+      *this, to, matching_prefix_length, &success);
+
+  // Then, if there are leftover non-matching functions, accumulate the
+  // remaining matrices.
+  if (success && matching_prefix_length < max_path_length) {
+    TransformationMatrix from_transform;
+    TransformationMatrix to_transform;
+    ApplyRemaining(FloatSize(), matching_prefix_length, from_transform);
+    to.ApplyRemaining(FloatSize(), matching_prefix_length, to_transform);
+
+    scoped_refptr<TransformOperation> from_matrix =
+        Matrix3DTransformOperation::Create(from_transform);
+    scoped_refptr<TransformOperation> to_matrix =
+        Matrix3DTransformOperation::Create(to_transform);
+    scoped_refptr<TransformOperation> matrix_op =
+        from_matrix->Accumulate(*to_matrix);
+
+    if (matrix_op)
+      result.Operations().push_back(matrix_op);
+    else
+      success = false;
+  }
+
+  // On failure, behavior is to replace.
+  return success ? result : to;
+}
+
 static void FindCandidatesInPlane(double px,
                                   double py,
                                   double nz,
diff --git a/third_party/blink/renderer/platform/transforms/transform_operations.h b/third_party/blink/renderer/platform/transforms/transform_operations.h
index cbe4e449..4e4978b 100644
--- a/third_party/blink/renderer/platform/transforms/transform_operations.h
+++ b/third_party/blink/renderer/platform/transforms/transform_operations.h
@@ -139,11 +139,6 @@
                            const double& max_progress,
                            FloatBox* bounds) const;
 
-  TransformOperations BlendPrefixByMatchingOperations(
-      const TransformOperations& from,
-      wtf_size_t matching_prefix_length,
-      double progress,
-      bool* success) const;
   scoped_refptr<TransformOperation> BlendRemainingByUsingMatrixInterpolation(
       const TransformOperations& from,
       wtf_size_t matching_prefix_length,
@@ -154,6 +149,10 @@
   TransformOperations Add(const TransformOperations& addend) const;
   TransformOperations Zoom(double factor) const;
 
+  // Perform accumulation of |to| onto |this|, as specified in
+  // https://drafts.csswg.org/css-transforms-2/#combining-transform-lists
+  TransformOperations Accumulate(const TransformOperations& to) const;
+
  private:
   Vector<scoped_refptr<TransformOperation>> operations_;
 };
diff --git a/third_party/blink/renderer/platform/transforms/transform_operations_test.cc b/third_party/blink/renderer/platform/transforms/transform_operations_test.cc
index ee743c30..3120462 100644
--- a/third_party/blink/renderer/platform/transforms/transform_operations_test.cc
+++ b/third_party/blink/renderer/platform/transforms/transform_operations_test.cc
@@ -380,9 +380,9 @@
 
   from_ops.BlendedBoundsForBox(box, to_ops, -0.25, 1.25, &bounds);
   // The perspective range was [20, 40] and blending will extrapolate that to
-  // [17, 53].  The cube has w/h/d of 10 and the observer is at 17, so the face
-  // closest the observer is 17-10=7.
-  double projected_size = 10.0 / 7.0 * 17.0;
+  // [17.777..., 53.333...].  The cube has w/h/d of 10 and the observer is at
+  // 17.777..., so the face closest the observer is 17.777...-10=7.777...
+  double projected_size = 10.0 / 7.7778 * 17.7778;
   EXPECT_PRED_FORMAT2(
       float_box_test::AssertAlmostEqual,
       FloatBox(0, 0, 0, projected_size, projected_size, projected_size),
diff --git a/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc b/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc
index e7280ef..86fc8ff 100644
--- a/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc
+++ b/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc
@@ -22,9 +22,54 @@
 #include "third_party/blink/renderer/platform/transforms/translate_transform_operation.h"
 
 #include "third_party/blink/renderer/platform/geometry/blend.h"
+#include "third_party/blink/renderer/platform/geometry/calculation_value.h"
 
 namespace blink {
 
+namespace {
+Length AddLengths(const Length& lhs, const Length& rhs) {
+  PixelsAndPercent lhs_pap = lhs.GetPixelsAndPercent();
+  PixelsAndPercent rhs_pap = rhs.GetPixelsAndPercent();
+
+  PixelsAndPercent result = PixelsAndPercent(lhs_pap.pixels + rhs_pap.pixels,
+                                             lhs_pap.percent + rhs_pap.percent);
+  if (result.percent == 0)
+    return Length(result.pixels, Length::kFixed);
+  if (result.pixels == 0)
+    return Length(result.percent, Length::kPercent);
+  return Length(CalculationValue::Create(result, kValueRangeAll));
+}
+
+TransformOperation::OperationType GetTypeForTranslate(const Length& x,
+                                                      const Length& y,
+                                                      double z) {
+  bool x_zero = x.IsZero();
+  bool y_zero = x.IsZero();
+  bool z_zero = !z;
+  if (!x_zero && !y_zero && !z_zero)
+    return TransformOperation::kTranslate3D;
+  if (y_zero && z_zero)
+    return TransformOperation::kTranslateX;
+  if (x_zero && z_zero)
+    return TransformOperation::kTranslateY;
+  if (x_zero && y_zero)
+    return TransformOperation::kTranslateZ;
+  return TransformOperation::kTranslate;
+}
+}  // namespace
+
+scoped_refptr<TransformOperation> TranslateTransformOperation::Accumulate(
+    const TransformOperation& other) {
+  DCHECK(other.CanBlendWith(*this));
+
+  const auto& other_op = ToTranslateTransformOperation(other);
+  Length new_x = AddLengths(x_, other_op.x_);
+  Length new_y = AddLengths(y_, other_op.y_);
+  double new_z = z_ + other_op.z_;
+  return TranslateTransformOperation::Create(
+      new_x, new_y, new_z, GetTypeForTranslate(new_x, new_y, new_z));
+}
+
 scoped_refptr<TransformOperation> TranslateTransformOperation::Blend(
     const TransformOperation* from,
     double progress,
diff --git a/third_party/blink/renderer/platform/transforms/translate_transform_operation.h b/third_party/blink/renderer/platform/transforms/translate_transform_operation.h
index 6ead517a..b73cb80 100644
--- a/third_party/blink/renderer/platform/transforms/translate_transform_operation.h
+++ b/third_party/blink/renderer/platform/transforms/translate_transform_operation.h
@@ -91,6 +91,8 @@
     return x_ == t->x_ && y_ == t->y_ && z_ == t->z_;
   }
 
+  scoped_refptr<TransformOperation> Accumulate(
+      const TransformOperation& other) override;
   scoped_refptr<TransformOperation> Blend(
       const TransformOperation* from,
       double progress,
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations
index b8844a5..5c936351 100644
--- a/third_party/blink/web_tests/MSANExpectations
+++ b/third_party/blink/web_tests/MSANExpectations
@@ -84,6 +84,8 @@
 crbug.com/902685 [ Linux ] http/tests/devtools/isolated-code-cache/cross-origin-test.js [ Pass Timeout ]
 crbug.com/902685 [ Linux ] virtual/site-isolated-code-cache/http/tests/devtools/isolated-code-cache/same-origin-test.js [ Pass Timeout ]
 crbug.com/902685 [ Linux ] virtual/site-isolated-code-cache/http/tests/devtools/isolated-code-cache/cross-origin-test.js [ Pass Timeout ]
+crbug.com/902685 [ Linux ] virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/same-origin-test.js [ Pass Timeout ]
+crbug.com/902685 [ Linux ] virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/cross-origin-test.js [ Pass Timeout ]
 
 # Timing out consistenly on WebKit Linux Trusty MSAN
 crbug.com/798957 [ Linux ] http/tests/devtools/audits/audits-limited-run.js [ Skip ]
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index fc8458b..c2f76dd 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -86,6 +86,7 @@
 crbug.com/902685 virtual/not-site-per-process/http/tests/devtools/isolated-code-cache/ [ Slow ]
 crbug.com/902685 http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Slow ]
 crbug.com/902685 virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Slow ]
+crbug.com/902685 virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/ [ Slow ]
 
 # Misc DevTools tests that are slow
 crbug.com/246190 [ Release ] http/tests/devtools/indexeddb/ [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 40ec7eb9..807878c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2076,10 +2076,6 @@
 # These are the failing tests because Chrome hasn't implemented according to the spec.
 crbug.com/645988 external/wpt/uievents/order-of-events/focus-events/focus-manual.html [ Failure ]
 
-# TODO(https://crbug.com/960132): Remove these when WebVR is removed.
-crbug.com/807152 vr/VRDisplay_rAF_fires_with_window_rAF.html [ Pass Failure ]
-crbug.com/813697 vr/getFrameData_oneframeupdate.html [ Pass Failure ]
-
 crbug.com/346473  fast/events/drag-on-mouse-move-cancelled.html [ Failure ]
 crbug.com/346473  virtual/mouseevent_fractional/fast/events/drag-on-mouse-move-cancelled.html [ Failure ]
 
@@ -5553,21 +5549,6 @@
 
 crbug.com/986019 virtual/threaded/animations/play-state.html [ Pass Failure ]
 
-crbug.com/914134 [ Mac ] vr/events_vrdisplayactivate.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/events_vrdisplaypresentchange.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/exitPresent_resolve.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/getLayers_notpresenting.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/getLayers_presenting.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/getLayers_presenting_nondefaultbounds.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/getLayers_update.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/requestAnimationFrame_consistentTimestamps.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/requestAnimationFrame_handoff.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/requestAnimationFrame_submitFrame_combinations.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/requestPresent_resolve.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/requestPresent_resolve_repeatwithgesture.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/requestPresent_resolve_repeatwithoutgesture.html [ Failure ]
-crbug.com/914134 [ Mac ] vr/requestPresent_resolve_then_reject.html [ Failure ]
-
 crbug.com/996219 [ Win ] fast/text/emoji-vertical-origin-visual.html [ Failure ]
 
 crbug.com/996597 [ Win ] css2.1/t0905-c5525-flthw-00-c-g.html [ Failure ]
@@ -5714,11 +5695,6 @@
 crbug.com/1013523 [ Release Linux Mac ] external/wpt/html/cross-origin-embedder-policy/require-corp.https.html [ Failure Pass ]
 crbug.com/1010472 [ Linux Mac Debug ] virtual/disable-deferred-rendering/fast/canvas/color-space/canvas-drawImage-offscreenCanvas.html [ Timeout Pass ]
 crbug.com/1010472 [ Mac Debug ] virtual/disable-deferred-rendering/fast/canvas/OffscreenCanvas-placeholder-createImageBitmap.html [ Failure Pass ]
-crbug.com/1013779 virtual/scalefactor200/fast/hidpi/static/calendar-picker-appearance.html [ Failure Pass ]
-crbug.com/1013779 [ Mac ] virtual/scalefactor200/fast/hidpi/static/data-suggestion-picker-appearance.html [ Failure Pass ]
-crbug.com/1013779 virtual/scalefactor200/fast/hidpi/static/pointerevents/pointerevent_touch-adjustment_click_target.html [ Failure Pass ]
-crbug.com/1013779 virtual/scalefactor200/fast/hidpi/static/popup-menu-appearance.html [ Failure Pass ]
-crbug.com/1013779 virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi.html [ Failure Pass ]
 crbug.com/990900 external/wpt/cookie-store/idlharness.tentative.https.any.serviceworker.html [ Pass Timeout ]
 crbug.com/990900 virtual/omt-worker-fetch/external/wpt/fetch/api/idlharness.any.html [ Pass Timeout ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 7b0a3ac..7246d1d 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -762,9 +762,16 @@
              "--site-per-process"]
   },
   {
+    "prefix": "split-http-cache-not-site-per-process",
+    "base": "http/tests/devtools/isolated-code-cache",
+    "args": ["--enable-features=SplitCacheByNetworkIsolationKey",
+             "--disable-site-isolation-trials"]
+  },
+  {
     "prefix": "not-site-per-process",
     "base": "http/tests/devtools/isolated-code-cache",
-    "args": ["--disable-site-isolation-trials"]
+    "args": ["--disable-site-isolation-trials",
+             "--disable-features=SplitCacheByNetworkIsolationKey"]
   },
   {
     "prefix": "not-site-per-process",
diff --git a/third_party/blink/web_tests/W3CImportExpectations b/third_party/blink/web_tests/W3CImportExpectations
index ec8f561..640928f 100644
--- a/third_party/blink/web_tests/W3CImportExpectations
+++ b/third_party/blink/web_tests/W3CImportExpectations
@@ -474,3 +474,12 @@
 external/wpt/webdriver/tests/new_session/invalid_capabilities.py [ Skip ]
 external/wpt/webdriver/tests/new_session/response.py [ Skip ]
 external/wpt/webdriver/tests/send_alert_text/send.py [ Skip ]
+
+# WebVr was never supported on Chrome more than as an experimental feature,
+# and even that level of support is now removed. WebVr was replaced with WebXr.
+external/wpt/webvr [ Skip ]
+external/wpt/feature-policy/resources/feature-policy-webvr.html [ Skip ]
+external/wpt/feature-policy/reporting/vr-reporting.https.html [ Skip ]
+external/wpt/feature-policy/reporting/vr-reporting.https.html.headers [ Skip ]
+external/wpt/feature-policy/reporting/vr-report-only.https.html [ Skip ]
+external/wpt/feature-policy/reporting/vr-report-only.https.html.headers [ Skip ]
diff --git a/third_party/blink/web_tests/animations/composition/transform-composition.html b/third_party/blink/web_tests/animations/composition/transform-composition.html
index ae22f3c..1522cf0f3 100644
--- a/third_party/blink/web_tests/animations/composition/transform-composition.html
+++ b/third_party/blink/web_tests/animations/composition/transform-composition.html
@@ -3,50 +3,11 @@
 <body>
 <script src="../interpolation/resources/interpolation-test.js"></script>
 <script>
-assertComposition({
-  property: 'transform',
-  underlying: 'translate(10px, 20px)',
-  addFrom: 'translate(100px, 200px)',
-  addTo: 'translate(200px, 400px)',
-}, [
-  {at: -0.5, is: 'translate(60px, 120px)'},
-  {at: 0, is: 'translate(110px, 220px)'},
-  {at: 0.25, is: 'translate(135px, 270px)'},
-  {at: 0.5, is: 'translate(160px, 320px)'},
-  {at: 0.75, is: 'translate(185px, 370px)'},
-  {at: 1, is: 'translate(210px, 420px)'},
-  {at: 1.5, is: 'translate(260px, 520px)'},
-]);
+// This file contains tests for the composition behavior of transforms that is
+// unrelated to the individual transform functions. For the transform functions
+// themselves, see the transform-*-composition.html subtests.
 
-assertComposition({
-  property: 'transform',
-  underlying: 'translate(10px, 20px)',
-  addFrom: 'translate(100px, 200px)',
-  replaceTo: 'translate(210px, 420px)',
-}, [
-  {at: -0.5, is: 'translate(60px, 120px)'},
-  {at: 0, is: 'translate(110px, 220px)'},
-  {at: 0.25, is: 'translate(135px, 270px)'},
-  {at: 0.5, is: 'translate(160px, 320px)'},
-  {at: 0.75, is: 'translate(185px, 370px)'},
-  {at: 1, is: 'translate(210px, 420px)'},
-  {at: 1.5, is: 'translate(260px, 520px)'},
-]);
-
-assertComposition({
-  property: 'transform',
-  underlying: 'rotateX(45deg)',
-  addFrom: 'rotateY(-100deg)',
-  addTo: 'rotateY(100deg)',
-}, [
-  {at: -0.5, is: 'rotateX(45deg) rotateY(-200deg)'},
-  {at: 0, is: 'rotateX(45deg) rotateY(-100deg)'},
-  {at: 0.25, is: 'rotateX(45deg) rotateY(-50deg)'},
-  {at: 0.5, is: 'rotateX(45deg) rotateY(0deg)'},
-  {at: 0.75, is: 'rotateX(45deg) rotateY(50deg)'},
-  {at: 1, is: 'rotateX(45deg) rotateY(100deg)'},
-  {at: 1.5, is: 'rotateX(45deg) rotateY(200deg)'},
-]);
+// ------------------ Addition -----------------
 
 assertComposition({
   property: 'transform',
@@ -63,21 +24,6 @@
   {at: 1.5, is: 'rotateX(250deg) rotateY(250deg) translate(160px, 320px)'},
 ]);
 
-assertComposition({
-  property: 'transform',
-  underlying: 'rotateX(45deg)',
-  addFrom: 'rotateY(0deg)',
-  addTo: 'rotateY(360deg)',
-}, [
-  {at: -0.5, is: 'rotateX(45deg) rotateY(-180deg)'},
-  {at: 0, is: 'rotateX(45deg) rotateY(0deg)'},
-  {at: 0.25, is: 'rotateX(45deg) rotateY(90deg)'},
-  {at: 0.5, is: 'rotateX(45deg) rotateY(180deg)'},
-  {at: 0.75, is: 'rotateX(45deg) rotateY(270deg)'},
-  {at: 1, is: 'rotateX(45deg) rotateY(360deg)'},
-  {at: 1.5, is: 'rotateX(45deg) rotateY(540deg)'},
-]);
-
 // Shorter list is extended with corresponding identity transforms for pairwise
 // interpolation.
 assertComposition({
@@ -127,5 +73,8 @@
   {at: 1.5, is: 'rotateX(45deg)'},
 ]);
 
+// ------------------ Accumulation -----------------
+
+// TODO(smcgruer): Add tests for accumulation behaviors.
 </script>
 </body>
diff --git a/third_party/blink/web_tests/animations/composition/transform-matrix-composition.html b/third_party/blink/web_tests/animations/composition/transform-matrix-composition.html
new file mode 100644
index 0000000..3ba6bdb
--- /dev/null
+++ b/third_party/blink/web_tests/animations/composition/transform-matrix-composition.html
@@ -0,0 +1,201 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+// For matrix and matrix3d, addition is defined as concatenation whilst
+// accumulation works by decomposing the matrix and then accumulating the
+// decomposed functions. We can therefore test the difference between the
+// two by mixing functions such that a naive multiplication would look
+// different than the accumulation behavior.
+//
+// Note that due to the complexities of decomposition the test space here is
+// huge; we cover some basic cases and hope that the tests for the individual
+// functions provide a lot of the remaining coverage.
+
+// Creates a matrix3d function, encoding the passed rotation and translation.
+// Note that the translate will not be affected by the rotation.
+function create3dMatrix(x, y, z, radians, translateX) {
+  // Normalize the rotation axes.
+  const length = Math.sqrt(x*x + y*y + z*z);
+  x /= length;
+  y /= length;
+  z /= length;
+
+  const sc = Math.sin(radians / 2) * Math.cos(radians / 2);
+  const sq = Math.sin(radians / 2) * Math.sin(radians / 2);
+
+  // https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined
+  // https://drafts.csswg.org/css-transforms-2/#Translate3dDefined
+  return 'matrix3d(' + [
+      1 - 2 * (y*y + z*z) * sq,
+      2 * (x * y * sq + z * sc),
+      2 * (x * z * sq - y * sc),
+      0,
+      2 * (x * y * sq - z * sc),
+      1 - 2 * (x*x + z*z) * sq,
+      2 * (y * z * sq + x * sc),
+      0,
+      2 * (x * z * sq + y * sc),
+      2 * (y * z * sq - x * sc),
+      1 - 2 * (x*x + y*y) * sq,
+      0,
+      translateX, 0, 0, 1].join() + ')';
+}
+
+// ------------ Addition tests --------------
+
+assertComposition({
+  property: 'transform',
+  // translateX(100px) rotate(90deg)
+  underlying: 'matrix(0, 1, -1, 0, 100, 0)',
+  // translateX(100px)
+  addFrom: 'matrix(1, 0, 0, 1, 100, 0)',
+  // translateX(200px)
+  addTo: 'matrix(1, 0, 0, 1, 200, 0)',
+}, [
+  {at: -0.5, is: 'matrix(0, 1, -1, 0, 100, 50)'},
+  {at: 0, is: 'matrix(0, 1, -1, 0, 100, 100)'},
+  {at: 0.25, is: 'matrix(0, 1, -1, 0, 100, 125)'},
+  {at: 0.5, is: 'matrix(0, 1, -1, 0, 100, 150)'},
+  {at: 0.75, is: 'matrix(0, 1, -1, 0, 100, 175)'},
+  {at: 1, is: 'matrix(0, 1, -1, 0, 100, 200)'},
+  {at: 1.5, is: 'matrix(0, 1, -1, 0, 100, 250)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  // translateX(100px) rotate3d(1, 1, 0, 45deg)
+  underlying: create3dMatrix(1, 1, 0, Math.PI / 4, 100),
+  // translateX(100px)
+  addFrom: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 100, 0, 0, 1)',
+  // translateX(200px)
+  addTo: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 200, 0, 0, 1)',
+}, [
+  // matrix3ds are hard to read; these are the decomposed forms for clarity
+  {at: -0.5, is: 'translateX(100px) rotate3d(1, 1, 0, 45deg) translateX(50px)'},
+  {at: 0, is: 'translateX(100px) rotate3d(1, 1, 0, 45deg) translateX(100px)'},
+  {at: 0.25, is: 'translateX(100px) rotate3d(1, 1, 0, 45deg) translateX(125px)'},
+  {at: 0.5, is: 'translateX(100px) rotate3d(1, 1, 0, 45deg) translateX(150px)'},
+  {at: 0.75, is: 'translateX(100px) rotate3d(1, 1, 0, 45deg) translateX(175px)'},
+  {at: 1, is: 'translateX(100px) rotate3d(1, 1, 0, 45deg) translateX(200px)'},
+  {at: 1.5, is: 'translateX(100px) rotate3d(1, 1, 0, 45deg) translateX(250px)'},
+]);
+
+// Addition of non-invertible matrices is still defined as concatenation so
+// includes the underlying value.
+
+assertComposition({
+  property: 'transform',
+  // Non-invertible.
+  underlying: 'matrix(1, 1, 0, 0, 0, 100)',
+  // translateX(100px)
+  addFrom: 'matrix(1, 0, 0, 1, 100, 0)',
+  // translateX(200px)
+  addTo: 'matrix(1, 0, 0, 1, 200, 0)',
+}, [
+  {at: -0.5, is: 'matrix(1, 1, 0, 0, 100, 200)'},
+  {at: 0, is: 'matrix(1, 1, 0, 0, 100, 200)'},
+  {at: 0.25, is: 'matrix(1, 1, 0, 0, 100, 200)'},
+  {at: 0.5, is: 'matrix(1, 1, 0, 0, 200, 300)'},
+  {at: 0.75, is: 'matrix(1, 1, 0, 0, 200, 300)'},
+  {at: 1, is: 'matrix(1, 1, 0, 0, 200, 300)'},
+  {at: 1.5, is: 'matrix(1, 1, 0, 0, 200, 300)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  // translateX(100px)
+  underlying: 'matrix(1, 0, 0, 1, 100, 0)',
+  // Non-invertible
+  addFrom: 'matrix(1, 1, 0, 0, 0, 100)',
+  // translateX(200px)
+  addTo: 'matrix(1, 0, 0, 1, 200, 0)',
+}, [
+  {at: -0.5, is: 'matrix(1, 1, 0, 0, 100, 100)'},
+  {at: 0, is: 'matrix(1, 1, 0, 0, 100, 100)'},
+  {at: 0.25, is: 'matrix(1, 1, 0, 0, 100, 100)'},
+  {at: 0.5, is: 'matrix(1, 0, 0, 1, 300, 0)'},
+  {at: 0.75, is: 'matrix(1, 0, 0, 1, 300, 0)'},
+  {at: 1, is: 'matrix(1, 0, 0, 1, 300, 0)'},
+  {at: 1.5, is: 'matrix(1, 0, 0, 1, 300, 0)'},
+]);
+
+// ------------ Accumulation tests --------------
+
+assertComposition({
+  property: 'transform',
+  // translateX(100px) rotate(90deg)
+  underlying: 'matrix(0, 1, -1, 0, 100, 0)',
+  // translateX(100px)
+  accumulateFrom: 'matrix(1, 0, 0, 1, 100, 0)',
+  // translateX(200px)
+  accumulateTo: 'matrix(1, 0, 0, 1, 200, 0)',
+}, [
+  {at: -0.5, is: 'matrix(0, 1, -1, 0, 150, 0)'},
+  {at: 0, is: 'matrix(0, 1, -1, 0, 200, 0)'},
+  {at: 0.25, is: 'matrix(0, 1, -1, 0, 225, 0)'},
+  {at: 0.5, is: 'matrix(0, 1, -1, 0, 250, 0)'},
+  {at: 0.75, is: 'matrix(0, 1, -1, 0, 275, 0)'},
+  {at: 1, is: 'matrix(0, 1, -1, 0, 300, 0)'},
+  {at: 1.5, is: 'matrix(0, 1, -1, 0, 350, 0)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  // translateX(100px) rotate3d(1, 1, 0, 45deg)
+  underlying: create3dMatrix(1, 1, 0, Math.PI / 4, 100),
+  // translateX(100px)
+  accumulateFrom: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 100, 0, 0, 1)',
+  // translateX(200px)
+  accumulateTo: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 200, 0, 0, 1)',
+}, [
+  // matrix3ds are hard to read; these are the decomposed forms for clarity
+  {at: -0.5, is: 'translateX(150px) rotate3d(1, 1, 0, 45deg)'},
+  {at: 0, is: 'translateX(200px) rotate3d(1, 1, 0, 45deg)'},
+  {at: 0.25, is: 'translateX(225px) rotate3d(1, 1, 0, 45deg)'},
+  {at: 0.5, is: 'translateX(250px) rotate3d(1, 1, 0, 45deg)'},
+  {at: 0.75, is: 'translateX(275px) rotate3d(1, 1, 0, 45deg)'},
+  {at: 1, is: 'translateX(300px) rotate3d(1, 1, 0, 45deg)'},
+  {at: 1.5, is: 'translateX(350px) rotate3d(1, 1, 0, 45deg)'},
+]);
+
+// Accumulation of non-invertible matrices falls back to replace behavior.
+
+assertComposition({
+  property: 'transform',
+  // Non-invertible.
+  underlying: 'matrix(1, 1, 0, 0, 0, 100)',
+  // translateX(100px)
+  accumulateFrom: 'matrix(1, 0, 0, 1, 100, 0)',
+  // translateX(200px)
+  accumulateTo: 'matrix(1, 0, 0, 1, 200, 0)',
+}, [
+  {at: -0.5, is: 'matrix(1, 0, 0, 1, 50, 0)'},
+  {at: 0, is: 'matrix(1, 0, 0, 1, 100, 0)'},
+  {at: 0.25, is: 'matrix(1, 0, 0, 1, 125, 0)'},
+  {at: 0.5, is: 'matrix(1, 0, 0, 1, 150, 0)'},
+  {at: 0.75, is: 'matrix(1, 0, 0, 1, 175, 0)'},
+  {at: 1, is: 'matrix(1, 0, 0, 1, 200, 0)'},
+  {at: 1.5, is: 'matrix(1, 0, 0, 1, 250, 0)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  // translateX(100px)
+  underlying: 'matrix(1, 0, 0, 1, 100, 0)',
+  // Non-invertible
+  accumulateFrom: 'matrix(1, 1, 0, 0, 0, 100)',
+  // translateX(200px)
+  accumulateTo: 'matrix(1, 0, 0, 1, 200, 0)',
+}, [
+  {at: -0.5, is: 'matrix(1, 1, 0, 0, 0, 100)'},
+  {at: 0, is: 'matrix(1, 1, 0, 0, 0, 100)'},
+  {at: 0.25, is: 'matrix(1, 1, 0, 0, 0, 100)'},
+  {at: 0.5, is: 'matrix(1, 0, 0, 1, 300, 0)'},
+  {at: 0.75, is: 'matrix(1, 0, 0, 1, 300, 0)'},
+  {at: 1, is: 'matrix(1, 0, 0, 1, 300, 0)'},
+  {at: 1.5, is: 'matrix(1, 0, 0, 1, 300, 0)'},
+]);
+</script>
+</body>
diff --git a/third_party/blink/web_tests/animations/composition/transform-perspective-composition.html b/third_party/blink/web_tests/animations/composition/transform-perspective-composition.html
new file mode 100644
index 0000000..b385bf6
--- /dev/null
+++ b/third_party/blink/web_tests/animations/composition/transform-perspective-composition.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+// Addition and accumulation of perspective values are very similar, but not
+// identical. We can test the difference by constructing a scenario where a
+// perspective parameter would go negative in one case (and thus be clamped
+// to 0), and would not go negative in the other case.
+//
+// In the test below, the values differ at 1.5 progress. The reason for this
+// is that at 1.5 progress, the addition case (which uses concatenation)
+// computes to:
+//
+//   perspective(10px) perspective(-50px)
+//
+// Since perspective cannot go negative, this is clamped to:
+//
+//   perspective(10px) identity
+//
+// The accumulation case, on the other hand, combines the components
+// and so ends up blending from perspective(5px) to perspective(8.33...px) at
+// 1.5 progress, which results in perspective(12.5px) - this is what you would
+// get with addition too, if not for the clamping behavior.
+
+// ------------ Addition tests --------------
+
+assertComposition({
+  property: 'transform',
+  underlying: 'perspective(10px)',
+  addFrom: 'perspective(10px)',
+  addTo: 'perspective(50px)',
+}, [
+  {at: -0.5, is: 'perspective(4.12px)'},
+  {at: 0, is: 'perspective(5px)'},
+  {at: 0.25, is: 'perspective(5.45px)'},
+  {at: 0.5, is: 'perspective(6.15px)'},
+  {at: 0.75, is: 'perspective(7.06px)'},
+  {at: 1, is: 'perspective(8.33px)'},
+  {at: 1.5, is: 'perspective(10px)'},
+]);
+
+// ------------ Accumulation tests --------------
+
+assertComposition({
+  property: 'transform',
+  underlying: 'perspective(10px)',
+  accumulateFrom: 'perspective(10px)',
+  accumulateTo: 'perspective(50px)',
+}, [
+  {at: -0.5, is: 'perspective(4.12px)'},
+  {at: 0, is: 'perspective(5px)'},
+  {at: 0.25, is: 'perspective(5.45px)'},
+  {at: 0.5, is: 'perspective(6.15px)'},
+  {at: 0.75, is: 'perspective(7.06px)'},
+  {at: 1, is: 'perspective(8.33px)'},
+  {at: 1.5, is: 'perspective(12.5px)'},
+]);
+</script>
+</body>
diff --git a/third_party/blink/web_tests/animations/composition/transform-rotate-composition.html b/third_party/blink/web_tests/animations/composition/transform-rotate-composition.html
new file mode 100644
index 0000000..09799fe
--- /dev/null
+++ b/third_party/blink/web_tests/animations/composition/transform-rotate-composition.html
@@ -0,0 +1,157 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+// ------------ Addition tests --------------
+
+assertComposition({
+  property: 'transform',
+  underlying: 'rotateX(20deg)',
+  addFrom: 'rotateX(40deg)',
+  addTo: 'rotateX(60deg)',
+}, [
+  {at: -0.5, is: 'rotateX(50deg)'},
+  {at: 0, is: 'rotateX(60deg)'},
+  {at: 0.25, is: 'rotateX(65deg)'},
+  {at: 0.5, is: 'rotateX(70deg)'},
+  {at: 0.75, is: 'rotateX(75deg)'},
+  {at: 1, is: 'rotateX(80deg)'},
+  {at: 1.5, is: 'rotateX(90deg)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'rotateY(20deg)',
+  addFrom: 'rotateY(40deg)',
+  addTo: 'rotateY(60deg)',
+}, [
+  {at: -0.5, is: 'rotateY(50deg)'},
+  {at: 0, is: 'rotateY(60deg)'},
+  {at: 0.25, is: 'rotateY(65deg)'},
+  {at: 0.5, is: 'rotateY(70deg)'},
+  {at: 0.75, is: 'rotateY(75deg)'},
+  {at: 1, is: 'rotateY(80deg)'},
+  {at: 1.5, is: 'rotateY(90deg)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'rotateZ(20deg)',
+  addFrom: 'rotateZ(40deg)',
+  addTo: 'rotateZ(60deg)',
+}, [
+  {at: -0.5, is: 'rotateZ(50deg)'},
+  {at: 0, is: 'rotateZ(60deg)'},
+  {at: 0.25, is: 'rotateZ(65deg)'},
+  {at: 0.5, is: 'rotateZ(70deg)'},
+  {at: 0.75, is: 'rotateZ(75deg)'},
+  {at: 1, is: 'rotateZ(80deg)'},
+  {at: 1.5, is: 'rotateZ(90deg)'},
+]);
+
+// When testing rotate functions in isolation, the additive and accumulation
+// behaviors are functionally identical. This test includes a skew to ensure
+// both methods are implemented; add should append the from/to after the skew.
+assertComposition({
+  property: 'transform',
+  underlying: 'rotate(45deg) skew(10deg, 20deg)',
+  addFrom: 'rotate(45deg)',
+  addTo: 'rotate(225deg)',
+}, [
+  {at: -0.5, is: 'rotate(45deg) skew(10deg, 20deg) rotate(-45deg)'},
+  {at: 0, is: 'rotate(45deg) skew(10deg, 20deg) rotate(45deg)'},
+  {at: 0.25, is: 'rotate(45deg) skew(10deg, 20deg) rotate(90deg)'},
+  {at: 0.5, is: 'rotate(45deg) skew(10deg, 20deg) rotate(135deg)'},
+  {at: 0.75, is: 'rotate(45deg) skew(10deg, 20deg) rotate(180deg)'},
+  {at: 1, is: 'rotate(45deg) skew(10deg, 20deg) rotate(225deg)'},
+  {at: 1.5, is: 'rotate(45deg) skew(10deg, 20deg) rotate(315deg)'},
+]);
+// ------------ Accumulation tests --------------
+
+assertComposition({
+  property: 'transform',
+  underlying: 'rotateX(20deg)',
+  accumulateFrom: 'rotateX(40deg)',
+  accumulateTo: 'rotateX(60deg)',
+}, [
+  {at: -0.5, is: 'rotateX(50deg)'},
+  {at: 0, is: 'rotateX(60deg)'},
+  {at: 0.25, is: 'rotateX(65deg)'},
+  {at: 0.5, is: 'rotateX(70deg)'},
+  {at: 0.75, is: 'rotateX(75deg)'},
+  {at: 1, is: 'rotateX(80deg)'},
+  {at: 1.5, is: 'rotateX(90deg)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'rotateY(20deg)',
+  accumulateFrom: 'rotateY(40deg)',
+  accumulateTo: 'rotateY(60deg)',
+}, [
+  {at: -0.5, is: 'rotateY(50deg)'},
+  {at: 0, is: 'rotateY(60deg)'},
+  {at: 0.25, is: 'rotateY(65deg)'},
+  {at: 0.5, is: 'rotateY(70deg)'},
+  {at: 0.75, is: 'rotateY(75deg)'},
+  {at: 1, is: 'rotateY(80deg)'},
+  {at: 1.5, is: 'rotateY(90deg)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'rotateZ(20deg)',
+  accumulateFrom: 'rotateZ(40deg)',
+  accumulateTo: 'rotateZ(60deg)',
+}, [
+  {at: -0.5, is: 'rotateZ(50deg)'},
+  {at: 0, is: 'rotateZ(60deg)'},
+  {at: 0.25, is: 'rotateZ(65deg)'},
+  {at: 0.5, is: 'rotateZ(70deg)'},
+  {at: 0.75, is: 'rotateZ(75deg)'},
+  {at: 1, is: 'rotateZ(80deg)'},
+  {at: 1.5, is: 'rotateZ(90deg)'},
+]);
+
+// The rotate functions all share the same primitive type (rotate3d), so can be
+// accumulated between. If primitive type matching is not properly being
+// performed, this test would likely fail with a fallback to replace behavior.
+assertComposition({
+  property: 'transform',
+  underlying: 'rotateX(45deg)',
+  accumulateFrom: 'rotateY(30deg)',
+  accumulateTo: 'rotateY(70deg)',
+}, [
+  // Due to how rotation is accumulated (addition of underlying angles), the
+  // behavior is identical to concatenating the components. The expectations
+  // are expressed as concatenations for readability.
+  {at: -0.5, is: 'rotateX(45deg) rotateY(10deg)'},
+  {at: 0, is: 'rotateX(45deg) rotateY(30deg)'},
+  {at: 0.25, is: 'rotateX(45deg) rotateY(40deg)'},
+  {at: 0.5, is: 'rotateX(45deg) rotateY(50deg)'},
+  {at: 0.75, is: 'rotateX(45deg) rotateY(60deg)'},
+  {at: 1, is: 'rotateX(45deg) rotateY(70deg)'},
+  {at: 1.5, is: 'rotateX(45deg) rotateY(90deg)'},
+]);
+
+// When testing rotate functions in isolation, the additive and accumulation
+// behaviors are functionally identical. This test includes a skew to ensure
+// both methods are implemented; accumulate should combine the rotate before
+// the skew.
+assertComposition({
+  property: 'transform',
+  underlying: 'rotate(45deg) skew(10deg, 20deg)',
+  accumulateFrom: 'rotate(45deg)',
+  accumulateTo: 'rotate(225deg)',
+}, [
+  {at: -0.5, is: 'rotate(0deg) skew(10deg, 20deg)'},
+  {at: 0, is: 'rotate(90deg) skew(10deg, 20deg)'},
+  {at: 0.25, is: 'rotate(135deg) skew(10deg, 20deg)'},
+  {at: 0.5, is: 'rotate(180deg) skew(10deg, 20deg)'},
+  {at: 0.75, is: 'rotate(225deg) skew(10deg, 20deg)'},
+  {at: 1, is: 'rotate(270deg) skew(10deg, 20deg)'},
+  {at: 1.5, is: 'rotate(360deg) skew(10deg, 20deg)'},
+]);
+</script>
+</body>
diff --git a/third_party/blink/web_tests/animations/composition/transform-scale-composition.html b/third_party/blink/web_tests/animations/composition/transform-scale-composition.html
new file mode 100644
index 0000000..560f10ec
--- /dev/null
+++ b/third_party/blink/web_tests/animations/composition/transform-scale-composition.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+// Addition (aka concatenation) of scale functions results in multiplying their
+// values (scale(2) scale(3) == scale(6)), whereas accumulation does a 1-based
+// sum of the components (accumulate(scale(2), scale(3)) == scale(2 + 3 - 1) ==
+// scale(4)).
+
+// ------------ Addition tests --------------
+
+assertComposition({
+  property: 'transform',
+  underlying: 'scaleX(2)',
+  addFrom: 'scaleX(3)',
+  addTo: 'scaleX(4)',
+}, [
+  {at: -0.5, is: 'scaleX(5)'},
+  {at: 0, is: 'scaleX(6)'},
+  {at: 0.25, is: 'scaleX(6.5)'},
+  {at: 0.5, is: 'scaleX(7)'},
+  {at: 0.75, is: 'scaleX(7.5)'},
+  {at: 1, is: 'scaleX(8)'},
+  {at: 1.5, is: 'scaleX(9)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'scaleY(2)',
+  addFrom: 'scaleY(3)',
+  addTo: 'scaleY(4)',
+}, [
+  {at: -0.5, is: 'scaleY(5)'},
+  {at: 0, is: 'scaleY(6)'},
+  {at: 0.25, is: 'scaleY(6.5)'},
+  {at: 0.5, is: 'scaleY(7)'},
+  {at: 0.75, is: 'scaleY(7.5)'},
+  {at: 1, is: 'scaleY(8)'},
+  {at: 1.5, is: 'scaleY(9)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'scaleZ(2)',
+  addFrom: 'scaleZ(3)',
+  addTo: 'scaleZ(4)',
+}, [
+  {at: -0.5, is: 'scaleZ(5)'},
+  {at: 0, is: 'scaleZ(6)'},
+  {at: 0.25, is: 'scaleZ(6.5)'},
+  {at: 0.5, is: 'scaleZ(7)'},
+  {at: 0.75, is: 'scaleZ(7.5)'},
+  {at: 1, is: 'scaleZ(8)'},
+  {at: 1.5, is: 'scaleZ(9)'},
+]);
+
+// ------------ Accumulation tests --------------
+
+assertComposition({
+  property: 'transform',
+  underlying: 'scaleX(2)',
+  accumulateFrom: 'scaleX(3)',
+  accumulateTo: 'scaleX(4)',
+}, [
+  {at: -0.5, is: 'scaleX(3.5)'},
+  {at: 0, is: 'scaleX(4)'},
+  {at: 0.25, is: 'scaleX(4.25)'},
+  {at: 0.5, is: 'scaleX(4.5)'},
+  {at: 0.75, is: 'scaleX(4.75)'},
+  {at: 1, is: 'scaleX(5)'},
+  {at: 1.5, is: 'scaleX(5.5)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'scaleY(2)',
+  accumulateFrom: 'scaleY(3)',
+  accumulateTo: 'scaleY(4)',
+}, [
+  {at: -0.5, is: 'scaleY(3.5)'},
+  {at: 0, is: 'scaleY(4)'},
+  {at: 0.25, is: 'scaleY(4.25)'},
+  {at: 0.5, is: 'scaleY(4.5)'},
+  {at: 0.75, is: 'scaleY(4.75)'},
+  {at: 1, is: 'scaleY(5)'},
+  {at: 1.5, is: 'scaleY(5.5)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'scaleZ(2)',
+  accumulateFrom: 'scaleZ(3)',
+  accumulateTo: 'scaleZ(4)',
+}, [
+  {at: -0.5, is: 'scaleZ(3.5)'},
+  {at: 0, is: 'scaleZ(4)'},
+  {at: 0.25, is: 'scaleZ(4.25)'},
+  {at: 0.5, is: 'scaleZ(4.5)'},
+  {at: 0.75, is: 'scaleZ(4.75)'},
+  {at: 1, is: 'scaleZ(5)'},
+  {at: 1.5, is: 'scaleZ(5.5)'},
+]);
+
+// The scale functions all share the same primitive type (scale3d), so can be
+// accumulated between.
+assertComposition({
+  property: 'transform',
+  underlying: 'scale(2, 4)',
+  accumulateFrom: 'scaleZ(3)',
+  accumulateTo: 'scaleZ(4)',
+}, [
+  {at: -0.5, is: 'scale3d(2, 4, 2.5)'},
+  {at: 0, is: 'scale3d(2, 4, 3)'},
+  {at: 0.25, is: 'scale3d(2, 4, 3.25)'},
+  {at: 0.5, is: 'scale3d(2, 4, 3.5)'},
+  {at: 0.75, is: 'scale3d(2, 4, 3.75)'},
+  {at: 1, is: 'scale3d(2, 4, 4)'},
+  {at: 1.5, is: 'scale3d(2, 4, 4.5)'},
+]);
+</script>
+</body>
diff --git a/third_party/blink/web_tests/animations/composition/transform-skew-composition.html b/third_party/blink/web_tests/animations/composition/transform-skew-composition.html
new file mode 100644
index 0000000..fc2f32f
--- /dev/null
+++ b/third_party/blink/web_tests/animations/composition/transform-skew-composition.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+// Addition (aka concatenation) of two skew functions skew(a) and skew(b)
+// results in computing tan(a) + tan(b), whereas accumulation results in summing
+// the components to get tan(a + b).
+
+// ------------ Addition tests --------------
+
+assertComposition({
+  property: 'transform',
+  underlying: 'skewX(10deg)',
+  addFrom: 'skewX(30deg)',
+  addTo: 'skewX(50deg)',
+}, [
+  {at: -0.5, is: 'skewX(10deg) skewX(20deg)'},
+  {at: 0, is: 'skewX(10deg) skewX(30deg)'},
+  {at: 0.25, is: 'skewX(10deg) skewX(35deg)'},
+  {at: 0.5, is: 'skewX(10deg) skewX(40deg)'},
+  {at: 0.75, is: 'skewX(10deg) skewX(45deg)'},
+  {at: 1, is: 'skewX(10deg) skewX(50deg)'},
+  {at: 1.5, is: 'skewX(10deg) skewX(60deg)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'skewY(10deg)',
+  addFrom: 'skewY(30deg)',
+  addTo: 'skewY(50deg)',
+}, [
+  {at: -0.5, is: 'skewY(10deg) skewY(20deg)'},
+  {at: 0, is: 'skewY(10deg) skewY(30deg)'},
+  {at: 0.25, is: 'skewY(10deg) skewY(35deg)'},
+  {at: 0.5, is: 'skewY(10deg) skewY(40deg)'},
+  {at: 0.75, is: 'skewY(10deg) skewY(45deg)'},
+  {at: 1, is: 'skewY(10deg) skewY(50deg)'},
+  {at: 1.5, is: 'skewY(10deg) skewY(60deg)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'skew(10deg, 20deg)',
+  addFrom: 'skew(30deg, 10deg)',
+  addTo: 'skew(50deg, 50deg)',
+}, [
+  {at: -0.5, is: 'skew(10deg, 20deg) skew(20deg, -10deg)'},
+  {at: 0, is: 'skew(10deg, 20deg) skew(30deg, 10deg)'},
+  {at: 0.25, is: 'skew(10deg, 20deg) skew(35deg, 20deg)'},
+  {at: 0.5, is: 'skew(10deg, 20deg) skew(40deg, 30deg)'},
+  {at: 0.75, is: 'skew(10deg, 20deg) skew(45deg, 40deg)'},
+  {at: 1, is: 'skew(10deg, 20deg) skew(50deg, 50deg)'},
+  {at: 1.5, is: 'skew(10deg, 20deg) skew(60deg, 70deg)'},
+]);
+
+// ------------ Accumulation tests --------------
+
+assertComposition({
+  property: 'transform',
+  underlying: 'skewX(45deg)',
+  accumulateFrom: 'skewX(30deg)',
+  accumulateTo: 'skewX(70deg)',
+}, [
+  {at: -0.5, is: 'skewX(55deg)'},
+  {at: 0, is: 'skewX(75deg)'},
+  {at: 0.25, is: 'skewX(85deg)'},
+  {at: 0.5, is: 'skewX(95deg)'},
+  {at: 0.75, is: 'skewX(105deg)'},
+  {at: 1, is: 'skewX(115deg)'},
+  {at: 1.5, is: 'skewX(135deg)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'skewY(45deg)',
+  accumulateFrom: 'skewY(30deg)',
+  accumulateTo: 'skewY(70deg)',
+}, [
+  {at: -0.5, is: 'skewY(55deg)'},
+  {at: 0, is: 'skewY(75deg)'},
+  {at: 0.25, is: 'skewY(85deg)'},
+  {at: 0.5, is: 'skewY(95deg)'},
+  {at: 0.75, is: 'skewY(105deg)'},
+  {at: 1, is: 'skewY(115deg)'},
+  {at: 1.5, is: 'skewY(135deg)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'skew(10deg, 45deg)',
+  accumulateFrom: 'skew(20deg, 30deg)',
+  accumulateTo: 'skew(40deg, 70deg)',
+}, [
+  {at: -0.5, is: 'skew(20deg, 55deg)'},
+  {at: 0, is: 'skew(30deg, 75deg)'},
+  {at: 0.25, is: 'skew(35deg, 85deg)'},
+  {at: 0.5, is: 'skew(40deg, 95deg)'},
+  {at: 0.75, is: 'skew(45deg, 105deg)'},
+  {at: 1, is: 'skew(50deg, 115deg)'},
+  {at: 1.5, is: 'skew(60deg, 135deg)'},
+]);
+
+// The skew{X,Y} functions DO NOT share the same primitive type, so cannot be
+// accumlated between directly. Instead, they fall back to matrix accumulation,
+// which this tests for.
+assertComposition({
+  property: 'transform',
+  underlying: 'skewX(45deg)',
+  accumulateFrom: 'skewY(45deg)',
+  accumulateTo: 'skewY(45deg)',
+}, [
+  // Note that this is not equivalent to any form of combined skews.
+  {at: 0.5, is: 'matrix(1, 1, 0.5, 1.5, 0, 0)'},
+]);
+</script>
+</body>
diff --git a/third_party/blink/web_tests/animations/composition/transform-translate-composition.html b/third_party/blink/web_tests/animations/composition/transform-translate-composition.html
new file mode 100644
index 0000000..65755dc
--- /dev/null
+++ b/third_party/blink/web_tests/animations/composition/transform-translate-composition.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+// ------------ Addition tests --------------
+
+assertComposition({
+  property: 'transform',
+  underlying: 'translate(10px, 20px)',
+  addFrom: 'translate(100px, 200px)',
+  addTo: 'translate(200px, 400px)',
+}, [
+  {at: -0.5, is: 'translate(60px, 120px)'},
+  {at: 0, is: 'translate(110px, 220px)'},
+  {at: 0.25, is: 'translate(135px, 270px)'},
+  {at: 0.5, is: 'translate(160px, 320px)'},
+  {at: 0.75, is: 'translate(185px, 370px)'},
+  {at: 1, is: 'translate(210px, 420px)'},
+  {at: 1.5, is: 'translate(260px, 520px)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'translate(10px, 20px)',
+  addFrom: 'translate(100px, 200px)',
+  replaceTo: 'translate(210px, 420px)',
+}, [
+  {at: -0.5, is: 'translate(60px, 120px)'},
+  {at: 0, is: 'translate(110px, 220px)'},
+  {at: 0.25, is: 'translate(135px, 270px)'},
+  {at: 0.5, is: 'translate(160px, 320px)'},
+  {at: 0.75, is: 'translate(185px, 370px)'},
+  {at: 1, is: 'translate(210px, 420px)'},
+  {at: 1.5, is: 'translate(260px, 520px)'},
+]);
+
+// When testing translate functions in isolation, the additive and accumulation
+// behaviors are functionally identical. This test includes a rotate to ensure
+// both methods are implemented; add should append the from/to after the rotate.
+assertComposition({
+  property: 'transform',
+  underlying: 'translateX(100px) rotate(90deg)',
+  addFrom: 'translateX(100px)',
+  addTo: 'translateX(200px)',
+}, [
+  {at: -0.5, is: 'translateX(100px) rotate(90deg) translateX(50px)'},
+  {at: 0, is: 'translateX(100px) rotate(90deg) translateX(100px)'},
+  {at: 0.25, is: 'translateX(100px) rotate(90deg) translateX(125px)'},
+  {at: 0.5, is: 'translateX(100px) rotate(90deg) translateX(150px)'},
+  {at: 0.75, is: 'translateX(100px) rotate(90deg) translateX(175px)'},
+  {at: 1, is: 'translateX(100px) rotate(90deg) translateX(200px)'},
+  {at: 1.5, is: 'translateX(100px) rotate(90deg) translateX(250px)'},
+]);
+
+// ------------ Accumulation tests --------------
+
+assertComposition({
+  property: 'transform',
+  underlying: 'translateX(100px)',
+  accumulateFrom: 'translateX(50px)',
+  accumulateTo: 'translateX(250px)',
+}, [
+  {at: -0.5, is: 'translateX(50px)'},
+  {at: 0, is: 'translateX(150px)'},
+  {at: 0.25, is: 'translateX(200px)'},
+  {at: 0.5, is: 'translateX(250px)'},
+  {at: 0.75, is: 'translateX(300px)'},
+  {at: 1, is: 'translateX(350px)'},
+  {at: 1.5, is: 'translateX(450px)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'translateY(100px)',
+  accumulateFrom: 'translateY(50px)',
+  accumulateTo: 'translateY(250px)',
+}, [
+  {at: -0.5, is: 'translateY(50px)'},
+  {at: 0, is: 'translateY(150px)'},
+  {at: 0.25, is: 'translateY(200px)'},
+  {at: 0.5, is: 'translateY(250px)'},
+  {at: 0.75, is: 'translateY(300px)'},
+  {at: 1, is: 'translateY(350px)'},
+  {at: 1.5, is: 'translateY(450px)'},
+]);
+
+assertComposition({
+  property: 'transform',
+  underlying: 'translateZ(100px)',
+  accumulateFrom: 'translateZ(50px)',
+  accumulateTo: 'translateZ(250px)',
+}, [
+  {at: -0.5, is: 'translateZ(50px)'},
+  {at: 0, is: 'translateZ(150px)'},
+  {at: 0.25, is: 'translateZ(200px)'},
+  {at: 0.5, is: 'translateZ(250px)'},
+  {at: 0.75, is: 'translateZ(300px)'},
+  {at: 1, is: 'translateZ(350px)'},
+  {at: 1.5, is: 'translateZ(450px)'},
+]);
+
+// The translate functions all share the same primitive type (translate3d), so
+// can be accumulated between.
+assertComposition({
+  property: 'transform',
+  underlying: 'translate(100px, 50px)',
+  accumulateFrom: 'translateZ(50px)',
+  accumulateTo: 'translateZ(250px)',
+}, [
+  {at: -0.5, is: 'translate3d(100px, 50px, -50px)'},
+  {at: 0, is: 'translate3d(100px, 50px, 50px)'},
+  {at: 0.25, is: 'translate3d(100px, 50px, 100px)'},
+  {at: 0.5, is: 'translate3d(100px, 50px, 150px)'},
+  {at: 0.75, is: 'translate3d(100px, 50px, 200px)'},
+  {at: 1, is: 'translate3d(100px, 50px, 250px)'},
+  {at: 1.5, is: 'translate3d(100px, 50px, 350px)'},
+]);
+
+// When testing translate functions in isolation, the additive and accumulation
+// behaviors are functionally identical. This test includes a rotate to ensure
+// both methods are implemented; accumulate should combine the transform before
+// the rotate.
+assertComposition({
+  property: 'transform',
+  underlying: 'translateX(100px) rotate(90deg)',
+  accumulateFrom: 'translateX(100px)',
+  accumulateTo: 'translateX(200px)',
+}, [
+  {at: -0.5, is: 'translateX(150px) rotate(90deg)'},
+  {at: 0, is: 'translateX(200px) rotate(90deg)'},
+  {at: 0.25, is: 'translateX(225px) rotate(90deg)'},
+  {at: 0.5, is: 'translateX(250px) rotate(90deg)'},
+  {at: 0.75, is: 'translateX(275px) rotate(90deg)'},
+  {at: 1, is: 'translateX(300px) rotate(90deg)'},
+  {at: 1.5, is: 'translateX(350px) rotate(90deg)'},
+]);
+</script>
+</body>
diff --git a/third_party/blink/web_tests/compositing/overflow/resources/update-widget-positions-on-nested-frames-and-scrollers-inner-frame.html b/third_party/blink/web_tests/compositing/overflow/resources/update-widget-positions-on-nested-frames-and-scrollers-inner-frame.html
index c8dfa67..6bc0eaa 100644
--- a/third_party/blink/web_tests/compositing/overflow/resources/update-widget-positions-on-nested-frames-and-scrollers-inner-frame.html
+++ b/third_party/blink/web_tests/compositing/overflow/resources/update-widget-positions-on-nested-frames-and-scrollers-inner-frame.html
@@ -17,10 +17,7 @@
         return;
 
       var select = document.getElementById('select');
-      // FIXME: it would be really nice to use getBoundingClientRect() and not
-      // hard-code pixel coordinates here, but since it's in an iframe, the
-      // coordinates are translated anyway.
-      clickToOpenPicker(50, 120, callback, function () {
+      openPicker(select, callback, function () {
           setTimeout(callback, 0);
       });
     }
@@ -47,7 +44,7 @@
           select.appendChild(option);
         }
 
-        if (x == 24) {
+        if (x == 23) {
           select.id = 'select';
         }
 
diff --git a/third_party/blink/web_tests/external/wpt/contacts/contacts-select.https.window.js b/third_party/blink/web_tests/external/wpt/contacts/contacts-select.https.window.js
index 5503ea69..617dee8 100644
--- a/third_party/blink/web_tests/external/wpt/contacts/contacts-select.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/contacts/contacts-select.https.window.js
@@ -59,38 +59,53 @@
 }, 'Supported contact properties are exposed.');
 
 contactsTestWithUserActivation(async (test, setSelectedContacts) => {
+  const dwightAddress = {
+    country: 'US',
+    city: 'Scranton',
+    addressLine: ['Schrute Farms'],
+  };
   // Returns two contacts with all information available.
   setSelectedContacts([
-      { name: ['Dwight Schrute'], email: ['dwight@schrutefarmsbnb.com'], tel: ['000-0000'] },
-      { name: ['Michael Scott', 'Prison Mike'], email: ['michael@dundermifflin.com'], tel: [] },
+      { name: ['Dwight Schrute'], email: ['dwight@schrutefarmsbnb.com'], tel: ['000-0000'], address: [dwightAddress] },
+      { name: ['Michael Scott', 'Prison Mike'], email: ['michael@dundermifflin.com'] },
   ]);
 
-  let results = await navigator.contacts.select(['name', 'email', 'tel'], { multiple: true });
+  let results = await navigator.contacts.select(['name', 'email', 'tel', 'address'], { multiple: true });
   assert_equals(results.length, 2);
   results = results.sort((c1, c2) => JSON.stringify(c1) < JSON.stringify(c2) ? -1 : 1);
 
   {
-    const dwight = results[0];
-
-    assert_own_property(dwight, 'name');
-    assert_own_property(dwight, 'email');
-    assert_own_property(dwight, 'tel');
-
-    assert_array_equals(dwight.name, ['Dwight Schrute']);
-    assert_array_equals(dwight.email, ['dwight@schrutefarmsbnb.com']);
-    assert_array_equals(dwight.tel, ['000-0000']);
-  }
-
-  {
-    const michael = results[1];
+    const michael = results[0];
 
     assert_own_property(michael, 'name');
     assert_own_property(michael, 'email');
     assert_own_property(michael, 'tel');
+    assert_own_property(michael, 'address');
 
     assert_array_equals(michael.name, ['Michael Scott', 'Prison Mike']);
     assert_array_equals(michael.email, ['michael@dundermifflin.com']);
     assert_array_equals(michael.tel, []);
+    assert_array_equals(michael.address, []);
+  }
+
+  {
+    const dwight = results[1];
+    assert_own_property(dwight, 'name');
+    assert_own_property(dwight, 'email');
+    assert_own_property(dwight, 'tel');
+    assert_own_property(dwight, 'address');
+
+    assert_array_equals(dwight.name, ['Dwight Schrute']);
+    assert_array_equals(dwight.email, ['dwight@schrutefarmsbnb.com']);
+    assert_array_equals(dwight.tel, ['000-0000']);
+
+    assert_equals(dwight.address.length, 1);
+    const selectedAddress = dwight.address[0];
+    assert_object_equals({
+      country: selectedAddress.country,
+      city: selectedAddress.city,
+      addressLine: selectedAddress.addressLine,
+    }, dwightAddress);
   }
 }, 'The Contact API correctly returns ContactInfo entries');
 
@@ -98,7 +113,7 @@
   // Returns two contacts with all information available.
   setSelectedContacts([
       { name: ['Dwight Schrute'], email: ['dwight@schrutefarmsbnb.com'], tel: ['000-0000'] },
-      { name: ['Michael Scott', 'Prison Mike'], email: ['michael@dundermifflin.com'], tel: [] },
+      { name: ['Michael Scott', 'Prison Mike'], email: ['michael@dundermifflin.com'] },
   ]);
 
   const results = await navigator.contacts.select(['name', 'email', 'tel']);
@@ -108,7 +123,7 @@
 
 contactsTestWithUserActivation(async (test, setSelectedContacts) => {
   // Returns partial information since no e-mail addresses are requested.
-  setSelectedContacts([{ name: ['Creed'], email: ['creedthoughts@www.creedthoughts.gov.www'], tel: [] }]);
+  setSelectedContacts([{ name: ['Creed'], email: ['creedthoughts@www.creedthoughts.gov.www'] }]);
 
   const results = await navigator.contacts.select(['name']);
 
@@ -125,7 +140,7 @@
 
 contactsTestWithUserActivation(async (test, setSelectedContacts) => {
   // Returns partial information since no e-mail addresses are requested.
-  setSelectedContacts([{ name: ['Kelly'], email: [], tel: [] }]);
+  setSelectedContacts([{ name: ['Kelly'] }]);
 
   // First request should work.
   const promise1 = new Promise((resolve, reject) => {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid-expected.txt
index 26a5800..3baf75f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid-expected.txt
@@ -8,6 +8,9 @@
 FAIL Property grid-template-columns value '1px repeat(1, 2px) 3px' computes to '1px repeat(1, 2px) 3px' assert_equals: expected "1px repeat(1, 2px) 3px" but got "1px 2px 3px"
 PASS Property grid-template-columns value '1px repeat(auto-fill, 2px) 3px' computes to '1px repeat(auto-fill, 2px) 3px'
 PASS Property grid-template-columns value '1px repeat(auto-fit, 2px) 3px' computes to '1px repeat(auto-fit, 2px) 3px'
+FAIL Property grid-template-columns value '1px [a] repeat(1, 2px 3px) [b] 4px' computes to '1px [a] repeat(1, 2px 3px) [b] 4px' assert_equals: expected "1px [a] repeat(1, 2px 3px) [b] 4px" but got "1px [a] 2px 3px [b] 4px"
+PASS Property grid-template-columns value '1px [a] repeat(auto-fill, 2px 3px) [b] 4px' computes to '1px [a] repeat(auto-fill, 2px 3px) [b] 4px'
+PASS Property grid-template-columns value '1px [a] repeat(auto-fit, 2px 3px) [b] 4px' computes to '1px [a] repeat(auto-fit, 2px 3px) [b] 4px'
 FAIL Property grid-template-columns value '1px [a] repeat(1, [b] 2px [c]) [d] 3px' computes to '1px [a] repeat(1, [b] 2px [c]) [d] 3px' assert_equals: expected "1px [a] repeat(1, [b] 2px [c]) [d] 3px" but got "1px [a b] 2px [c d] 3px"
 PASS Property grid-template-columns value '1px [a] repeat(auto-fill, [b] 2px [c]) [d] 3px' computes to '1px [a] repeat(auto-fill, [b] 2px [c]) [d] 3px'
 PASS Property grid-template-columns value '1px [a] repeat(auto-fit, [b] 2px [c]) [d] 3px' computes to '1px [a] repeat(auto-fit, [b] 2px [c]) [d] 3px'
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid.html
index b95914f..11b0393 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed-nogrid.html
@@ -25,6 +25,9 @@
 test_computed_value("grid-template-columns", "1px repeat(1, 2px) 3px");
 test_computed_value("grid-template-columns", "1px repeat(auto-fill, 2px) 3px");
 test_computed_value("grid-template-columns", "1px repeat(auto-fit, 2px) 3px");
+test_computed_value("grid-template-columns", "1px [a] repeat(1, 2px 3px) [b] 4px");
+test_computed_value("grid-template-columns", "1px [a] repeat(auto-fill, 2px 3px) [b] 4px");
+test_computed_value("grid-template-columns", "1px [a] repeat(auto-fit, 2px 3px) [b] 4px");
 test_computed_value("grid-template-columns", "1px [a] repeat(1, [b] 2px [c]) [d] 3px");
 test_computed_value("grid-template-columns", "1px [a] repeat(auto-fill, [b] 2px [c]) [d] 3px");
 test_computed_value("grid-template-columns", "1px [a] repeat(auto-fit, [b] 2px [c]) [d] 3px");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed.html
index f6d0d97..54380ef4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-columns-computed.html
@@ -25,6 +25,9 @@
 test_computed_value("grid-template-columns", "1px repeat(1, 2px) 3px", "1px 2px 3px");
 test_computed_value("grid-template-columns", "1px repeat(auto-fill, 2px) 3px", "1px 2px 3px");
 test_computed_value("grid-template-columns", "1px repeat(auto-fit, 2px) 3px", "1px 0px 3px");
+test_computed_value("grid-template-columns", "1px [a] repeat(1, 2px 3px) [b] 4px", "1px [a] 2px 3px [b] 4px");
+test_computed_value("grid-template-columns", "1px [a] repeat(auto-fill, 2px 3px) [b] 4px", "1px [a] 2px 3px [b] 4px");
+test_computed_value("grid-template-columns", "1px [a] repeat(auto-fit, 2px 3px) [b] 4px", "1px [a] 0px 0px [b] 4px");
 test_computed_value("grid-template-columns", "1px [a] repeat(1, [b] 2px [c]) [d] 3px", "1px [a b] 2px [c d] 3px");
 test_computed_value("grid-template-columns", "1px [a] repeat(auto-fill, [b] 2px [c]) [d] 3px", "1px [a b] 2px [c d] 3px");
 test_computed_value("grid-template-columns", "1px [a] repeat(auto-fit, [b] 2px [c]) [d] 3px", "1px [a b] 0px [c d] 3px");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-nogrid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-nogrid-expected.txt
index b2946e95..c6ba7b0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-nogrid-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-nogrid-expected.txt
@@ -8,6 +8,9 @@
 FAIL Property grid-template-rows value '1px repeat(1, 2px) 3px' computes to '1px repeat(1, 2px) 3px' assert_equals: expected "1px repeat(1, 2px) 3px" but got "1px 2px 3px"
 PASS Property grid-template-rows value '1px repeat(auto-fill, 2px) 3px' computes to '1px repeat(auto-fill, 2px) 3px'
 PASS Property grid-template-rows value '1px repeat(auto-fit, 2px) 3px' computes to '1px repeat(auto-fit, 2px) 3px'
+FAIL Property grid-template-rows value '1px [a] repeat(1, 2px 3px) [b] 4px' computes to '1px [a] repeat(1, 2px 3px) [b] 4px' assert_equals: expected "1px [a] repeat(1, 2px 3px) [b] 4px" but got "1px [a] 2px 3px [b] 4px"
+PASS Property grid-template-rows value '1px [a] repeat(auto-fill, 2px 3px) [b] 4px' computes to '1px [a] repeat(auto-fill, 2px 3px) [b] 4px'
+PASS Property grid-template-rows value '1px [a] repeat(auto-fit, 2px 3px) [b] 4px' computes to '1px [a] repeat(auto-fit, 2px 3px) [b] 4px'
 FAIL Property grid-template-rows value '1px [a] repeat(1, [b] 2px [c]) [d] 3px' computes to '1px [a] repeat(1, [b] 2px [c]) [d] 3px' assert_equals: expected "1px [a] repeat(1, [b] 2px [c]) [d] 3px" but got "1px [a b] 2px [c d] 3px"
 PASS Property grid-template-rows value '1px [a] repeat(auto-fill, [b] 2px [c]) [d] 3px' computes to '1px [a] repeat(auto-fill, [b] 2px [c]) [d] 3px'
 PASS Property grid-template-rows value '1px [a] repeat(auto-fit, [b] 2px [c]) [d] 3px' computes to '1px [a] repeat(auto-fit, [b] 2px [c]) [d] 3px'
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-nogrid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-nogrid.html
index 03e601ac..057a7fa 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-nogrid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-nogrid.html
@@ -25,6 +25,9 @@
 test_computed_value("grid-template-rows", "1px repeat(1, 2px) 3px");
 test_computed_value("grid-template-rows", "1px repeat(auto-fill, 2px) 3px");
 test_computed_value("grid-template-rows", "1px repeat(auto-fit, 2px) 3px");
+test_computed_value("grid-template-rows", "1px [a] repeat(1, 2px 3px) [b] 4px");
+test_computed_value("grid-template-rows", "1px [a] repeat(auto-fill, 2px 3px) [b] 4px");
+test_computed_value("grid-template-rows", "1px [a] repeat(auto-fit, 2px 3px) [b] 4px");
 test_computed_value("grid-template-rows", "1px [a] repeat(1, [b] 2px [c]) [d] 3px");
 test_computed_value("grid-template-rows", "1px [a] repeat(auto-fill, [b] 2px [c]) [d] 3px");
 test_computed_value("grid-template-rows", "1px [a] repeat(auto-fit, [b] 2px [c]) [d] 3px");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent-expected.txt
deleted file mode 100644
index 27c7ad78..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed-withcontent-expected.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-This is a testharness.js-based test.
-PASS Property grid-template-rows value 'none' computes to '600px'
-PASS Property grid-template-rows value '20%' computes to '120px'
-PASS Property grid-template-rows value 'calc(-0.5em + 10px)' computes to '0px'
-PASS Property grid-template-rows value 'calc(0.5em + 10px)' computes to '30px'
-PASS Property grid-template-rows value 'calc(30% + 40px)' computes to '220px'
-PASS Property grid-template-rows value '5fr' computes to '600px'
-PASS Property grid-template-rows value 'min-content' computes to '60px'
-PASS Property grid-template-rows value 'max-content' computes to '60px'
-PASS Property grid-template-rows value 'auto' computes to '600px'
-PASS Property grid-template-rows value 'minmax(10px, auto)' computes to '600px'
-PASS Property grid-template-rows value 'minmax(20%, max-content)' computes to '120px'
-PASS Property grid-template-rows value 'minmax(min-content, calc(-0.5em + 10px))' computes to '60px'
-PASS Property grid-template-rows value 'minmax(auto, 0)' computes to '60px'
-PASS Property grid-template-rows value 'fit-content(70px)' computes to '60px'
-PASS Property grid-template-rows value 'fit-content(20%)' computes to '60px'
-PASS Property grid-template-rows value 'fit-content(calc(-0.5em + 10px))' computes to '60px'
-PASS Property grid-template-rows value 'repeat(1, 10px)' computes to '10px'
-PASS Property grid-template-rows value 'repeat(1, [one two] 20%)' computes to '[one two] 120px'
-PASS Property grid-template-rows value 'repeat(2, minmax(10px, auto))' computes to '325px 275px'
-PASS Property grid-template-rows value 'repeat(2, fit-content(20%) [three four] 30px 40px [five six])' computes to '60px [three four] 30px 40px [five six] 0px [three four] 30px 40px [five six]'
-PASS Property grid-template-rows value 'min-content repeat(5, minmax(10px, auto))' computes to '60px 108px 108px 108px 108px 108px'
-PASS Property grid-template-rows value '[] 150px [] 1fr []' computes to '150px 450px'
-PASS Property grid-template-rows value 'repeat(auto-fill, 200px)' computes to '200px 200px 200px'
-PASS Property grid-template-rows value 'repeat(auto-fit, [one] 20%)' computes to '[one] 120px [one] 0px [one] 0px [one] 0px [one] 0px'
-PASS Property grid-template-rows value 'repeat(auto-fill, minmax(100px, 5fr) [two])' computes to '100px [two] 100px [two] 100px [two] 100px [two] 100px [two] 100px [two]'
-PASS Property grid-template-rows value 'repeat(auto-fit, [three] minmax(max-content, 6em) [four])' computes to '[three] 240px [four three] 0px [four]'
-FAIL Property grid-template-rows value '[one] repeat(2, minmax(50px, auto)) [two] 30px [three] repeat(auto-fill, 10px) 40px [four five] repeat(2, minmax(200px, auto)) [six]' computes to '[one] 50px 50px [two] 30px [three] 10px 10px 10px 40px [four five] 200px 200px [six]' assert_equals: expected "[one] 50px 50px [two] 30px [three] 10px 10px 10px 40px [four five] 200px 200px [six]" but got "[one] 50px 50px [two] 30px [three] 10px 10px [four five] 10px 40px [six] 200px 200px"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed.html
index 4072262..b7584e1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-rows-computed.html
@@ -25,6 +25,9 @@
 test_computed_value("grid-template-rows", "1px repeat(1, 2px) 3px", "1px 2px 3px");
 test_computed_value("grid-template-rows", "1px repeat(auto-fill, 2px) 3px", "1px 2px 3px");
 test_computed_value("grid-template-rows", "1px repeat(auto-fit, 2px) 3px", "1px 0px 3px");
+test_computed_value("grid-template-rows", "1px [a] repeat(1, 2px 3px) [b] 4px", "1px [a] 2px 3px [b] 4px");
+test_computed_value("grid-template-rows", "1px [a] repeat(auto-fill, 2px 3px) [b] 4px", "1px [a] 2px 3px [b] 4px");
+test_computed_value("grid-template-rows", "1px [a] repeat(auto-fit, 2px 3px) [b] 4px", "1px [a] 0px 0px [b] 4px");
 test_computed_value("grid-template-rows", "1px [a] repeat(1, [b] 2px [c]) [d] 3px", "1px [a b] 2px [c d] 3px");
 test_computed_value("grid-template-rows", "1px [a] repeat(auto-fill, [b] 2px [c]) [d] 3px", "1px [a b] 2px [c d] 3px");
 test_computed_value("grid-template-rows", "1px [a] repeat(auto-fit, [b] 2px [c]) [d] 3px", "1px [a b] 0px [c d] 3px");
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-report-only.https.html b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-report-only.https.html
deleted file mode 100644
index b64a201..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-report-only.https.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <script src='/resources/testharness.js'></script>
-    <script src='/resources/testharnessreport.js'></script>
-  </head>
-  <body>
-    <script>
-const check_report_format = ([reports, observer]) => {
-  const report = reports[0];
-  assert_equals(report.type, "feature-policy-violation");
-  assert_equals(report.url, document.location.href);
-  assert_equals(report.body.featureId, "vr");
-  assert_equals(report.body.sourceFile, document.location.href);
-  assert_equals(typeof report.body.lineNumber, "number");
-  assert_equals(typeof report.body.columnNumber, "number");
-  assert_equals(report.body.disposition, "report");
-};
-
-promise_test(async t => {
-  const report = new Promise(resolve => {
-    new ReportingObserver((reports, observer) => resolve([reports, observer]),
-                          {types: ['feature-policy-violation']}).observe();
-  });
-  await navigator.getVRDisplays();
-  check_report_format(await report);
-}, "VR report only mode");
-    </script>
-  </body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-report-only.https.html.headers b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-report-only.https.html.headers
deleted file mode 100644
index 0761021..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-report-only.https.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy-Report-Only: vr 'none'
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-reporting.https.html b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-reporting.https.html
deleted file mode 100644
index 42a2e73..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-reporting.https.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <script src='/resources/testharness.js'></script>
-    <script src='/resources/testharnessreport.js'></script>
-  </head>
-  <body>
-    <script>
-var check_report_format = (reports, observer) => {
-  let report = reports[0];
-  assert_equals(report.type, "feature-policy-violation");
-  assert_equals(report.url, document.location.href);
-  assert_equals(report.body.featureId, "vr");
-  assert_equals(report.body.sourceFile, document.location.href);
-  assert_equals(typeof report.body.lineNumber, "number");
-  assert_equals(typeof report.body.columnNumber, "number");
-  assert_equals(report.body.disposition, "enforce");
-};
-
-promise_test(async (t) => {
-  const report = new Promise(resolve => {
-    new ReportingObserver((reports, observer) => resolve([reports, observer]),
-                          {types: ['feature-policy-violation']}).observe();
-  });
-  await promise_rejects(t, 'SecurityError', navigator.getVRDisplays(),
-                        "VR device access should not be allowed in this document.");
-  const [reports, observer] = await report;
-  check_report_format(reports, observer);
-}, "VR Report Format");
-    </script>
-  </body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-reporting.https.html.headers b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-reporting.https.html.headers
deleted file mode 100644
index d021af7..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/vr-reporting.https.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: vr 'none'
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-webvr.html b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-webvr.html
deleted file mode 100644
index 64a152b..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-webvr.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<script>
-'use strict';
-
-Promise.resolve().then(() => navigator.getVRDisplays()).then(displays => {
-  window.parent.postMessage({ enabled: true }, '*');
-}, error => {
-  window.parent.postMessage({ enabled: false }, '*');
-});
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/contacts_manager_mock.js b/third_party/blink/web_tests/external/wpt/resources/chromium/contacts_manager_mock.js
index ae4c33b..8e7c9ee 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/contacts_manager_mock.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/contacts_manager_mock.js
@@ -18,18 +18,38 @@
       this.selectedContacts_ = [];
     }
 
-    async select(multiple, includeNames, includeEmails, includeTel) {
+    formatAddress_(address) {
+      // These are all required fields in the mojo definition.
+      return {
+        country: address.country || '',
+        addressLine: address.addressLine || [],
+        region: address.region || '',
+        city: address.city || '',
+        dependentLocality: address.dependentLocality || '',
+        postalCode: address.postCode || '',
+        sortingCode: address.sortingCode || '',
+        organization: address.organization || '',
+        recipient: address.recipient || '',
+        phone: address.phone || '',
+      };
+    }
+
+    async select(multiple, includeNames, includeEmails, includeTel, includeAddresses) {
       if (this.selectedContacts_ === null)
         return {contacts: null};
 
       const contactInfos = this.selectedContacts_.map(contact => {
         const contactInfo = new blink.mojom.ContactInfo();
         if (includeNames)
-          contactInfo.name = contact.name;
+          contactInfo.name = contact.name || [];
         if (includeEmails)
-          contactInfo.email = contact.email;
+          contactInfo.email = contact.email || [];
         if (includeTel)
-          contactInfo.tel = contact.tel;
+          contactInfo.tel = contact.tel || [];
+        if (includeAddresses) {
+          contactInfo.address = contact.address || [];
+          contactInfo.address = contactInfo.address.map(address => this.formatAddress_(address));
+        }
         return contactInfo;
       });
 
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
index 7b2bd8e..f12ce9d 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 563 tests; 533 PASS, 30 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 563 tests; 544 PASS, 19 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS align-content (type: discrete) has testAccumulation function
 PASS align-content: "flex-end" onto "flex-start"
@@ -506,22 +506,22 @@
 PASS transform (type: transformList) has testAccumulation function
 PASS transform: translate
 PASS transform: rotate
-FAIL transform: scale assert_approx_equals: expected matrix(-2, 0, 0, -2, 0, 0) but got matrix(-6, 0, 0, -6, 0, 0): The value should be matrix(-2, 0, 0, -2, 0, 0) at 0ms but got matrix(-6, 0, 0, -6, 0, 0) expected -2 +/- 0.0001 but got -6
-FAIL transform: skew assert_approx_equals: expected matrix(1, 0.57735, -0.36397, 1, 0, 0) but got matrix(1.06418, 0.540297, -0.401023, 0.898198, 0, 0): The value should be matrix(1, 0.57735, -0.36397, 1, 0, 0) at 0ms but got matrix(1.06418, 0.540297, -0.401023, 0.898198, 0, 0) expected 1 +/- 0.0001 but got 1.06418
+PASS transform: scale
+PASS transform: skew
 PASS transform: rotate on translate
-FAIL transform: translate on rotate assert_approx_equals: expected matrix(0, 1, -1, 0, 100, 0) but got matrix(0, 1, -1, 0, 0, 100): The value should be matrix(0, 1, -1, 0, 100, 0) at 0ms but got matrix(0, 1, -1, 0, 0, 100) expected 100 +/- 0.0001 but got 0
+PASS transform: translate on rotate
 PASS transform: rotate and translate on rotate
-FAIL transform: rotate on rotate and translate assert_approx_equals: expected matrix(0, 1, -1, 0, 0, 100) but got matrix(0, 1, -1, 0, 70.7107, 70.7107): The value should be matrix(0, 1, -1, 0, 0, 100) at 0ms but got matrix(0, 1, -1, 0, 70.7107, 70.7107) expected 0 +/- 0.0001 but got 70.7107
-FAIL transform: matrix assert_approx_equals: expected matrix(0, 1, -1, 0, 100, 0) but got matrix(0, 1, -1, 0, 0, 100): The value should be matrix(0, 1, -1, 0, 100, 0) at 0ms but got matrix(0, 1, -1, 0, 0, 100) expected 100 +/- 0.0001 but got 0
+PASS transform: rotate on rotate and translate
+PASS transform: matrix
 PASS transform: rotate3d
 PASS transform: matrix3d
 PASS transform: none
-FAIL transform: non-invertible matrices (non-invertible onto invertible) assert_approx_equals: expected matrix(1, 1, 0, 0, 0, 100) but got matrix(-1, -1, 0, 0, 200, -100): The value should be matrix(1, 1, 0, 0, 0, 100) at 0ms but got matrix(-1, -1, 0, 0, 200, -100) expected 1 +/- 0.0001 but got -1
-FAIL transform: non-invertible matrices (invertible onto non-invertible) assert_approx_equals: expected matrix(-1, 0, 0, -1, 200, 0) but got matrix(-1, -1, 0, 0, 200, 300): The value should be matrix(-1, 0, 0, -1, 200, 0) at 0ms but got matrix(-1, -1, 0, 0, 200, 300) expected 0 +/- 0.0001 but got -1
-FAIL transform: non-invertible matrices in matched transform lists (non-invertible onto invertible) assert_approx_equals: expected matrix(-1, -1, 0, 0, 100, 100) but got matrix(-1, 1, 0, 0, 350, -100): The value should be matrix(-1, -1, 0, 0, 100, 100) at 0ms but got matrix(-1, 1, 0, 0, 350, -100) expected -1 +/- 0.0001 but got 1
-FAIL transform: non-invertible matrices in matched transform lists (invertible onto non-invertible) assert_approx_equals: expected matrix(0, -1, 1, 0, 250, 0) but got matrix(0, 0, -1, -1, -150, -150): The value should be matrix(0, -1, 1, 0, 250, 0) at 0ms but got matrix(0, 0, -1, -1, -150, -150) expected -1 +/- 0.0001 but got 0
-FAIL transform: non-invertible matrices in mismatched transform lists (non-invertible onto invertible) assert_approx_equals: expected matrix(1, 1, 1, 1, 100, 100) but got matrix(-2, -2, -2, -2, 50, -200): The value should be matrix(1, 1, 1, 1, 100, 100) at 0ms but got matrix(-2, -2, -2, -2, 50, -200) expected 1 +/- 0.0001 but got -2
-FAIL transform: non-invertible matrices in mismatched transform lists (invertible onto non-invertible) assert_approx_equals: expected matrix(-2, 0, 0, -2, 250, 0) but got matrix(-2, -2, -2, -2, 350, 350): The value should be matrix(-2, 0, 0, -2, 250, 0) at 0ms but got matrix(-2, -2, -2, -2, 350, 350) expected 0 +/- 0.0001 but got -2
+PASS transform: non-invertible matrices (non-invertible onto invertible)
+PASS transform: non-invertible matrices (invertible onto non-invertible)
+PASS transform: non-invertible matrices in matched transform lists (non-invertible onto invertible)
+PASS transform: non-invertible matrices in matched transform lists (invertible onto non-invertible)
+PASS transform: non-invertible matrices in mismatched transform lists (non-invertible onto invertible)
+PASS transform: non-invertible matrices in mismatched transform lists (invertible onto non-invertible)
 PASS transform-box (type: discrete) has testAccumulation function
 FAIL transform-box: "border-box" onto "fill-box" assert_equals: The value should be border-box at 0ms expected "border-box" but got "fill-box"
 PASS transform-box: "fill-box" onto "border-box"
diff --git a/third_party/blink/web_tests/external/wpt/webvr/META.yml b/third_party/blink/web_tests/external/wpt/webvr/META.yml
deleted file mode 100644
index b50e559..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/META.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-spec: https://immersive-web.github.io/webvr/spec/1.1/
-suggested_reviewers:
-  - klausw
diff --git a/third_party/blink/web_tests/external/wpt/webvr/OWNERS b/third_party/blink/web_tests/external/wpt/webvr/OWNERS
deleted file mode 100644
index f514e5d4..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-file://third_party/blink/renderer/modules/vr/OWNERS
-
-# TEAM: xr-dev@chromium.org
-# COMPONENT: Blink>WebXR>VR
-# WPT-NOTIFY: true
diff --git a/third_party/blink/web_tests/external/wpt/webvr/idlharness.https-expected.txt b/third_party/blink/web_tests/external/wpt/webvr/idlharness.https-expected.txt
deleted file mode 100644
index abb45a2e..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/idlharness.https-expected.txt
+++ /dev/null
@@ -1,101 +0,0 @@
-This is a testharness.js-based test.
-Found 97 tests; 87 PASS, 10 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Partial interface Navigator: original interface defined
-PASS Partial interface Navigator: member names are unique
-PASS Partial interface Window: original interface defined
-PASS Partial interface Window: member names are unique
-PASS Partial interface Gamepad: original interface defined
-PASS Partial interface Gamepad: member names are unique
-FAIL Window interface: attribute onvrdisplayconnect assert_own_property: The global object must have a property "onvrdisplayconnect" expected property "onvrdisplayconnect" missing
-FAIL Window interface: attribute onvrdisplaydisconnect assert_own_property: The global object must have a property "onvrdisplaydisconnect" expected property "onvrdisplaydisconnect" missing
-FAIL Window interface: attribute onvrdisplayactivate assert_own_property: The global object must have a property "onvrdisplayactivate" expected property "onvrdisplayactivate" missing
-FAIL Window interface: attribute onvrdisplaydeactivate assert_own_property: The global object must have a property "onvrdisplaydeactivate" expected property "onvrdisplaydeactivate" missing
-FAIL Window interface: attribute onvrdisplayblur assert_own_property: The global object must have a property "onvrdisplayblur" expected property "onvrdisplayblur" missing
-FAIL Window interface: attribute onvrdisplayfocus assert_own_property: The global object must have a property "onvrdisplayfocus" expected property "onvrdisplayfocus" missing
-FAIL Window interface: attribute onvrdisplaypresentchange assert_own_property: The global object must have a property "onvrdisplaypresentchange" expected property "onvrdisplaypresentchange" missing
-PASS Navigator interface: operation getVRDisplays()
-FAIL Navigator interface: attribute activeVRDisplays assert_true: The prototype object must have a property "activeVRDisplays" expected true got false
-FAIL Navigator interface: attribute vrEnabled assert_true: The prototype object must have a property "vrEnabled" expected true got false
-PASS Gamepad interface: attribute displayId
-PASS VRDisplay interface: existence and properties of interface object
-PASS VRDisplay interface object length
-PASS VRDisplay interface object name
-PASS VRDisplay interface: existence and properties of interface prototype object
-PASS VRDisplay interface: existence and properties of interface prototype object's "constructor" property
-PASS VRDisplay interface: existence and properties of interface prototype object's @@unscopables property
-PASS VRDisplay interface: attribute isPresenting
-PASS VRDisplay interface: attribute capabilities
-PASS VRDisplay interface: attribute stageParameters
-PASS VRDisplay interface: operation getEyeParameters(VREye)
-PASS VRDisplay interface: attribute displayId
-PASS VRDisplay interface: attribute displayName
-PASS VRDisplay interface: operation getFrameData(VRFrameData)
-PASS VRDisplay interface: attribute depthNear
-PASS VRDisplay interface: attribute depthFar
-PASS VRDisplay interface: operation requestAnimationFrame(FrameRequestCallback)
-PASS VRDisplay interface: operation cancelAnimationFrame(long)
-PASS VRDisplay interface: operation requestPresent([object Object])
-PASS VRDisplay interface: operation exitPresent()
-PASS VRDisplay interface: operation getLayers()
-PASS VRDisplay interface: operation submitFrame()
-PASS VRDisplayCapabilities interface: existence and properties of interface object
-PASS VRDisplayCapabilities interface object length
-PASS VRDisplayCapabilities interface object name
-PASS VRDisplayCapabilities interface: existence and properties of interface prototype object
-PASS VRDisplayCapabilities interface: existence and properties of interface prototype object's "constructor" property
-PASS VRDisplayCapabilities interface: existence and properties of interface prototype object's @@unscopables property
-PASS VRDisplayCapabilities interface: attribute hasPosition
-PASS VRDisplayCapabilities interface: attribute hasExternalDisplay
-PASS VRDisplayCapabilities interface: attribute canPresent
-PASS VRDisplayCapabilities interface: attribute maxLayers
-PASS VRPose interface: existence and properties of interface object
-PASS VRPose interface object length
-PASS VRPose interface object name
-PASS VRPose interface: existence and properties of interface prototype object
-PASS VRPose interface: existence and properties of interface prototype object's "constructor" property
-PASS VRPose interface: existence and properties of interface prototype object's @@unscopables property
-PASS VRPose interface: attribute position
-PASS VRPose interface: attribute linearVelocity
-PASS VRPose interface: attribute linearAcceleration
-PASS VRPose interface: attribute orientation
-PASS VRPose interface: attribute angularVelocity
-PASS VRPose interface: attribute angularAcceleration
-PASS VRFrameData interface: existence and properties of interface object
-PASS VRFrameData interface object length
-PASS VRFrameData interface object name
-PASS VRFrameData interface: existence and properties of interface prototype object
-PASS VRFrameData interface: existence and properties of interface prototype object's "constructor" property
-PASS VRFrameData interface: existence and properties of interface prototype object's @@unscopables property
-PASS VRFrameData interface: attribute leftProjectionMatrix
-PASS VRFrameData interface: attribute leftViewMatrix
-PASS VRFrameData interface: attribute rightProjectionMatrix
-PASS VRFrameData interface: attribute rightViewMatrix
-PASS VRFrameData interface: attribute pose
-PASS VREyeParameters interface: existence and properties of interface object
-PASS VREyeParameters interface object length
-PASS VREyeParameters interface object name
-PASS VREyeParameters interface: existence and properties of interface prototype object
-PASS VREyeParameters interface: existence and properties of interface prototype object's "constructor" property
-PASS VREyeParameters interface: existence and properties of interface prototype object's @@unscopables property
-PASS VREyeParameters interface: attribute offset
-PASS VREyeParameters interface: attribute renderWidth
-PASS VREyeParameters interface: attribute renderHeight
-PASS VRStageParameters interface: existence and properties of interface object
-PASS VRStageParameters interface object length
-PASS VRStageParameters interface object name
-PASS VRStageParameters interface: existence and properties of interface prototype object
-PASS VRStageParameters interface: existence and properties of interface prototype object's "constructor" property
-PASS VRStageParameters interface: existence and properties of interface prototype object's @@unscopables property
-PASS VRStageParameters interface: attribute sittingToStandingTransform
-PASS VRStageParameters interface: attribute sizeX
-PASS VRStageParameters interface: attribute sizeZ
-PASS VRDisplayEvent interface: existence and properties of interface object
-FAIL VRDisplayEvent interface object length assert_equals: wrong value for VRDisplayEvent.length expected 2 but got 1
-PASS VRDisplayEvent interface object name
-PASS VRDisplayEvent interface: existence and properties of interface prototype object
-PASS VRDisplayEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS VRDisplayEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS VRDisplayEvent interface: attribute display
-PASS VRDisplayEvent interface: attribute reason
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webvr/idlharness.https.html b/third_party/blink/web_tests/external/wpt/webvr/idlharness.https.html
deleted file mode 100644
index 2a89c57..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/idlharness.https.html
+++ /dev/null
@@ -1,242 +0,0 @@
-<!doctype html>
-<html>
-  <head>
-    <meta charset=utf-8>
-    <title>WebVR IDL test</title>
-    <link rel="help" href="https://w3c.github.io/webvr/">
-
-    <script src=/resources/testharness.js></script>
-    <script src=/resources/testharnessreport.js></script>
-    <script src=/resources/WebIDLParser.js></script>
-    <script src=/resources/idlharness.js></script>
-<script id="webvr_idl" type=text/plain>
-// Archived version of the WebVR spec from
-// https://w3c.github.io/webvr/archive/prerelease/1.1/index.html
-
-interface VRDisplay : EventTarget {
-  readonly attribute boolean isPresenting;
-
-  /**
-   * Dictionary of capabilities describing the VRDisplay.
-   */
-  [SameObject] readonly attribute VRDisplayCapabilities capabilities;
-
-  /**
-   * If this VRDisplay supports room-scale experiences, the optional
-   * stage attribute contains details on the room-scale parameters.
-   * The stageParameters attribute can not change between null
-   * and non-null once the VRDisplay is enumerated; however,
-   * the values within VRStageParameters may change after
-   * any call to VRDisplay.submitFrame as the user may re-configure
-   * their environment at any time.
-   */
-  readonly attribute VRStageParameters? stageParameters;
-
-  /**
-   * Return the current VREyeParameters for the given eye.
-   */
-  VREyeParameters getEyeParameters(VREye whichEye);
-
-  /**
-   * An identifier for this distinct VRDisplay. Used as an
-   * association point in the Gamepad API.
-   */
-  readonly attribute unsigned long displayId;
-
-  /**
-   * A display name, a user-readable name identifying it.
-   */
-  readonly attribute DOMString displayName;
-
-  /**
-   * Populates the passed VRFrameData with the information required to render
-   * the current frame.
-   */
-  boolean getFrameData(VRFrameData frameData);
-
-  /**
-   * z-depth defining the near plane of the eye view frustum
-   * enables mapping of values in the render target depth
-   * attachment to scene coordinates. Initially set to 0.01.
-   */
-  attribute double depthNear;
-
-  /**
-   * z-depth defining the far plane of the eye view frustum
-   * enables mapping of values in the render target depth
-   * attachment to scene coordinates. Initially set to 10000.0.
-   */
-  attribute double depthFar;
-
-  /**
-   * The callback passed to `requestAnimationFrame` will be called
-   * any time a new frame should be rendered. When the VRDisplay is
-   * presenting the callback will be called at the native refresh
-   * rate of the HMD. When not presenting this function acts
-   * identically to how window.requestAnimationFrame acts. Content should
-   * make no assumptions of frame rate or vsync behavior as the HMD runs
-   * asynchronously from other displays and at differing refresh rates.
-   */
-  long requestAnimationFrame(FrameRequestCallback callback);
-
-  /**
-   * Passing the value returned by `requestAnimationFrame` to
-   * `cancelAnimationFrame` will unregister the callback.
-   */
-  void cancelAnimationFrame(long handle);
-
-  /**
-   * Begin presenting to the VRDisplay. Must be called in response to a user gesture.
-   * Repeat calls while already presenting will update the layers being displayed.
-   * If the number of values in the leftBounds/rightBounds arrays is not 0 or 4 for any of the passed layers the promise is rejected
-   * If the source of any of the layers is not present (null), the promise is rejected.
-   */
-  Promise<void> requestPresent(sequence<VRLayerInit> layers);
-
-  /**
-   * Stops presenting to the VRDisplay.
-   */
-  Promise<void> exitPresent();
-
-  /**
-   * Get the layers currently being presented.
-   */
-  sequence<VRLayerInit> getLayers();
-
-  /**
-   * The layer provided to the VRDisplay will be captured and presented
-   * in the HMD. Calling this function has the same effect on the source
-   * canvas as any other operation that uses its source image, and canvases
-   * created without preserveDrawingBuffer set to true will be cleared.
-   */
-  void submitFrame();
-};
-
-typedef (HTMLCanvasElement or
-         OffscreenCanvas) VRSource;
-
-dictionary VRLayerInit {
-  VRSource? source = null;
-
-  sequence<float> leftBounds = [];
-  sequence<float> rightBounds = [];
-};
-
-interface VRDisplayCapabilities {
-  readonly attribute boolean hasPosition;
-  readonly attribute boolean hasExternalDisplay;
-  readonly attribute boolean canPresent;
-  readonly attribute unsigned long maxLayers;
-};
-
-enum VREye {
-  "left",
-  "right"
-};
-
-interface VRPose {
-  readonly attribute Float32Array? position;
-  readonly attribute Float32Array? linearVelocity;
-  readonly attribute Float32Array? linearAcceleration;
-
-  readonly attribute Float32Array? orientation;
-  readonly attribute Float32Array? angularVelocity;
-  readonly attribute Float32Array? angularAcceleration;
-};
-
-[Constructor]
-interface VRFrameData {
-  readonly attribute Float32Array leftProjectionMatrix;
-  readonly attribute Float32Array leftViewMatrix;
-
-  readonly attribute Float32Array rightProjectionMatrix;
-  readonly attribute Float32Array rightViewMatrix;
-
-  readonly attribute VRPose pose;
-};
-
-interface VREyeParameters {
-  readonly attribute Float32Array offset;
-
-  readonly attribute unsigned long renderWidth;
-  readonly attribute unsigned long renderHeight;
-};
-
-interface VRStageParameters {
-  readonly attribute Float32Array sittingToStandingTransform;
-
-  readonly attribute float sizeX;
-  readonly attribute float sizeZ;
-};
-
-partial interface Navigator {
-  Promise<sequence<VRDisplay>> getVRDisplays();
-  readonly attribute FrozenArray<VRDisplay> activeVRDisplays;
-  readonly attribute boolean vrEnabled;
-};
-
-enum VRDisplayEventReason {
-  "mounted",
-  "navigation",
-  "requested",
-  "unmounted"
-};
-
-[Constructor(DOMString type, VRDisplayEventInit eventInitDict)]
-interface VRDisplayEvent : Event {
-  readonly attribute VRDisplay display;
-  readonly attribute VRDisplayEventReason? reason;
-};
-
-dictionary VRDisplayEventInit : EventInit {
-  required VRDisplay display;
-  VRDisplayEventReason reason;
-};
-
-partial interface Window {
-  attribute EventHandler onvrdisplayconnect;
-  attribute EventHandler onvrdisplaydisconnect;
-  attribute EventHandler onvrdisplayactivate;
-  attribute EventHandler onvrdisplaydeactivate;
-  attribute EventHandler onvrdisplayblur;
-  attribute EventHandler onvrdisplayfocus;
-  attribute EventHandler onvrdisplaypresentchange;
-};
-
-partial interface Gamepad {
-  readonly attribute unsigned long displayId;
-};
-</script>
-  </head>
-  <body>
-    <h1 class="instructions">Description</h1>
-    <p class="instructions">
-      This test verifies that implementations of the WebVR API match its WebIDL definition.
-    </p>
-
-    <div id='log'></div>
-
-    <script>
-      setup( () => {
-        var idl_array = new IdlArray();
-        idl_array.add_untested_idls("[Global=Window, Exposed=Window] interface Window {};");
-        idl_array.add_untested_idls("interface Navigator {};");
-        idl_array.add_untested_idls("interface Event {};");
-        idl_array.add_untested_idls("interface EventTarget {};");
-        idl_array.add_untested_idls("interface HTMLIFrameElement {};");
-        idl_array.add_untested_idls("interface Gamepad {};");
-
-        idl_array.add_untested_idls(`dictionary EventInit {
-          boolean bubbles = false;
-          boolean cancelable = false;
-          boolean composed = false;
-        };`);
-
-        idl_array.add_idls(document.getElementById("webvr_idl").textContent);
-
-        idl_array.test();
-        done();
-      }, {explicit_done: true});
-    </script>
-  </body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html
deleted file mode 100644
index 567499c..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src=/feature-policy/resources/featurepolicy.js></script>
-
-  <script>
-    'use strict';
-    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
-    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
-      same_origin_src;
-    var header = 'Feature-Policy header vr "none"';
-
-    promise_test(() => {
-      return navigator.getVRDisplays().then(() => {
-        assert_unreached('expected promise to reject');
-      }, error => {
-      });
-    }, header + ' disallows the top-level document.');
-
-    async_test(t => {
-      test_feature_availability(
-          'navigator.getVRDisplays()', t, same_origin_src,
-          expect_feature_unavailable_default);
-    }, header + ' disallows same-origin iframes.');
-
-    async_test(t => {
-      test_feature_availability(
-          'navigator.getVRDisplays()', t, cross_origin_src,
-          expect_feature_unavailable_default);
-    }, header + ' disallows cross-origin iframes.');
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html.headers
deleted file mode 100644
index d021af7..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/webvr-disabled-by-feature-policy.https.sub.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: vr 'none'
diff --git a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html b/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html
deleted file mode 100644
index da01dafd..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src=/feature-policy/resources/featurepolicy.js></script>
-  <script>
-    'use strict';
-    var relative_path = '/feature-policy/resources/feature-policy-webvr.html';
-    var base_src = '/feature-policy/resources/redirect-on-load.html#';
-    var same_origin_src = base_src + relative_path;
-    var cross_origin_src = base_src + 'https://{{domains[www]}}:{{ports[https][0]}}' +
-        relative_path;
-    var header = 'Feature-Policy allow="vr" attribute';
-
-    async_test(t => {
-      test_feature_availability(
-          'navigator.getVRDisplays()', t, same_origin_src,
-          expect_feature_available_default, 'vr');
-    }, header + ' allows same-origin relocation');
-
-    async_test(t => {
-      test_feature_availability(
-          'navigator.getVRDisplays()', t, cross_origin_src,
-          expect_feature_unavailable_default, 'vr');
-    }, header + ' disallows cross-origin relocation');
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute.https.sub.html b/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute.https.sub.html
deleted file mode 100644
index d715f90..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy-attribute.https.sub.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src=/feature-policy/resources/featurepolicy.js></script>
-  <script>
-    'use strict';
-    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
-    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
-      same_origin_src;
-    var header = 'Feature-Policy allow="vr" attribute';
-
-    async_test(t => {
-      test_feature_availability(
-          'navigator.getVRDisplays()', t, same_origin_src,
-          expect_feature_available_default, 'vr');
-    }, header + ' allows same-origin iframe');
-
-    async_test(t => {
-      test_feature_availability(
-          'navigator.getVRDisplays()', t, cross_origin_src,
-          expect_feature_available_default, 'vr');
-    }, header + ' allows cross-origin iframe');
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html
deleted file mode 100644
index ee02566..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src=/feature-policy/resources/featurepolicy.js></script>
-
-  <script>
-    'use strict';
-    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
-    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
-      same_origin_src;
-    var header = 'Feature-Policy header vr *';
-
-    promise_test(
-        () => navigator.getVRDisplays(),
-        header + ' allows the top-level document.');
-
-    async_test(t => {
-      test_feature_availability(
-          'navigator.getVRDisplays()', t, same_origin_src,
-          expect_feature_available_default);
-    }, header + ' allows same-origin iframes.');
-
-    async_test(t => {
-      test_feature_availability(
-          'navigator.getVRDisplays()', t, cross_origin_src,
-          expect_feature_available_default);
-    }, header + ' allows cross-origin iframes.');
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html.headers
deleted file mode 100644
index e7427ee..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-by-feature-policy.https.sub.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: vr *
diff --git a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html b/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html
deleted file mode 100644
index bd7e82f..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src=/feature-policy/resources/featurepolicy.js></script>
-
-  <script>
-    'use strict';
-    var same_origin_src = '/feature-policy/resources/feature-policy-webvr.html';
-    var cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
-      same_origin_src;
-    var header = 'Feature-Policy header vr "self"';
-
-    promise_test(
-        () => navigator.getVRDisplays(),
-        header + ' allows the top-level document.');
-
-    async_test(t => {
-      test_feature_availability(
-          'navigator.getVRDisplays()', t, same_origin_src,
-          expect_feature_available_default);
-    }, header + ' allows same-origin iframes.');
-
-    async_test(t => {
-      test_feature_availability(
-          'navigator.getVRDisplays()', t, cross_origin_src,
-          expect_feature_unavailable_default);
-    }, header + ' disallows cross-origin iframes.');
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html.headers b/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html.headers
deleted file mode 100644
index 87d343d..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/webvr-enabled-on-self-origin-by-feature-policy.https.sub.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: vr 'self'
diff --git a/third_party/blink/web_tests/external/wpt/webvr/webvr-supported-by-feature-policy.html b/third_party/blink/web_tests/external/wpt/webvr/webvr-supported-by-feature-policy.html
deleted file mode 100644
index 416e286..0000000
--- a/third_party/blink/web_tests/external/wpt/webvr/webvr-supported-by-feature-policy.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!DOCTYPE html>
-<title>Test that (obsolete) vr is advertised in the feature list</title>
-<!-- Some WebVR implementations used "vr", but this is now obsolete and WebXR is moving in a different direction.-->>
-<link rel="help" href="https://github.com/immersive-web/webxr/issues/308">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-test(() => {
-    assert_in_array('vr', document.featurePolicy.features());
-}, 'document.featurePolicy.features should advertise (obsolete) vr.');
-</script>
diff --git a/third_party/blink/web_tests/fast/forms/resources/picker-common.js b/third_party/blink/web_tests/fast/forms/resources/picker-common.js
index 8db456d..9b95148 100644
--- a/third_party/blink/web_tests/fast/forms/resources/picker-common.js
+++ b/third_party/blink/web_tests/fast/forms/resources/picker-common.js
@@ -45,17 +45,8 @@
             eventSender.keyDown("ArrowDown", ["altKey"]);
         }
     }
-    popupWindow = internals.pagePopupWindow;
-    if (typeof callback === "function" && popupWindow)
-        setPopupOpenCallback(callback);
-    else if (typeof errorCallback === "function" && !popupWindow)
-        errorCallback();
-}
-
-function clickToOpenPicker(x, y, callback, errorCallback) {
-    eventSender.mouseMoveTo(x, y);
-    eventSender.mouseDown();
-    eventSender.mouseUp();
+    // To avoid flakiness of test about focused status of the element.
+    element.focus();
     popupWindow = internals.pagePopupWindow;
     if (typeof callback === "function" && popupWindow)
         setPopupOpenCallback(callback);
diff --git a/third_party/blink/web_tests/fast/hidpi/static/validation-bubble-appearance-hidpi.html b/third_party/blink/web_tests/fast/hidpi/static/validation-bubble-appearance-hidpi.html
index 03d3e9a..208db74 100644
--- a/third_party/blink/web_tests/fast/hidpi/static/validation-bubble-appearance-hidpi.html
+++ b/third_party/blink/web_tests/fast/hidpi/static/validation-bubble-appearance-hidpi.html
@@ -2,10 +2,12 @@
 <body>
 <div style="width:100%; height:4em; overflow:scroll">
 <div style="position:relative; left:90%; top:1000px; width:100%; font-family:system-ui; font-size:16px">
-Font-size in the validation bubble should be same as this paragraph.<input type=checkbox required>
+Font-size in the validation bubble should be same as this paragraph.<input id=checkbox type=checkbox required>
 </div>
 </div>
 <script>
-document.querySelector('input').reportValidity();
+checkbox.reportValidity();
+// To avoid flakiness of test about focused status of the element.
+checkbox.focus();
 </script>
 </body>
diff --git a/third_party/blink/web_tests/platform/linux/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png b/third_party/blink/web_tests/platform/linux/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
index 459948dd..2cf57b2 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
+++ b/third_party/blink/web_tests/platform/linux/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png b/third_party/blink/web_tests/platform/linux/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
index 459948dd..2cf57b2 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
index d7a3577..1f98884 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/webxr/ar-module/idlharness.https.window-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/webxr/ar-module/idlharness.https.window-expected.txt
deleted file mode 100644
index 6f31597..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/webxr/ar-module/idlharness.https.window-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-FAIL idl_test validation Validation error at line 19 in webxr, inside `enum XRSessionMode`:
-enum XRSessionMode {
-     ^ The name "XRSessionMode" of type "enum" was already seen
-PASS Partial interface XRSession: original interface defined
-PASS Partial interface XRSession: member names are unique
-PASS XRSession interface: attribute environmentBlendMode
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
index d7a3577..1f98884 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
index 2d26010..21bdc04 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
index 2d26010..21bdc04 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/fast/hidpi/static/calendar-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/fast/hidpi/static/calendar-picker-appearance-expected.png
index 6ddedb9..c6dbf9a 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/fast/hidpi/static/calendar-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/fast/hidpi/static/calendar-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/fast/hidpi/static/data-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/fast/hidpi/static/data-suggestion-picker-appearance-expected.png
index 2c84db24..f343a9b 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/fast/hidpi/static/data-suggestion-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/fast/hidpi/static/data-suggestion-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
index 40840209..44d141d 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
index 40840209..44d141d 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/prefer_compositing_to_lcd_text/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png b/third_party/blink/web_tests/platform/mac/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
index 532a1a4c..c066138a 100644
--- a/third_party/blink/web_tests/platform/mac/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
+++ b/third_party/blink/web_tests/platform/mac/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
index 368a0bfe..c4beace 100644
--- a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/fast/hidpi/static/validation-bubble-appearance-hidpi-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/not-site-per-process/README.md b/third_party/blink/web_tests/virtual/not-site-per-process/README.md
index 2c70610a..7da7f6c6 100644
--- a/third_party/blink/web_tests/virtual/not-site-per-process/README.md
+++ b/third_party/blink/web_tests/virtual/not-site-per-process/README.md
@@ -15,7 +15,11 @@
 Tests under `virtual/not-site-per-process` are run with
 `--disable-site-isolation-trials` cmdline flag which turns off site
 isolation.  This is needed to preserve test coverage provided by around
-60 tests that fail when run with site isolation.
+60 tests that fail when run with site isolation. `isolated-code-cache` tests are
+also run with `--disable-features=SplitCacheByNetworkIsolationKey` which turns
+off HTTP cache partitioning. This is needed as a test expects cross-origin
+resources to be cached. Equivalent tests with the feature enabled can be found
+under `virtual/split-http-cache-not-site-per-process`.
 
 When modifying the list of files that behave differently with and without
 OOPIFs, please consider modifying all the locations below:
diff --git a/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt b/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt
new file mode 100644
index 0000000..58f5e0e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/README.txt
@@ -0,0 +1,6 @@
+# This suite runs the tests in http/tests/devtools/isolated-code-cache with
+# --enable-features=SplitCacheByNetworkIsolationKey
+# --disable-site-isolation-trials
+# This feature partitions the HTTP cache by network isolation key for improved
+# security. Tracking bug: crbug.com/910708. This test disables site isolation,
+# which would cause similar results.
diff --git a/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/cross-origin-test-expected.txt b/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/cross-origin-test-expected.txt
new file mode 100644
index 0000000..81e758db
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/cross-origin-test-expected.txt
@@ -0,0 +1,82 @@
+Tests V8 code cache for javascript resources
+
+---First navigation - produce and consume code cache ------
+
+v8.compile Properties:
+{
+    data : {
+        columnNumber : 0
+        lineNumber : 0
+        notStreamedReason : "script too small"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.js
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compile"
+}
+Text details for v8.compile: v8-cache-script.js:1
+v8.compile Properties:
+{
+    data : {
+        columnNumber : 0
+        lineNumber : 0
+        notStreamedReason : "already used streamed data"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.js
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compile"
+}
+Text details for v8.compile: v8-cache-script.js:1
+v8.compile Properties:
+{
+    data : {
+        columnNumber : 0
+        lineNumber : 0
+        notStreamedReason : "already used streamed data"
+        producedCacheSize : <number>
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.js
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compile"
+}
+Text details for v8.compile: v8-cache-script.js:1
+v8.compile Properties:
+{
+    data : {
+        cacheConsumeOptions : "code"
+        cacheRejected : false
+        columnNumber : 0
+        consumedCacheSize : <number>
+        lineNumber : 0
+        notStreamedReason : "already used streamed data"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.js
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compile"
+}
+Text details for v8.compile: v8-cache-script.js:1
+
+--- Second navigation - from a different origin ------
+
+v8.compile Properties:
+{
+    data : {
+        columnNumber : 0
+        lineNumber : 0
+        notStreamedReason : "script too small"
+        streamed : <boolean>
+        url : .../devtools/resources/v8-cache-script.js
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.compile"
+}
+Text details for v8.compile: v8-cache-script.js:1
+
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
index 324728f0..361cfd4 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
@@ -17,6 +17,5 @@
 picture-in-picture
 sync-xhr
 usb
-vr
 xr-spatial-tracking
 
diff --git a/third_party/blink/web_tests/vr/VRDisplay_rAF_fires_with_window_rAF.html b/third_party/blink/web_tests/vr/VRDisplay_rAF_fires_with_window_rAF.html
deleted file mode 100644
index 2d6b6056..0000000
--- a/third_party/blink/web_tests/vr/VRDisplay_rAF_fires_with_window_rAF.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t, device_controller) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-    let counter = 0;
-    let rAFTime = 0;
-
-    function onWindowAnimationFrame1(time) {
-      window.requestAnimationFrame(onWindowAnimationFrame1);
-      rAFTime = time;
-      t.step(function() {
-        assert_equals(counter % 4, 0);
-      });
-      counter++;
-    }
-
-    function onDisplayAnimationFrame1(time) {
-      display.requestAnimationFrame(onDisplayAnimationFrame1);
-      t.step(function() {
-        assert_equals(time, rAFTime);
-        assert_equals(counter % 4, 1);
-      });
-      counter++;
-    }
-
-    function onDisplayAnimationFrame2(time) {
-      display.requestAnimationFrame(onDisplayAnimationFrame2);
-      t.step(function() {
-        assert_equals(time, rAFTime);
-        assert_equals(counter % 4, 2);
-      });
-      counter++;
-    }
-
-    function onWindowAnimationFrame2(time) {
-      window.requestAnimationFrame(onWindowAnimationFrame2);
-      t.step(function() {
-        assert_equals(time, rAFTime);
-        assert_equals(counter % 4, 3);
-      });
-      if (counter == 15) {
-        t.done();
-      }
-      counter++;
-    }
-
-    function onFirstAnimationFrame() {
-      window.requestAnimationFrame(onWindowAnimationFrame1);
-      display.requestAnimationFrame(onDisplayAnimationFrame1);
-      display.requestAnimationFrame(onDisplayAnimationFrame2);
-      window.requestAnimationFrame(onWindowAnimationFrame2);
-    }
-
-    // Ensure that there's a non-null pose available for the mock framework to
-    // return, and wait for one rAF callback to let it propagate. This is a
-    // workaround for delayed promise execution in the mocking framework.
-    device_controller.setPose(VALID_POSE);
-    display.requestAnimationFrame(onFirstAnimationFrame);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"display and window rAF callbacks fire together");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/events_vrdisplayactivate.html b/third_party/blink/web_tests/vr/events_vrdisplayactivate.html
deleted file mode 100644
index c6f4bea7..0000000
--- a/third_party/blink/web_tests/vr/events_vrdisplayactivate.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t, deviceController) => {
-  let watcherDone = new Event("watcherdone");
-  let eventWatcher = new EventWatcher(t, window, ["vrdisplayactivate",
-                                                  "watcherdone"]);
-  eventWatcher.wait_for(["vrdisplayactivate",
-                         "watcherdone"])
-    .then( () => {
-      t.done();
-    });
-
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    function onDisplayActivate() {
-      display.requestPresent([{source: webglCanvas}]).then( () => {
-        window.dispatchEvent(watcherDone);
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "requestPresent rejected");
-        t.done();
-      });
-    }
-    window.addEventListener("vrdisplayactivate", onDisplayActivate, false);
-
-    // Timeout here is required because addEventListener sends a call to mojo to
-    // register the listener on the backend, which doesn't go through until the
-    // javascript pauses.
-    setTimeout(() => {
-      deviceController.forceActivate(2 /*Display mounted, although any int 0-3 is valid*/);
-    }, 100);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test vrdisplayactivate fires when display activates and resulting event can be used to present");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/events_vrdisplayconnect.html b/third_party/blink/web_tests/vr/events_vrdisplayconnect.html
deleted file mode 100644
index ec53079..0000000
--- a/third_party/blink/web_tests/vr/events_vrdisplayconnect.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<!DOCTYPE html>
-<script> let legacy_vr_test = true; </script>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  let watcherDone = new Event("watcherdone");
-  let eventWatcher = new EventWatcher(t, window, ["vrdisplayconnect",
-                                                  "watcherdone"]);
-  eventWatcher.wait_for(["vrdisplayconnect",
-                         "watcherdone"])
-    .then( () => {
-      t.done();
-    });
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    t.step( () => {
-      assert_equals(displays.length, 1);
-    }, "Starting with a single display");
-
-    navigator.vr.test.simulateDeviceConnection(VALID_NON_IMMERSIVE_DEVICE);
-
-    setTimeout( () => {
-      navigator.getVRDisplays().then( (displays) => {
-        t.step( () => {
-          // VR only supplies one display now to be compatible with the WebXR
-          // implementation.
-          assert_equals(displays.length, 1);
-        }, "Check display");
-        window.dispatchEvent(watcherDone);
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "second getVRDisplays rejected");
-      });
-    }, 100);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test vrdisplayconnect fires once when new display connected");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/events_vrdisplaypresentchange.html b/third_party/blink/web_tests/vr/events_vrdisplaypresentchange.html
deleted file mode 100644
index ac0681f..0000000
--- a/third_party/blink/web_tests/vr/events_vrdisplaypresentchange.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    let watcherDone = new Event("watcherdone");
-    let eventWatcher = new EventWatcher(t, window, ["vrdisplaypresentchange",
-                                                    "watcherdone"]);
-    eventWatcher.wait_for(["vrdisplaypresentchange", /*First requestPresent*/
-                           "vrdisplaypresentchange", /*First exitPresent*/
-                           "watcherdone"])
-      .then( () => {
-        t.done();
-      });
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        // Should not fire again if already presenting
-        display.requestPresent([{ source: webglCanvas }]).then( () => {
-          display.exitPresent().then( () => {
-            // Should not fire again if not presenting
-            display.exitPresent().then( () => {
-              t.step( () => {
-                assert_unreached();
-              }, "Second exitPresent succeeded");
-              t.done();
-            }, (err) => {
-              setTimeout( () => {window.dispatchEvent(watcherDone);}, 100);
-            });
-          }, (err) => {
-            t.step( () => {
-              assert_unreached(err);
-            }, "exitPresent rejected");
-          });
-        }, (err) => {
-          t.step( () => {
-            assert_unreached(err);
-          }, "second requestPresent rejected");
-          t.done();
-        });
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "requestPresent rejected");
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached();
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test vrdisplaypresentchange fires only when expected");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/exitPresent_reject_notpresenting.html b/third_party/blink/web_tests/vr/exitPresent_reject_notpresenting.html
deleted file mode 100644
index 47c362e2..0000000
--- a/third_party/blink/web_tests/vr/exitPresent_reject_notpresenting.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    return display.exitPresent().then( () => {
-      t.step( () => {
-        assert_unreached();
-      }, "exitPresent succeeded unexpectedly");
-    }, (err) => {
-      t.step( () => {
-        assert_true(err instanceof DOMException);
-        assert_equals(err.name, "InvalidStateError");
-        assert_false(display.isPresenting);
-      }, "Request rejected properly");
-    }).then( () => {
-      t.done();
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done()
-  });
-}, [fakeDisplays["Pixel"]],
-"Test exitPresent rejects if not presenting");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/exitPresent_resolve.html b/third_party/blink/web_tests/vr/exitPresent_resolve.html
deleted file mode 100644
index 9410813..0000000
--- a/third_party/blink/web_tests/vr/exitPresent_resolve.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          assert_true(display.isPresenting);
-        }, "Present request succeeded");
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "Present request rejected");
-      }).then( () => {
-        display.exitPresent().then( () => {
-          t.step( () => {
-            assert_false(display.isPresenting);
-          }, "Display should no longer be presenting");
-        }, (err) => {
-          t.step( () => {
-            assert_unreached(err);
-          }, "Should never reach here");
-        }).then( () => {
-          t.done();
-        });
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test exitPresent resolves");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getEyeParameters_match.html b/third_party/blink/web_tests/vr/getEyeParameters_match.html
deleted file mode 100644
index b120c6ed..0000000
--- a/third_party/blink/web_tests/vr/getEyeParameters_match.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-let fakeDisplay = fakeDisplays["Pixel"];
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-    let scale = fakeDisplay.webvrDefaultFramebufferScale;
-
-    function compareEyes(actual, expected) {
-      t.step( () => {
-        assert_approx_equals(actual.offset[0], expected.headFromEye.matrix[12],
-            FLOAT_EPSILON);
-        assert_approx_equals(actual.offset[1], expected.headFromEye.matrix[13],
-            FLOAT_EPSILON);
-        assert_approx_equals(actual.offset[2], expected.headFromEye.matrix[14],
-            FLOAT_EPSILON);
-        assert_equals(actual.renderWidth, expected.renderWidth * scale);
-        assert_equals(actual.renderHeight, expected.renderHeight * scale);
-      }, "Returned eye parameters match provided parameters");
-    }
-
-    compareEyes(display.getEyeParameters("left"), fakeDisplay.leftEye);
-    compareEyes(display.getEyeParameters("right"), fakeDisplay.rightEye);
-    t.done();
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplay],
-"Test that actual eye parameters match the expected parameters");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getEyeParameters_null.html b/third_party/blink/web_tests/vr/getEyeParameters_null.html
deleted file mode 100644
index 057b446..0000000
--- a/third_party/blink/web_tests/vr/getEyeParameters_null.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-let fakeDisplay = fakeDisplays["FakeMagicWindowOnly"];
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-
-    t.step( () => {
-      assert_equals(display.getEyeParameters("left"), null);
-      assert_equals(display.getEyeParameters("right"), null);
-    }, "Eye parameters are null");
-
-    t.done();
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplay],
-"Test that device which doesn't present has no eye parameters");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getFrameData_noupdate.html b/third_party/blink/web_tests/vr/getFrameData_noupdate.html
deleted file mode 100644
index ae66e1a..0000000
--- a/third_party/blink/web_tests/vr/getFrameData_noupdate.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t, device_controller) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-    device_controller.setPose(VALID_POSE);
-    var fd = new VRFrameData();
-    t.step( () => {
-      assert_false(display.getFrameData(fd));
-    }, "getFrameData did not update object initially");
-
-    function checkLater() {
-      t.step( () => {
-        assert_false(display.getFrameData(fd));
-      }, "getFrameData did not update object after delay");
-      t.done();
-    }
-
-    window.setTimeout(checkLater, 500);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"getFrameData does not update objects until data is available after rAF call");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getFrameData_oneframeupdate.html b/third_party/blink/web_tests/vr/getFrameData_oneframeupdate.html
deleted file mode 100644
index ce21537..0000000
--- a/third_party/blink/web_tests/vr/getFrameData_oneframeupdate.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t, device_controller) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-    var expected_pose = VALID_POSE;
-    var fd = new VRFrameData();
-    var counter = 0;
-
-    function onFrame() {
-      if (counter == 0) {
-        t.step( () => {
-          assert_false(display.getFrameData(fd));
-        }, "Expecting to not get framedata since there is no pose");
-        device_controller.setPose(expected_pose);
-        t.step( () => {
-          assert_false(display.getFrameData(fd));
-        }, "Does not update within the same frame");
-      } else {
-        t.step( () => {
-          assert_true(display.getFrameData(fd));
-          assert_not_equals(fd.pose, null);
-          for (let field in expected_pose) {
-            assert_equals(fd.pose[field].length, expected_pose[field].length);
-            for (let i = 0; i < expected_pose[field].length; i++) {
-              assert_approx_equals(fd.pose[field][i], expected_pose[field][i],
-                  FLOAT_EPSILON);
-            }
-          }
-        }, "Pose was updated in the next frame");
-        t.done();
-      }
-      counter++;
-      // Use rAF late so that the mock's setPose above can supply its data
-      // before RequestVSync calls GetPose. This is an artifact of the mocking
-      // framework, the real implementation doesn't have this restriction.
-      display.requestAnimationFrame(onFrame);
-    }
-
-    display.requestAnimationFrame(onFrame);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-  });
-}, [fakeDisplays["Pixel"]],
-"getFrameData updates on the next frame");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getFrameData_samewithinframe.html b/third_party/blink/web_tests/vr/getFrameData_samewithinframe.html
deleted file mode 100644
index 1370ce0..0000000
--- a/third_party/blink/web_tests/vr/getFrameData_samewithinframe.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t, device_controller) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-    var expected_pose = VALID_POSE;
-    var fd1 = new VRFrameData();
-    var fd2 = new VRFrameData();
-    device_controller.setPose(expected_pose);
-
-    function onFrame() {
-      display.requestAnimationFrame(onFrame);
-
-      t.step( () => {
-        assert_true(display.getFrameData(fd1));
-        assert_true(display.getFrameData(fd2));
-      }, "getFrameData successfully updated object");
-
-      t.step( () => {
-        for (let i = 0; i < 16; i++) {
-          assert_equals(fd1.leftProjectionMatrix[i],
-              fd2.leftProjectionMatrix[i]);
-          assert_equals(fd1.leftViewMatrix[i], fd2.leftViewMatrix[i]);
-          assert_equals(fd1.rightProjectionMatrix[i],
-              fd2.rightProjectionMatrix[i]);
-          assert_equals(fd1.rightViewMatrix[i], fd2.rightViewMatrix[i]);
-        }
-
-        let counter = 0;
-        for (let field in fd1.pose) {
-          counter++;
-          assert_equals(fd1.pose[field].length, fd2.pose[field].length);
-          assert_greater_than(fd1.pose[field].length, 0);
-          for (let i = 0; i < fd1.pose[field].length; i++) {
-            assert_equals(fd1.pose[field][i], fd2.pose[field][i]);
-          }
-        }
-
-        assert_equals(counter, 6);
-      }, "frame data matches within the same frame");
-      t.done();
-    }
-
-    display.requestAnimationFrame(onFrame);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-  });
-}, [fakeDisplays["Pixel"]],
-"getFrameData returns the same data within a single frame");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getFrameData_windowRAF.html b/third_party/blink/web_tests/vr/getFrameData_windowRAF.html
deleted file mode 100644
index 46f6cf29..0000000
--- a/third_party/blink/web_tests/vr/getFrameData_windowRAF.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t, device_controller) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-    var expected_pose = VALID_POSE;
-    var fd = new VRFrameData();
-    var counter = 0;
-
-    function onFrame() {
-      window.requestAnimationFrame(onFrame);
-      if (counter == 0) {
-        t.step( () => {
-          assert_false(display.getFrameData(fd));
-        }, "Expecting to not get framedata since there is no pose");
-        device_controller.setPose(expected_pose);
-        t.step( () => {
-          assert_false(display.getFrameData(fd));
-        }, "Does not update within the same frame");
-      } else {
-        t.step( () => {
-          assert_false(display.getFrameData(fd));
-        }, "getFrameData is expected to return false when not in a display RAF callback");
-        t.done();
-      }
-      counter++;
-    }
-
-    window.requestAnimationFrame(onFrame);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-  });
-}, [fakeDisplays["Pixel"]],
-"getFrameData updates on the next frame");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getLayers_notpresenting.html b/third_party/blink/web_tests/vr/getLayers_notpresenting.html
deleted file mode 100644
index aa027a0..0000000
--- a/third_party/blink/web_tests/vr/getLayers_notpresenting.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    t.step( () => {
-      assert_equals(display.getLayers().length, 0);
-    }, "No layers initially returned");
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        display.exitPresent().then( () => {
-          t.step( () => {
-            assert_equals(display.getLayers().length, 0);
-          }, "No layers returned after exiting");
-        }, (err) => {
-          t.step( () => {
-            assert_unreached();
-          }, "exitPresent failed");
-        });
-      }, (err) => {
-        t.step( () => {
-          assert_unreached();
-        }, "requestPresent failed");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached();
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test getLayers returns an empty array when not presenting");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getLayers_presenting.html b/third_party/blink/web_tests/vr/getLayers_presenting.html
deleted file mode 100644
index ff6f17a..0000000
--- a/third_party/blink/web_tests/vr/getLayers_presenting.html
+++ /dev/null
@@ -1,61 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    t.step( () => {
-      assert_equals(display.getLayers().length, 0);
-    }, "No layers initially returned");
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          let layers = display.getLayers();
-          assert_equals(layers.length, 1);
-          let layer = layers[0];
-
-          assert_equals(layer.source, webglCanvas);
-          assert_equals(layer.leftBounds.length, 4);
-          assert_equals(layer.rightBounds.length, 4);
-
-          let expectedLeft = [0.0, 0.0, 0.5, 1.0];
-          let expectedRight = [0.5, 0.0, 0.5, 1.0];
-          for (let i = 0; i < layer.leftBounds.length; i++) {
-            assert_approx_equals(layer.leftBounds[i], expectedLeft[i],
-                FLOAT_EPSILON);
-            assert_approx_equals(layer.rightBounds[i], expectedRight[i],
-                FLOAT_EPSILON);
-          }
-        }, "Layer is correct");
-      }, (err) => {
-        t.step( () => {
-          assert_unreached();
-        }, "requestPresent failed");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached();
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test getLayers returns given canvas and default bounds when expected");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getLayers_presenting_nondefaultbounds.html b/third_party/blink/web_tests/vr/getLayers_presenting_nondefaultbounds.html
deleted file mode 100644
index f251dea..0000000
--- a/third_party/blink/web_tests/vr/getLayers_presenting_nondefaultbounds.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    t.step( () => {
-      assert_equals(display.getLayers().length, 0);
-    }, "No layers initially returned");
-
-    runWithUserGesture( () => {
-      let expectedLeft = [0.0, 0.1, 0.2, 0.3];
-      let expectedRight = [0.4, 0.5, 0.6, 0.7];
-      display.requestPresent([{ source : webglCanvas,
-                                leftBounds : expectedLeft,
-                                rightBounds : expectedRight}]).then( () => {
-        t.step( () => {
-          let layers = display.getLayers();
-          assert_equals(layers.length, 1);
-          let layer = layers[0];
-
-          assert_equals(layer.source, webglCanvas);
-          assert_equals(layer.leftBounds.length, 4);
-          assert_equals(layer.rightBounds.length, 4);
-
-          for (let i = 0; i < layer.leftBounds.length; i++) {
-            assert_approx_equals(layer.leftBounds[i], expectedLeft[i],
-                FLOAT_EPSILON);
-            assert_approx_equals(layer.rightBounds[i], expectedRight[i],
-                FLOAT_EPSILON);
-          }
-        }, "Layer is correct");
-      }, (err) => {
-        t.step( () => {
-          assert_unreached();
-        }, "requestPresent failed");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached();
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test getLayers returns non-default bounds when specified in the layer");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getLayers_update.html b/third_party/blink/web_tests/vr/getLayers_update.html
deleted file mode 100644
index cd0efdd..0000000
--- a/third_party/blink/web_tests/vr/getLayers_update.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<canvas id="webgl-canvas2"></canvas>
-<script>
-var webglCanvas2 = document.getElementById("webgl-canvas2");
-var gl2 = webglCanvas2.getContext("webgl", glAttributes);
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    t.step( () => {
-      assert_equals(display.getLayers().length, 0);
-    }, "No layers initially returned");
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          assert_equals(display.getLayers()[0].source, webglCanvas);
-        }, "Initial layer source matches first canvas");
-        display.requestPresent([{ source : webglCanvas2 }]).then( () => {
-          t.step( () => {
-            assert_equals(display.getLayers()[0].source, webglCanvas2);
-            assert_not_equals(display.getLayers()[0].source, webglCanvas);
-          }, "Source now matches second canvas");
-        }, (err) => {
-          t.step( () => {
-            assert_unreached(err);
-          }, "Second requestPresent failed")
-        }).then( () => {
-          t.done();
-        });
-      }, (err) => {
-        t.step( () => {
-          assert_unreached();
-        }, "First requestPresent failed");
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached();
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test getLayers returns updated source layer when requestPresent is re-called");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getVRDisplays_always_resolves.html b/third_party/blink/web_tests/vr/getVRDisplays_always_resolves.html
deleted file mode 100644
index ea26338..0000000
--- a/third_party/blink/web_tests/vr/getVRDisplays_always_resolves.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script>
-
-promise_test(t => navigator.getVRDisplays().then(devices => {
-  assert_true(devices != null);
-  assert_true(devices instanceof Array);
-  assert_greater_than_equal(0, devices.length);
-
-  if (devices.length > 0)
-    assert_true(devices[0] instanceof VRDisplay);
-}), "Test that getVRDisplays always resolves with at least an empty sequence.");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getVRDisplays_detached.html b/third_party/blink/web_tests/vr/getVRDisplays_detached.html
deleted file mode 100644
index fc9345a..0000000
--- a/third_party/blink/web_tests/vr/getVRDisplays_detached.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<title>Detached use of navigator.getVRDisplays()</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-</head>
-<body>
-<iframe sandbox="allow-same-origin" id="subframe"></iframe>
-<script>
-promise_test((t) => {
-  var nav = window.frames[0].navigator;
-  document.getElementById("subframe").remove();
-  return promise_rejects(
-      t,
-      new DOMException("The object is no longer associated with a document.",
-                       "InvalidStateError"),
-      nav.getVRDisplays());
-}, "getVRDisplays is rejected in a detached context.");
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/vr/getVRDisplays_one_display.html b/third_party/blink/web_tests/vr/getVRDisplays_one_display.html
deleted file mode 100644
index 21defbe..0000000
--- a/third_party/blink/web_tests/vr/getVRDisplays_one_display.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    t.step( () => {
-      assert_true(displays != null);
-      assert_equals(displays.length, 1);
-      let display = displays[0];
-      assert_true(display.capabilities.canPresent);
-      assert_approx_equals(display.depthNear, 0.01, FLOAT_EPSILON);
-      assert_approx_equals(display.depthFar, 10000.0, FLOAT_EPSILON);
-    }, "getVRDisplays returned correct results");
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-  }).then( () => {
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"WebVR properly returns a single display");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/getVRDisplays_zero_display.html b/third_party/blink/web_tests/vr/getVRDisplays_zero_display.html
deleted file mode 100644
index 6f7242f..0000000
--- a/third_party/blink/web_tests/vr/getVRDisplays_zero_display.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<script>
-
-vr_test( (t) => {
-  return  navigator.getVRDisplays().then( (displays) => {
-    t.step( () => {
-      assert_true(displays != null);
-      assert_equals(displays.length, 0);
-    }, "getVRDisplays returned correct results");
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-  }).then( () => {
-    t.done();
-  });
-}, [],
-"WebVR properly returns zero displays");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/multiple_requestAnimationFrame_called.html b/third_party/blink/web_tests/vr/multiple_requestAnimationFrame_called.html
deleted file mode 100644
index 99451734..0000000
--- a/third_party/blink/web_tests/vr/multiple_requestAnimationFrame_called.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-    let onAnimationFrame3_time = -1;
-    function onAnimationFrame() {
-      display.requestAnimationFrame(onAnimationFrame3);
-    }
-
-    function onAnimationFrame2() {
-      display.requestAnimationFrame(onAnimationFrame4);
-    }
-
-    function onAnimationFrame3(time) {
-      onAnimationFrame3_time = time;
-    }
-
-    function onAnimationFrame4(time) {
-      t.step(function() {
-        assert_equals(time, onAnimationFrame3_time);
-      });
-      t.done();
-    }
-
-    display.requestAnimationFrame(onAnimationFrame);
-    display.requestAnimationFrame(onAnimationFrame2);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"multiple requestAnimationFrame requests call the correct callbacks");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestAnimationFrame_called.html b/third_party/blink/web_tests/vr/requestAnimationFrame_called.html
deleted file mode 100644
index 3164e38..0000000
--- a/third_party/blink/web_tests/vr/requestAnimationFrame_called.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-
-    function onAnimationFrame() {
-      t.done();
-    }
-
-    display.requestAnimationFrame(onAnimationFrame);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"requestAnimationFrame properly calls the provided callback");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestAnimationFrame_consistentTimestamps.html b/third_party/blink/web_tests/vr/requestAnimationFrame_consistentTimestamps.html
deleted file mode 100644
index 66fbc18..0000000
--- a/third_party/blink/web_tests/vr/requestAnimationFrame_consistentTimestamps.html
+++ /dev/null
@@ -1,71 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-// This test verifies that timestamps are consistent when switching between
-// magic window and presentation mode.
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-
-    let display = displays[0];
-
-    let usingPresentingRAF = false;
-    let lastTime = -1;
-    let lastrAFTime = -1;
-    let tolerance_ms = 250;
-
-    function onAnimationFrame(time) {
-      var currentTime = window.performance.now();
-      if (lastTime != -1) {
-        var timeDiff = currentTime - lastTime;
-        var rAFDiff = time - lastrAFTime;
-        if (usingPresentingRAF) {
-          t.step( () => {
-            assert_approx_equals(rAFDiff, timeDiff, tolerance_ms);
-          }, "timestamps not continuous");
-        }
-      }
-      lastTime = currentTime;
-      lastrAFTime = time;
-
-      if (usingPresentingRAF) {
-        t.done();
-      }
-      if (!display.isPresenting) {
-        runWithUserGesture( () => {
-          display.requestPresent([{ source : webglCanvas }]).then( () => {
-            t.step( () => {
-              assert_true(display.isPresenting);
-            }, "Display should be presenting");
-            usingPresentingRAF = true;
-            display.requestAnimationFrame(onAnimationFrame);
-          }, (err) => {
-            t.step( () => {
-              assert_unreached(err);
-            }, "requestPresent rejected");
-          });
-        });
-      }
-    }
-    display.requestAnimationFrame(onAnimationFrame);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"requestAnimationFrame timestamp is consistent between magic window and presentation.");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestAnimationFrame_handoff.html b/third_party/blink/web_tests/vr/requestAnimationFrame_handoff.html
deleted file mode 100644
index ee6e371..0000000
--- a/third_party/blink/web_tests/vr/requestAnimationFrame_handoff.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-// This test verifies that an application can hand off from window.rAF
-// to vrDisplay.rAF, specifically where there is no pending vrDisplay.rAF
-// at the time presentation starts.
-//
-// Caveat: This does *not* suffice to fully verify the current Android/GVR
-// implementation. In the real implementation, the normal window VSync events
-// (including window.rAF processing tied to them) are shut down, but in the
-// layout test they continue running. So it's possible for this test to pass
-// even though the scenario doesn't work in the real implementation. Keeping
-// it as a layout test anyway as a sanity check - if this fails, we definitely
-// have a problem.
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-
-    let usingDisplayRAF = false;
-
-    function onAnimationFrame() {
-      if (display.isPresenting) {
-        if (usingDisplayRAF) {
-          t.done();
-        }
-
-        usingDisplayRAF = true;
-        display.requestAnimationFrame(onAnimationFrame);
-      } else {
-        window.requestAnimationFrame(onAnimationFrame);
-      }
-    }
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          assert_true(display.isPresenting);
-        }, "Display should be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "requestPresent rejected");
-      });
-    });
-
-    window.requestAnimationFrame(onAnimationFrame);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"requestAnimationFrame properly switches from window to vrDisplay");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestAnimationFrame_invalidhandle.html b/third_party/blink/web_tests/vr/requestAnimationFrame_invalidhandle.html
deleted file mode 100644
index 7d778621..0000000
--- a/third_party/blink/web_tests/vr/requestAnimationFrame_invalidhandle.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-    let canFinish = false;
-    let counter = 0;
-
-    function onAnimationFrame() {
-      // Intentionally display.rAF at the beginning, ensuring that there's an
-      // outstanding callback when t.done() is called. This is to make sure it
-      // doesn't cause any unexpected behavior like it did with
-      // crbug.com/679401
-      display.requestAnimationFrame(onAnimationFrame);
-      if (counter > 10) {
-        t.done();
-      }
-      if (counter > 0) {
-        counter++;
-      }
-      if (canFinish) {
-        canFinish = false;
-        counter++;
-      }
-    }
-
-    let handle = display.requestAnimationFrame(onAnimationFrame);
-    display.cancelAnimationFrame(0);
-    display.cancelAnimationFrame(-1);
-    display.cancelAnimationFrame(handle + 1);
-    display.cancelAnimationFrame(handle - 1);
-    display.cancelAnimationFrame(0.5);
-    display.cancelAnimationFrame(null);
-    canFinish = true;
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"cancelAnimationFrame does not have unexpected behavior when given invalid handles");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestAnimationFrame_submitFrame_combinations.html b/third_party/blink/web_tests/vr/requestAnimationFrame_submitFrame_combinations.html
deleted file mode 100644
index 75b1c03..0000000
--- a/third_party/blink/web_tests/vr/requestAnimationFrame_submitFrame_combinations.html
+++ /dev/null
@@ -1,106 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../wpt_internal/webxr/resources/xr-internal-device-mocking.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t, device_controller) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-
-    let prePresentFrameCount = 0;
-
-    function onFrameStartup() {
-      // Wait until presentation starts, then proceed
-      // with the remaining tests.
-      if (display.isPresenting) {
-        t.step( () => {
-          assert_unreached(err);
-        }, "unexpectedly presenting");
-      } else {
-        // Run a few frames in "magic mirror" mode, then start presentation.
-        if (++prePresentFrameCount == 3) {
-          runWithUserGesture(startPresentation);
-        } else {
-          display.requestAnimationFrame(onFrameStartup);
-        }
-      }
-    }
-
-    function getSubmitFrameCount() {
-      return device_controller.getSubmitFrameCount();
-    }
-
-    function getMissingFrameCount() {
-      return device_controller.getMissingFrameCount();
-    }
-
-    function onFrame1() {
-      assert_equals(getSubmitFrameCount(), 0);
-      assert_equals(getMissingFrameCount(), 0);
-      // case (b): submit frame first, then rAF
-      display.submitFrame();
-      display.requestAnimationFrame(onFrame2);
-    }
-
-    function onFrame2() {
-      assert_equals(getSubmitFrameCount(), 1);
-      assert_equals(getMissingFrameCount(), 0);
-      // case (c): rAF first, then submit frame
-      display.requestAnimationFrame(onFrame3);
-      display.submitFrame();
-    }
-
-    function onFrame3(time) {
-      assert_equals(getSubmitFrameCount(), 2);
-      assert_equals(getMissingFrameCount(), 0);
-      // case (d): don't submit a frame.
-      display.requestAnimationFrame(onFrame4);
-    }
-
-    function onFrame4(time) {
-      // If we get here, we're done.
-      assert_equals(getSubmitFrameCount(), 2);
-      assert_equals(getMissingFrameCount(), 1);
-      t.done();
-    }
-
-    function startPresentation() {
-      assert_equals(getSubmitFrameCount(), 0);
-      assert_equals(getMissingFrameCount(), 0);
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          assert_equals(getSubmitFrameCount(), 0);
-          assert_equals(getMissingFrameCount(), 0);
-          // case (a): in requestPresent promise, outside animating context.
-          assert_true(display.isPresenting);
-          display.requestAnimationFrame(onFrame1);
-        }, "Display should be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "requestPresent rejected");
-      });
-    }
-
-    // Start: case (a) outside animating context.
-    display.requestAnimationFrame(onFrameStartup);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"multiple requestAnimationFrame requests call the correct callbacks");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestAnimationFrame_unregister.html b/third_party/blink/web_tests/vr/requestAnimationFrame_unregister.html
deleted file mode 100644
index 2269b80..0000000
--- a/third_party/blink/web_tests/vr/requestAnimationFrame_unregister.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-
-    function onAnimationFrameBad() {
-      t.step( () => {
-        assert_unreached();
-      }, "Unregistered callback was called");
-      t.done();
-    }
-
-    let counter = 0;
-    function onAnimationFrameGood() {
-      counter++;
-      if (counter >= 4) {
-        t.done();
-        // Intentionally don't return immediately so that display.rAF gets
-        // called again to make sure it doesn't cause unexpected behavior like
-        // it did with crbug.com/679401
-      }
-      display.requestAnimationFrame(onAnimationFrameGood);
-    }
-
-    let handle = display.requestAnimationFrame(onAnimationFrameBad);
-    display.cancelAnimationFrame(handle);
-    display.requestAnimationFrame(onAnimationFrameGood);
-  }, (err) => {
-    t.step( () => {
-      assert_unreached("getVRDisplays rejected");
-    });
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"requestAnimationFrame callbacks can be unregistered with cancelAnimationFrame");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_reject_NaN_bounds.html b/third_party/blink/web_tests/vr/requestPresent_reject_NaN_bounds.html
deleted file mode 100644
index cee1556..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_reject_NaN_bounds.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{
-          source : webglCanvas,
-          leftBounds: [0, 0, 1, 1],
-          rightBounds: [NaN, NaN, NaN, NaN]
-        }])
-      .then( () => {
-        t.step( () => {
-          assert_unreached();
-        }, "Display should not be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_false(display.isPresenting);
-        }, "requestPresent rejected and not presenting");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent rejects if provided a bad leftBounds");
-
-</script>
-
diff --git a/third_party/blink/web_tests/vr/requestPresent_reject_badleftbounds.html b/third_party/blink/web_tests/vr/requestPresent_reject_badleftbounds.html
deleted file mode 100644
index f123330..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_reject_badleftbounds.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{
-          source : webglCanvas,
-          leftBounds: [0, 1] // Too short
-        }])
-      .then( () => {
-        t.step( () => {
-          assert_unreached();
-        }, "Display should not be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_false(display.isPresenting);
-        }, "requestPresent rejected and not presenting");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent rejects if provided a bad leftBounds");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_reject_badrightbounds.html b/third_party/blink/web_tests/vr/requestPresent_reject_badrightbounds.html
deleted file mode 100644
index c60914a..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_reject_badrightbounds.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{
-          source : webglCanvas,
-          rightBounds: [0, 1, 2, 3, 4] // Too long
-        }])
-      .then( () => {
-        t.step( () => {
-          assert_unreached();
-        }, "Display should not be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_false(display.isPresenting);
-        }, "requestPresent rejected and not presenting");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent rejects if provided a bad rightBounds");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_reject_nogesture.html b/third_party/blink/web_tests/vr/requestPresent_reject_nogesture.html
deleted file mode 100644
index d7ede9f9..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_reject_nogesture.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    promise_test( function() {
-      return promise_rejects(this, "InvalidStateError",
-          displays[0].requestPresent([{ source : webglCanvas }]));
-    }, "requestPresent rejected");
-
-    t.done();
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent rejects without user gesture");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_reject_nolayers.html b/third_party/blink/web_tests/vr/requestPresent_reject_nolayers.html
deleted file mode 100644
index 39ecf80b..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_reject_nolayers.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([]).then( () => {
-        t.step( () => {
-          assert_unreached();
-        }, "Display should not be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_false(display.isPresenting);
-        }, "requestPresent rejected and not presenting");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent rejects if not provided any layers");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_reject_nosource.html b/third_party/blink/web_tests/vr/requestPresent_reject_nosource.html
deleted file mode 100644
index 06d31e06..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_reject_nosource.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{}]).then( () => {
-        t.step( () => {
-          assert_unreached();
-        }, "Display should not be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_false(display.isPresenting);
-        }, "requestPresent rejected and not presenting");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent rejects if not provided a layer source");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_reject_notsupported.html b/third_party/blink/web_tests/vr/requestPresent_reject_notsupported.html
deleted file mode 100644
index dc96dbe..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_reject_notsupported.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          assert_unreached();
-        }, "Display should not be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_false(display.isPresenting);
-        }, "requestPresent rejected and not presenting");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["FakeMagicWindowOnly"]],
-"Test requestPresent rejects if display does not support it");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_reject_nowebgl.html b/third_party/blink/web_tests/vr/requestPresent_reject_nowebgl.html
deleted file mode 100644
index 10610a8c..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_reject_nowebgl.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<canvas id="canvas-2d"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var canvas2d = document.getElementById("canvas-2d");
-    var ctx = canvas2d.getContext("2d");
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : canvas2d }]).then( () => {
-        t.step( () => {
-          assert_unreached();
-        }, "Display should not be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_false(display.isPresenting);
-        }, "requestPresent rejected and not presenting");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent rejects if provided a non-webgl canvas");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_reject_nullsource.html b/third_party/blink/web_tests/vr/requestPresent_reject_nullsource.html
deleted file mode 100644
index 8aae417b..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_reject_nullsource.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : null }]).then( () => {
-        t.step( () => {
-          assert_unreached();
-        }, "Display should not be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_false(display.isPresenting);
-        }, "requestPresent rejected and not presenting");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent rejects if given a null source");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_reject_toomanylayers.html b/third_party/blink/web_tests/vr/requestPresent_reject_toomanylayers.html
deleted file mode 100644
index 4bd8f25a..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_reject_toomanylayers.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-    var max_layers = display.capabilities.maxLayers;
-    var layers = [];
-    for (var i = 0; i <= max_layers; ++i) {
-      layers.push({ source : webglCanvas });
-    }
-
-    runWithUserGesture( () => {
-      display.requestPresent(layers).then( () => {
-        t.step( () => {
-          assert_unreached();
-        }, "Display should not be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_false(display.isPresenting);
-        }, "requestPresent rejected and not presenting");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent rejects if provided too many layers");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_resolve.html b/third_party/blink/web_tests/vr/requestPresent_resolve.html
deleted file mode 100644
index 6dbd91c..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_resolve.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          assert_true(display.isPresenting);
-        }, "Display should be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "requestPresent rejected");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached();
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent resolves");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_resolve_repeatwithgesture.html b/third_party/blink/web_tests/vr/requestPresent_resolve_repeatwithgesture.html
deleted file mode 100644
index e80ef293..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_resolve_repeatwithgesture.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<!DOCTYPE html>
-<!-- Specific regression test for crbug.com/654909 -->
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          assert_true(display.isPresenting);
-        }, "Display should be presenting");
-
-        // Call requestPresent again after a short delay
-        setTimeout( () => {
-          runWithUserGesture( () => {
-            display.requestPresent([{ source : webglCanvas }]).then( () => {
-              t.step( () => {
-                assert_true(display.isPresenting);
-              }, "Display should still be presenting");
-            }, (err) => {
-              t.step( () => {
-                assert_unreached(err);
-              }, "Should never reach here");
-            }).then( () => {
-              t.done();
-            });
-          });
-        }, 100);
-
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "Should never reach here");
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent resolves with a user gesture when already presenting");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_resolve_repeatwithoutgesture.html b/third_party/blink/web_tests/vr/requestPresent_resolve_repeatwithoutgesture.html
deleted file mode 100644
index 3f09cb9..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_resolve_repeatwithoutgesture.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          assert_true(display.isPresenting);
-        }, "Display should be presenting");
-
-        // Call requestPresent again after a short delay, but without a user
-        // gesture. Should resolve because it's already presenting.
-        setTimeout( () => {
-          display.requestPresent([{ source : webglCanvas }]).then( () => {
-            t.step( () => {
-              assert_true(display.isPresenting);
-            }, "Display should still be presenting");
-          }, (err) => {
-            t.step( () => {
-              assert_unreached(err);
-            }, "Should never reach here");
-          }).then( () => {
-            t.done();
-          });
-        }, 100);
-
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "Should never reach here");
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent resolves without a user gesture when already presenting");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_resolve_then_reject.html b/third_party/blink/web_tests/vr/requestPresent_resolve_then_reject.html
deleted file mode 100644
index 762adff..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_resolve_then_reject.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          assert_true(display.isPresenting);
-        }, "Display should be presenting");
-        display.requestPresent([{ source : null }]).then( () => {
-          t.step( () => {
-            assert_unreached();
-          }, "Second requestPresent succeeded");
-        }, (err) => {
-          t.step( () => {
-            assert_false(display.isPresenting);
-          }, "Should no longer be presenting");
-        }).then( () => {
-          t.done();
-        });
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "requestPresent rejected");
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached();
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test display stops presenting if requestPresent rejects");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/requestPresent_resolve_webgl2.html b/third_party/blink/web_tests/vr/requestPresent_resolve_webgl2.html
deleted file mode 100644
index d8c72a89..0000000
--- a/third_party/blink/web_tests/vr/requestPresent_resolve_webgl2.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-
-<canvas id="webgl2-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-
-vr_test( (t) => {
-  if (!gl) {
-    // WebGL 2 is not supported. This is legal.
-    t.done();
-    return Promise.resolve();
-  }
-
-  return navigator.getVRDisplays().then( (displays) => {
-    var display = displays[0];
-
-    runWithUserGesture( () => {
-      display.requestPresent([{ source : webglCanvas }]).then( () => {
-        t.step( () => {
-          assert_true(display.isPresenting);
-        }, "Display should be presenting");
-      }, (err) => {
-        t.step( () => {
-          assert_unreached(err);
-        }, "Should never reach here");
-      }).then( () => {
-        t.done();
-      });
-    });
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplays["Pixel"]],
-"Test requestPresent resolves when provided a WebGL2 canvas");
-
-</script>
diff --git a/third_party/blink/web_tests/vr/resources/presentation-setup.js b/third_party/blink/web_tests/vr/resources/presentation-setup.js
deleted file mode 100644
index b024216..0000000
--- a/third_party/blink/web_tests/vr/resources/presentation-setup.js
+++ /dev/null
@@ -1,20 +0,0 @@
-var webgl2 = false;
-var webglCanvas = document.getElementById("webgl-canvas");
-if (!webglCanvas) {
-  webglCanvas = document.getElementById("webgl2-canvas");
-  webgl2 = true;
-}
-var glAttributes = {
-  alpha : false,
-  antialias : false,
-};
-var gl = webglCanvas.getContext(webgl2 ? "webgl2" : "webgl", glAttributes);
-
-function runWithUserGesture(fn) {
-  function thunk() {
-    document.removeEventListener("keypress", thunk, false);
-    fn()
-  }
-  document.addEventListener("keypress", thunk, false);
-  eventSender.keyDown(" ", []);
-}
diff --git a/third_party/blink/web_tests/vr/resources/test-constants.js b/third_party/blink/web_tests/vr/resources/test-constants.js
deleted file mode 100644
index 56612113..0000000
--- a/third_party/blink/web_tests/vr/resources/test-constants.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// assert_equals can fail when comparing floats due to precision errors, so
-// use assert_approx_equals with this constant instead
-var FLOAT_EPSILON = 0.000001;
-
-// A valid VRPose for when we don't care about specific values
-var VALID_POSE = {
-  position: [1.1, 2.2, 3.3],
-  linearVelocity: [0.1, 0.2, 0.3],
-  linearAcceleration: [0.0, 0.1, 0.2],
-  orientation: [0.1, 0.2, 0.3, 0.4],
-  angularVelocity: [1.1, 2.1, 3.1],
-  angularAcceleration: [1.0, 2.0, 3.0]
-}
diff --git a/third_party/blink/web_tests/vr/resources/vr-test-utils.js b/third_party/blink/web_tests/vr/resources/vr-test-utils.js
deleted file mode 100644
index 496c1a71..0000000
--- a/third_party/blink/web_tests/vr/resources/vr-test-utils.js
+++ /dev/null
@@ -1,183 +0,0 @@
-'use strict';
-
-MockVRService.prototype.setListeningForActivate = function(client) {
-  for (let i = 0; i < this.runtimes_.length; i++) {
-    this.runtimes_[i].displayClient_ = client;
-  }
-};
-
-MockVRService.prototype.getImmersiveVRDisplayInfo = function() {
-  return Promise.resolve(
-      {info: this.runtimes_[0] ? this.runtimes_[0].displayInfo_ : null});
-};
-
-MockRuntime.prototype.setPose = function(pose) {
-  if (pose == null) {
-    this.pose_ = null;
-  } else {
-    this.pose_ = {
-      orientation: null,
-      position: null,
-      angularVelocity: null,
-      linearVelocity: null,
-      angularAcceleration: null,
-      linearAcceleration: null,
-      inputState: null,
-      poseReset: false,
-      poseIndex: 0
-    };
-    for (var field in pose) {
-      if (this.pose_.hasOwnProperty(field)) {
-        let val = pose[field];
-        if (field === "position") {
-          this.pose_[field] = { x: val[0], y: val[1], z: val[2] };
-        } else if (field === "orientation") {
-          this.pose_[field] = { x: val[0], y: val[1], z: val[2], w: val[3] };
-        }else if (field === "angularVelocity" || field === "linearVelocity" ||
-                   field === "angularAcceleration" || field === "linearAcceleration") {
-          this.pose_[field] = { x: val[0], y: val[1], z: val[2] };
-        } else {
-          this.pose_[field] = pose[field];
-        }
-      }
-    }
-  }
-};
-
-MockRuntime.prototype.forceActivate = function(reason) {
-  this.displayClient_.onActivate(reason);
-};
-
-function getGFXTransformFromPosition(x, y, z) {
-  let transform = new gfx.mojom.Transform();
-  transform.matrix = getMatrixFromTransform({
-    position: [x, y, z],
-    orientation: [0, 0, 0, 1]
-  });
-  return transform;
-}
-
-function vr_test(func, vrDisplays, name, properties) {
-  let chain = Promise.resolve();
-  let firstDeviceController;
-  vrDisplays.forEach(display => {
-    return chain.then(
-        navigator.vr.test
-            .simulateDeviceConnection(
-                {supportsImmersive: display.capabilities.canPresent})
-            .then((deviceController) => {
-              deviceController.displayInfo_ = display;
-              if (!firstDeviceController) {
-                firstDeviceController = deviceController;
-              }
-            }));
-  });
-  chain.then(() => {
-    let t = async_test(name, properties);
-    func(t, firstDeviceController);
-  })
-}
-
-function fakeVRDisplays(){
-  let generic_left_fov = {
-    upDegrees : 45,
-    downDegrees : 45,
-    leftDegrees : 50,
-    rightDegrees : 40,
-  };
-
-  let generic_right_fov = {
-    upDegrees : 45,
-    downDegrees : 45,
-    leftDegrees : 40,
-    rightDegrees : 50,
-  };
-
-  let generic_left_eye = {
-    fieldOfView : generic_left_fov,
-    headFromEye : getGFXTransformFromPosition(-0.03, 0, 0),
-    renderWidth : 1024,
-    renderHeight : 1024
-  };
-
-  let generic_right_eye = {
-    fieldOfView : generic_right_fov,
-    headFromEye : getGFXTransformFromPosition(0.03, 0, 0),
-    renderWidth : 1024,
-    renderHeight : 1024
-  };
-
-  let genericStanding = new gfx.mojom.Transform();
-  genericStanding.matrix =  [0.0, 0.1, 0.2, 0.3,
-                             0.4, 0.5, 0.6, 0.7,
-                             0.8, 0.9, 1.0, 0.1,
-                             0.2, 0.3, 0.4, 0.5];
-
-  return {
-    FakeMagicWindowOnly: {
-      displayName : "FakeVRDisplay",
-      capabilities : {
-        hasPosition : false,
-        hasExternalDisplay : false,
-        canPresent : false
-      },
-      stageParameters : null,
-      leftEye : null,
-      rightEye : null,
-      webvrDefaultFramebufferScale: 1.0,
-    },
-
-    FakeRoomScale: {
-      displayName : "FakeVRDisplayRoom",
-      capabilities : {
-        hasPosition : true,
-        hasExternalDisplay : true,
-        canPresent : true,
-        maxLayers : 1
-      },
-      stageParameters : {
-        standingTransform : genericStanding,
-        sizeX : 5.0,
-        sizeZ : 3.0,
-      },
-      leftEye : generic_left_eye,
-      rightEye : generic_right_eye,
-      webvrDefaultFramebufferScale: 1.0,
-    },
-
-    Pixel: { // Pixel info as of Dec. 22 2016
-      displayName : "Google, Inc. Daydream View",
-      capabilities : {
-        hasPosition : false,
-        hasExternalDisplay : false,
-        canPresent : true,
-        maxLayers : 1
-      },
-      stageParameters : null,
-      leftEye : {
-        fieldOfView : {
-          upDegrees : 48.316,
-          downDegrees : 50.099,
-          leftDegrees : 35.197,
-          rightDegrees : 50.899,
-        },
-        headFromEye : getGFXTransformFromPosition(-0.032, 0, 0),
-        renderWidth : 1920,
-        renderHeight : 2160
-      },
-      rightEye : {
-        fieldOfView : {
-          upDegrees : 48.316,
-          downDegrees : 50.099,
-          leftDegrees: 50.899,
-          rightDegrees: 35.197
-        },
-        headFromEye : getGFXTransformFromPosition(0.032, 0, 0),
-        renderWidth : 1920,
-        renderHeight : 2160
-      },
-      webvrDefaultFramebufferScale: 0.5,
-    }
-    // TODO(bsheedy) add more displays like Rift/Vive
-  };
-};
diff --git a/third_party/blink/web_tests/vr/stageParameters_match.html b/third_party/blink/web_tests/vr/stageParameters_match.html
deleted file mode 100644
index e1771f33..0000000
--- a/third_party/blink/web_tests/vr/stageParameters_match.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script> let legacy_vr_test = true; </script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="resources/vr-test-utils.js "></script>
-<script src="resources/test-constants.js"></script>
-
-<canvas id="webgl-canvas"></canvas>
-<script src="resources/presentation-setup.js"></script>
-<script>
-let fakeDisplays = fakeVRDisplays();
-let fakeDisplay = fakeDisplays["FakeRoomScale"];
-
-vr_test( (t) => {
-  return navigator.getVRDisplays().then( (displays) => {
-    let display = displays[0];
-    let expectedParams = fakeDisplay.stageParameters;
-    let actualParams = display.stageParameters;
-
-    t.step( () => {
-      assert_true(display.capabilities.hasExternalDisplay);
-      assert_true(display.capabilities.hasPosition);
-      assert_equals(actualParams.sittingToStandingTransform.length, 16);
-      for (let i = 0; i < actualParams.sittingToStandingTransform.length; i++) {
-        assert_approx_equals(expectedParams.standingTransform.matrix[i],
-            actualParams.sittingToStandingTransform[i], FLOAT_EPSILON);
-      }
-
-      assert_approx_equals(expectedParams.sizeX, actualParams.sizeX,
-          FLOAT_EPSILON);
-      assert_approx_equals(expectedParams.sizeZ, actualParams.sizeZ,
-          FLOAT_EPSILON);
-    }, "Expected and actual values match");
-
-    t.done();
-  }, (err) => {
-    t.step( () => {
-      assert_unreached(err);
-    }, "getVRDisplays rejected");
-    t.done();
-  });
-}, [fakeDisplay],
-"Test actual stageParameters match expected parameters");
-
-</script>
diff --git a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
index 4a1e9fc6..91bfdbf 100644
--- a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
+++ b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
@@ -57,7 +57,6 @@
 unsized-media
 usb
 vertical-scroll
-vr
 wake-lock
 xr-spatial-tracking
 
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 3228441..80325d9 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
@@ -1157,6 +1157,9 @@
     attribute @@toStringTag
     getter offset
     method constructor
+interface ContactAddress : PaymentAddress
+    attribute @@toStringTag
+    method constructor
 interface ContactsManager
     attribute @@toStringTag
     method constructor
@@ -2496,12 +2499,9 @@
     getter axes
     getter buttons
     getter connected
-    getter displayId
-    getter hand
     getter id
     getter index
     getter mapping
-    getter pose
     getter timestamp
     getter vibrationActuator
     method constructor
@@ -2531,17 +2531,6 @@
     method constructor
     method playEffect
     method reset
-interface GamepadPose
-    attribute @@toStringTag
-    getter angularAcceleration
-    getter angularVelocity
-    getter hasOrientation
-    getter hasPosition
-    getter linearAcceleration
-    getter linearVelocity
-    getter orientation
-    getter position
-    method constructor
 interface Geolocation
     attribute @@toStringTag
     method clearWatch
@@ -5220,7 +5209,6 @@
     method getInstalledRelatedApps
     method getUserAgent
     method getUserMedia
-    method getVRDisplays
     method javaEnabled
     method registerProtocolHandler
     method requestMIDIAccess
@@ -8307,67 +8295,6 @@
     getter hasBeenActive
     getter isActive
     method constructor
-interface VRDisplay : EventTarget
-    attribute @@toStringTag
-    getter capabilities
-    getter depthFar
-    getter depthNear
-    getter displayId
-    getter displayName
-    getter isPresenting
-    getter stageParameters
-    method cancelAnimationFrame
-    method constructor
-    method exitPresent
-    method getEyeParameters
-    method getFrameData
-    method getLayers
-    method requestAnimationFrame
-    method requestPresent
-    method submitFrame
-    setter depthFar
-    setter depthNear
-interface VRDisplayCapabilities
-    attribute @@toStringTag
-    getter canPresent
-    getter hasExternalDisplay
-    getter hasPosition
-    getter maxLayers
-    method constructor
-interface VRDisplayEvent : Event
-    attribute @@toStringTag
-    getter display
-    getter reason
-    method constructor
-interface VREyeParameters
-    attribute @@toStringTag
-    getter offset
-    getter renderHeight
-    getter renderWidth
-    method constructor
-interface VRFrameData
-    attribute @@toStringTag
-    getter leftProjectionMatrix
-    getter leftViewMatrix
-    getter pose
-    getter rightProjectionMatrix
-    getter rightViewMatrix
-    method constructor
-interface VRPose
-    attribute @@toStringTag
-    getter angularAcceleration
-    getter angularVelocity
-    getter linearAcceleration
-    getter linearVelocity
-    getter orientation
-    getter position
-    method constructor
-interface VRStageParameters
-    attribute @@toStringTag
-    getter sittingToStandingTransform
-    getter sizeX
-    getter sizeZ
-    method constructor
 interface VTTCue : TextTrackCue
     attribute @@toStringTag
     getter align
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocked_by_getVRDisplays.https.html b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocked_by_getVRDisplays.https.html
deleted file mode 100644
index 4f0ee6c7..0000000
--- a/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocked_by_getVRDisplays.https.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-// This behavior is not in the webxr spec, so this is an internal-only test.
-promise_test((t) => navigator.getVRDisplays().then(() => {
-  assert_true(navigator.xr == null);
-}), "Test that access to navigator.xr is blocked once navigator.getVRDisplays has been called.");
-
-</script>
diff --git a/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocks_getVRDisplays.https.html b/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocks_getVRDisplays.https.html
deleted file mode 100644
index 1c712a8b..0000000
--- a/third_party/blink/web_tests/wpt_internal/webxr/navigator_xr_blocks_getVRDisplays.https.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-// This behavior is not in the webxr spec, so this is an internal-only test.
-promise_test((t) => navigator.xr.requestSession('immersive-vr').catch(() => {
-  return promise_rejects(t, "InvalidStateError", navigator.getVRDisplays());
-}), "Test that calls to navigator.getVRDisplays are blocked once navigator.xr has been accessed.");
-
-</script>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 2a0c91e..3195a30 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -18475,6 +18475,7 @@
   <int value="634" label="InsecureContentAllowedForUrls"/>
   <int value="635" label="InsecureContentBlockedForUrls"/>
   <int value="636" label="DeviceWebBasedAttestationAllowedUrls"/>
+  <int value="637" label="BlockExternalExtensions"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index cb215d7..2eaa36a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -1036,6 +1036,9 @@
 </histogram>
 
 <histogram name="Ads.Media.BytesReceived" units="KB" expires_after="2020-01-26">
+  <obsolete>
+    Deprecated 10/2019 in issue 1000058; no longer needed.
+  </obsolete>
   <owner>johnidel@chromium.org</owner>
   <summary>
     Total number of bytes buffered over the lifetime of a WebMediaPlayer inside
@@ -62220,6 +62223,9 @@
 </histogram>
 
 <histogram name="Media.BytesReadFromCache" units="KB" expires_after="M82">
+  <obsolete>
+    Deprecated 10/2019 in issue 1000058; no longer needed.
+  </obsolete>
   <owner>hubbe@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>Kb read by media demuxer from MultiBuffer cache.</summary>
@@ -62227,6 +62233,9 @@
 
 <histogram name="Media.BytesReadFromNetwork" units="KB"
     expires_after="2020-04-05">
+  <obsolete>
+    Deprecated 10/2019 in issue 1000058; no longer needed.
+  </obsolete>
   <owner>hubbe@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>Kb read by from network into MultiBuffer cache.</summary>
@@ -62234,6 +62243,9 @@
 
 <histogram base="true" name="Media.BytesReceived" units="KB"
     expires_after="2019-10-15">
+  <obsolete>
+    Deprecated 10/2019 in issue 1000058; no longer needed.
+  </obsolete>
   <owner>hubbe@chromium.org</owner>
   <owner>dalecurtis@chromium.org</owner>
   <summary>
@@ -162550,6 +162562,78 @@
   </summary>
 </histogram>
 
+<histogram name="WebRTC.Video.DecodeTimePerFrameInMs.H264.4k.Hw" units="ms"
+    expires_after="2020-10-23">
+  <owner>kron@chromium.org</owner>
+  <summary>
+    The decode time per frame for a received video stream. Continously updated
+    after each frame has been decoded.
+  </summary>
+</histogram>
+
+<histogram name="WebRTC.Video.DecodeTimePerFrameInMs.H264.4k.Sw" units="ms"
+    expires_after="2020-10-23">
+  <owner>kron@chromium.org</owner>
+  <summary>
+    The decode time per frame for a received video stream. Continously updated
+    after each frame has been decoded.
+  </summary>
+</histogram>
+
+<histogram name="WebRTC.Video.DecodeTimePerFrameInMs.H264.Hd.Hw" units="ms"
+    expires_after="2020-10-23">
+  <owner>kron@chromium.org</owner>
+  <summary>
+    The decode time per frame for a received video stream. Continously updated
+    after each frame has been decoded.
+  </summary>
+</histogram>
+
+<histogram name="WebRTC.Video.DecodeTimePerFrameInMs.H264.Hd.Sw" units="ms"
+    expires_after="2020-10-23">
+  <owner>kron@chromium.org</owner>
+  <summary>
+    The decode time per frame for a received video stream. Continously updated
+    after each frame has been decoded.
+  </summary>
+</histogram>
+
+<histogram name="WebRTC.Video.DecodeTimePerFrameInMs.Vp9.4k.Hw" units="ms"
+    expires_after="2020-10-23">
+  <owner>kron@chromium.org</owner>
+  <summary>
+    The decode time per frame for a received video stream. Continously updated
+    after each frame has been decoded.
+  </summary>
+</histogram>
+
+<histogram name="WebRTC.Video.DecodeTimePerFrameInMs.Vp9.4k.Sw" units="ms"
+    expires_after="2020-10-23">
+  <owner>kron@chromium.org</owner>
+  <summary>
+    The decode time per frame for a received video stream. Continously updated
+    after each frame has been decoded.
+  </summary>
+</histogram>
+
+<histogram name="WebRTC.Video.DecodeTimePerFrameInMs.Vp9.Hd.Hw" units="ms"
+    expires_after="2020-10-23">
+  <owner>kron@chromium.org</owner>
+  <summary>
+    The decode time per frame for a received video stream. Continously updated
+    after each frame has been decoded.
+  </summary>
+</histogram>
+
+<histogram name="WebRTC.Video.DecodeTimePerFrameInMs.Vp9.Hd.Sw" units="ms"
+    expires_after="2020-10-23">
+  <owner>kron@chromium.org</owner>
+  <summary>
+    The decode time per frame for a received video stream. Continously updated
+    after each frame has been decoded.
+  </summary>
+</histogram>
+
 <histogram name="WebRTC.Video.DelayedFramesToRenderer" units="%"
     expires_after="2020-04-19">
   <owner>asapersson@chromium.org</owner>
@@ -171995,6 +172079,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="MediaBytesReceivedTypes" separator=".">
+  <obsolete>
+    Deprecated 10/2019 in issue 1000058; no longer needed.
+  </obsolete>
   <suffix name="EME"
       label="Bytes appended to EME SourceBuffers. Can include either SRC or
              MSE bytes, but is predominantly MSE."/>
diff --git a/tools/perf/benchmarks/system_health.py b/tools/perf/benchmarks/system_health.py
index 6a4818ee..98b5eee 100644
--- a/tools/perf/benchmarks/system_health.py
+++ b/tools/perf/benchmarks/system_health.py
@@ -157,9 +157,7 @@
   """Webview startup time benchmark
 
   Benchmark that measures how long WebView takes to start up
-  and load a blank page. Since thie metric only requires the trace
-  markers recorded in atrace, Chrome tracing is not enabled for this
-  benchmark.
+  and load a blank page.
   """
   options = {'pageset_repeat': 20}
   SUPPORTED_PLATFORMS = [story.expectations.ANDROID_WEBVIEW]
@@ -168,10 +166,12 @@
     return page_sets.SystemHealthBlankStorySet()
 
   def CreateCoreTimelineBasedMeasurementOptions(self):
-    options = timeline_based_measurement.Options()
+    cat_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter(
+        filter_string='startup')
+    options = timeline_based_measurement.Options(cat_filter)
     options.SetTimelineBasedMetrics(['webviewStartupMetric'])
     options.config.enable_atrace_trace = True
-    options.config.enable_chrome_trace = False
+    options.config.enable_chrome_trace = True
     options.config.atrace_config.app_name = 'org.chromium.webview_shell'
     return options
 
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 4d7ff006..a7ae5084 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -350,6 +350,7 @@
 crbug.com/1008028 [ desktop ] system_health.memory_desktop/browse:news:hackernews:2018 [ Skip ]
 crbug.com/1009838 [ mac ] system_health.memory_desktop/browse:tools:maps:2019 [ Skip ]
 crbug.com/1008001 [ win ] system_health.memory_desktop/browse:tools:sheets:2019 [ Skip ]
+crbug.com/1017290 [ linux ] system_health.memory_desktop/browse:search:google_india:2018 [ Skip ]
 
 # Benchmark: system_health.memory_mobile
 crbug.com/1013317 [ android ] system_health.memory_mobile/load:news:nytimes [ Skip ]
@@ -385,7 +386,6 @@
 crbug.com/947267 [ android-nexus-5x ] system_health.memory_mobile/background:media:imgur [ Skip ]
 crbug.com/954949 [ android-nexus-5x android-webview ] system_health.memory_mobile/browse:news:washingtonpost [ Skip ]
 crbug.com/961417 [ android-go ] system_health.memory_mobile/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
-crbug.com/1016701 [ android-nexus-5x ] system_health.memory_mobile/browse:social:tumblr_infinite_scroll:2018 [ Skip ]
 crbug.com/1002640 [ android-pixel-2 ] system_health.memory_mobile/browse:chrome:newtab [ Skip ]
 crbug.com/1002665 [ android-pixel-2 ] system_health.memory_mobile/browse:chrome:omnibox [ Skip ]
 
diff --git a/tools/perf/page_sets/data/system_health_mobile.json b/tools/perf/page_sets/data/system_health_mobile.json
index 4ab41b35..fb9a589 100644
--- a/tools/perf/page_sets/data/system_health_mobile.json
+++ b/tools/perf/page_sets/data/system_health_mobile.json
@@ -118,7 +118,7 @@
             "DEFAULT": "system_health_mobile_062.wprgo"
         },
         "browse:social:tumblr_infinite_scroll:2018": {
-            "DEFAULT": "system_health_mobile_d5a778aa6f.wprgo"
+            "DEFAULT": "system_health_mobile_8f7c29a3a2.wprgo"
         },
         "browse:social:twitter": {
             "DEFAULT": "system_health_mobile_023.wprgo"
@@ -339,4 +339,4 @@
     },
     "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.",
     "platform_specific": true
-}
\ No newline at end of file
+}
diff --git a/tools/perf/page_sets/data/system_health_mobile_8f7c29a3a2.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_8f7c29a3a2.wprgo.sha1
new file mode 100644
index 0000000..561d8de5
--- /dev/null
+++ b/tools/perf/page_sets/data/system_health_mobile_8f7c29a3a2.wprgo.sha1
@@ -0,0 +1 @@
+8f7c29a3a22c7b6a72dde7354384551ba20a0f3f
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/system_health_mobile_d5a778aa6f.wprgo.sha1 b/tools/perf/page_sets/data/system_health_mobile_d5a778aa6f.wprgo.sha1
deleted file mode 100644
index f084a90..0000000
--- a/tools/perf/page_sets/data/system_health_mobile_d5a778aa6f.wprgo.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d5a778aa6f7f38f5fa19999a9642d74d13eca27d
\ No newline at end of file
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h
index 75896ebe..ec76d05 100644
--- a/ui/accessibility/ax_node.h
+++ b/ui/accessibility/ax_node.h
@@ -49,11 +49,11 @@
       ax::mojom::TextAffinity focus_affinity;
     };
 
-    // See AXTree.
+    // See AXTree::GetAXTreeID.
     virtual AXTreeID GetAXTreeID() const = 0;
-    // See AXTree.
+    // See AXTree::GetTableInfo.
     virtual AXTableInfo* GetTableInfo(const AXNode* table_node) const = 0;
-    // See AXTree.
+    // See AXTree::GetFromId.
     virtual AXNode* GetFromId(int32_t id) const = 0;
 
     virtual int32_t GetPosInSet(const AXNode& node,
diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc
index b8c08bc..e68fae8 100644
--- a/ui/aura/window_tree_host.cc
+++ b/ui/aura/window_tree_host.cc
@@ -38,7 +38,6 @@
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/icc_profile.h"
-#include "ui/gfx/overlay_transform_utils.h"
 #include "ui/gfx/switches.h"
 #include "ui/platform_window/platform_window_init_properties.h"
 
@@ -161,14 +160,6 @@
   return invert;
 }
 
-void WindowTreeHost::SetDisplayTransformHint(gfx::OverlayTransform transform) {
-  if (compositor()->display_transform() == transform)
-    return;
-
-  compositor()->SetDisplayTransformHint(transform);
-  UpdateCompositorScaleAndSize();
-}
-
 gfx::Transform WindowTreeHost::GetRootTransformForLocalEventCoordinates()
     const {
   return GetRootTransform();
@@ -193,20 +184,6 @@
   window()->SetBounds(transformed_bounds_in_pixels);
 }
 
-void WindowTreeHost::UpdateCompositorScaleAndSize() {
-  gfx::Rect new_bounds = GetBoundsInPixels();
-  if (compositor_->display_transform() == gfx::OVERLAY_TRANSFORM_ROTATE_90 ||
-      compositor_->display_transform() == gfx::OVERLAY_TRANSFORM_ROTATE_270) {
-    new_bounds.Transpose();
-  }
-
-  // Allocate a new LocalSurfaceId for the new size or scale factor.
-  window_->AllocateLocalSurfaceId();
-  ScopedLocalSurfaceIdValidator lsi_validator(window());
-  compositor_->SetScaleAndSize(device_scale_factor_, new_bounds.size(),
-                               window_->GetLocalSurfaceIdAllocation());
-}
-
 void WindowTreeHost::ConvertDIPToScreenInPixels(gfx::Point* point) const {
   ConvertDIPToPixels(point);
   gfx::Point location = GetLocationOnScreenInPixels();
@@ -480,12 +457,16 @@
   // these two.
   if (!compositor_)
     return;
-
   display::Display display =
       display::Screen::GetScreen()->GetDisplayNearestWindow(window());
   device_scale_factor_ = display.device_scale_factor();
   UpdateRootWindowSizeInPixels();
-  UpdateCompositorScaleAndSize();
+
+  // Allocate a new LocalSurfaceId for the new state.
+  window_->AllocateLocalSurfaceId();
+  ScopedLocalSurfaceIdValidator lsi_validator(window());
+  compositor_->SetScaleAndSize(device_scale_factor_, new_size_in_pixels,
+                               window_->GetLocalSurfaceIdAllocation());
 
   for (WindowTreeHostObserver& observer : observers_)
     observer.OnHostResized(this);
diff --git a/ui/aura/window_tree_host.h b/ui/aura/window_tree_host.h
index 36b2c941..850424d3 100644
--- a/ui/aura/window_tree_host.h
+++ b/ui/aura/window_tree_host.h
@@ -28,7 +28,6 @@
 #include "ui/events/event_source.h"
 #include "ui/events/platform_event.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/overlay_transform.h"
 
 namespace gfx {
 class Point;
@@ -101,8 +100,6 @@
   virtual void SetRootTransform(const gfx::Transform& transform);
   virtual gfx::Transform GetInverseRootTransform() const;
 
-  void SetDisplayTransformHint(gfx::OverlayTransform transform);
-
   // These functions are used in event translation for translating the local
   // coordinates of LocatedEvents. Default implementation calls to non-local
   // ones (e.g. GetRootTransform()).
@@ -117,10 +114,6 @@
   // allows for inconsistencies.
   void UpdateRootWindowSizeInPixels();
 
-  // Updates the compositor's size and scale from display size, scale and
-  // transform hint.
-  void UpdateCompositorScaleAndSize();
-
   // Converts |point| from the root window's coordinate system to native
   // screen's.
   void ConvertDIPToScreenInPixels(gfx::Point* point) const;
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 6d693ce..f2ecb8c 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -456,14 +456,6 @@
   }
 }
 
-void Compositor::SetDisplayTransformHint(gfx::OverlayTransform transform) {
-  if (display_transform_ == transform)
-    return;
-
-  display_transform_ = transform;
-  context_factory_private_->SetDisplayTransformHint(this, display_transform_);
-}
-
 void Compositor::SetBackgroundColor(SkColor color) {
   host_->set_background_color(color);
 
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index e8ae619..17b9c783 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -37,7 +37,6 @@
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/gfx/overlay_transform.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -157,11 +156,6 @@
   virtual void AddVSyncParameterObserver(
       Compositor* compositor,
       viz::mojom::VSyncParameterObserverPtr observer) = 0;
-
-  // Set the transform/rotation info for the display output surface that this
-  // compositor represents.
-  virtual void SetDisplayTransformHint(Compositor* compositor,
-                                       gfx::OverlayTransform transform) = 0;
 };
 
 // This class abstracts the creation of the 3D context for the compositor. It is
@@ -296,10 +290,6 @@
       const gfx::ColorSpace& color_space,
       float sdr_white_level = gfx::ColorSpace::kDefaultSDRWhiteLevel);
 
-  // Set the transform/rotation info for the display output surface.
-  void SetDisplayTransformHint(gfx::OverlayTransform transform);
-  gfx::OverlayTransform display_transform() const { return display_transform_; }
-
   // Returns the size of the widget that is being drawn to in pixel coordinates.
   const gfx::Size& size() const { return size_; }
 
@@ -523,8 +513,6 @@
 
   const char* trace_environment_name_;
 
-  gfx::OverlayTransform display_transform_ = gfx::OVERLAY_TRANSFORM_NONE;
-
   base::WeakPtrFactory<Compositor> context_creation_weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(Compositor);
diff --git a/ui/compositor/host/host_context_factory_private.cc b/ui/compositor/host/host_context_factory_private.cc
index b511d3ca..ce20a9b 100644
--- a/ui/compositor/host/host_context_factory_private.cc
+++ b/ui/compositor/host/host_context_factory_private.cc
@@ -143,8 +143,6 @@
   compositor_data.display_private->Resize(compositor->size());
   compositor_data.display_private->SetOutputIsSecure(
       compositor_data.output_is_secure);
-  compositor_data.display_private->SetDisplayTransformHint(
-      compositor->display_transform());
 
   // Create LayerTreeFrameSink with the browser end of CompositorFrameSink.
   cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams params;
@@ -315,17 +313,6 @@
   }
 }
 
-void HostContextFactoryPrivate::SetDisplayTransformHint(
-    Compositor* compositor,
-    gfx::OverlayTransform transform) {
-  auto iter = compositor_data_map_.find(compositor);
-  if (iter == compositor_data_map_.end())
-    return;
-
-  if (iter->second.display_private)
-    iter->second.display_private->SetDisplayTransformHint(transform);
-}
-
 HostContextFactoryPrivate::CompositorData::CompositorData() = default;
 HostContextFactoryPrivate::CompositorData::CompositorData(
     CompositorData&& other) = default;
diff --git a/ui/compositor/host/host_context_factory_private.h b/ui/compositor/host/host_context_factory_private.h
index f910207b..36f9e80 100644
--- a/ui/compositor/host/host_context_factory_private.h
+++ b/ui/compositor/host/host_context_factory_private.h
@@ -90,8 +90,6 @@
   void AddVSyncParameterObserver(
       Compositor* compositor,
       viz::mojom::VSyncParameterObserverPtr observer) override;
-  void SetDisplayTransformHint(Compositor* compositor,
-                               gfx::OverlayTransform transform) override;
 
  private:
   struct CompositorData {
diff --git a/ui/compositor/test/in_process_context_factory.h b/ui/compositor/test/in_process_context_factory.h
index 0e1c66c..6dd96c5e 100644
--- a/ui/compositor/test/in_process_context_factory.h
+++ b/ui/compositor/test/in_process_context_factory.h
@@ -99,8 +99,6 @@
   void AddVSyncParameterObserver(
       ui::Compositor* compositor,
       viz::mojom::VSyncParameterObserverPtr observer) override {}
-  void SetDisplayTransformHint(Compositor* compositor,
-                               gfx::OverlayTransform transform) override {}
   void AddObserver(ContextFactoryObserver* observer) override;
   void RemoveObserver(ContextFactoryObserver* observer) override;
   bool SyncTokensRequiredForDisplayCompositor() override;
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 903e67b..9c54aa7 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -54,11 +54,6 @@
   'names': ['glActiveTexture'],
   'arguments': 'GLenum texture', },
 { 'return_type': 'void',
-  'known_as': 'glApplyFramebufferAttachmentCMAAINTEL',
-  'versions': [{ 'name': 'glApplyFramebufferAttachmentCMAAINTEL',
-                 'extensions': ['GL_INTEL_framebuffer_CMAA'] }],
-  'arguments': 'void', },
-{ 'return_type': 'void',
   'names': ['glAttachShader'],
   'arguments': 'GLuint program, GLuint shader', },
 { 'return_type': 'void',
diff --git a/ui/gl/gl_bindings_api_autogen_gl.h b/ui/gl/gl_bindings_api_autogen_gl.h
index ba6e23f5..c3af76e 100644
--- a/ui/gl/gl_bindings_api_autogen_gl.h
+++ b/ui/gl/gl_bindings_api_autogen_gl.h
@@ -14,7 +14,6 @@
 
 void glActiveShaderProgramFn(GLuint pipeline, GLuint program) override;
 void glActiveTextureFn(GLenum texture) override;
-void glApplyFramebufferAttachmentCMAAINTELFn(void) override;
 void glAttachShaderFn(GLuint program, GLuint shader) override;
 void glBeginQueryFn(GLenum target, GLuint id) override;
 void glBeginTransformFeedbackFn(GLenum primitiveMode) override;
diff --git a/ui/gl/gl_bindings_autogen_gl.cc b/ui/gl/gl_bindings_autogen_gl.cc
index aa49c5e..2f8af7d 100644
--- a/ui/gl/gl_bindings_autogen_gl.cc
+++ b/ui/gl/gl_bindings_autogen_gl.cc
@@ -409,8 +409,6 @@
       gfx::HasExtension(extensions, "GL_EXT_window_rectangles");
   ext.b_GL_IMG_multisampled_render_to_texture =
       gfx::HasExtension(extensions, "GL_IMG_multisampled_render_to_texture");
-  ext.b_GL_INTEL_framebuffer_CMAA =
-      gfx::HasExtension(extensions, "GL_INTEL_framebuffer_CMAA");
   ext.b_GL_KHR_blend_equation_advanced =
       gfx::HasExtension(extensions, "GL_KHR_blend_equation_advanced");
   ext.b_GL_KHR_debug = gfx::HasExtension(extensions, "GL_KHR_debug");
@@ -446,12 +444,6 @@
         GetGLProcAddress("glActiveShaderProgram"));
   }
 
-  if (ext.b_GL_INTEL_framebuffer_CMAA) {
-    fn.glApplyFramebufferAttachmentCMAAINTELFn =
-        reinterpret_cast<glApplyFramebufferAttachmentCMAAINTELProc>(
-            GetGLProcAddress("glApplyFramebufferAttachmentCMAAINTEL"));
-  }
-
   if (!ver->is_es || ver->IsAtLeastGLES(3u, 0u)) {
     fn.glBeginQueryFn =
         reinterpret_cast<glBeginQueryProc>(GetGLProcAddress("glBeginQuery"));
@@ -2856,10 +2848,6 @@
   driver_->fn.glActiveTextureFn(texture);
 }
 
-void GLApiBase::glApplyFramebufferAttachmentCMAAINTELFn(void) {
-  driver_->fn.glApplyFramebufferAttachmentCMAAINTELFn();
-}
-
 void GLApiBase::glAttachShaderFn(GLuint program, GLuint shader) {
   driver_->fn.glAttachShaderFn(program, shader);
 }
@@ -6076,12 +6064,6 @@
   gl_api_->glActiveTextureFn(texture);
 }
 
-void TraceGLApi::glApplyFramebufferAttachmentCMAAINTELFn(void) {
-  TRACE_EVENT_BINARY_EFFICIENT0(
-      "gpu", "TraceGLAPI::glApplyFramebufferAttachmentCMAAINTEL")
-  gl_api_->glApplyFramebufferAttachmentCMAAINTELFn();
-}
-
 void TraceGLApi::glAttachShaderFn(GLuint program, GLuint shader) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glAttachShader")
   gl_api_->glAttachShaderFn(program, shader);
@@ -9861,13 +9843,6 @@
   gl_api_->glActiveTextureFn(texture);
 }
 
-void DebugGLApi::glApplyFramebufferAttachmentCMAAINTELFn(void) {
-  GL_SERVICE_LOG("glApplyFramebufferAttachmentCMAAINTEL"
-                 << "("
-                 << ")");
-  gl_api_->glApplyFramebufferAttachmentCMAAINTELFn();
-}
-
 void DebugGLApi::glAttachShaderFn(GLuint program, GLuint shader) {
   GL_SERVICE_LOG("glAttachShader"
                  << "(" << program << ", " << shader << ")");
@@ -14856,10 +14831,6 @@
   NoContextHelper("glActiveTexture");
 }
 
-void NoContextGLApi::glApplyFramebufferAttachmentCMAAINTELFn(void) {
-  NoContextHelper("glApplyFramebufferAttachmentCMAAINTEL");
-}
-
 void NoContextGLApi::glAttachShaderFn(GLuint program, GLuint shader) {
   NoContextHelper("glAttachShader");
 }
diff --git a/ui/gl/gl_bindings_autogen_gl.h b/ui/gl/gl_bindings_autogen_gl.h
index 40fe42a..3df7342 100644
--- a/ui/gl/gl_bindings_autogen_gl.h
+++ b/ui/gl/gl_bindings_autogen_gl.h
@@ -20,7 +20,6 @@
 typedef void(GL_BINDING_CALL* glActiveShaderProgramProc)(GLuint pipeline,
                                                          GLuint program);
 typedef void(GL_BINDING_CALL* glActiveTextureProc)(GLenum texture);
-typedef void(GL_BINDING_CALL* glApplyFramebufferAttachmentCMAAINTELProc)(void);
 typedef void(GL_BINDING_CALL* glAttachShaderProc)(GLuint program,
                                                   GLuint shader);
 typedef void(GL_BINDING_CALL* glBeginQueryProc)(GLenum target, GLuint id);
@@ -1930,7 +1929,6 @@
   bool b_GL_EXT_unpack_subimage;
   bool b_GL_EXT_window_rectangles;
   bool b_GL_IMG_multisampled_render_to_texture;
-  bool b_GL_INTEL_framebuffer_CMAA;
   bool b_GL_KHR_blend_equation_advanced;
   bool b_GL_KHR_debug;
   bool b_GL_KHR_parallel_shader_compile;
@@ -1954,8 +1952,6 @@
 struct ProcsGL {
   glActiveShaderProgramProc glActiveShaderProgramFn;
   glActiveTextureProc glActiveTextureFn;
-  glApplyFramebufferAttachmentCMAAINTELProc
-      glApplyFramebufferAttachmentCMAAINTELFn;
   glAttachShaderProc glAttachShaderFn;
   glBeginQueryProc glBeginQueryFn;
   glBeginTransformFeedbackProc glBeginTransformFeedbackFn;
@@ -2462,7 +2458,6 @@
 
   virtual void glActiveShaderProgramFn(GLuint pipeline, GLuint program) = 0;
   virtual void glActiveTextureFn(GLenum texture) = 0;
-  virtual void glApplyFramebufferAttachmentCMAAINTELFn(void) = 0;
   virtual void glAttachShaderFn(GLuint program, GLuint shader) = 0;
   virtual void glBeginQueryFn(GLenum target, GLuint id) = 0;
   virtual void glBeginTransformFeedbackFn(GLenum primitiveMode) = 0;
@@ -4116,8 +4111,6 @@
 #define glActiveShaderProgram \
   ::gl::g_current_gl_context->glActiveShaderProgramFn
 #define glActiveTexture ::gl::g_current_gl_context->glActiveTextureFn
-#define glApplyFramebufferAttachmentCMAAINTEL \
-  ::gl::g_current_gl_context->glApplyFramebufferAttachmentCMAAINTELFn
 #define glAttachShader ::gl::g_current_gl_context->glAttachShaderFn
 #define glBeginQuery ::gl::g_current_gl_context->glBeginQueryFn
 #define glBeginTransformFeedback \
diff --git a/ui/gl/gl_bindings_autogen_mock.cc b/ui/gl/gl_bindings_autogen_mock.cc
index e65a9f3..4067c72 100644
--- a/ui/gl/gl_bindings_autogen_mock.cc
+++ b/ui/gl/gl_bindings_autogen_mock.cc
@@ -34,12 +34,6 @@
   interface_->ActiveTexture(texture);
 }
 
-void GL_BINDING_CALL
-MockGLInterface::Mock_glApplyFramebufferAttachmentCMAAINTEL(void) {
-  MakeGlMockFunctionUnique("glApplyFramebufferAttachmentCMAAINTEL");
-  interface_->ApplyFramebufferAttachmentCMAAINTEL();
-}
-
 void GL_BINDING_CALL MockGLInterface::Mock_glAttachShader(GLuint program,
                                                           GLuint shader) {
   MakeGlMockFunctionUnique("glAttachShader");
@@ -5069,9 +5063,6 @@
     return reinterpret_cast<GLFunctionPointerType>(Mock_glActiveShaderProgram);
   if (strcmp(name, "glActiveTexture") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glActiveTexture);
-  if (strcmp(name, "glApplyFramebufferAttachmentCMAAINTEL") == 0)
-    return reinterpret_cast<GLFunctionPointerType>(
-        Mock_glApplyFramebufferAttachmentCMAAINTEL);
   if (strcmp(name, "glAttachShader") == 0)
     return reinterpret_cast<GLFunctionPointerType>(Mock_glAttachShader);
   if (strcmp(name, "glBeginQuery") == 0)
diff --git a/ui/gl/gl_bindings_autogen_mock.h b/ui/gl/gl_bindings_autogen_mock.h
index a5b213b..579b2f9 100644
--- a/ui/gl/gl_bindings_autogen_mock.h
+++ b/ui/gl/gl_bindings_autogen_mock.h
@@ -15,7 +15,6 @@
 static void GL_BINDING_CALL Mock_glActiveShaderProgram(GLuint pipeline,
                                                        GLuint program);
 static void GL_BINDING_CALL Mock_glActiveTexture(GLenum texture);
-static void GL_BINDING_CALL Mock_glApplyFramebufferAttachmentCMAAINTEL(void);
 static void GL_BINDING_CALL Mock_glAttachShader(GLuint program, GLuint shader);
 static void GL_BINDING_CALL Mock_glBeginQuery(GLenum target, GLuint id);
 static void GL_BINDING_CALL Mock_glBeginQueryARB(GLenum target, GLuint id);
diff --git a/ui/gl/gl_mock_autogen_gl.h b/ui/gl/gl_mock_autogen_gl.h
index d8b0d20d..cadd371 100644
--- a/ui/gl/gl_mock_autogen_gl.h
+++ b/ui/gl/gl_mock_autogen_gl.h
@@ -14,7 +14,6 @@
 
 MOCK_METHOD2(ActiveShaderProgram, void(GLuint pipeline, GLuint program));
 MOCK_METHOD1(ActiveTexture, void(GLenum texture));
-MOCK_METHOD0(ApplyFramebufferAttachmentCMAAINTEL, void());
 MOCK_METHOD2(AttachShader, void(GLuint program, GLuint shader));
 MOCK_METHOD2(BeginQuery, void(GLenum target, GLuint id));
 MOCK_METHOD1(BeginTransformFeedback, void(GLenum primitiveMode));
diff --git a/ui/gl/gl_stub_autogen_gl.h b/ui/gl/gl_stub_autogen_gl.h
index 2a48e5f..dc15d683 100644
--- a/ui/gl/gl_stub_autogen_gl.h
+++ b/ui/gl/gl_stub_autogen_gl.h
@@ -13,7 +13,6 @@
 
 void glActiveShaderProgramFn(GLuint pipeline, GLuint program) override {}
 void glActiveTextureFn(GLenum texture) override {}
-void glApplyFramebufferAttachmentCMAAINTELFn() override {}
 void glAttachShaderFn(GLuint program, GLuint shader) override {}
 void glBeginQueryFn(GLenum target, GLuint id) override {}
 void glBeginTransformFeedbackFn(GLenum primitiveMode) override {}
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index 876a316d..fec3a15d 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -53,6 +53,10 @@
     "browser/navigation_impl.h",
     "browser/profile_impl.cc",
     "browser/profile_impl.h",
+    "browser/safe_browsing/safe_browsing_ui_manager.cc",
+    "browser/safe_browsing/safe_browsing_ui_manager.h",
+    "browser/safe_browsing/url_checker_delegate_impl.cc",
+    "browser/safe_browsing/url_checker_delegate_impl.h",
     "browser/weblayer_content_browser_overlay_manifest.cc",
     "browser/weblayer_content_browser_overlay_manifest.h",
     "browser/webui/web_ui_controller_factory.cc",
@@ -61,6 +65,8 @@
     "browser/webui/weblayer_internals_ui.h",
     "common/content_client_impl.cc",
     "common/content_client_impl.h",
+    "common/features.cc",
+    "common/features.h",
     "public/browser_controller.h",
     "public/browser_observer.h",
     "public/download_delegate.h",
@@ -89,6 +95,9 @@
     "//base:base_static",
     "//base/third_party/dynamic_annotations",
     "//cc",
+    "//components/safe_browsing",
+    "//components/safe_browsing/browser",
+    "//components/safe_browsing/db:database_manager",
     "//content:resources",
     "//content/app/resources",
     "//content/public/app:both",
@@ -125,6 +134,10 @@
     "//weblayer/browser/webui:mojo_bindings",
   ]
 
+  if (is_android) {
+    deps += [ "//components/safe_browsing/android:remote_database_manager" ]
+  }
+
   if (enable_vulkan) {
     deps += [ "//gpu/vulkan/init" ]
   }
diff --git a/weblayer/browser/DEPS b/weblayer/browser/DEPS
index dad9ebcf..a67e10e 100644
--- a/weblayer/browser/DEPS
+++ b/weblayer/browser/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+cc",
   "+components/embedder_support",
+  "+components/safe_browsing",
   "+content/public",
   "+mojo/public",
   "+net",
diff --git a/weblayer/browser/browser_controller_impl.cc b/weblayer/browser/browser_controller_impl.cc
index 97712877..600386c 100644
--- a/weblayer/browser/browser_controller_impl.cc
+++ b/weblayer/browser/browser_controller_impl.cc
@@ -175,17 +175,9 @@
 
 #endif
 
-void BrowserControllerImpl::LoadingStateChanged(content::WebContents* source,
-                                                bool to_different_document) {
-  bool is_loading = web_contents_->IsLoading();
-  for (auto& observer : observers_)
-    observer.LoadingStateChanged(is_loading, to_different_document);
-}
-
 void BrowserControllerImpl::LoadProgressChanged(content::WebContents* source,
                                                 double progress) {
-  for (auto& observer : observers_)
-    observer.LoadProgressChanged(progress);
+  navigation_controller_->NotifyLoadProgressChanged(progress);
 }
 
 void BrowserControllerImpl::DidNavigateMainFramePostCommit(
diff --git a/weblayer/browser/browser_controller_impl.h b/weblayer/browser/browser_controller_impl.h
index 2533db8..3a6b4d8d 100644
--- a/weblayer/browser/browser_controller_impl.h
+++ b/weblayer/browser/browser_controller_impl.h
@@ -74,8 +74,6 @@
 #endif
 
   // content::WebContentsDelegate:
-  void LoadingStateChanged(content::WebContents* source,
-                           bool to_different_document) override;
   void LoadProgressChanged(content::WebContents* source,
                            double progress) override;
   void DidNavigateMainFramePostCommit(
diff --git a/weblayer/browser/browser_observer_proxy.cc b/weblayer/browser/browser_observer_proxy.cc
index a45fa5c..ede1316e 100644
--- a/weblayer/browser/browser_observer_proxy.cc
+++ b/weblayer/browser/browser_observer_proxy.cc
@@ -34,18 +34,6 @@
   Java_BrowserObserverProxy_visibleUrlChanged(env, java_observer_, jstring_url);
 }
 
-void BrowserObserverProxy::LoadingStateChanged(bool is_loading,
-                                               bool to_different_document) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_BrowserObserverProxy_loadingStateChanged(env, java_observer_, is_loading,
-                                                to_different_document);
-}
-
-void BrowserObserverProxy::LoadProgressChanged(double progress) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_BrowserObserverProxy_loadProgressChanged(env, java_observer_, progress);
-}
-
 static jlong JNI_BrowserObserverProxy_CreateBrowserObserverProxy(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& proxy,
diff --git a/weblayer/browser/browser_observer_proxy.h b/weblayer/browser/browser_observer_proxy.h
index 4d76955..1b50553e 100644
--- a/weblayer/browser/browser_observer_proxy.h
+++ b/weblayer/browser/browser_observer_proxy.h
@@ -26,9 +26,6 @@
 
   // BrowserObserver:
   void DisplayedUrlChanged(const GURL& url) override;
-  void LoadingStateChanged(bool is_loading,
-                           bool to_different_document) override;
-  void LoadProgressChanged(double progress) override;
 
  private:
   BrowserController* browser_controller_;
diff --git a/weblayer/browser/content_browser_client_impl.cc b/weblayer/browser/content_browser_client_impl.cc
index 4a601c0..217894b 100644
--- a/weblayer/browser/content_browser_client_impl.cc
+++ b/weblayer/browser/content_browser_client_impl.cc
@@ -12,6 +12,8 @@
 #include "base/path_service.h"
 #include "base/stl_util.h"
 #include "build/build_config.h"
+#include "components/safe_browsing/android/remote_database_manager.h"
+#include "components/safe_browsing/browser/browser_url_loader_throttle.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/devtools_manager_delegate.h"
 #include "content/public/browser/network_service_instance.h"
@@ -26,7 +28,10 @@
 #include "url/origin.h"
 #include "weblayer/browser/browser_controller_impl.h"
 #include "weblayer/browser/browser_main_parts_impl.h"
+#include "weblayer/browser/safe_browsing/safe_browsing_ui_manager.h"
+#include "weblayer/browser/safe_browsing/url_checker_delegate_impl.h"
 #include "weblayer/browser/weblayer_content_browser_overlay_manifest.h"
+#include "weblayer/common/features.h"
 #include "weblayer/public/fullscreen_delegate.h"
 #include "weblayer/public/main.h"
 
@@ -45,6 +50,19 @@
 #include "services/service_manager/sandbox/win/sandbox_win.h"
 #endif
 
+namespace {
+
+bool IsSafebrowsingSupported() {
+  // TODO(timvolodine): consider the non-android case, see crbug.com/1015809.
+  // TODO(timvolodine): consider refactoring this out into safe_browsing/.
+#if defined(OS_ANDROID)
+  return true;
+#endif
+  return false;
+}
+
+}  // namespace
+
 namespace weblayer {
 
 ContentBrowserClientImpl::ContentBrowserClientImpl(MainParams* params)
@@ -143,6 +161,46 @@
 #endif
 }
 
+std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
+ContentBrowserClientImpl::CreateURLLoaderThrottles(
+    const network::ResourceRequest& request,
+    content::BrowserContext* browser_context,
+    const base::RepeatingCallback<content::WebContents*()>& wc_getter,
+    content::NavigationUIData* navigation_ui_data,
+    int frame_tree_node_id) {
+  std::vector<std::unique_ptr<blink::URLLoaderThrottle>> result;
+
+  if (base::FeatureList::IsEnabled(features::kWebLayerSafeBrowsing) &&
+      IsSafebrowsingSupported()) {
+    result.push_back(safe_browsing::BrowserURLLoaderThrottle::Create(
+        base::BindOnce(
+            [](ContentBrowserClientImpl* client, content::ResourceContext*) {
+              return client->GetSafeBrowsingUrlCheckerDelegate();
+            },
+            base::Unretained(this)),
+        wc_getter, frame_tree_node_id, browser_context->GetResourceContext()));
+  }
+
+  return result;
+}
+
+scoped_refptr<safe_browsing::UrlCheckerDelegate>
+ContentBrowserClientImpl::GetSafeBrowsingUrlCheckerDelegate() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+#if defined(OS_ANDROID)
+  if (!safe_browsing_url_checker_delegate_) {
+    // TODO(timvolodine): consider a better place for the database manager and
+    // the ui manager (also w.r.t. future safebrowsing init sequence).
+    safe_browsing_url_checker_delegate_ = new UrlCheckerDelegateImpl(
+        new safe_browsing::RemoteSafeBrowsingDatabaseManager(),
+        new SafeBrowsingUIManager());
+  }
+#endif
+
+  return safe_browsing_url_checker_delegate_;
+}
+
 #if defined(OS_LINUX) || defined(OS_ANDROID)
 void ContentBrowserClientImpl::GetAdditionalMappedFilesForChildProcess(
     const base::CommandLine& command_line,
diff --git a/weblayer/browser/content_browser_client_impl.h b/weblayer/browser/content_browser_client_impl.h
index c911070..e8e4c623 100644
--- a/weblayer/browser/content_browser_client_impl.h
+++ b/weblayer/browser/content_browser_client_impl.h
@@ -15,6 +15,10 @@
 #include "content/public/browser/content_browser_client.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 
+namespace safe_browsing {
+class UrlCheckerDelegate;
+}
+
 namespace weblayer {
 
 struct MainParams;
@@ -43,6 +47,13 @@
       const base::FilePath& relative_partition_path) override;
   void OnNetworkServiceCreated(
       network::mojom::NetworkService* network_service) override;
+  std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
+  CreateURLLoaderThrottles(
+      const network::ResourceRequest& request,
+      content::BrowserContext* browser_context,
+      const base::RepeatingCallback<content::WebContents*()>& wc_getter,
+      content::NavigationUIData* navigation_ui_data,
+      int frame_tree_node_id) override;
 
 #if defined(OS_LINUX) || defined(OS_ANDROID)
   void GetAdditionalMappedFilesForChildProcess(
@@ -52,7 +63,13 @@
 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
 
  private:
+  scoped_refptr<safe_browsing::UrlCheckerDelegate>
+  GetSafeBrowsingUrlCheckerDelegate();
+
   MainParams* params_;
+
+  scoped_refptr<safe_browsing::UrlCheckerDelegate>
+      safe_browsing_url_checker_delegate_;
 };
 
 }  // namespace weblayer
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java
index 3425109..1128d16f 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserObserverProxy.java
@@ -37,17 +37,6 @@
         mClient.visibleUrlChanged(string);
     }
 
-    @CalledByNative
-    private void loadingStateChanged(boolean isLoading, boolean toDifferentDocument)
-            throws RemoteException {
-        mClient.loadingStateChanged(isLoading, toDifferentDocument);
-    }
-
-    @CalledByNative
-    private void loadProgressChanged(double progress) throws RemoteException {
-        mClient.loadProgressChanged(progress);
-    }
-
     @NativeMethods
     interface Natives {
         long createBrowserObserverProxy(BrowserObserverProxy proxy, long browserController);
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java b/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java
index 9bd86d50..8b5446b 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ContentView.java
@@ -41,7 +41,7 @@
 public class ContentView extends FrameLayout
         implements ViewEventSink.InternalAccessDelegate, SmartClipProvider,
                    OnHierarchyChangeListener, OnSystemUiVisibilityChangeListener {
-    private static final String TAG = "cr.ContentView";
+    private static final String TAG = "ContentView";
 
     // Default value to signal that the ContentView's size need not be overridden.
     public static final int DEFAULT_MEASURE_SPEC =
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
index 6dcb190..8b4c226 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java
@@ -122,6 +122,17 @@
     }
 
     @CalledByNative
+    private void loadStateChanged(boolean isLoading, boolean toDifferentDocument)
+            throws RemoteException {
+        mNavigationControllerClient.loadStateChanged(isLoading, toDifferentDocument);
+    }
+
+    @CalledByNative
+    private void loadProgressChanged(double progress) throws RemoteException {
+        mNavigationControllerClient.loadProgressChanged(progress);
+    }
+
+    @CalledByNative
     private void onFirstContentfulPaint() throws RemoteException {
         mNavigationControllerClient.onFirstContentfulPaint();
     }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl
index a17e4b4..750a0b47 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl
@@ -10,6 +10,4 @@
  */
 interface IBrowserControllerClient {
   void visibleUrlChanged(in String url) = 0;
-  void loadingStateChanged(boolean is_loading, boolean to_different_document) = 1;
-  void loadProgressChanged(double progress) = 2;
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationControllerClient.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationControllerClient.aidl
index fd77146..801e502 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationControllerClient.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/INavigationControllerClient.aidl
@@ -21,5 +21,9 @@
 
   void navigationFailed(IClientNavigation navigation) = 5;
 
-  void onFirstContentfulPaint() = 6;
+  void loadStateChanged(boolean isLoading, boolean toDifferentDocument) = 6;
+
+  void loadProgressChanged(double progress) = 7;
+
+  void onFirstContentfulPaint() = 8;
 }
diff --git a/weblayer/browser/navigation_controller_impl.cc b/weblayer/browser/navigation_controller_impl.cc
index 75c1671..d53ffd12 100644
--- a/weblayer/browser/navigation_controller_impl.cc
+++ b/weblayer/browser/navigation_controller_impl.cc
@@ -25,8 +25,7 @@
 
 NavigationControllerImpl::NavigationControllerImpl(
     BrowserControllerImpl* browser_controller)
-    : WebContentsObserver(browser_controller->web_contents()),
-      browser_controller_(browser_controller) {}
+    : WebContentsObserver(browser_controller->web_contents()) {}
 
 NavigationControllerImpl::~NavigationControllerImpl() = default;
 
@@ -55,6 +54,17 @@
 }
 #endif
 
+void NavigationControllerImpl::NotifyLoadProgressChanged(double progress) {
+#if defined(OS_ANDROID)
+  if (java_controller_) {
+    Java_NavigationControllerImpl_loadProgressChanged(
+        AttachCurrentThread(), java_controller_, progress);
+  }
+#endif
+  for (auto& observer : observers_)
+    observer.LoadProgressChanged(progress);
+}
+
 void NavigationControllerImpl::AddObserver(NavigationObserver* observer) {
   observers_.AddObserver(observer);
 }
@@ -67,52 +77,46 @@
   content::NavigationController::LoadURLParams params(url);
   params.transition_type = ui::PageTransitionFromInt(
       ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
-  browser_controller_->web_contents()->GetController().LoadURLWithParams(
-      params);
+  web_contents()->GetController().LoadURLWithParams(params);
   // So that if the user had entered the UI in a bar it stops flashing the
   // caret.
-  browser_controller_->web_contents()->Focus();
+  web_contents()->Focus();
 }
 
 void NavigationControllerImpl::GoBack() {
-  browser_controller_->web_contents()->GetController().GoBack();
+  web_contents()->GetController().GoBack();
 }
 
 void NavigationControllerImpl::GoForward() {
-  browser_controller_->web_contents()->GetController().GoForward();
+  web_contents()->GetController().GoForward();
 }
 
 bool NavigationControllerImpl::CanGoBack() {
-  return browser_controller_->web_contents()->GetController().CanGoBack();
+  return web_contents()->GetController().CanGoBack();
 }
 
 bool NavigationControllerImpl::CanGoForward() {
-  return browser_controller_->web_contents()->GetController().CanGoForward();
+  return web_contents()->GetController().CanGoForward();
 }
 
 void NavigationControllerImpl::Reload() {
-  browser_controller_->web_contents()->GetController().Reload(
-      content::ReloadType::NORMAL, false);
+  web_contents()->GetController().Reload(content::ReloadType::NORMAL, false);
 }
 
 void NavigationControllerImpl::Stop() {
-  browser_controller_->web_contents()->Stop();
+  web_contents()->Stop();
 }
 
 int NavigationControllerImpl::GetNavigationListSize() {
-  return browser_controller_->web_contents()->GetController().GetEntryCount();
+  return web_contents()->GetController().GetEntryCount();
 }
 
 int NavigationControllerImpl::GetNavigationListCurrentIndex() {
-  return browser_controller_->web_contents()
-      ->GetController()
-      .GetCurrentEntryIndex();
+  return web_contents()->GetController().GetCurrentEntryIndex();
 }
 
 GURL NavigationControllerImpl::GetNavigationEntryDisplayURL(int index) {
-  auto* entry =
-      browser_controller_->web_contents()->GetController().GetEntryAtIndex(
-          index);
+  auto* entry = web_contents()->GetController().GetEntryAtIndex(index);
   if (!entry)
     return GURL();
   return entry->GetVirtualURL();
@@ -206,6 +210,14 @@
   navigation_map_.erase(navigation_map_.find(navigation_handle));
 }
 
+void NavigationControllerImpl::DidStartLoading() {
+  NotifyLoadStateChanged();
+}
+
+void NavigationControllerImpl::DidStopLoading() {
+  NotifyLoadStateChanged();
+}
+
 void NavigationControllerImpl::DidFirstVisuallyNonEmptyPaint() {
 #if defined(OS_ANDROID)
   Java_NavigationControllerImpl_onFirstContentfulPaint(AttachCurrentThread(),
@@ -216,6 +228,20 @@
     observer.OnFirstContentfulPaint();
 }
 
+void NavigationControllerImpl::NotifyLoadStateChanged() {
+#if defined(OS_ANDROID)
+  if (java_controller_) {
+    Java_NavigationControllerImpl_loadStateChanged(
+        AttachCurrentThread(), java_controller_, web_contents()->IsLoading(),
+        web_contents()->IsLoadingToDifferentDocument());
+  }
+#endif
+  for (auto& observer : observers_) {
+    observer.LoadStateChanged(web_contents()->IsLoading(),
+                              web_contents()->IsLoadingToDifferentDocument());
+  }
+}
+
 #if defined(OS_ANDROID)
 static jlong JNI_NavigationControllerImpl_GetNavigationController(
     JNIEnv* env,
diff --git a/weblayer/browser/navigation_controller_impl.h b/weblayer/browser/navigation_controller_impl.h
index e4b9d3dde..868a6b4 100644
--- a/weblayer/browser/navigation_controller_impl.h
+++ b/weblayer/browser/navigation_controller_impl.h
@@ -67,6 +67,11 @@
       int index);
 #endif
 
+  // Called by BrowserControllerImpl when the loading progress has changed.
+  // TODO(estade): move LoadProgressChanged from WebContentsDelegate to
+  // WebContentsObserver to avoid this extra bounce.
+  void NotifyLoadProgressChanged(double progress);
+
  private:
   // NavigationController implementation:
   void AddObserver(NavigationObserver* observer) override;
@@ -91,9 +96,12 @@
       content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
+  void DidStartLoading() override;
+  void DidStopLoading() override;
   void DidFirstVisuallyNonEmptyPaint() override;
 
-  BrowserControllerImpl* browser_controller_;
+  void NotifyLoadStateChanged();
+
   base::ObserverList<NavigationObserver>::Unchecked observers_;
   std::map<content::NavigationHandle*, std::unique_ptr<NavigationImpl>>
       navigation_map_;
diff --git a/weblayer/browser/safe_browsing/OWNERS b/weblayer/browser/safe_browsing/OWNERS
new file mode 100644
index 0000000..1fd89e0
--- /dev/null
+++ b/weblayer/browser/safe_browsing/OWNERS
@@ -0,0 +1 @@
+timvolodine@chromium.org
diff --git a/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc b/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc
new file mode 100644
index 0000000..a5dbb6a
--- /dev/null
+++ b/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "weblayer/browser/safe_browsing/safe_browsing_ui_manager.h"
+
+#include "content/public/browser/browser_thread.h"
+
+using content::BrowserThread;
+
+namespace weblayer {
+
+SafeBrowsingUIManager::SafeBrowsingUIManager() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // TODO(timvolodine): properly init the ui manager and the context.
+}
+
+SafeBrowsingUIManager::~SafeBrowsingUIManager() {}
+
+void SafeBrowsingUIManager::DisplayBlockingPage(
+    const UnsafeResource& resource) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // TODO(timvolodine): check if we can reuse the base class implementation here
+  // as is.
+}
+
+void SafeBrowsingUIManager::SendSerializedThreatDetails(
+    const std::string& serialized) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // TODO(timvolodine): figure out if we want to send any threat reporting here.
+  // Note the base implementation does not send anything.
+}
+
+}  // namespace weblayer
diff --git a/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h b/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h
new file mode 100644
index 0000000..66830af
--- /dev/null
+++ b/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h
@@ -0,0 +1,30 @@
+// 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 WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_UI_MANAGER_H_
+#define WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_UI_MANAGER_H_
+
+#include "components/safe_browsing/base_ui_manager.h"
+
+namespace weblayer {
+
+class SafeBrowsingUIManager : public safe_browsing::BaseUIManager {
+ public:
+  // Construction needs to happen on the UI thread.
+  SafeBrowsingUIManager();
+
+  // BaseUIManager overrides.
+  void DisplayBlockingPage(const UnsafeResource& resource) override;
+  void SendSerializedThreatDetails(const std::string& serialized) override;
+
+ protected:
+  ~SafeBrowsingUIManager() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUIManager);
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_UI_MANAGER_H_
\ No newline at end of file
diff --git a/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc b/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
new file mode 100644
index 0000000..93ad4ae7
--- /dev/null
+++ b/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc
@@ -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.
+
+#include "weblayer/browser/safe_browsing/url_checker_delegate_impl.h"
+
+#include "components/safe_browsing/db/database_manager.h"
+#include "weblayer/browser/safe_browsing/safe_browsing_ui_manager.h"
+
+namespace weblayer {
+
+UrlCheckerDelegateImpl::UrlCheckerDelegateImpl(
+    scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager,
+    scoped_refptr<SafeBrowsingUIManager> ui_manager)
+    : database_manager_(std::move(database_manager)),
+      ui_manager_(std::move(ui_manager)) {}
+
+UrlCheckerDelegateImpl::~UrlCheckerDelegateImpl() = default;
+
+void UrlCheckerDelegateImpl::MaybeDestroyPrerenderContents(
+    const base::Callback<content::WebContents*()>& web_contents_getter) {}
+
+void UrlCheckerDelegateImpl::StartDisplayingBlockingPageHelper(
+    const security_interstitials::UnsafeResource& resource,
+    const std::string& method,
+    const net::HttpRequestHeaders& headers,
+    bool is_main_frame,
+    bool has_user_gesture) {
+  // TODO(timvolodine): figure out what to do here.
+}
+
+bool UrlCheckerDelegateImpl::IsUrlWhitelisted(const GURL& url) {
+  // TODO(timvolodine): false for now, we may want whitelisting support later.
+  return false;
+}
+
+bool UrlCheckerDelegateImpl::ShouldSkipRequestCheck(
+    content::ResourceContext* resource_context,
+    const GURL& original_url,
+    int frame_tree_node_id,
+    int render_process_id,
+    int render_frame_id,
+    bool originated_from_service_worker) {
+  // TODO(timvolodine): this is needed when safebrowsing is not enabled.
+  // For now in the context of weblayer we consider safebrowsing as always
+  // enabled. This may change in the future.
+  return false;
+}
+
+void UrlCheckerDelegateImpl::NotifySuspiciousSiteDetected(
+    const base::RepeatingCallback<content::WebContents*()>&
+        web_contents_getter) {}
+
+const safe_browsing::SBThreatTypeSet& UrlCheckerDelegateImpl::GetThreatTypes() {
+  // TODO(timvolodine): revisit with the relevant threat types.
+  return threat_types_;
+}
+
+safe_browsing::SafeBrowsingDatabaseManager*
+UrlCheckerDelegateImpl::GetDatabaseManager() {
+  return database_manager_.get();
+}
+
+safe_browsing::BaseUIManager* UrlCheckerDelegateImpl::GetUIManager() {
+  return ui_manager_.get();
+}
+
+}  // namespace weblayer
diff --git a/weblayer/browser/safe_browsing/url_checker_delegate_impl.h b/weblayer/browser/safe_browsing/url_checker_delegate_impl.h
new file mode 100644
index 0000000..feeada2
--- /dev/null
+++ b/weblayer/browser/safe_browsing/url_checker_delegate_impl.h
@@ -0,0 +1,59 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBLAYER_BROWSER_SAFE_BROWSING_URL_CHECKER_DELEGATE_IMPL_H_
+#define WEBLAYER_BROWSER_SAFE_BROWSING_URL_CHECKER_DELEGATE_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/safe_browsing/browser/url_checker_delegate.h"
+
+namespace weblayer {
+
+class SafeBrowsingUIManager;
+
+class UrlCheckerDelegateImpl : public safe_browsing::UrlCheckerDelegate {
+ public:
+  UrlCheckerDelegateImpl(
+      scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
+          database_manager,
+      scoped_refptr<SafeBrowsingUIManager> ui_manager);
+
+ private:
+  ~UrlCheckerDelegateImpl() override;
+
+  // Implementation of UrlCheckerDelegate:
+  void MaybeDestroyPrerenderContents(
+      const base::Callback<content::WebContents*()>& web_contents_getter)
+      override;
+  void StartDisplayingBlockingPageHelper(
+      const security_interstitials::UnsafeResource& resource,
+      const std::string& method,
+      const net::HttpRequestHeaders& headers,
+      bool is_main_frame,
+      bool has_user_gesture) override;
+  bool IsUrlWhitelisted(const GURL& url) override;
+  bool ShouldSkipRequestCheck(content::ResourceContext* resource_context,
+                              const GURL& original_url,
+                              int frame_tree_node_id,
+                              int render_process_id,
+                              int render_frame_id,
+                              bool originated_from_service_worker) override;
+  void NotifySuspiciousSiteDetected(
+      const base::RepeatingCallback<content::WebContents*()>&
+          web_contents_getter) override;
+  const safe_browsing::SBThreatTypeSet& GetThreatTypes() override;
+  safe_browsing::SafeBrowsingDatabaseManager* GetDatabaseManager() override;
+  safe_browsing::BaseUIManager* GetUIManager() override;
+
+  scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager_;
+  scoped_refptr<SafeBrowsingUIManager> ui_manager_;
+  safe_browsing::SBThreatTypeSet threat_types_;
+
+  DISALLOW_COPY_AND_ASSIGN(UrlCheckerDelegateImpl);
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_BROWSER_SAFE_BROWSING_URL_CHECKER_DELEGATE_IMPL_H_
\ No newline at end of file
diff --git a/weblayer/common/features.cc b/weblayer/common/features.cc
new file mode 100644
index 0000000..2164695
--- /dev/null
+++ b/weblayer/common/features.cc
@@ -0,0 +1,17 @@
+// 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 "weblayer/common/features.h"
+
+namespace weblayer {
+namespace features {
+
+// Weblayer features in alphabetical order.
+
+// Safebrowsing support for weblayer.
+const base::Feature kWebLayerSafeBrowsing{"WebLayerSafeBrowsing",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace features
+}  // namespace weblayer
diff --git a/weblayer/common/features.h b/weblayer/common/features.h
new file mode 100644
index 0000000..cf083ad
--- /dev/null
+++ b/weblayer/common/features.h
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBLAYER_COMMON_FEATURES_H_
+#define WEBLAYER_COMMON_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace weblayer {
+namespace features {
+
+// Weblayer features in alphabetical order.
+
+extern const base::Feature kWebLayerSafeBrowsing;
+
+}  // namespace features
+}  // namespace weblayer
+
+#endif  // WEBLAYER_COMMON_FEATURES_H_
diff --git a/weblayer/public/browser_observer.h b/weblayer/public/browser_observer.h
index 86b5027b..9fcd404 100644
--- a/weblayer/public/browser_observer.h
+++ b/weblayer/public/browser_observer.h
@@ -15,18 +15,6 @@
 
   // The URL bar should be updated to |url|.
   virtual void DisplayedUrlChanged(const GURL& url) {}
-
-  // Indicates that loading has started (|is_loading| is true) or is done
-  // (|is_loading| is false). |to_different_document| will be true unless the
-  // load is a fragment navigation, or triggered by
-  // history.pushState/replaceState.
-  virtual void LoadingStateChanged(bool is_loading,
-                                   bool to_different_document) {}
-
-  // Indicates that the load progress of the WebContents has changed. This
-  // corresponds to WebContentsDelegate::LoadProgressChanged, meaning |progress|
-  // ranges from 0.0 to 1.0.
-  virtual void LoadProgressChanged(double progress) {}
 };
 
 }  // namespace weblayer
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserController.java b/weblayer/public/java/org/chromium/weblayer/BrowserController.java
index ea3bb7b..ef7478d 100644
--- a/weblayer/public/java/org/chromium/weblayer/BrowserController.java
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserController.java
@@ -142,20 +142,6 @@
                 observer.visibleUrlChanged(uri);
             }
         }
-
-        @Override
-        public void loadingStateChanged(boolean isLoading, boolean toDifferentDocument) {
-            for (BrowserObserver observer : mObservers) {
-                observer.loadingStateChanged(isLoading, toDifferentDocument);
-            }
-        }
-
-        @Override
-        public void loadProgressChanged(double progress) {
-            for (BrowserObserver observer : mObservers) {
-                observer.loadProgressChanged(progress);
-            }
-        }
     }
 
     private final class DownloadDelegateClientImpl extends IDownloadDelegateClient.Stub {
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserObserver.java b/weblayer/public/java/org/chromium/weblayer/BrowserObserver.java
index cb214160..94355de 100644
--- a/weblayer/public/java/org/chromium/weblayer/BrowserObserver.java
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserObserver.java
@@ -18,20 +18,4 @@
      * @param url The new user-visible url.
      */
     public void visibleUrlChanged(@NonNull Uri url) {}
-
-    /**
-     * The load state of the document has changed.
-     *
-     * @param isLoading Whether any resource is loading.
-     * @param toDifferentDocument True if the main frame is loading a different document. Only valid
-     *        when |isLoading| is true.
-     */
-    public void loadingStateChanged(boolean isLoading, boolean toDifferentDocument) {}
-
-    /**
-     * The progress of loading the main frame in the document has changed.
-     *
-     * @param progress A value in the range of 0.0-1.0.
-     */
-    public void loadProgressChanged(double progress) {}
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/NavigationController.java b/weblayer/public/java/org/chromium/weblayer/NavigationController.java
index 98f71b4..4a1e292 100644
--- a/weblayer/public/java/org/chromium/weblayer/NavigationController.java
+++ b/weblayer/public/java/org/chromium/weblayer/NavigationController.java
@@ -170,6 +170,20 @@
         }
 
         @Override
+        public void loadStateChanged(boolean isLoading, boolean toDifferentDocument) {
+            for (NavigationObserver observer : mObservers) {
+                observer.loadStateChanged(isLoading, toDifferentDocument);
+            }
+        }
+
+        @Override
+        public void loadProgressChanged(double progress) {
+            for (NavigationObserver observer : mObservers) {
+                observer.loadProgressChanged(progress);
+            }
+        }
+
+        @Override
         public void onFirstContentfulPaint() {
             for (NavigationObserver observer : mObservers) {
                 observer.onFirstContentfulPaint();
diff --git a/weblayer/public/java/org/chromium/weblayer/NavigationObserver.java b/weblayer/public/java/org/chromium/weblayer/NavigationObserver.java
index 3698dc2..73d3ab6 100644
--- a/weblayer/public/java/org/chromium/weblayer/NavigationObserver.java
+++ b/weblayer/public/java/org/chromium/weblayer/NavigationObserver.java
@@ -92,6 +92,22 @@
     public void navigationFailed(@NonNull Navigation navigation) {}
 
     /**
+     * The load state of the document has changed.
+     *
+     * @param isLoading Whether any resource is loading.
+     * @param toDifferentDocument True if the main frame is loading a different document. Only valid
+     *        when |isLoading| is true.
+     */
+    public void loadStateChanged(boolean isLoading, boolean toDifferentDocument) {}
+
+    /**
+     * The progress of loading the main frame in the document has changed.
+     *
+     * @param progress A value in the range of 0.0-1.0.
+     */
+    public void loadProgressChanged(double progress) {}
+
+    /**
      * This is fired after each navigation has completed to indicate that the first paint after a
      * non-empty layout has finished.
      */
diff --git a/weblayer/public/navigation_observer.h b/weblayer/public/navigation_observer.h
index bd2410f..e8d86e1 100644
--- a/weblayer/public/navigation_observer.h
+++ b/weblayer/public/navigation_observer.h
@@ -12,10 +12,14 @@
 // now this only notifies for the main frame.
 //
 // The lifecycle of a navigation:
-// 1) NavigationStarted
-// 2) 0 or more NavigationRedirected
-// 3) 0 or 1 NavigationCommitted
-// 4) NavigationCompleted or NavigationFailed
+// 1) A navigation is initiated, such as by NavigationController::Navigate()
+//    or within the page
+// 2) LoadStateChanged() first invoked
+// 3) NavigationStarted
+// 4) 0 or more NavigationRedirected
+// 5) 0 or 1 NavigationCommitted
+// 6) NavigationCompleted or NavigationFailed
+// 7) Main frame completes loading, LoadStateChanged() last invoked
 class NavigationObserver {
  public:
   virtual ~NavigationObserver() {}
@@ -78,6 +82,16 @@
   // keep a reference to it afterward.
   virtual void NavigationFailed(Navigation* navigation) {}
 
+  // Indicates that loading has started (|is_loading| is true) or is done
+  // (|is_loading| is false). |to_different_document| will be true unless the
+  // load is a fragment navigation, or triggered by
+  // history.pushState/replaceState.
+  virtual void LoadStateChanged(bool is_loading, bool to_different_document) {}
+
+  // Indicates that the load progress of the page has changed. |progress|
+  // ranges from 0.0 to 1.0.
+  virtual void LoadProgressChanged(double progress) {}
+
   // This is fired after each navigation has completed to indicate that the
   // first paint after a non-empty layout has finished.
   virtual void OnFirstContentfulPaint() {}
diff --git a/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java b/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java
index 194dd42..6dd3d9f 100644
--- a/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java
+++ b/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java
@@ -28,7 +28,7 @@
 
 /** An Activity base class for running browser tests against WebLayerShell. */
 public class WebLayerBrowserTestsActivity extends NativeBrowserTestActivity {
-    private static final String TAG = "cr.native_test";
+    private static final String TAG = "native_test";
 
     private WebLayer mWebLayer;
     private Profile mProfile;
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/BrowserObserverTest.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/BrowserObserverTest.java
index 5adf55e..a90334a1 100644
--- a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/BrowserObserverTest.java
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/BrowserObserverTest.java
@@ -7,7 +7,6 @@
 import android.net.Uri;
 import android.support.test.filters.SmallTest;
 
-import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -31,9 +30,6 @@
     @Rule
     public WebLayerShellActivityTestRule mActivityTestRule = new WebLayerShellActivityTestRule();
 
-    // URL used for base tests.
-    private static final String URL = "data:text";
-
     private static class Observer extends BrowserObserver {
         public static class BrowserObserverValueRecorder {
             private List<String> mObservedValues =
@@ -62,27 +58,11 @@
 
         public BrowserObserverValueRecorder visibleUrlChangedCallback =
                 new BrowserObserverValueRecorder();
-        public BrowserObserverValueRecorder loadingStateChangedCallback =
-                new BrowserObserverValueRecorder();
-        public BrowserObserverValueRecorder loadProgressChangedCallback =
-                new BrowserObserverValueRecorder();
 
         @Override
         public void visibleUrlChanged(Uri url) {
             visibleUrlChangedCallback.recordValue(url.toString());
         }
-
-        @Override
-        public void loadingStateChanged(boolean isLoading, boolean toDifferentDocument) {
-            loadingStateChangedCallback.recordValue(
-                    Boolean.toString(isLoading) + " " + Boolean.toString(toDifferentDocument));
-        }
-
-        @Override
-        public void loadProgressChanged(double progress) {
-            loadProgressChangedCallback.recordValue(
-                    progress == 1 ? "load complete" : "load started");
-        }
     }
 
     @Test
@@ -100,29 +80,5 @@
 
         /* Verify that the visible URL changes to the target. */
         observer.visibleUrlChangedCallback.waitUntilValueObserved(url);
-
-        /* Wait until the BrowserObserver is notified of load completion. */
-        observer.loadingStateChangedCallback.waitUntilValueObserved("false true");
-        observer.loadProgressChangedCallback.waitUntilValueObserved("load complete");
-
-        /* Verify that the BrowserObserver was notified of load progress /before/ load completion.
-         */
-        int finishStateIndex =
-                observer.loadingStateChangedCallback.getObservedValues().indexOf("false true");
-        int finishProgressIndex =
-                observer.loadProgressChangedCallback.getObservedValues().indexOf("load complete");
-        int startStateIndex =
-                observer.loadingStateChangedCallback.getObservedValues().lastIndexOf("true true");
-        int startProgressIndex =
-                observer.loadProgressChangedCallback.getObservedValues().lastIndexOf(
-                        "load started");
-
-        Assert.assertNotEquals(startStateIndex, -1);
-        Assert.assertNotEquals(startProgressIndex, -1);
-        Assert.assertNotEquals(finishStateIndex, -1);
-        Assert.assertNotEquals(finishProgressIndex, -1);
-
-        Assert.assertTrue(startStateIndex < finishStateIndex);
-        Assert.assertTrue(startProgressIndex < finishProgressIndex);
     }
 }
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
index 3002d38..2338e83 100644
--- a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/NavigationTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
 import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
@@ -19,11 +20,16 @@
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.weblayer.Navigation;
 import org.chromium.weblayer.NavigationController;
 import org.chromium.weblayer.NavigationObserver;
 import org.chromium.weblayer.shell.WebLayerShellActivity;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -54,9 +60,38 @@
             }
         }
 
+        public static class NavigationObserverValueRecorder {
+            private List<String> mObservedValues =
+                    Collections.synchronizedList(new ArrayList<String>());
+
+            public void recordValue(String parameter) {
+                mObservedValues.add(parameter);
+            }
+
+            public List<String> getObservedValues() {
+                return mObservedValues;
+            }
+
+            public void waitUntilValueObserved(String expectation) {
+                CriteriaHelper.pollInstrumentationThread(
+                        new Criteria() {
+                            @Override
+                            public boolean isSatisfied() {
+                                return mObservedValues.contains(expectation);
+                            }
+                        },
+                        CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL,
+                        CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+            }
+        }
+
         public NavigationCallbackHelper onStartedCallback = new NavigationCallbackHelper();
         public NavigationCallbackHelper onCommittedCallback = new NavigationCallbackHelper();
         public NavigationCallbackHelper onCompletedCallback = new NavigationCallbackHelper();
+        public NavigationObserverValueRecorder loadStateChangedCallback =
+                new NavigationObserverValueRecorder();
+        public NavigationObserverValueRecorder loadProgressChangedCallback =
+                new NavigationObserverValueRecorder();
         public CallbackHelper onFirstContentfulPaintCallback = new CallbackHelper();
 
         @Override
@@ -78,6 +113,18 @@
         public void onFirstContentfulPaint() {
             onFirstContentfulPaintCallback.notifyCalled();
         }
+
+        @Override
+        public void loadStateChanged(boolean isLoading, boolean toDifferentDocument) {
+            loadStateChangedCallback.recordValue(
+                    Boolean.toString(isLoading) + " " + Boolean.toString(toDifferentDocument));
+        }
+
+        @Override
+        public void loadProgressChanged(double progress) {
+            loadProgressChangedCallback.recordValue(
+                    progress == 1 ? "load complete" : "load started");
+        }
     }
 
     private final Observer mObserver = new Observer();
@@ -104,6 +151,39 @@
 
     @Test
     @SmallTest
+    public void testLoadStateUpdates() throws Exception {
+        WebLayerShellActivity activity = mActivityTestRule.launchShellWithUrl(null);
+        setNavigationObserver(activity);
+        mActivityTestRule.navigateAndWait(URL1);
+
+        /* Wait until the NavigationObserver is notified of load completion. */
+        mObserver.loadStateChangedCallback.waitUntilValueObserved("false false");
+        mObserver.loadProgressChangedCallback.waitUntilValueObserved("load complete");
+
+        /* Verify that the NavigationObserver was notified of load progress /before/ load
+         * completion.
+         */
+        int finishStateIndex =
+                mObserver.loadStateChangedCallback.getObservedValues().indexOf("false false");
+        int finishProgressIndex =
+                mObserver.loadProgressChangedCallback.getObservedValues().indexOf("load complete");
+        int startStateIndex =
+                mObserver.loadStateChangedCallback.getObservedValues().lastIndexOf("true true");
+        int startProgressIndex =
+                mObserver.loadProgressChangedCallback.getObservedValues().lastIndexOf(
+                        "load started");
+
+        assertNotEquals(startStateIndex, -1);
+        assertNotEquals(startProgressIndex, -1);
+        assertNotEquals(finishStateIndex, -1);
+        assertNotEquals(finishProgressIndex, -1);
+
+        assertTrue(startStateIndex < finishStateIndex);
+        assertTrue(startProgressIndex < finishProgressIndex);
+    }
+
+    @Test
+    @SmallTest
     public void testGoBackAndForward() throws Exception {
         WebLayerShellActivity activity = mActivityTestRule.launchShellWithUrl(URL1);
         setNavigationObserver(activity);
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/WebLayerShellActivityTestRule.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/WebLayerShellActivityTestRule.java
index ec55d3a..42fb8e51 100644
--- a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/WebLayerShellActivityTestRule.java
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/WebLayerShellActivityTestRule.java
@@ -22,7 +22,6 @@
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.weblayer.BrowserController;
-import org.chromium.weblayer.BrowserObserver;
 import org.chromium.weblayer.Navigation;
 import org.chromium.weblayer.NavigationObserver;
 import org.chromium.weblayer.shell.WebLayerShellActivity;
@@ -51,11 +50,9 @@
                     checkComplete();
                 }
             }
-        };
 
-        private BrowserObserver mBrowserObserver = new BrowserObserver() {
             @Override
-            public void loadingStateChanged(boolean isLoading, boolean toDifferentDocument) {
+            public void loadStateChanged(boolean isLoading, boolean toDifferentDocument) {
                 mDoneLoading = !isLoading;
                 checkComplete();
             }
@@ -68,7 +65,6 @@
 
         public void navigateAndWait() {
             TestThreadUtils.runOnUiThreadBlocking(() -> {
-                mController.addObserver(mBrowserObserver);
                 mController.getNavigationController().addObserver(mNavigationObserver);
                 mController.getNavigationController().navigate(Uri.parse(mUrl));
             });
@@ -78,7 +74,6 @@
                 throw new RuntimeException(e);
             }
             TestThreadUtils.runOnUiThreadBlocking(() -> {
-                mController.removeObserver(mBrowserObserver);
                 mController.getNavigationController().removeObserver(mNavigationObserver);
             });
         }
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
index 0d4e6e0..42f25db5 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
@@ -34,6 +34,7 @@
 import org.chromium.weblayer.DownloadDelegate;
 import org.chromium.weblayer.FullscreenDelegate;
 import org.chromium.weblayer.NavigationController;
+import org.chromium.weblayer.NavigationObserver;
 import org.chromium.weblayer.Profile;
 import org.chromium.weblayer.UnsupportedVersionException;
 import org.chromium.weblayer.WebLayer;
@@ -212,9 +213,10 @@
             public void visibleUrlChanged(Uri uri) {
                 mUrlView.setText(uri.toString());
             }
-
+        });
+        mBrowserController.getNavigationController().addObserver(new NavigationObserver() {
             @Override
-            public void loadingStateChanged(boolean isLoading, boolean toDifferentDocument) {
+            public void loadStateChanged(boolean isLoading, boolean toDifferentDocument) {
                 mLoadProgressBar.setVisibility(
                         isLoading && toDifferentDocument ? View.VISIBLE : View.INVISIBLE);
             }
diff --git a/weblayer/shell/browser/shell.cc b/weblayer/shell/browser/shell.cc
index 5fab8e4..35bdbb8 100644
--- a/weblayer/shell/browser/shell.cc
+++ b/weblayer/shell/browser/shell.cc
@@ -32,13 +32,17 @@
 Shell::Shell(std::unique_ptr<BrowserController> browser_controller)
     : browser_controller_(std::move(browser_controller)), window_(nullptr) {
   windows_.push_back(this);
-  if (browser_controller_)
+  if (browser_controller_) {
     browser_controller_->AddObserver(this);
+    browser_controller_->GetNavigationController()->AddObserver(this);
+  }
 }
 
 Shell::~Shell() {
-  if (browser_controller_)
+  if (browser_controller_) {
+    browser_controller_->GetNavigationController()->RemoveObserver(this);
     browser_controller_->RemoveObserver(this);
+  }
   PlatformCleanUp();
 
   for (size_t i = 0; i < windows_.size(); ++i) {
@@ -104,26 +108,27 @@
   PlatformInitialize(GetShellDefaultSize());
 }
 
-void Shell::LoadingStateChanged(bool is_loading, bool to_different_document) {
-  int current_index = browser_controller_->GetNavigationController()
-                          ->GetNavigationListCurrentIndex();
-  int max_index =
-      browser_controller_->GetNavigationController()->GetNavigationListSize() -
-      1;
+void Shell::DisplayedUrlChanged(const GURL& url) {
+  PlatformSetAddressBarURL(url);
+}
 
-  PlatformEnableUIControl(BACK_BUTTON, current_index > 0);
-  PlatformEnableUIControl(FORWARD_BUTTON, current_index < max_index);
-  PlatformEnableUIControl(STOP_BUTTON, to_different_document && is_loading);
+void Shell::LoadStateChanged(bool is_loading, bool to_different_document) {
+  NavigationController* navigation_controller =
+      browser_controller_->GetNavigationController();
+
+  PlatformEnableUIControl(STOP_BUTTON, is_loading && to_different_document);
+
+  // TODO(estade): These should be updated in callbacks that correspond to the
+  // back/forward list changing, such as NavigationEntriesDeleted.
+  PlatformEnableUIControl(BACK_BUTTON, navigation_controller->CanGoBack());
+  PlatformEnableUIControl(FORWARD_BUTTON,
+                          navigation_controller->CanGoForward());
 }
 
 void Shell::LoadProgressChanged(double progress) {
   PlatformSetLoadProgress(progress);
 }
 
-void Shell::DisplayedUrlChanged(const GURL& url) {
-  PlatformSetAddressBarURL(url);
-}
-
 gfx::Size Shell::AdjustWindowSize(const gfx::Size& initial_size) {
   if (!initial_size.IsEmpty())
     return initial_size;
diff --git a/weblayer/shell/browser/shell.h b/weblayer/shell/browser/shell.h
index 3b36bba..0f239df 100644
--- a/weblayer/shell/browser/shell.h
+++ b/weblayer/shell/browser/shell.h
@@ -17,6 +17,7 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
 #include "weblayer/public/browser_observer.h"
+#include "weblayer/public/navigation_observer.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/scoped_java_ref.h"
@@ -38,10 +39,7 @@
 
 // This represents one window of the Web Shell, i.e. all the UI including
 // buttons and url bar, as well as the web content area.
-// On desktop this is used for the demo Shell application and also
-// weblayer_browsertests. On Android this is only used for
-// weblayer_browsertests.
-class Shell : public BrowserObserver {
+class Shell : public BrowserObserver, public NavigationObserver {
  public:
   ~Shell() override;
 
@@ -82,11 +80,12 @@
   explicit Shell(std::unique_ptr<BrowserController> browser_controller);
 
   // BrowserObserver implementation:
-  void LoadingStateChanged(bool is_loading,
-                           bool to_different_document) override;
-  void LoadProgressChanged(double progress) override;
   void DisplayedUrlChanged(const GURL& url) override;
 
+  // NavigationObserver implementation:
+  void LoadStateChanged(bool is_loading, bool to_different_document) override;
+  void LoadProgressChanged(double progress) override;
+
   // Helper to create a new Shell.
   static Shell* CreateShell(
       std::unique_ptr<BrowserController> browser_controller,