diff --git a/AUTHORS b/AUTHORS
index bcc75b5..75f0518 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1184,6 +1184,7 @@
 Vivek Galatage <vivek.vg@samsung.com>
 Volker Sorge <volker.sorge@gmail.com>
 Waihung Fu <fufranci@amazon.com>
+wafuwafu13 <mariobaske@i.softbank.jp>
 Wojciech Bielawski <wojciech.bielawski@gmail.com>
 Wanming Lin <wanming.lin@intel.com>
 Wei Li <wei.c.li@intel.com>
@@ -1279,6 +1280,7 @@
 Zoltan Czirkos <czirkos.zoltan@gmail.com>
 Zoltan Herczeg <zherczeg.u-szeged@partner.samsung.com>
 Zoltan Kuscsik <zoltan.kuscsik@linaro.org>
+Zoru Lee <donzoru@gmail.com>
 Zsolt Borbely <zsborbely.u-szeged@partner.samsung.com>
 方觉 (Fang Jue) <fangjue23303@gmail.com>
 迷渡 <justjavac@gmail.com>
diff --git a/DEPS b/DEPS
index c08fa90b..c1dcbae 100644
--- a/DEPS
+++ b/DEPS
@@ -245,15 +245,15 @@
   # 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': 'c2b31fb04a80bfcc9125ce9184c4d7808f72a5d6',
+  'skia_revision': 'dc67736cd6ad65154eb2c5fce4ad7a8e540cb52c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '13a6d9f4a5d01b467b7a9847c5a73c6875cd8569',
+  'v8_revision': '91c4a756d0ea0626eb75afc2b98c8a6ba26c5819',
   # 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': '4d893a93596e55c2a9d3b39494edd393f6d74086',
+  'angle_revision': 'fce481863806a5067fc5f34d313d19c9f03259a0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -312,7 +312,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': '390aa6fa4b4756f2a2e6baeeb260b3a29216e3d0',
+  'catapult_revision': '1a34b98c2ce22f4eb491a1052d39e8cfb99a8698',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '9c57bbe1eb338f967425d28609cee2841c79b7b5',
+  'devtools_frontend_revision': '48b94ed5138576eb83c5b6d89265f40bb89b5f55',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -360,7 +360,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '168b7eb10db53cade9fe6e0fc2b69941a8357eac',
+  'dawn_revision': 'b08e1e830776b069b524567c14c4a7d4b903b92e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -722,7 +722,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': '9jpY7QH7Sp9fCUcPhFl_M4dooNUHMJSI6jNiW2DO13sC',
+          'version': '9ozH8HBM5EXnyMroTWbwKahxTWJhm8_Ei2_FaHsYzaAC',
         },
       ],
       'dep_type': 'cipd',
@@ -733,7 +733,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'XOGQ658xVT9CGYDsBiB4djj5iaV63cjxNDNRvCJnPDgC',
+          'version': 'Udc72iws5U7fJ11zdF1gH8rjQGwh0qWDZFMLQHcG8mYC',
         },
       ],
       'dep_type': 'cipd',
@@ -744,7 +744,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'Lg-1XYBVIxtiSf6uP-mr8ZKH8zyYbg0A5kJFGW130V4C',
+          'version': 'iAEG3BvNpruA2RzolM1GZtDtFUJuRA5slyTK8R47PkkC',
         },
       ],
       'dep_type': 'cipd',
@@ -805,7 +805,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'aaokkxPemSJZijMa3UcEE57MN52L-Z3Xldi7x5si6VwC',
+          'version': 'uCdjLL9wK03L3vQeI9K5L_Ovx5AWKs8f8eNg9amENjIC',
       },
     ],
     'condition': 'checkout_android',
@@ -1024,7 +1024,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'e8f05de1c5fc17a14d1603c15c4307afadef4e75',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'd88c3db311bfc3942012a5fee53d65f9ed684ca6',
       'condition': 'checkout_chromeos',
   },
 
@@ -1648,7 +1648,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'fca7b339442bd70c5dc49bb33ee7f9466b560a97',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'a8beb40873c2376e2109992d72f5325ec746da44',
+    Var('webrtc_git') + '/src.git' + '@' + 'c6fba9af67482fbe387f4914f1afefe3decb95b3',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1730,7 +1730,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e03a6882eb47c43cf466c096a33f653e4fc560a6',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8d4ebb884aa41990de4d4169e8849e43bb0cb5a1',
     'condition': 'checkout_src_internal',
   },
 
@@ -1760,7 +1760,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'WY0tQg6qg0jBaEPv_GQVI8gVtxpiMQtdm6FI_SxzkZUC',
+        'version': '-T9XxdiRwA0u_-Ype3ftz-W4XfUfqbAysehwGh723vUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1771,7 +1771,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'no_LrUVP0ysgx533rBnA-9NUSNzVoBOe5DOqHKY7olkC',
+        'version': 'sd65r0mDLT61Ps1_aoELbetm8pu4dWZwkxQoPOzn0i4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1782,7 +1782,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': '-nP64jzj-qcmn0oU7A5zR4RO_o70v_rnMtAAYpjPJbAC',
+        'version': 'nWLAVGp-CNxas3koUm2I-6Zi0b18nlv4mKyANWOo1L4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/components/arc/mojom/auth.mojom b/ash/components/arc/mojom/auth.mojom
index 373d1f547..51a01c85b 100644
--- a/ash/components/arc/mojom/auth.mojom
+++ b/ash/components/arc/mojom/auth.mojom
@@ -24,6 +24,10 @@
 
   // Account is not present in Chrome OS Account Manager.
   [MinVersion=21] CHROME_ACCOUNT_NOT_FOUND = 21,
+
+  // NOTE: If you add any entries to this enum, you must also update the
+  // corresponding UMA ArcAuthMainAccountResolutionStatus at
+  // tools/metrics/histograms/enums.xml.
 };
 
 [Extensible]
diff --git a/ash/webui/camera_app_ui/resources/js/error.ts b/ash/webui/camera_app_ui/resources/js/error.ts
index 912b220..6a05798 100644
--- a/ash/webui/camera_app_ui/resources/js/error.ts
+++ b/ash/webui/camera_app_ui/resources/js/error.ts
@@ -43,7 +43,7 @@
  * Gets stack frames from error.
  * @return return null if failed to get frames from error.
  */
-export function getStackFrames(error: Error): StackFrame[]|null {
+function getStackFrames(error: Error): StackFrame[]|null {
   const prevPrepareStackTrace = Error.prepareStackTrace;
   Error.prepareStackTrace = (error, stack) => {
     try {
diff --git a/ash/webui/camera_app_ui/resources/js/gallerybutton.js b/ash/webui/camera_app_ui/resources/js/gallerybutton.js
index 78f7898..a04e71a 100644
--- a/ash/webui/camera_app_ui/resources/js/gallerybutton.js
+++ b/ash/webui/camera_app_ui/resources/js/gallerybutton.js
@@ -5,6 +5,7 @@
 import {assert, assertInstanceof} from './assert.js';
 import * as dom from './dom.js';
 import {reportError} from './error.js';
+import {Filenamer} from './models/file_namer.js';
 import * as filesystem from './models/file_system.js';
 import {
   DirectoryAccessEntry,  // eslint-disable-line no-unused-vars
@@ -214,8 +215,13 @@
   /**
    * @override
    */
-  async savePhoto(blob, name) {
+  async savePhoto(blob, name, metadata) {
     const file = await filesystem.saveBlob(blob, name);
+    if (metadata !== null) {
+      const metadataBlob =
+          new Blob([JSON.stringify(metadata, null, 2)], {type: MimeType.JSON});
+      await filesystem.saveBlob(metadataBlob, Filenamer.getMetadataName(name));
+    }
 
     ChromeHelper.getInstance().sendNewCaptureBroadcast(
         {isVideo: false, name: file.name});
diff --git a/ash/webui/camera_app_ui/resources/js/js.gni b/ash/webui/camera_app_ui/resources/js/js.gni
index 40a7533e..cbf6234 100644
--- a/ash/webui/camera_app_ui/resources/js/js.gni
+++ b/ash/webui/camera_app_ui/resources/js/js.gni
@@ -34,11 +34,10 @@
   "models/async_interval.ts",
   "models/async_writer.ts",
   "models/barcode.ts",
-  "models/barcode_worker_interface.js",
   "models/barcode_worker.ts",
   "models/ffmpeg/video_processor_args.js",
   "models/ffmpeg/video_processor.js",
-  "models/file_namer.js",
+  "models/file_namer.ts",
   "models/file_system_access_entry.js",
   "models/file_system.js",
   "models/idb.js",
diff --git a/ash/webui/camera_app_ui/resources/js/models/barcode.ts b/ash/webui/camera_app_ui/resources/js/models/barcode.ts
index 554c000..06da233 100644
--- a/ash/webui/camera_app_ui/resources/js/models/barcode.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/barcode.ts
@@ -6,7 +6,7 @@
 import * as Comlink from '../lib/comlink.js';
 
 import {clearAsyncInterval, setAsyncInterval} from './async_interval.js';
-import {BarcodeWorkerInterface} from './barcode_worker_interface.js';
+import {BarcodeWorkerInterface} from './barcode_worker.js';
 
 // The delay interval between consecutive barcode detections.
 const SCAN_INTERVAL = 200;
diff --git a/ash/webui/camera_app_ui/resources/js/models/barcode_worker.ts b/ash/webui/camera_app_ui/resources/js/models/barcode_worker.ts
index d0ac838..b961a28 100644
--- a/ash/webui/camera_app_ui/resources/js/models/barcode_worker.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/barcode_worker.ts
@@ -4,12 +4,10 @@
 
 import * as Comlink from '../lib/comlink.js';
 
-import {BarcodeWorkerInterface} from './barcode_worker_interface.js';
-
 /**
  * A barcode worker to detect barcode from images.
  */
-class BarcodeWorker implements BarcodeWorkerInterface {
+class BarcodeWorker {
   private readonly detector_ = new BarcodeDetector({formats: ['qr_code']});
 
   async detect(bitmap: ImageBitmap): Promise<string> {
@@ -41,4 +39,8 @@
   }
 }
 
+// Only export types to ensure that the file is not imported by other files at
+// runtime.
+export type BarcodeWorkerInterface = BarcodeWorker;
+
 Comlink.expose(new BarcodeWorker());
diff --git a/ash/webui/camera_app_ui/resources/js/models/barcode_worker_interface.js b/ash/webui/camera_app_ui/resources/js/models/barcode_worker_interface.js
deleted file mode 100644
index 9999194f..0000000
--- a/ash/webui/camera_app_ui/resources/js/models/barcode_worker_interface.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2020 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 {assertNotReached} from '../assert.js';
-
-/**
- * The interface for a barcode worker. All methods are marked as async since
- * it will be used with Comlink and Web Workers.
- * @interface
- */
-export class BarcodeWorkerInterface {
-  /**
-   * Detects barcodes from an image bitmap.
-   * @param {!ImageBitmap} bitmap
-   * @return {!Promise<?string>} The detected barcode value, or null if no
-   *     barcode is detected.
-   * @abstract
-   */
-  async detect(bitmap) {
-    assertNotReached();
-  }
-}
diff --git a/ash/webui/camera_app_ui/resources/js/models/file_namer.js b/ash/webui/camera_app_ui/resources/js/models/file_namer.js
deleted file mode 100644
index d50bc20..0000000
--- a/ash/webui/camera_app_ui/resources/js/models/file_namer.js
+++ /dev/null
@@ -1,137 +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.
-
-import {assertNotReached} from '../assert.js';
-import {
-  MimeType,
-  VideoType,  // eslint-disable-line no-unused-vars
-} from '../type.js';
-
-/**
- * The prefix of image files.
- * @type {string}
- */
-export const IMAGE_PREFIX = 'IMG_';
-
-/**
- * The prefix of video files.
- * @type {string}
- */
-export const VIDEO_PREFIX = 'VID_';
-
-/**
- * The prefix of scanned document files.
- * @type {string}
- */
-export const DOCUMENT_PREFIX = 'SCN_';
-
-/**
- * The suffix of burst image files.
- * @type {string}
- */
-const BURST_SUFFIX = '_BURST';
-
-/**
- * The suffix of cover image for a series of burst image files.
- * @type {string}
- */
-const BURST_COVER_SUFFIX = '_COVER';
-
-/**
- * Transforms from capture timestamp to datetime name.
- * @param {number} timestamp Timestamp to be transformed.
- * @return {string} Transformed datetime name.
- */
-function timestampToDatetimeName(timestamp) {
-  const pad = (n) => (n < 10 ? '0' : '') + n;
-  const date = new Date(timestamp);
-  return date.getFullYear() + pad(date.getMonth() + 1) + pad(date.getDate()) +
-      '_' + pad(date.getHours()) + pad(date.getMinutes()) +
-      pad(date.getSeconds());
-}
-
-/**
- * Filenamer for single camera session.
- */
-export class Filenamer {
-  /**
-   * @param {number=} timestamp Timestamp of camera session.
-   */
-  constructor(timestamp) {
-    /**
-     * Timestamp of camera session.
-     * @type {number}
-     * @private
-     */
-    this.timestamp_ = timestamp === undefined ? Date.now() : timestamp;
-
-    /**
-     * Number of already saved burst images.
-     * @type {number}
-     * @private
-     */
-    this.burstCount_ = 0;
-  }
-
-  /**
-   * Creates new filename for burst image.
-   * @param {boolean} isCover If the image is set as cover of the burst.
-   * @return {string} New filename.
-   */
-  newBurstName(isCover) {
-    const prependZeros = (n, width) => {
-      n = n + '';
-      return new Array(Math.max(0, width - n.length) + 1).join('0') + n;
-    };
-    return IMAGE_PREFIX + timestampToDatetimeName(this.timestamp_) +
-        BURST_SUFFIX + prependZeros(++this.burstCount_, 5) +
-        (isCover ? BURST_COVER_SUFFIX : '') + '.jpg';
-  }
-
-  /**
-   * Creates new filename for video.
-   * @param {VideoType} videoType
-   * @return {string} New filename.
-   */
-  newVideoName(videoType) {
-    return VIDEO_PREFIX + timestampToDatetimeName(this.timestamp_) + '.' +
-        videoType;
-  }
-
-  /**
-   * Creates new filename for image.
-   * @return {string} New filename.
-   */
-  newImageName() {
-    return IMAGE_PREFIX + timestampToDatetimeName(this.timestamp_) + '.jpg';
-  }
-
-  /**
-   * Creates new filename for pdf.
-   * @param {!MimeType} type
-   * @return {string} New filename.
-   */
-  newDocumentName(type) {
-    const ext = (() => {
-      switch (type) {
-        case MimeType.JPEG:
-          return 'jpg';
-        case MimeType.PDF:
-          return 'pdf';
-      }
-      assertNotReached(`Unknown type ${type}`);
-    })();
-    return DOCUMENT_PREFIX + timestampToDatetimeName(this.timestamp_) + '.' +
-        ext;
-  }
-
-  /**
-   * Get the metadata name from image name.
-   * @param {string} imageName Name of image to derive the metadata name.
-   * @return {string} Metadata name of the image.
-   */
-  static getMetadataName(imageName) {
-    return imageName.replace(/\.[^/.]+$/, '.json');
-  }
-}
diff --git a/ash/webui/camera_app_ui/resources/js/models/file_namer.ts b/ash/webui/camera_app_ui/resources/js/models/file_namer.ts
new file mode 100644
index 0000000..e7331abf
--- /dev/null
+++ b/ash/webui/camera_app_ui/resources/js/models/file_namer.ts
@@ -0,0 +1,127 @@
+// 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 {assertNotReached} from '../assert.js';
+import {
+  MimeType,
+  VideoType,
+} from '../type.js';
+
+/**
+ * The prefix of image files.
+ */
+export const IMAGE_PREFIX = 'IMG_';
+
+/**
+ * The prefix of video files.
+ */
+export const VIDEO_PREFIX = 'VID_';
+
+/**
+ * The prefix of scanned document files.
+ */
+export const DOCUMENT_PREFIX = 'SCN_';
+
+/**
+ * The suffix of burst image files.
+ */
+const BURST_SUFFIX = '_BURST';
+
+/**
+ * The suffix of cover image for a series of burst image files.
+ */
+const BURST_COVER_SUFFIX = '_COVER';
+
+/**
+ * Transforms from capture timestamp to datetime name.
+ * @param timestamp Timestamp to be transformed.
+ * @return Transformed datetime name.
+ */
+function timestampToDatetimeName(timestamp: number): string {
+  const pad = (n) => (n < 10 ? '0' : '') + n;
+  const date = new Date(timestamp);
+  return date.getFullYear() + pad(date.getMonth() + 1) + pad(date.getDate()) +
+      '_' + pad(date.getHours()) + pad(date.getMinutes()) +
+      pad(date.getSeconds());
+}
+
+/**
+ * Filenamer for single camera session.
+ */
+export class Filenamer {
+  /**
+   * Timestamp of camera session.
+   */
+  private readonly timestamp: number;
+  /**
+   * Number of already saved burst images.
+   */
+  private burstCount = 0;
+
+  /**
+   * @param timestamp Timestamp of camera session.
+   */
+  constructor(timestamp?: number) {
+    this.timestamp = timestamp ?? Date.now();
+  }
+
+  /**
+   * Creates new filename for burst image.
+   * @param isCover If the image is set as cover of the burst.
+   * @return New filename.
+   */
+  newBurstName(isCover: boolean): string {
+    const prependZeros = (n, width) => {
+      n = n + '';
+      return new Array(Math.max(0, width - n.length) + 1).join('0') + n;
+    };
+    return IMAGE_PREFIX + timestampToDatetimeName(this.timestamp) +
+        BURST_SUFFIX + prependZeros(++this.burstCount, 5) +
+        (isCover ? BURST_COVER_SUFFIX : '') + '.jpg';
+  }
+
+  /**
+   * Creates new filename for video.
+   * @return New filename.
+   */
+  newVideoName(videoType: VideoType): string {
+    return VIDEO_PREFIX + timestampToDatetimeName(this.timestamp) + '.' +
+        videoType;
+  }
+
+  /**
+   * Creates new filename for image.
+   * @return New filename.
+   */
+  newImageName(): string {
+    return IMAGE_PREFIX + timestampToDatetimeName(this.timestamp) + '.jpg';
+  }
+
+  /**
+   * Creates new filename for pdf.
+   * @return New filename.
+   */
+  newDocumentName(mimeType: MimeType): string {
+    const ext = (() => {
+      switch (mimeType) {
+        case MimeType.JPEG:
+          return 'jpg';
+        case MimeType.PDF:
+          return 'pdf';
+      }
+      assertNotReached(`Unknown type ${mimeType}`);
+    })();
+    return DOCUMENT_PREFIX + timestampToDatetimeName(this.timestamp) + '.' +
+        ext;
+  }
+
+  /**
+   * Get the metadata name from image name.
+   * @param imageName Name of image to derive the metadata name.
+   * @return Metadata name of the image.
+   */
+  static getMetadataName(imageName: string): string {
+    return imageName.replace(/\.[^/.]+$/, '.json');
+  }
+}
diff --git a/ash/webui/camera_app_ui/resources/js/models/result_saver.ts b/ash/webui/camera_app_ui/resources/js/models/result_saver.ts
index 04ec4cc..be363c39 100644
--- a/ash/webui/camera_app_ui/resources/js/models/result_saver.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/result_saver.ts
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {Metadata} from '../type.js';
+
 import {VideoSaver} from './video_saver.js';
 
 /**
@@ -13,8 +15,9 @@
    * Saves photo capture result.
    * @param blob Data of the photo to be added.
    * @param name Name of the photo to be saved.
+   * @param metadata Data of the photo to be added.
    */
-  savePhoto(blob: Blob, name: string): Promise<void>;
+  savePhoto(blob: Blob, name: string, metadata: Metadata|null): Promise<void>;
 
   /**
    * Saves gif capture result.
diff --git a/ash/webui/camera_app_ui/resources/js/type.ts b/ash/webui/camera_app_ui/resources/js/type.ts
index c6977ff..038a5cf 100644
--- a/ash/webui/camera_app_ui/resources/js/type.ts
+++ b/ash/webui/camera_app_ui/resources/js/type.ts
@@ -61,6 +61,7 @@
 export enum MimeType {
   GIF = 'image/gif',
   JPEG = 'image/jpeg',
+  JSON = 'application/json',
   MP4 = 'video/mp4',
   PDF = 'application/pdf',
 }
@@ -173,6 +174,10 @@
   resolution: Resolution;
 }
 
+// The key-value pair of the entries in metadata are stored as key-value of an
+// |Object| type
+export type Metadata = Record<string, unknown>;
+
 export interface PerfInformation {
   hasError?: boolean;
   resolution?: Resolution;
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera.js b/ash/webui/camera_app_ui/resources/js/views/camera.js
index 52b33dd..f51c04ed 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera.js
+++ b/ash/webui/camera_app_ui/resources/js/views/camera.js
@@ -18,6 +18,7 @@
 import * as dom from '../dom.js';
 import * as error from '../error.js';
 import {Flag} from '../flag.js';
+import {Point} from '../geometry.js';  // eslint-disable-line no-unused-vars
 import {I18nString} from '../i18n_string.js';
 import * as metrics from '../metrics.js';
 import {Filenamer} from '../models/file_namer.js';
@@ -40,8 +41,10 @@
   ErrorLevel,
   ErrorType,
   Facing,
+  ImageBlob,  // eslint-disable-line no-unused-vars
   MimeType,
   Mode,
+  PerfEvent,
   Resolution,
   Rotation,
   ViewName,
@@ -229,7 +232,7 @@
      */
     this.modes_ = new Modes(
         this.defaultMode_, photoPreferrer, videoPreferrer, () => this.start(),
-        this, this, this);
+        this);
 
     /**
      * @type {!Facing}
@@ -653,7 +656,8 @@
         // what we need to rotate the captured video with.
         this.outputVideoRotation_ = (360 - cameraFrameRotation) % 360;
         await timertick.start();
-        await this.modes_.current.startCapture();
+        const captureDone = await this.modes_.current.startCapture();
+        await captureDone();
       } catch (e) {
         hasError = true;
         if (e instanceof CanceledError) {
@@ -692,17 +696,33 @@
   }
 
   /**
+   * @template T
+   * @param {!Promise<T>} pendingPhotoResult
+   * @return {!Promise<T>}
+   * @private
+   */
+  async checkPhotoResult_(pendingPhotoResult) {
+    try {
+      return /** !Promise<!T> */ (await pendingPhotoResult);
+    } catch (e) {
+      this.onPhotoError();
+      throw e;
+    }
+  }
+
+  /**
    * @override
    */
-  async handleResultPhoto({resolution, blob, isVideoSnapshot}, name) {
+  async handleVideoSnapshot({resolution, blob, timestamp}) {
     metrics.sendCaptureEvent({
       facing: this.facingMode_,
       resolution,
       shutterType: this.shutterType_,
-      isVideoSnapshot,
+      isVideoSnapshot: true,
     });
     try {
-      await this.resultSaver_.savePhoto(blob, name);
+      const name = (new Filenamer(timestamp)).newImageName();
+      await this.resultSaver_.savePhoto(blob, name, /* metadata */ null);
     } catch (e) {
       toast.show(I18nString.ERROR_MSG_SAVE_FILE_FAILED);
       throw e;
@@ -712,9 +732,118 @@
   /**
    * @override
    */
-  async handleResultDocument({blob, resolution, mimeType}, name) {
+  async onPhotoError() {
+    toast.show(I18nString.ERROR_MSG_TAKE_PHOTO_FAILED);
+  }
+
+  /**
+   * @override
+   */
+  async onNoPortrait() {
+    toast.show(I18nString.ERROR_MSG_TAKE_PORTRAIT_BOKEH_PHOTO_FAILED);
+  }
+
+  /**
+   * @override
+   */
+  async onPhotoCaptureDone(pendingPhotoResult) {
+    state.set(PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, true);
     try {
-      await this.resultSaver_.savePhoto(blob, name);
+      const {resolution, blob, timestamp, metadata} =
+          await this.checkPhotoResult_(pendingPhotoResult);
+
+      metrics.sendCaptureEvent({
+        facing: this.facingMode_,
+        resolution,
+        shutterType: this.shutterType_,
+        isVideoSnapshot: false,
+      });
+
+      try {
+        const name = (new Filenamer(timestamp)).newImageName();
+        await this.resultSaver_.savePhoto(blob, name, metadata);
+      } catch (e) {
+        toast.show(I18nString.ERROR_MSG_SAVE_FILE_FAILED);
+        throw e;
+      }
+      state.set(
+          PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false,
+          {resolution, facing: this.facingMode_});
+    } catch (e) {
+      state.set(
+          PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false, {hasError: true});
+      throw e;
+    }
+  }
+
+  /**
+   * @override
+   */
+  async onPortraitCaptureDone(pendingPortraitResult) {
+    state.set(PerfEvent.PORTRAIT_MODE_CAPTURE_POST_PROCESSING, true);
+    let hasError = false;
+    try {
+      const {timestamp, resolution, blob, metadata, pendingPortrait} =
+          await this.checkPhotoResult_(pendingPortraitResult);
+      const portraitBlobAndMetadata =
+          await this.checkPhotoResult_(pendingPortrait);
+
+      metrics.sendCaptureEvent({
+        facing: this.facingMode_,
+        resolution,
+        shutterType: this.shutterType_,
+        isVideoSnapshot: false,
+      });
+
+      try {
+        // Save reference.
+        const filenamer = new Filenamer(timestamp);
+        const name = filenamer.newBurstName(false);
+        await this.resultSaver_.savePhoto(blob, name, metadata);
+
+        // Save portrait.
+        if (portraitBlobAndMetadata !== null) {
+          const {blob, metadata} = portraitBlobAndMetadata;
+          const name = filenamer.newBurstName(true);
+          await this.resultSaver_.savePhoto(blob, name, metadata);
+        } else {
+          toast.show(I18nString.ERROR_MSG_TAKE_PORTRAIT_BOKEH_PHOTO_FAILED);
+        }
+      } catch (e) {
+        toast.show(I18nString.ERROR_MSG_SAVE_FILE_FAILED);
+        throw e;
+      }
+    } catch (e) {
+      hasError = true;
+      throw e;
+    } finally {
+      state.set(
+          PerfEvent.PORTRAIT_MODE_CAPTURE_POST_PROCESSING, false,
+          {hasError, facing: this.facingMode_});
+    }
+  }
+
+  /**
+   * @override
+   */
+  async onDocumentCaptureDone(pendingPhotoResult) {
+    const {blob: rawBlob, resolution, timestamp, metadata} =
+        await this.checkPhotoResult_(pendingPhotoResult);
+    const helper = await ChromeHelper.getInstance();
+    const corners = await helper.scanDocumentCorners(rawBlob);
+    const reviewResult =
+        await this.reviewDocument_({blob: rawBlob, resolution}, corners);
+    if (reviewResult === null) {
+      throw new CanceledError('Cancelled after review document');
+    }
+    const {docBlob, mimeType} = reviewResult;
+    let blob = docBlob;
+    if (mimeType === MimeType.PDF) {
+      blob = await helper.convertToPdf(blob);
+    }
+    try {
+      const name = (new Filenamer(timestamp)).newDocumentName(mimeType);
+      await this.resultSaver_.savePhoto(blob, name, metadata);
     } catch (e) {
       toast.show(I18nString.ERROR_MSG_SAVE_FILE_FAILED);
       throw e;
@@ -745,9 +874,16 @@
   }
 
   /**
-   * @override
+   * @param {!ImageBlob} originImage Original photo to be cropped document from.
+   * @param {?Array<!Point>} refCorners Initial reference document corner
+   *     positions detected by scan API. Sets to null if scan API cannot find
+   *     any reference corner from |rawBlob|.
+   * @return {!Promise<?{docBlob: !Blob, mimeType: !MimeType}>} Returns the
+   *     processed document blob and which mime type user choose to save. Null
+   *     for cancel document.
+   * @private
    */
-  async reviewDocument(originImage, refCorners) {
+  async reviewDocument_(originImage, refCorners) {
     const needFirstRecrop = refCorners === null;
     const allowRecrop = loadTimeData.getChromeFlag(Flag.DOCUMENT_MANUAL_CROP);
     if (needFirstRecrop && !allowRecrop) {
@@ -908,29 +1044,15 @@
   /**
    * @override
    */
-  async handleResultVideo({resolution, duration, videoSaver, everPaused}) {
-    metrics.sendCaptureEvent({
-      recordType: metrics.RecordType.NORMAL_VIDEO,
-      facing: this.facingMode_,
-      duration,
-      resolution,
-      shutterType: this.shutterType_,
-      everPaused,
-    });
-    try {
-      await this.resultSaver_.finishSaveVideo(videoSaver);
-    } catch (e) {
-      toast.show(I18nString.ERROR_MSG_SAVE_FILE_FAILED);
-      throw e;
-    }
-  }
-
-  /**
-   * @override
-   */
-  async handleResultGif({name, getBlob, resolution, duration}) {
+  async onGifCaptureDone({name, gifSaver, resolution, duration}) {
     nav.open(ViewName.FLASH);
-    const blob = await getBlob();
+
+    // Measure the latency of gif encoder finishing rest of the encoding
+    // works.
+    state.set(PerfEvent.GIF_CAPTURE_POST_PROCESSING, true);
+    const blob = await gifSaver.endWrite();
+    state.set(PerfEvent.GIF_CAPTURE_POST_PROCESSING, false);
+
     const sendEvent = (gifResult) => {
       metrics.sendCaptureEvent({
         recordType: metrics.RecordType.GIF,
@@ -970,6 +1092,31 @@
   /**
    * @override
    */
+  async onVideoCaptureDone({resolution, videoSaver, duration, everPaused}) {
+    state.set(PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, true);
+    try {
+      metrics.sendCaptureEvent({
+        recordType: metrics.RecordType.NORMAL_VIDEO,
+        facing: this.facingMode_,
+        duration,
+        resolution,
+        shutterType: this.shutterType_,
+        everPaused,
+      });
+      await this.resultSaver_.finishSaveVideo(videoSaver);
+      state.set(
+          PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false,
+          {resolution, facing: this.facingMode_});
+    } catch (e) {
+      state.set(
+          PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false, {hasError: true});
+      throw e;
+    }
+  }
+
+  /**
+   * @override
+   */
   layout() {
     this.layout_.update();
   }
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/index.ts b/ash/webui/camera_app_ui/resources/js/views/camera/mode/index.ts
index e637f78..74248ced 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/index.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/index.ts
@@ -32,7 +32,7 @@
   PhotoFactory,
   PhotoHandler,
 } from './photo.js';
-import {PortraitFactory} from './portrait.js';
+import {PortraitFactory, PortraitHandler} from './portrait.js';
 import {
   ScanFactory,
   ScanHandler,
@@ -129,9 +129,7 @@
       photoPreferrer: PhotoConstraintsPreferrer,
       videoPreferrer: VideoConstraintsPreferrer,
       private readonly doSwitchMode: DoSwitchMode,
-      photoHandler: PhotoHandler,
-      videoHandler: VideoHandler,
-      scanHandler: ScanHandler,
+      handler: PhotoHandler&PortraitHandler&ScanHandler&VideoHandler,
   ) {
     /**
      * Returns a set of general constraints for fake cameras.
@@ -191,7 +189,7 @@
           const params = this.getCaptureParams();
           return new VideoFactory(
               params.constraints, params.captureResolution,
-              params.videoSnapshotResolution, videoHandler);
+              params.videoSnapshotResolution, handler);
         },
         isSupported: async () => true,
         isSupportPTZ: () => true,
@@ -238,7 +236,7 @@
         getCaptureFactory: () => {
           const params = this.getCaptureParams();
           return new PhotoFactory(
-              params.constraints, params.captureResolution, photoHandler);
+              params.constraints, params.captureResolution, handler);
         },
         isSupported: async () => true,
         isSupportPTZ: checkSupportPTZForPhotoMode,
@@ -253,7 +251,7 @@
         getCaptureFactory: () => {
           const params = this.getCaptureParams();
           return new SquareFactory(
-              params.constraints, params.captureResolution, photoHandler);
+              params.constraints, params.captureResolution, handler);
         },
         isSupported: async () => true,
         isSupportPTZ: checkSupportPTZForPhotoMode,
@@ -268,7 +266,7 @@
         getCaptureFactory: () => {
           const params = this.getCaptureParams();
           return new PortraitFactory(
-              params.constraints, params.captureResolution, photoHandler);
+              params.constraints, params.captureResolution, handler);
         },
         isSupported: async (deviceId) => {
           if (deviceId === null) {
@@ -292,7 +290,7 @@
         getCaptureFactory: () => {
           const params = this.getCaptureParams();
           return new ScanFactory(
-              params.constraints, params.captureResolution, scanHandler);
+              params.constraints, params.captureResolution, handler);
         },
         isSupported: async () => state.get(state.State.SHOW_SCAN_MODE),
         isSupportPTZ: checkSupportPTZForPhotoMode,
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/mode_base.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/mode_base.js
index 7460343e..72866e05 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/mode_base.js
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/mode_base.js
@@ -40,7 +40,7 @@
 
     /**
      * Promise for ongoing capture operation.
-     * @type {?Promise}
+     * @type {?Promise<function(): Promise<void>>}
      * @private
      */
     this.capture_ = null;
@@ -48,11 +48,19 @@
 
   /**
    * Initiates video/photo capture operation.
-   * @return {!Promise} Promise for ongoing capture operation.
+   * @return {!Promise<function(): Promise<void>>} Promise for ongoing capture
+   *     operation and resolved to handler function which should be run after
+   *     capture finished.
    */
   startCapture() {
     if (this.capture_ === null) {
-      this.capture_ = this.start_().finally(() => this.capture_ = null);
+      this.capture_ = (async () => {
+        try {
+          return await this.start_();
+        } finally {
+          this.capture_ = null;
+        }
+      })();
     }
     return this.capture_;
   }
@@ -104,7 +112,9 @@
 
   /**
    * Initiates video/photo capture operation under this mode.
-   * @return {!Promise}
+   * @return {!Promise<function(): !Promise<void>>} Promise for ongoing capture
+   *     operation and resolved to handler function which should be run after
+   *     capture finished.
    * @protected
    * @abstract
    */
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/photo.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/photo.js
index 19bb83f..e06c46c 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/photo.js
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/photo.js
@@ -5,9 +5,6 @@
 import {assertNotReached} from '../../../assert.js';
 // eslint-disable-next-line no-unused-vars
 import {StreamConstraints} from '../../../device/stream_constraints.js';
-import {I18nString} from '../../../i18n_string.js';
-import {Filenamer} from '../../../models/file_namer.js';
-import * as filesystem from '../../../models/file_system.js';
 import {DeviceOperator, parseMetadata} from '../../../mojo/device_operator.js';
 import {CrosImageCapture} from '../../../mojo/image_capture.js';
 import {
@@ -19,14 +16,14 @@
   MojoEndpoint,  // eslint-disable-line no-unused-vars
 } from '../../../mojo/util.js';
 import * as state from '../../../state.js';
-import * as toast from '../../../toast.js';
 import {
-  CanceledError,
-  Facing,  // eslint-disable-line no-unused-vars
+  Facing,    // eslint-disable-line no-unused-vars
+  Metadata,  // eslint-disable-line no-unused-vars
   PerfEvent,
   Resolution,
 } from '../../../type.js';
 import * as util from '../../../util.js';
+import {WaitableEvent} from '../../../waitable_event.js';
 
 import {ModeBase, ModeFactory} from './mode_base.js';
 
@@ -35,7 +32,8 @@
  * @typedef {{
  *     resolution: !Resolution,
  *     blob: !Blob,
- *     isVideoSnapshot?: boolean,
+ *     timestamp: number,
+ *     metadata: ?Metadata,
  * }}
  */
 export let PhotoResult;
@@ -47,17 +45,6 @@
  */
 export class PhotoHandler {
   /**
-   * Handles the result photo.
-   * @param {!PhotoResult} photo Captured photo result.
-   * @param {string} name Name of the photo result to be saved as.
-   * @return {!Promise}
-   * @abstract
-   */
-  handleResultPhoto(photo, name) {
-    assertNotReached();
-  }
-
-  /**
    * Plays UI effect when taking photo.
    */
   playShutterEffect() {}
@@ -69,6 +56,23 @@
   waitPreviewReady() {
     assertNotReached();
   }
+
+  /**
+   * Called when error happen in the capture process.
+   * @abstract
+   */
+  onPhotoError() {
+    assertNotReached();
+  }
+
+  /**
+   * @param {!Promise<!PhotoResult>} pendingPhotoResult
+   * @return {!Promise<void>}
+   * @abstract
+   */
+  onPhotoCaptureDone(pendingPhotoResult) {
+    assertNotReached();
+  }
 }
 
 /**
@@ -114,11 +118,12 @@
     this.metadataObserver_ = null;
 
     /**
-     * Metadata names ready to be saved.
-     * @type {!Array<string>}
+     * Pending |PhotoResult| waiting for arrival of their corresponding
+     * metadata..
+     * @type {!Array<!WaitableEvent<!Metadata>>}
      * @protected
      */
-    this.metadataNames_ = [];
+    this.pendingResultForMetadata_ = [];
   }
 
   /**
@@ -134,42 +139,44 @@
    * @override
    */
   async start_() {
-    const imageName = (new Filenamer()).newImageName();
-    if (this.metadataObserver_ !== null) {
-      this.metadataNames_.push(Filenamer.getMetadataName(imageName));
-    }
-
+    const timestamp = Date.now();
     state.set(PerfEvent.PHOTO_CAPTURE_SHUTTER, true);
-    try {
-      const blob = await this.takePhoto_();
-      state.set(PerfEvent.PHOTO_CAPTURE_SHUTTER, false, {facing: this.facing_});
-      state.set(PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, true);
+    const {blob, pendingMetadata} = await (async () => {
+      let hasError = false;
+      try {
+        return await this.takePhoto_();
+      } catch (e) {
+        hasError = true;
+        this.handler_.onPhotoError();
+        throw e;
+      } finally {
+        state.set(
+            PerfEvent.PHOTO_CAPTURE_SHUTTER, false,
+            hasError ? {hasError} : {facing: this.facing_});
+      }
+    })();
+
+    const pendingPhotoResult = (async () => {
       const image = await util.blobToImage(blob);
       const resolution = new Resolution(image.width, image.height);
-      await this.handler_.handleResultPhoto({resolution, blob}, imageName);
-      state.set(
-          PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false,
-          {resolution, facing: this.facing_});
-    } catch (e) {
-      state.set(PerfEvent.PHOTO_CAPTURE_SHUTTER, false, {hasError: true});
-      state.set(
-          PerfEvent.PHOTO_CAPTURE_POST_PROCESSING, false, {hasError: true});
-      if (!(e instanceof CanceledError)) {
-        toast.show(I18nString.ERROR_MSG_TAKE_PHOTO_FAILED);
-      }
-      throw e;
-    }
+      return {resolution, blob, timestamp, metadata: await pendingMetadata};
+    })();
+
+    return async () => this.handler_.onPhotoCaptureDone(pendingPhotoResult);
   }
 
   /**
    * @private
-   * @return {!Promise<!Blob>}
+   * @return {!Promise<{blob: !Blob, pendingMetadata: ?Promise<!Metadata>}>}
    */
   async takePhoto_() {
     if (state.get(state.State.ENABLE_PTZ)) {
       // Workaround for b/184089334 on PTZ camera to use preview frame as
       // photo result.
-      return this.crosImageCapture_.grabJpegFrame();
+      return {
+        blob: await this.crosImageCapture_.grabJpegFrame(),
+        pendingMetadata: null,
+      };
     }
     let photoSettings;
     if (this.captureResolution_) {
@@ -184,10 +191,20 @@
         imageHeight: caps.imageHeight.max,
       });
     }
+
+    let /** ?WaitableEvent<!Metadata> */ waitForMetadata = null;
+    if (this.metadataObserver_ !== null) {
+      waitForMetadata =
+          /** @type{!WaitableEvent<!Metadata>} */ (new WaitableEvent());
+      this.pendingResultForMetadata_.push(waitForMetadata);
+    }
     await this.handler_.waitPreviewReady();
     const results = await this.crosImageCapture_.takePhoto(photoSettings);
     this.handler_.playShutterEffect();
-    return results[0];
+    return {
+      blob: await results[0],
+      pendingMetadata: waitForMetadata?.wait() ?? null,
+    };
   }
 
   /**
@@ -213,7 +230,7 @@
     });
 
     const callback = (metadata) => {
-      const parsedMetadata = {};
+      const parsedMetadata = /** @type {!Record<string, unknown>} */ ({});
       for (const entry of metadata.entries) {
         const key = cameraMetadataTagInverseLookup[entry.tag];
         if (key === undefined) {
@@ -225,11 +242,7 @@
         parsedMetadata[key] = val;
       }
 
-      filesystem.saveBlob(
-          new Blob(
-              [JSON.stringify(parsedMetadata, null, 2)],
-              {type: 'application/json'}),
-          this.metadataNames_.shift());
+      this.pendingResultForMetadata_.shift()?.signal(parsedMetadata);
     };
 
     const deviceId = this.stream_.getVideoTracks()[0].getSettings().deviceId;
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/portrait.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/portrait.js
index 2ef5166b..90c4baad 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/portrait.js
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/portrait.js
@@ -2,18 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {assertNotReached} from '../../../assert.js';
+// eslint-disable-next-line no-unused-vars
+import {StreamConstraints} from '../../../device/stream_constraints.js';
 import {I18nString} from '../../../i18n_string.js';
-import {Filenamer} from '../../../models/file_namer.js';
 import {CrosImageCapture} from '../../../mojo/image_capture.js';
 import {Effect} from '../../../mojo/type.js';
-import * as state from '../../../state.js';
 import * as toast from '../../../toast.js';
 import {
-  Facing,  // eslint-disable-line no-unused-vars
-  PerfEvent,
+  Facing,      // eslint-disable-line no-unused-vars
+  Metadata,    // eslint-disable-line no-unused-vars
   Resolution,  // eslint-disable-line no-unused-vars
 } from '../../../type.js';
 import * as util from '../../../util.js';
+import {WaitableEvent} from '../../../waitable_event.js';
 
 import {
   Photo,
@@ -22,6 +24,34 @@
 } from './photo.js';
 
 /**
+ * Contains photo taking result.
+ * @typedef {{
+ *     timestamp: number,
+ *     resolution: !Resolution,
+ *     blob: !Blob,
+ *     metadata: ?Metadata,
+ *     pendingPortrait: !Promise<?{blob: !Blob, metadata: ?Metadata}>,
+ * }}
+ */
+export let PortraitResult;
+
+/**
+ * Provides external dependency functions used by portrait mode and handles the
+ * captured result photo.
+ * @interface
+ */
+export class PortraitHandler extends PhotoHandler {
+  /**
+   * @param {!Promise<!PortraitResult>} pendingPortraitResult
+   * @return {!Promise<void>}
+   * @abstract
+   */
+  onPortraitCaptureDone(pendingPortraitResult) {
+    assertNotReached();
+  }
+}
+
+/**
  * Portrait mode capture controller.
  */
 export class Portrait extends Photo {
@@ -29,16 +59,23 @@
    * @param {!MediaStream} stream
    * @param {!Facing} facing
    * @param {!Resolution} captureResolution
-   * @param {!PhotoHandler} handler
+   * @param {!PortraitHandler} handler
    */
   constructor(stream, facing, captureResolution, handler) {
     super(stream, facing, captureResolution, handler);
+
+    /**
+     * @const {!PortraitHandler}
+     * @private
+     */
+    this.portraitHandler_ = handler;
   }
 
   /**
    * @override
    */
   async start_() {
+    const timestamp = Date.now();
     if (this.crosImageCapture_ === null) {
       this.crosImageCapture_ =
           new CrosImageCapture(this.stream_.getVideoTracks()[0]);
@@ -58,66 +95,50 @@
       });
     }
 
-    const filenamer = new Filenamer();
-    const refImageName = filenamer.newBurstName(false);
-    const portraitImageName = filenamer.newBurstName(true);
-
+    let /** !Promise<!Blob> */ reference;
+    let /** !Promise<!Blob> */ portrait;
+    let /** ?WaitableEvent<!Metadata> */ waitForMetadata = null;
+    let /** ?WaitableEvent<!Metadata> */ waitForPortraitMetadata = null;
     if (this.metadataObserver_ !== null) {
-      [refImageName, portraitImageName].forEach((/** string */ imageName) => {
-        this.metadataNames_.push(Filenamer.getMetadataName(imageName));
-      });
+      waitForMetadata =
+          /** @type{!WaitableEvent<!Metadata>} */ (new WaitableEvent());
+      waitForPortraitMetadata =
+          /** @type{!WaitableEvent<!Metadata>} */ (new WaitableEvent());
+      this.pendingResultForMetadata_.push(
+          waitForMetadata, waitForPortraitMetadata);
     }
-
-    let /** ?Promise<!Blob> */ reference;
-    let /** ?Promise<!Blob> */ portrait;
-
     try {
       [reference, portrait] = await this.crosImageCapture_.takePhoto(
           photoSettings, [Effect.PORTRAIT_MODE]);
-      this.handler_.playShutterEffect();
+      this.portraitHandler_.playShutterEffect();
     } catch (e) {
       toast.show(I18nString.ERROR_MSG_TAKE_PHOTO_FAILED);
       throw e;
     }
 
-    state.set(PerfEvent.PORTRAIT_MODE_CAPTURE_POST_PROCESSING, true);
-    let hasError = false;
+    const pendingPortraitResult = (async () => {
+      const blob = await reference;
+      const image = await util.blobToImage(blob);
+      const resolution = new Resolution(image.width, image.height);
+      const metadata = await (waitForMetadata?.wait() ?? null);
+      const pendingPortrait = (async () => {
+        let /** !Blob */ portraitBlob;
+        try {
+          portraitBlob = await portrait;
+        } catch (e) {
+          // Portrait image may failed due to absence of human faces.
+          // TODO(inker): Log non-intended error.
+          return null;
+        }
+        const metadata = await (waitForPortraitMetadata?.wait() ?? null);
+        return {blob: portraitBlob, metadata};
+      })();
 
-    /**
-     * @param {!Promise<!Blob>} p
-     * @param {string} imageName
-     * @return {!Promise}
-     */
-    const saveResult = async (p, imageName) => {
-      const isPortrait = Object.is(p, portrait);
-      let /** ?Blob */ blob = null;
-      try {
-        blob = await p;
-      } catch (e) {
-        hasError = true;
-        toast.show(
-            isPortrait ? I18nString.ERROR_MSG_TAKE_PORTRAIT_BOKEH_PHOTO_FAILED :
-                         I18nString.ERROR_MSG_TAKE_PHOTO_FAILED);
-        throw e;
-      }
-      const {width, height} = await util.blobToImage(blob);
-      await this.handler_.handleResultPhoto(
-          {resolution: new Resolution(width, height), blob}, imageName);
-    };
+      return {timestamp, resolution, blob, metadata, pendingPortrait};
+    })();
 
-    const refSave = saveResult(reference, refImageName);
-    const portraitSave = saveResult(portrait, portraitImageName);
-    try {
-      await portraitSave;
-    } catch (e) {
-      hasError = true;
-      // Portrait image may failed due to absence of human faces.
-      // TODO(inker): Log non-intended error.
-    }
-    await refSave;
-    state.set(
-        PerfEvent.PORTRAIT_MODE_CAPTURE_POST_PROCESSING, false,
-        {hasError, facing: this.facing_});
+    return () => this.portraitHandler_.onPortraitCaptureDone(
+               pendingPortraitResult);
   }
 }
 
@@ -126,11 +147,27 @@
  */
 export class PortraitFactory extends PhotoFactory {
   /**
+   * @param {!StreamConstraints} constraints Constraints for preview
+   *     stream.
+   * @param {!Resolution} captureResolution
+   * @param {!PortraitHandler} handler
+   */
+  constructor(constraints, captureResolution, handler) {
+    super(constraints, captureResolution, handler);
+
+    /**
+     * @const {!PhotoHandler}
+     * @protected
+     */
+    this.portraitHandler_ = handler;
+  }
+
+  /**
    * @override
    */
   produce() {
     return new Portrait(
         this.previewStream_, this.facing_, this.captureResolution_,
-        this.handler_);
+        this.portraitHandler_);
   }
 }
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/scan.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/scan.js
index a534825e..4d40bf4 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/scan.js
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/scan.js
@@ -2,17 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assertNotReached} from '../../../assert.js';
 // eslint-disable-next-line no-unused-vars
 import {StreamConstraints} from '../../../device/stream_constraints.js';
 import {Point} from '../../../geometry.js';
-import {Filenamer} from '../../../models/file_namer.js';
-import {ChromeHelper} from '../../../mojo/chrome_helper.js';
 import {
-  CanceledError,
-  Facing,     // eslint-disable-line no-unused-vars
-  ImageBlob,  // eslint-disable-line no-unused-vars
-  MimeType,
+  Facing,      // eslint-disable-line no-unused-vars
+  ImageBlob,   // eslint-disable-line no-unused-vars
   Resolution,  // eslint-disable-line no-unused-vars
 } from '../../../type.js';
 
@@ -20,19 +15,10 @@
 import {
   Photo,
   PhotoHandler,  // eslint-disable-line no-unused-vars
+  PhotoResult,   // eslint-disable-line no-unused-vars
 } from './photo.js';
 
 /**
- * Contains photo taking result.
- * @typedef {{
- *     resolution: !Resolution,
- *     blob: !Blob,
- *     mimeType: !MimeType,
- * }}
- */
-export let DocumentResult;
-
-/**
  * @param {!Resolution} size Size of image to be cropped document from.
  * @return {!Array<!Point>}
  */
@@ -58,47 +44,13 @@
  * captured result photo.
  * @interface
  */
-export class ScanHandler {
+export class ScanHandler extends PhotoHandler {
   /**
-   * Plays UI effect when taking photo.
-   * @abstract
-   */
-  playShutterEffect() {
-    assertNotReached();
-  }
-
-  /**
-   * @param {!ImageBlob} originImage Original photo to be cropped document from.
-   * @param {?Array<!Point>} refCorners Initial reference document corner
-   *     positions detected by scan API. Sets to null if scan API cannot find
-   *     any reference corner from |rawBlob|.
-   * @return {!Promise<?{docBlob: !Blob, mimeType: !MimeType}>} Returns the
-   *     processed document blob and which mime type user choose to save. Null
-   *     for cancel document.
-   * @abstract
-   */
-  async reviewDocument(originImage, refCorners) {
-    assertNotReached();
-  }
-
-  /**
-   * Handles the result document.
-   * @param {!DocumentResult} result
-   * @param {string} name Name of the document result to be saved as.
+   * @param {!Promise<!PhotoResult>} pendingPhotoResult
    * @return {!Promise}
    * @abstract
    */
-  handleResultDocument(result, name) {
-    assertNotReached();
-  }
-
-  /**
-   * @return {!Promise}
-   * @abstract
-   */
-  waitPreviewReady() {
-    assertNotReached();
-  }
+  async onDocumentCaptureDone(pendingPhotoResult) {}
 }
 
 /**
@@ -111,6 +63,7 @@
   constructor(handler) {
     /**
      * @const {!ScanHandler}
+     * @private
      */
     this.handler_ = handler;
   }
@@ -118,28 +71,6 @@
   /**
    * @override
    */
-  async handleResultPhoto({blob: rawBlob, resolution}) {
-    const namer = new Filenamer();
-    const helper = await ChromeHelper.getInstance();
-    const corners = await helper.scanDocumentCorners(rawBlob);
-    const reviewResult = await this.handler_.reviewDocument(
-        {blob: rawBlob, resolution}, corners);
-    if (reviewResult === null) {
-      throw new CanceledError('Cancelled after review document');
-    }
-    const {docBlob, mimeType} = reviewResult;
-    const name = namer.newDocumentName(mimeType);
-    let blob = docBlob;
-    if (mimeType === MimeType.PDF) {
-      blob = await helper.convertToPdf(blob);
-    }
-    await this.handler_.handleResultDocument(
-        {blob, resolution, mimeType}, name);
-  }
-
-  /**
-   * @override
-   */
   playShutterEffect() {
     this.handler_.playShutterEffect();
   }
@@ -150,6 +81,20 @@
   waitPreviewReady() {
     return this.handler_.waitPreviewReady();
   }
+
+  /**
+   * @override
+   */
+  onPhotoError() {
+    this.handler_.onPhotoError();
+  }
+
+  /**
+   * @override
+   */
+  onPhotoCaptureDone(pendingPhotoResult) {
+    return this.handler_.onDocumentCaptureDone(pendingPhotoResult);
+  }
 }
 
 /**
@@ -164,12 +109,6 @@
    */
   constructor(stream, facing, captureResolution, handler) {
     super(stream, facing, captureResolution, new DocumentPhotoHandler(handler));
-
-    /**
-     * @const {!ScanHandler}
-     * @protected
-     */
-    this.scanHandler_ = handler;
   }
 }
 
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/square.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/square.js
index e6a8af6..a7046cc5 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/square.js
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/square.js
@@ -56,14 +56,6 @@
   /**
    * @override
    */
-  async handleResultPhoto(result, name) {
-    result.blob = await cropSquare(result.blob);
-    await this.handler_.handleResultPhoto(result, name);
-  }
-
-  /**
-   * @override
-   */
   playShutterEffect() {
     this.handler_.playShutterEffect();
   }
@@ -74,6 +66,28 @@
   waitPreviewReady() {
     return this.handler_.waitPreviewReady();
   }
+
+  /**
+   * @override
+   */
+  onPhotoError() {
+    this.handler_.onPhotoError();
+  }
+
+  /**
+   * @override
+   */
+  async onPhotoCaptureDone(pendingPhotoResult) {
+    const pendingSquarePhotoResult = (async () => {
+      const photoResult = await pendingPhotoResult;
+      const croppedBlob = await cropSquare(photoResult.blob);
+      return {
+        ...photoResult,
+        blob: croppedBlob,
+      };
+    })();
+    await this.handler_.onPhotoCaptureDone(pendingSquarePhotoResult);
+  }
 }
 
 /**
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js
index 01bdbb7..b5442fe 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js
@@ -34,7 +34,6 @@
   ErrorType,
   Facing,  // eslint-disable-line no-unused-vars
   NoChunkError,
-  PerfEvent,
   Resolution,
   VideoType,
 } from '../../../type.js';
@@ -114,48 +113,28 @@
 }
 
 /**
- * Contains video recording result.
+ * @typedef {{
+ *   blob: !Blob,
+ *   resolution: !Resolution,
+ *   timestamp: number,
+ * }}
  */
-export class VideoResult {
-  /**
-   * @param {{
-   *     resolution: !Resolution,
-   *     duration: number,
-   *     videoSaver: !VideoSaver,
-   *     everPaused: boolean,
-   * }} params
-   */
-  constructor({resolution, duration, videoSaver, everPaused}) {
-    /**
-     * @const {!Resolution}
-     * @public
-     */
-    this.resolution = resolution;
+export let VideoSnapshotResult;
 
-    /**
-     * @const {number}
-     * @public
-     */
-    this.duration = duration;
-
-    /**
-     * @const {!VideoSaver}
-     * @public
-     */
-    this.videoSaver = videoSaver;
-
-    /**
-     * @const {boolean}
-     * @public
-     */
-    this.everPaused = everPaused;
-  }
-}
+/**
+ * @typedef {{
+ *   resolution: !Resolution,
+ *   duration: number,
+ *   videoSaver: !VideoSaver,
+ *   everPaused: boolean,
+ * }}
+ */
+export let VideoResult;
 
 /**
  * @typedef {{
  *   name: string,
- *   getBlob: function(): !Promise<!Blob>,
+ *   gifSaver: !GifSaver,
  *   resolution: !Resolution,
  *   duration: number,
  * }}
@@ -178,33 +157,12 @@
   }
 
   /**
-   * Handles the result video.
-   * @param {!VideoResult} video Captured video result.
-   * @return {!Promise}
-   * @abstract
-   */
-  handleResultVideo(video) {
-    assertNotReached();
-  }
-
-  /**
-   * Handles the result gif video.
-   * @param {!GifResult} result
-   * @return {!Promise}
-   * @abstract
-   */
-  handleResultGif(result) {
-    assertNotReached();
-  }
-
-  /**
    * Handles the result video snapshot.
-   * @param {!PhotoResult} photo photo Captured video snapshot photo.
-   * @param {string} name Name of the video snapshot result to be saved as.
+   * @param {!VideoSnapshotResult} videoSnapshotResult
    * @return {!Promise}
    * @abstract
    */
-  handleResultPhoto(photo, name) {
+  handleVideoSnapshot(videoSnapshotResult) {
     assertNotReached();
   }
 
@@ -223,6 +181,24 @@
   getPreviewVideo() {
     assertNotReached();
   }
+
+  /**
+   * @param {!GifResult} gifResult
+   * @return {!Promise<void>}
+   * @abstract
+   */
+  async onGifCaptureDone(gifResult) {
+    assertNotReached();
+  }
+
+  /**
+   * @param {!VideoResult} videoResult
+   * @return {!Promise<void>}
+   * @abstract
+   */
+  async onVideoCaptureDone(videoResult) {
+    assertNotReached();
+  }
 }
 
 /**
@@ -398,6 +374,7 @@
     state.set(state.State.SNAPSHOTTING, true);
     this.snapshotting_ = (async () => {
       try {
+        const timestamp = Date.now();
         let blob;
         if (await this.isBlobVideoSnapshotEnabled()) {
           const photoSettings = /** @type {!PhotoSettings} */ ({
@@ -411,14 +388,11 @@
         }
 
         this.handler_.playShutterEffect();
-        const imageName = (new Filenamer()).newImageName();
-        await this.handler_.handleResultPhoto(
-            {
-              resolution: this.captureResolution_,
-              blob,
-              isVideoSnapshot: true,
-            },
-            imageName);
+        await this.handler_.handleVideoSnapshot({
+          blob,
+          resolution: this.captureResolution_,
+          timestamp,
+        });
       } finally {
         state.set(state.State.SNAPSHOTTING, false);
         this.snapshotting_ = null;
@@ -581,18 +555,11 @@
       state.set(state.State.RECORDING, false);
       this.gifRecordTime_.stop({pause: false});
 
-      // TODO(b:191950622): Close capture stream before handleResultGif()
+      // TODO(b:191950622): Close capture stream before onGifCaptureDone()
       // opening preview page when multi-stream recording enabled.
-      await this.handler_.handleResultGif({
+      return () => this.handler_.onGifCaptureDone({
         name: gifName,
-        getBlob: async () => {
-          // Measure the latency of gif encoder finishing rest of the encoding
-          // works.
-          state.set(PerfEvent.GIF_CAPTURE_POST_PROCESSING, true);
-          const blob = await gifSaver.endWrite();
-          state.set(PerfEvent.GIF_CAPTURE_POST_PROCESSING, false);
-          return blob;
-        },
+        gifSaver,
         resolution: this.captureResolution_,
         duration: this.gifRecordTime_.inMilliseconds(),
       });
@@ -622,29 +589,19 @@
 
       if (isVideoTooShort()) {
         toast.show(I18nString.ERROR_MSG_VIDEO_TOO_SHORT);
-        if (videoSaver !== null) {
-          await videoSaver.cancel();
-        }
-        return;
+        await videoSaver.cancel();
+        return () => this.snapshotting_;
       }
 
-      state.set(PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, true);
-
-      try {
-        await this.handler_.handleResultVideo(new VideoResult({
+      return async () => {
+        await this.handler_.onVideoCaptureDone({
           resolution: this.captureResolution_,
           duration: this.recordTime_.inMilliseconds(),
           videoSaver,
           everPaused: this.everPaused_,
-        }));
-        state.set(
-            PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false,
-            {resolution: this.captureResolution_, facing: this.facing_});
-      } catch (e) {
-        state.set(
-            PerfEvent.VIDEO_CAPTURE_POST_PROCESSING, false, {hasError: true});
-        throw e;
-      }
+        });
+        await this.snapshotting_;
+      };
     }
   }
 
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera_intent.js b/ash/webui/camera_app_ui/resources/js/views/camera_intent.js
index 93a7c9f..05e34fc0 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera_intent.js
+++ b/ash/webui/camera_app_ui/resources/js/views/camera_intent.js
@@ -116,10 +116,10 @@
   /**
    * @override
    */
-  async handleResultPhoto(result, name) {
+  async handlePhotoResult(result, name) {
     this.photoResult_ = result;
     try {
-      await this.resultSaver_.savePhoto(result.blob, name);
+      await this.resultSaver_.savePhoto(result.blob, name, /* metadata */ null);
     } catch (e) {
       toast.show(I18nString.ERROR_MSG_SAVE_FILE_FAILED);
       throw e;
@@ -129,7 +129,7 @@
   /**
    * @override
    */
-  async handleResultVideo(result) {
+  async handleVideoResult(result) {
     this.videoResult_ = result;
     try {
       await this.resultSaver_.finishSaveVideo(result.videoSaver);
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index fbf5705..fc3f22f6 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -122,7 +122,8 @@
 
   source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::ScriptSrc,
-      "script-src chrome://resources chrome://test 'self';");
+      "script-src chrome://resources chrome://test chrome://webui-test "
+      "'self';");
 
   // Allow requesting a chrome-untrusted://personalization/ iframe.
   web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme);
diff --git a/ash/webui/personalization_app/resources/BUILD.gn b/ash/webui/personalization_app/resources/BUILD.gn
index 3d29b3bc..9692702 100644
--- a/ash/webui/personalization_app/resources/BUILD.gn
+++ b/ash/webui/personalization_app/resources/BUILD.gn
@@ -119,6 +119,7 @@
 }
 
 ts_library("build_ts") {
+  composite = true
   root_dir = "$target_gen_dir/$preprocess_folder"
   out_dir = "$target_gen_dir/tsc"
   tsconfig_base = "tsconfig_base.json"
diff --git a/ash/webui/personalization_app/resources/common/icons.js b/ash/webui/personalization_app/resources/common/icons.js
index 0c3786b..ea1a005fe 100644
--- a/ash/webui/personalization_app/resources/common/icons.js
+++ b/ash/webui/personalization_app/resources/common/icons.js
@@ -18,6 +18,9 @@
  * @see https://github.com/PolymerElements/iron-iconset-svg/blob/v3.0.1/demo/svg-sample-icons.js
  */
 
+// Force tsc to consider this file a module.
+export {};
+
 const template = document.createElement('template');
 template.innerHTML = `{__html_template__}`;
 document.head.appendChild(template.content);
diff --git a/ash/webui/personalization_app/resources/common/styles.js b/ash/webui/personalization_app/resources/common/styles.js
index e995946..ded470b 100644
--- a/ash/webui/personalization_app/resources/common/styles.js
+++ b/ash/webui/personalization_app/resources/common/styles.js
@@ -12,6 +12,9 @@
  * above.
  */
 
+// Force tsc to consider this file a module.
+export {};
+
 const template = document.createElement('dom-module');
 template.innerHTML = `{__html_template__}`;
 template.register('common-style');
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 146a6842..35d597a 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20211223.2.2
+7.20211224.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 146a6842..35d597a 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20211223.2.2
+7.20211224.3.1
diff --git a/chrome/VERSION b/chrome/VERSION
index a8a3ba9..7256973 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=99
 MINOR=0
-BUILD=4785
+BUILD=4788
 PATCH=0
diff --git a/chrome/android/DEPS b/chrome/android/DEPS
index 2a2b880f..416e813b 100644
--- a/chrome/android/DEPS
+++ b/chrome/android/DEPS
@@ -88,9 +88,6 @@
 
   # Exceptions to the Chrome*Activity dependency restriction. These will all eventually be removed
   # new code should rely on acceptable dependency aquisition patterns.
-  "AutofillAssistantUiController\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "AutofillAssistantFacade\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 0eb08fe..fca2758e 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -93,6 +93,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantSnackbarFactoryChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObscuringUtilChrome.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabUtilChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTextUtils.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionHandlerImpl.java",
@@ -197,6 +198,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantInfoPopup.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingHelperImpl.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDependencyInjector.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionImpl.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index 78887f9..a7520ab 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -249,7 +249,7 @@
                 }
             }
         };
-        controller.addObserver(mBottomSheetObserver);
+        mBottomSheetController.addObserver(mBottomSheetObserver);
 
         // Show or hide the bottom sheet content when the Autofill Assistant visibility is changed.
         model.addObserver((source, propertyKey) -> {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index c73554a..663bd53 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -11,10 +11,10 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
-import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
+import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
-import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
+import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.base.ApplicationViewportInsetSupplier;
 import org.chromium.ui.util.AccessibilityUtil;
 
@@ -32,8 +32,8 @@
             @Nullable AssistantTabObscuringUtil tabObscuringUtil,
             @Nullable AssistantOverlayCoordinator overlayCoordinator,
             AssistantKeyboardCoordinator.Delegate keyboardCoordinatorDelegate,
-            @NonNull ActivityKeyboardVisibilityDelegate keyboardDelegate, @NonNull View rootView,
-            @NonNull BrowserControlsManager browserControlsManager,
+            @NonNull KeyboardVisibilityDelegate keyboardDelegate, @NonNull View rootView,
+            @NonNull BrowserControlsStateProvider browserControls,
             @NonNull ApplicationViewportInsetSupplier applicationBottomInsetProvider,
             AccessibilityUtil accessibilityUtil, AssistantInfoPageUtil infoPageUtil,
             @Nullable AssistantProfileImageUtil profileImageUtil) {
@@ -42,14 +42,14 @@
             mOverlayCoordinator = overlayCoordinator;
         } else {
             mModel = new AssistantModel();
-            mOverlayCoordinator = new AssistantOverlayCoordinator(activity, browserControlsManager,
+            mOverlayCoordinator = new AssistantOverlayCoordinator(activity, browserControls,
                     rootView, controller.getScrimCoordinator(), mModel.getOverlayModel(),
                     accessibilityUtil);
         }
 
         mBottomBarCoordinator = new AssistantBottomBarCoordinator(activity, mModel,
                 mOverlayCoordinator, controller, applicationBottomInsetProvider, tabObscuringUtil,
-                browserControlsManager, accessibilityUtil, infoPageUtil, profileImageUtil);
+                browserControls, accessibilityUtil, infoPageUtil, profileImageUtil);
         mKeyboardCoordinator = new AssistantKeyboardCoordinator(activity, keyboardDelegate,
                 rootView, mModel, keyboardCoordinatorDelegate, controller);
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesChrome.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesChrome.java
index 56127d2..32868f2 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesChrome.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesChrome.java
@@ -7,47 +7,57 @@
 import android.app.Activity;
 import android.view.View;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.ActivityUtils;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
+import org.chromium.chrome.browser.ui.TabObscuringHandler;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.base.ApplicationViewportInsetSupplier;
+import org.chromium.ui.base.WindowAndroid;
 
 /**
  * Implementation of {@link AssistantDependencies} for Chrome.
  */
 public class AssistantDependenciesChrome
-        implements AssistantDependencies, AssistantStaticDependenciesChrome {
+        extends AssistantStaticDependenciesChrome implements AssistantDependencies {
     private Activity mActivity;
+    private WindowAndroid mWindowAndroid;
     private BottomSheetController mBottomSheetController;
     private BrowserControlsStateProvider mBrowserControls;
     private KeyboardVisibilityDelegate mKeyboardVisibilityDelegate;
     private ApplicationViewportInsetSupplier mBottomInsetProvider;
     private ActivityTabProvider mActivityTabProvider;
+    private TabObscuringHandler mTabObscuringHandler;
     private View mRootView;
     private AssistantSnackbarFactory mSnackbarFactory;
 
     public AssistantDependenciesChrome(Activity activity) {
-        onActivityAttachmentChanged(activity);
+        maybeUpdateDependencies(activity);
     }
 
-    public boolean onActivityAttachmentChanged(Activity activity) {
+    @Override
+    public boolean maybeUpdateDependencies(Activity activity) {
+        if (activity == mActivity) return true;
         if (!(activity instanceof ChromeActivity)) return false;
         ChromeActivity chromeActivity = (ChromeActivity) activity;
 
         Supplier<View> rootView = chromeActivity.getCompositorViewHolderSupplier();
 
         mActivity = chromeActivity;
-        mBottomSheetController =
-                BottomSheetControllerProvider.from(chromeActivity.getWindowAndroid());
+        mWindowAndroid = chromeActivity.getWindowAndroid();
+        mBottomSheetController = BottomSheetControllerProvider.from(mWindowAndroid);
         mBrowserControls = chromeActivity.getBrowserControlsManager();
-        mKeyboardVisibilityDelegate = chromeActivity.getWindowAndroid().getKeyboardDelegate();
-        mBottomInsetProvider =
-                chromeActivity.getWindowAndroid().getApplicationBottomInsetProvider();
+        mKeyboardVisibilityDelegate = mWindowAndroid.getKeyboardDelegate();
+        mBottomInsetProvider = mWindowAndroid.getApplicationBottomInsetProvider();
         mActivityTabProvider = chromeActivity.getActivityTabProvider();
+        mTabObscuringHandler = chromeActivity.getTabObscuringHandler();
         mRootView = rootView.get();
         mSnackbarFactory =
                 new AssistantSnackbarFactoryChrome(mActivity, chromeActivity.getSnackbarManager());
@@ -55,11 +65,24 @@
     }
 
     @Override
+    public boolean maybeUpdateDependencies(WebContents webContents) {
+        @Nullable
+        Activity activity = ActivityUtils.getActivityFromWebContents(webContents);
+        if (activity == null) return false;
+        return maybeUpdateDependencies(activity);
+    }
+
+    @Override
     public Activity getActivity() {
         return mActivity;
     }
 
     @Override
+    public WindowAndroid getWindowAndroid() {
+        return mWindowAndroid;
+    }
+
+    @Override
     public BottomSheetController getBottomSheetController() {
         return mBottomSheetController;
     }
@@ -85,6 +108,11 @@
     }
 
     @Override
+    public TabObscuringHandler getTabObscuringHandler() {
+        return mTabObscuringHandler;
+    }
+
+    @Override
     public View getRootView() {
         return mRootView;
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesFactoryChrome.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesFactoryChrome.java
index 1676cbc..a739311 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesFactoryChrome.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesFactoryChrome.java
@@ -17,6 +17,6 @@
 
     @Override
     public AssistantStaticDependencies createStaticDependencies() {
-        return new AssistantStaticDependenciesChrome() {};
+        return new AssistantStaticDependenciesChrome();
     }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantKeyboardCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantKeyboardCoordinator.java
index 6ed5716..2725812 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantKeyboardCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantKeyboardCoordinator.java
@@ -10,15 +10,15 @@
 
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
+import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.KeyboardVisibilityDelegate.KeyboardVisibilityListener;
-import org.chromium.ui.base.ActivityKeyboardVisibilityDelegate;
 
 /**
  * Coordinator responsible for enabling or disabling the soft keyboard.
  */
 class AssistantKeyboardCoordinator {
     private final Activity mActivity;
-    private final ActivityKeyboardVisibilityDelegate mKeyboardDelegate;
+    private final KeyboardVisibilityDelegate mKeyboardDelegate;
     private final View mRootView;
     private final KeyboardVisibilityListener mKeyboardVisibilityListener =
             this::onKeyboardVisibilityChanged;
@@ -32,7 +32,7 @@
 
     // TODO(b/173103628): refactor and inject the keyboard delegate directly.
     AssistantKeyboardCoordinator(Activity activity,
-            ActivityKeyboardVisibilityDelegate keyboardVisibilityDelegate, View rootView,
+            KeyboardVisibilityDelegate keyboardVisibilityDelegate, View rootView,
             AssistantModel model, Delegate delegate, BottomSheetController controller) {
         mActivity = activity;
         mKeyboardDelegate = keyboardVisibilityDelegate;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java
index 8cf9fdfe..ddb2c8e1 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java
@@ -8,6 +8,8 @@
 
 import androidx.annotation.Nullable;
 
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.ui.TabObscuringHandler;
@@ -22,15 +24,26 @@
 /**
  * Provides default implementations of {@link AssistantStaticDependencies} for Chrome.
  */
-public interface AssistantStaticDependenciesChrome extends AssistantStaticDependencies {
+@JNINamespace("autofill_assistant")
+public class AssistantStaticDependenciesChrome implements AssistantStaticDependencies {
+    private long mNativePointer;
+
     @Override
-    default AccessibilityUtil getAccessibilityUtil() {
+    public long getNativePointer() {
+        if (mNativePointer == 0) {
+            mNativePointer = AssistantStaticDependenciesChromeJni.get().init(this);
+        }
+        return mNativePointer;
+    }
+
+    @Override
+    public AccessibilityUtil getAccessibilityUtil() {
         return ChromeAccessibilityUtil.get();
     }
 
     @Override
     @Nullable
-    default AssistantTabObscuringUtil getTabObscuringUtilOrNull(WindowAndroid windowAndroid) {
+    public AssistantTabObscuringUtil getTabObscuringUtilOrNull(WindowAndroid windowAndroid) {
         TabObscuringHandler tabObscuringHandler =
                 TabObscuringHandlerSupplier.getValueOrNullFrom(windowAndroid);
         assert tabObscuringHandler != null;
@@ -42,18 +55,23 @@
     }
 
     @Override
-    default AssistantInfoPageUtil getInfoPageUtil() {
+    public AssistantInfoPageUtil getInfoPageUtil() {
         return new AssistantInfoPageUtilChrome();
     }
 
     @Override
-    default AssistantFeedbackUtil getFeedbackUtil() {
+    public AssistantFeedbackUtil getFeedbackUtil() {
         return new AssistantFeedbackUtilChrome();
     }
 
     @Override
+    public AssistantTabUtil getTabUtil() {
+        return new AssistantTabUtilChrome();
+    }
+
+    @Override
     @Nullable
-    default String getSignedInAccountEmailOrNull() {
+    public String getSignedInAccountEmailOrNull() {
         IdentityManager identityManager = IdentityServicesProvider.get().getIdentityManager(
                 Profile.getLastUsedRegularProfile());
         return CoreAccountInfo.getEmailFrom(
@@ -62,10 +80,15 @@
 
     @Override
     @Nullable
-    default AssistantProfileImageUtil getProfileImageUtilOrNull(Context context) {
+    public AssistantProfileImageUtil getProfileImageUtilOrNull(Context context) {
         String signedInAccountEmail = getSignedInAccountEmailOrNull();
         if (signedInAccountEmail == null) return null;
 
         return new AssistantProfileImageUtilChrome(context, signedInAccountEmail);
     }
+
+    @NativeMethods
+    interface Natives {
+        long init(AssistantStaticDependencies staticDependencies);
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabUtilChrome.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabUtilChrome.java
new file mode 100644
index 0000000..0230424
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabUtilChrome.java
@@ -0,0 +1,26 @@
+
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import android.app.Activity;
+
+import org.chromium.base.task.PostTask;
+import org.chromium.chrome.browser.customtabs.CustomTabActivity;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
+
+/**
+ * Implementation of {@link AssistantTabUtil} for Chrome.
+ */
+public class AssistantTabUtilChrome implements AssistantTabUtil {
+    @Override
+    public void scheduleCloseCustomTab(Activity activity) {
+        if (!(activity instanceof CustomTabActivity)) {
+            return;
+        }
+
+        PostTask.postTask(UiThreadTaskTraits.DEFAULT, activity::finish);
+    }
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 41feed3..de5cab9 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -7,7 +7,6 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.view.View;
 import android.view.Window;
 
 import androidx.annotation.Nullable;
@@ -15,22 +14,15 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.base.supplier.Supplier;
-import org.chromium.base.task.PostTask;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
-import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
-import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabUtils;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
-import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -52,10 +44,11 @@
 // TODO(crbug.com/806868): This class should be removed once all logic is in native side and the
 // model is directly modified by the native AssistantMediator.
 public class AutofillAssistantUiController {
-    private static Set<ChromeActivity> sActiveChromeActivities;
+    private static final Set<Activity> sActiveActivities = new HashSet<>();
+
     private long mNativeUiController;
 
-    private final ChromeActivity mActivity;
+    private final Activity mActivity;
     private final AssistantCoordinator mCoordinator;
     private final AssistantDependencies mDependencies;
     private final ActivityTabProvider.ActivityTabTabObserver mActivityTabObserver;
@@ -79,82 +72,43 @@
     public static Profile getProfile() {
         return Profile.getLastUsedRegularProfile();
     }
+
     /**
-     * Finds an activity to which a AA UI can be added.
-     *
-     * <p>The activity must not already have an AA UI installed.
+     * Returns {@code true} if an activity without a ui controller was found.
      */
     @CalledByNative
-    @Nullable
-    private static ChromeActivity findAppropriateActivity(WebContents webContents) {
-        ChromeActivity activity = ChromeActivity.fromWebContents(webContents);
-        if (activity != null && isActive(activity)) {
-            return null;
-        }
-
-        return activity;
-    }
-
-    /**
-     * Returns {@code true} if an AA UI is active on the given activity.
-     *
-     * <p>Used to avoid creating duplicate coordinators views.
-     *
-     * <p>TODO(crbug.com/806868): Refactor to have AssistantCoordinator owned by the activity, so
-     * it's easy to guarantee that there will be at most one per activity.
-     */
-    private static boolean isActive(ChromeActivity activity) {
-        if (sActiveChromeActivities == null) {
-            return false;
-        }
-
-        return sActiveChromeActivities.contains(activity);
+    private static boolean shouldCreateNewInstance(
+            WebContents webContents, AssistantDependencies dependencies) {
+        return dependencies.maybeUpdateDependencies(webContents)
+                && !sActiveActivities.contains(dependencies.getActivity());
     }
 
     @CalledByNative
-    private static AutofillAssistantUiController create(ChromeActivity activity,
-            boolean allowTabSwitching, long nativeUiController, AssistantDependencies dependencies,
+    private AutofillAssistantUiController(long nativeUiController,
+            AssistantDependencies dependencies, boolean allowTabSwitching,
             @Nullable AssistantOverlayCoordinator overlayCoordinator) {
-        BottomSheetController sheetController =
-                BottomSheetControllerProvider.from(activity.getWindowAndroid());
-        assert activity != null;
-        assert sheetController != null;
-
-        if (sActiveChromeActivities == null) {
-            sActiveChromeActivities = new HashSet<>();
-        }
-        sActiveChromeActivities.add(activity);
-
-        // TODO(crbug.com/1048983): Have the params be passed in to the constructor directly rather
-        //         than obtaining them from ChromeActivity getters.
-        return new AutofillAssistantUiController(activity, sheetController, allowTabSwitching,
-                nativeUiController, dependencies, overlayCoordinator);
-    }
-
-    private AutofillAssistantUiController(ChromeActivity activity, BottomSheetController controller,
-            boolean allowTabSwitching, long nativeUiController, AssistantDependencies dependencies,
-            @Nullable AssistantOverlayCoordinator overlayCoordinator) {
-        mNativeUiController = nativeUiController;
-        mActivity = activity;
         mDependencies = dependencies;
-        Supplier<View> rootView = activity.getCompositorViewHolderSupplier();
+        mActivity = dependencies.getActivity();
+        sActiveActivities.add(mActivity);
+
+        mNativeUiController = nativeUiController;
         mSnackbarFactory = dependencies.getSnackbarFactory();
         mFeedbackUtil = dependencies.getFeedbackUtil();
 
         // NOTE: Only create one instance of this unless you know what you are doing.
         @Nullable
         AssistantTabObscuringUtil tabObscuringUtil =
-                dependencies.getTabObscuringUtilOrNull(activity.getWindowAndroid());
+                dependencies.getTabObscuringUtilOrNull(dependencies.getWindowAndroid());
 
-        mCoordinator = new AssistantCoordinator(activity, controller, tabObscuringUtil,
-                overlayCoordinator, this::safeNativeOnKeyboardVisibilityChanged,
-                activity.getWindowAndroid().getKeyboardDelegate(), rootView.get(),
-                activity.getBrowserControlsManager(),
-                activity.getWindowAndroid().getApplicationBottomInsetProvider(),
+        mCoordinator = new AssistantCoordinator(mActivity, dependencies.getBottomSheetController(),
+                tabObscuringUtil, overlayCoordinator, this::safeNativeOnKeyboardVisibilityChanged,
+                dependencies.getKeyboardVisibilityDelegate(), dependencies.getRootView(),
+                dependencies.getBrowserControls(), dependencies.getBottomInsetProvider(),
                 dependencies.getAccessibilityUtil(), dependencies.getInfoPageUtil(),
-                dependencies.getProfileImageUtilOrNull(activity));
+                dependencies.getProfileImageUtilOrNull(mActivity));
+
         mActivityTabObserver = new ActivityTabProvider.ActivityTabTabObserver(
-                activity.getActivityTabProvider(), /* shouldTrigger = */ true) {
+                dependencies.getActivityTabProvider(), /* shouldTrigger = */ true) {
             @Override
             protected void onObservingDifferentTab(Tab tab, boolean hint) {
                 if (mWebContents == null) {
@@ -174,7 +128,7 @@
                 if (!allowTabSwitching) {
                     if (tab == null || tab.getWebContents() != mWebContents) {
                         safeNativeOnFatalError(
-                                activity.getString(R.string.autofill_assistant_give_up),
+                                mActivity.getString(R.string.autofill_assistant_give_up),
                                 DropOutReason.TAB_CHANGED);
                     }
                     return;
@@ -294,7 +248,7 @@
         mNativeUiController = 0;
         mActivityTabObserver.destroy();
         mCoordinator.destroy();
-        sActiveChromeActivities.remove(mActivity);
+        sActiveActivities.remove(mActivity);
     }
 
     /**
@@ -303,9 +257,7 @@
      */
     @CalledByNative
     private void scheduleCloseCustomTab() {
-        if (mActivity instanceof CustomTabActivity) {
-            PostTask.postTask(UiThreadTaskTraits.DEFAULT, mActivity::finish);
-        }
+        mDependencies.getTabUtil().scheduleCloseCustomTab(mActivity);
     }
 
     @CalledByNative
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencies.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencies.java
index 303ac0a..4622757 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencies.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencies.java
@@ -11,9 +11,12 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
+import org.chromium.chrome.browser.ui.TabObscuringHandler;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.base.ApplicationViewportInsetSupplier;
+import org.chromium.ui.base.WindowAndroid;
 
 /**
  * Generic dependencies interface. The concrete implementation will depend on the browser framework,
@@ -24,8 +27,18 @@
  */
 @JNINamespace("autofill_assistant")
 public interface AssistantDependencies extends AssistantStaticDependencies {
+    /**
+     * Updates dependencies that are tied to the activity.
+     * @return Whether a new activity could be found.
+     */
+    boolean maybeUpdateDependencies(Activity activity);
+
+    boolean maybeUpdateDependencies(WebContents webContents);
+
     Activity getActivity();
 
+    WindowAndroid getWindowAndroid();
+
     BottomSheetController getBottomSheetController();
 
     BrowserControlsStateProvider getBrowserControls();
@@ -36,6 +49,8 @@
 
     ActivityTabProvider getActivityTabProvider();
 
+    TabObscuringHandler getTabObscuringHandler();
+
     View getRootView();
 
     AssistantSnackbarFactory getSnackbarFactory();
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java
index b39fdf3..5ccd057 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java
@@ -19,6 +19,9 @@
  */
 @JNINamespace("autofill_assistant")
 public interface AssistantStaticDependencies {
+    @CalledByNative
+    long getNativePointer();
+
     AccessibilityUtil getAccessibilityUtil();
 
     /**
@@ -33,6 +36,8 @@
 
     AssistantFeedbackUtil getFeedbackUtil();
 
+    AssistantTabUtil getTabUtil();
+
     @Nullable
     String getSignedInAccountEmailOrNull();
 
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabUtil.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabUtil.java
new file mode 100644
index 0000000..82e7eaf
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabUtil.java
@@ -0,0 +1,18 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import android.app.Activity;
+
+/**
+ * Utility class for closing custom tabs. Implementations might differ depending on where
+ * Autofill Assistant is running (e.g. WebLayer, Chrome).
+ */
+public interface AssistantTabUtil {
+    /**
+     * Finishes the activity if it is a CustomTabActivity.
+     */
+    void scheduleCloseCustomTab(Activity activity);
+}
diff --git a/chrome/android/features/autofill_assistant/public/java_sources.gni b/chrome/android/features/autofill_assistant/public/java_sources.gni
index 8094941..0641219ca 100644
--- a/chrome/android/features/autofill_assistant/public/java_sources.gni
+++ b/chrome/android/features/autofill_assistant/public/java_sources.gni
@@ -17,6 +17,7 @@
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantSnackbarFactory.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObscuringUtil.java",
+  "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabUtil.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionHandler.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectAction.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandler.java",
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index bddd7691..e3d4455 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2585,6 +2585,7 @@
       "android/autofill_assistant/assistant_bottom_bar_delegate.h",
       "android/autofill_assistant/assistant_collect_user_data_delegate.cc",
       "android/autofill_assistant/assistant_collect_user_data_delegate.h",
+      "android/autofill_assistant/assistant_field_trial_util.h",
       "android/autofill_assistant/assistant_form_delegate.cc",
       "android/autofill_assistant/assistant_form_delegate.h",
       "android/autofill_assistant/assistant_generic_ui_delegate.cc",
@@ -2597,6 +2598,10 @@
       "android/autofill_assistant/assistant_overlay_delegate.h",
       "android/autofill_assistant/client_android.cc",
       "android/autofill_assistant/client_android.h",
+      "android/autofill_assistant/dependencies.cc",
+      "android/autofill_assistant/dependencies.h",
+      "android/autofill_assistant/dependencies_chrome.cc",
+      "android/autofill_assistant/dependencies_chrome.h",
       "android/autofill_assistant/features_android.cc",
       "android/autofill_assistant/generic_ui_events_android.cc",
       "android/autofill_assistant/generic_ui_events_android.h",
diff --git a/chrome/browser/android/autofill_assistant/assistant_field_trial_util.h b/chrome/browser/android/autofill_assistant/assistant_field_trial_util.h
new file mode 100644
index 0000000..cf3abde
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/assistant_field_trial_util.h
@@ -0,0 +1,23 @@
+// Copyright 2021 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_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_FIELD_TRIAL_UTIL_H_
+#define CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_FIELD_TRIAL_UTIL_H_
+
+#include "base/strings/string_piece.h"
+
+namespace autofill_assistant {
+
+class AssistantFieldTrialUtil {
+ public:
+  virtual ~AssistantFieldTrialUtil() = default;
+
+  virtual bool RegisterSyntheticFieldTrial(
+      base::StringPiece trial_name,
+      base::StringPiece group_name) const = 0;
+};
+
+}  // namespace autofill_assistant
+
+#endif  // CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_FIELD_TRIAL_UTIL_H_
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index 2b94236..13a11d1 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/autofill/android/personal_data_manager_android.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -53,6 +52,8 @@
 using base::android::AttachCurrentThread;
 using base::android::JavaParamRef;
 using base::android::JavaRef;
+using base::android::ScopedJavaGlobalRef;
+using base::android::ScopedJavaLocalRef;
 
 namespace autofill_assistant {
 namespace {
@@ -64,13 +65,16 @@
 
 }  // namespace
 
-static base::android::ScopedJavaLocalRef<jobject>
+static ScopedJavaLocalRef<jobject>
 JNI_AutofillAssistantClient_CreateForWebContents(
     JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jweb_contents,
-    const base::android::JavaParamRef<jobject>& jdependencies) {
+    const JavaParamRef<jobject>& jweb_contents,
+    const JavaParamRef<jobject>& jdependencies) {
   auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents);
-  ClientAndroid::CreateForWebContents(web_contents, jdependencies);
+  std::unique_ptr<Dependencies> dependencies =
+      Dependencies::CreateFromJavaObject(
+          ScopedJavaGlobalRef<jobject>(jdependencies));
+  ClientAndroid::CreateForWebContents(web_contents, std::move(dependencies));
   return ClientAndroid::FromWebContents(web_contents)->GetJavaObject();
 }
 
@@ -89,7 +93,7 @@
 
 static void JNI_AutofillAssistantClient_OnOnboardingUiChange(
     JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jweb_contents,
+    const JavaParamRef<jobject>& jweb_contents,
     jboolean shown) {
   RuntimeManager* runtime_manager = RuntimeManager::GetForWebContents(
       content::WebContents::FromJavaWebContents(jweb_contents));
@@ -97,14 +101,13 @@
     runtime_manager->SetUIState(shown ? UIState::kShown : UIState::kNotShown);
 }
 
-ClientAndroid::ClientAndroid(
-    content::WebContents* web_contents,
-    const base::android::JavaRef<jobject>& jdependencies)
+ClientAndroid::ClientAndroid(content::WebContents* web_contents,
+                             std::unique_ptr<Dependencies> dependencies)
     : content::WebContentsUserData<ClientAndroid>(*web_contents),
-      jdependencies_(jdependencies),
       java_object_(Java_AutofillAssistantClient_Constructor(
           AttachCurrentThread(),
-          reinterpret_cast<intptr_t>(this))) {}
+          reinterpret_cast<intptr_t>(this))),
+      dependencies_(std::move(dependencies)) {}
 
 ClientAndroid::~ClientAndroid() {
   if (controller_ != nullptr && started_) {
@@ -163,11 +166,11 @@
   }
 
   // Register TTS Synthetic Field Trial.
-  ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
+  const bool enable_tts =
+      trigger_context->GetScriptParameters().GetEnableTts().value_or(false);
+  dependencies_->CreateFieldTrialUtil()->RegisterSyntheticFieldTrial(
       kAutofillAssistantTtsTrialName,
-      trigger_context->GetScriptParameters().GetEnableTts().value_or(false)
-          ? kEnabledGroupName
-          : kDisabledGroupName);
+      enable_tts ? kEnabledGroupName : kDisabledGroupName);
 
   DCHECK(!trigger_context->GetDirectAction());
   if (VLOG_IS_ON(2)) {
@@ -405,7 +408,7 @@
 base::android::ScopedJavaGlobalRef<jobject> ClientAndroid::GetDependencies(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller) {
-  return jdependencies_;
+  return dependencies_->GetJavaObject();
 }
 
 int ClientAndroid::FindDirectAction(const std::string& action_name) {
@@ -434,7 +437,7 @@
     const base::android::JavaRef<jobject>& joverlay_coordinator) {
   if (!ui_controller_android_) {
     ui_controller_android_ = UiControllerAndroid::CreateFromWebContents(
-        GetWebContents(), jdependencies_, joverlay_coordinator);
+        GetWebContents(), dependencies_->GetJavaObject(), joverlay_coordinator);
     if (!ui_controller_android_) {
       // The activity is not or not yet in a mode where attaching the UI is
       // possible.
@@ -560,7 +563,7 @@
 
 bool ClientAndroid::IsAccessibilityEnabled() const {
   return Java_AutofillAssistantClient_isAccessibilityEnabled(
-      AttachCurrentThread(), jdependencies_);
+      AttachCurrentThread(), dependencies_->GetJavaObject());
 }
 
 bool ClientAndroid::IsSpokenFeedbackAccessibilityServiceEnabled() const {
diff --git a/chrome/browser/android/autofill_assistant/client_android.h b/chrome/browser/android/autofill_assistant/client_android.h
index 6232dca4..18dd1859 100644
--- a/chrome/browser/android/autofill_assistant/client_android.h
+++ b/chrome/browser/android/autofill_assistant/client_android.h
@@ -11,6 +11,7 @@
 #include "base/android/scoped_java_ref.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/android/autofill_assistant/dependencies.h"
 #include "chrome/browser/android/autofill_assistant/ui_controller_android.h"
 #include "components/autofill_assistant/browser/client.h"
 #include "components/autofill_assistant/browser/controller.h"
@@ -148,7 +149,7 @@
   friend class content::WebContentsUserData<ClientAndroid>;
 
   explicit ClientAndroid(content::WebContents* web_contents,
-                         const base::android::JavaRef<jobject>& jdependencies);
+                         std::unique_ptr<Dependencies> dependencies);
 
   void CreateController(
       std::unique_ptr<Service> service,
@@ -172,12 +173,12 @@
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
-  const base::android::ScopedJavaGlobalRef<jobject> jdependencies_;
-
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
   std::unique_ptr<Controller> controller_;
   mutable std::unique_ptr<WebsiteLoginManager> website_login_manager_;
 
+  const std::unique_ptr<Dependencies> dependencies_;
+
   // True if Start() was called. This turns on the tracking of dropouts.
   bool started_ = false;
 
diff --git a/chrome/browser/android/autofill_assistant/dependencies.cc b/chrome/browser/android/autofill_assistant/dependencies.cc
new file mode 100644
index 0000000..ed0cc2d
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/dependencies.cc
@@ -0,0 +1,43 @@
+// Copyright 2021 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/android/autofill_assistant/dependencies.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_piece.h"
+#include "chrome/android/features/autofill_assistant/jni_headers_public/AssistantStaticDependencies_jni.h"
+
+using ::base::android::AttachCurrentThread;
+using ::base::android::JavaParamRef;
+using ::base::android::ScopedJavaGlobalRef;
+
+namespace autofill_assistant {
+
+std::unique_ptr<Dependencies> Dependencies::CreateFromJavaObject(
+    ScopedJavaGlobalRef<jobject> java_object) {
+  const jlong object_ptr = Java_AssistantStaticDependencies_getNativePointer(
+      AttachCurrentThread(), java_object);
+  return base::WrapUnique(reinterpret_cast<Dependencies*>(object_ptr));
+}
+
+Dependencies::Dependencies(JNIEnv* env,
+                           const JavaParamRef<jobject>& java_object)
+    : java_object_(java_object) {}
+
+ScopedJavaGlobalRef<jobject> Dependencies::GetJavaObject() const {
+  return java_object_;
+}
+
+ScopedJavaGlobalRef<jobject> Dependencies::GetInfoPageUtil(
+    const ScopedJavaGlobalRef<jobject>& java_object) {
+  return ScopedJavaGlobalRef<jobject>(
+      Java_AssistantStaticDependencies_getInfoPageUtil(AttachCurrentThread(),
+                                                       java_object));
+}
+
+Dependencies::~Dependencies() = default;
+
+}  // namespace autofill_assistant
diff --git a/chrome/browser/android/autofill_assistant/dependencies.h b/chrome/browser/android/autofill_assistant/dependencies.h
new file mode 100644
index 0000000..13a0d7a
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/dependencies.h
@@ -0,0 +1,42 @@
+// Copyright 2021 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_ANDROID_AUTOFILL_ASSISTANT_DEPENDENCIES_H_
+#define CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_DEPENDENCIES_H_
+
+#include <memory>
+#include "base/android/scoped_java_ref.h"
+#include "base/strings/string_piece.h"
+#include "chrome/browser/android/autofill_assistant/assistant_field_trial_util.h"
+
+namespace autofill_assistant {
+
+// Interface for platform delegates that provide platform-dependent features
+// and dependencies to the starter.
+class Dependencies {
+ public:
+  static std::unique_ptr<Dependencies> CreateFromJavaObject(
+      base::android::ScopedJavaGlobalRef<jobject> java_object);
+
+  base::android::ScopedJavaGlobalRef<jobject> GetJavaObject() const;
+
+  static base::android::ScopedJavaGlobalRef<jobject> GetInfoPageUtil(
+      const base::android::ScopedJavaGlobalRef<jobject>& java_object);
+
+  virtual ~Dependencies();
+
+  virtual std::unique_ptr<AssistantFieldTrialUtil> CreateFieldTrialUtil()
+      const = 0;
+
+ protected:
+  Dependencies(JNIEnv* env,
+               const base::android::JavaParamRef<jobject>& java_object);
+
+ private:
+  const base::android::ScopedJavaGlobalRef<jobject> java_object_;
+};
+
+}  // namespace autofill_assistant
+
+#endif  // CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_DEPENDENCIES_H_
diff --git a/chrome/browser/android/autofill_assistant/dependencies_chrome.cc b/chrome/browser/android/autofill_assistant/dependencies_chrome.cc
new file mode 100644
index 0000000..ac9c1fe
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/dependencies_chrome.cc
@@ -0,0 +1,43 @@
+// Copyright 2021 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/android/autofill_assistant/dependencies_chrome.h"
+
+#include "base/android/scoped_java_ref.h"
+#include "base/strings/string_piece.h"
+#include "chrome/android/features/autofill_assistant/jni_headers/AssistantStaticDependenciesChrome_jni.h"
+#include "chrome/browser/android/autofill_assistant/assistant_field_trial_util.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
+
+using ::base::StringPiece;
+using ::base::android::JavaParamRef;
+using ::base::android::ScopedJavaGlobalRef;
+
+namespace autofill_assistant {
+
+static jlong JNI_AssistantStaticDependenciesChrome_Init(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& java_object) {
+  return reinterpret_cast<intptr_t>(new DependenciesChrome(env, java_object));
+}
+
+DependenciesChrome::DependenciesChrome(JNIEnv* env,
+                                       const JavaParamRef<jobject>& java_object)
+    : Dependencies(env, java_object) {}
+
+class AssistantFieldTrialUtilChrome : public AssistantFieldTrialUtil {
+  bool RegisterSyntheticFieldTrial(
+      base::StringPiece trial_name,
+      base::StringPiece group_name) const override {
+    return ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
+        trial_name, group_name);
+  }
+};
+
+std::unique_ptr<AssistantFieldTrialUtil>
+DependenciesChrome::CreateFieldTrialUtil() const {
+  return std::make_unique<AssistantFieldTrialUtilChrome>();
+}
+
+}  // namespace autofill_assistant
diff --git a/chrome/browser/android/autofill_assistant/dependencies_chrome.h b/chrome/browser/android/autofill_assistant/dependencies_chrome.h
new file mode 100644
index 0000000..ebb7c59
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/dependencies_chrome.h
@@ -0,0 +1,29 @@
+// Copyright 2021 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_ANDROID_AUTOFILL_ASSISTANT_DEPENDENCIES_CHROME_H_
+#define CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_DEPENDENCIES_CHROME_H_
+
+#include "chrome/browser/android/autofill_assistant/dependencies.h"
+
+#include "base/android/scoped_java_ref.h"
+#include "base/strings/string_piece.h"
+#include "components/metrics/metrics_service_accessor.h"
+
+namespace autofill_assistant {
+
+// Interface for platform delegates that provide platform-dependent features
+// and dependencies to the starter.
+class DependenciesChrome : public Dependencies {
+ public:
+  DependenciesChrome(JNIEnv* env,
+                     const base::android::JavaParamRef<jobject>& java_object);
+
+  std::unique_ptr<AssistantFieldTrialUtil> CreateFieldTrialUtil()
+      const override;
+};
+
+}  // namespace autofill_assistant
+
+#endif  // CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_DEPENDENCIES_CHROME_H_
diff --git a/chrome/browser/android/autofill_assistant/starter_android.cc b/chrome/browser/android/autofill_assistant/starter_android.cc
index 42fda252..1365194d 100644
--- a/chrome/browser/android/autofill_assistant/starter_android.cc
+++ b/chrome/browser/android/autofill_assistant/starter_android.cc
@@ -23,8 +23,11 @@
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "url/gurl.h"
 
-using base::android::JavaParamRef;
-using base::android::ScopedJavaLocalRef;
+using ::base::android::AttachCurrentThread;
+using ::base::android::JavaObjectArrayReader;
+using ::base::android::JavaParamRef;
+using ::base::android::ScopedJavaGlobalRef;
+using ::base::android::ScopedJavaLocalRef;
 
 namespace autofill_assistant {
 
@@ -58,7 +61,7 @@
 
 void StarterAndroid::Detach(JNIEnv* env, const JavaParamRef<jobject>& jcaller) {
   java_object_ = nullptr;
-  java_dependencies_ = nullptr;
+  dependencies_ = nullptr;
   starter_.reset();
 }
 
@@ -67,7 +70,7 @@
   CreateJavaDependenciesIfNecessary();
   return std::make_unique<TriggerScriptBridgeAndroid>(
       base::android::AttachCurrentThread(),
-      GetWebContents().GetJavaWebContents(), java_dependencies_);
+      GetWebContents().GetJavaWebContents(), dependencies_->GetJavaObject());
 }
 
 std::unique_ptr<ServiceRequestSender>
@@ -123,7 +126,7 @@
 void StarterAndroid::OnActivityAttachmentChanged(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller) {
-  java_dependencies_ = nullptr;
+  dependencies_ = nullptr;
   if (!starter_) {
     return;
   }
@@ -180,7 +183,7 @@
 }
 
 void StarterAndroid::HideOnboarding() {
-  if (!java_dependencies_) {
+  if (!dependencies_) {
     return;
   }
   Java_Starter_hideOnboarding(base::android::AttachCurrentThread(),
@@ -240,20 +243,25 @@
 }
 
 void StarterAndroid::CreateJavaDependenciesIfNecessary() {
-  if (java_dependencies_) {
+  if (dependencies_) {
     return;
   }
 
-  base::android::JavaObjectArrayReader<jobject> array(
+  JavaObjectArrayReader<jobject> array(
       Java_Starter_getOrCreateDependenciesAndOnboardingHelper(
-          base::android::AttachCurrentThread(), java_object_));
+          AttachCurrentThread(), java_object_));
 
   DCHECK_EQ(array.size(), 2);
   if (array.size() != 2) {
     return;
   }
 
-  java_dependencies_ = *array.begin();
+  ScopedJavaGlobalRef<jobject> java_dependencies =
+      ScopedJavaGlobalRef<jobject>(*array.begin());
+  if (!java_dependencies.is_null()) {
+    dependencies_ = Dependencies::CreateFromJavaObject(java_dependencies);
+  }
+
   java_onboarding_helper_ = *(++array.begin());
 }
 
@@ -282,7 +290,8 @@
     std::unique_ptr<TriggerContext> trigger_context,
     const absl::optional<TriggerScriptProto>& trigger_script) {
   CreateJavaDependenciesIfNecessary();
-  ClientAndroid::CreateForWebContents(&GetWebContents(), java_dependencies_);
+  ClientAndroid::CreateForWebContents(&GetWebContents(),
+                                      std::move(dependencies_));
   auto* client_android = ClientAndroid::FromWebContents(&GetWebContents());
   DCHECK(client_android);
 
diff --git a/chrome/browser/android/autofill_assistant/starter_android.h b/chrome/browser/android/autofill_assistant/starter_android.h
index 94501de3..c8feb28 100644
--- a/chrome/browser/android/autofill_assistant/starter_android.h
+++ b/chrome/browser/android/autofill_assistant/starter_android.h
@@ -11,6 +11,7 @@
 #include "base/android/jni_weak_ref.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/android/autofill_assistant/dependencies.h"
 #include "components/autofill_assistant/browser/metrics.h"
 #include "components/autofill_assistant/browser/onboarding_result.h"
 #include "components/autofill_assistant/browser/starter.h"
@@ -120,8 +121,9 @@
 
   WEB_CONTENTS_USER_DATA_KEY_DECL();
   std::unique_ptr<Starter> starter_;
+  std::unique_ptr<Dependencies> dependencies_;
+
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
-  base::android::ScopedJavaGlobalRef<jobject> java_dependencies_;
   base::android::ScopedJavaGlobalRef<jobject> java_onboarding_helper_;
   std::unique_ptr<WebsiteLoginManager> website_login_manager_;
   base::OnceCallback<void(Metrics::FeatureModuleInstallation result)>
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index e7b092ea..9afb89f 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -29,8 +29,8 @@
 #include "chrome/android/features/autofill_assistant/jni_headers/AssistantPlaceholdersConfiguration_jni.h"
 #include "chrome/android/features/autofill_assistant/jni_headers/AutofillAssistantUiController_jni.h"
 #include "chrome/android/features/autofill_assistant/jni_headers_public/AssistantDependencies_jni.h"
-#include "chrome/android/features/autofill_assistant/jni_headers_public/AssistantStaticDependencies_jni.h"
 #include "chrome/browser/android/autofill_assistant/client_android.h"
+#include "chrome/browser/android/autofill_assistant/dependencies.h"
 #include "chrome/browser/android/autofill_assistant/generic_ui_root_controller_android.h"
 #include "chrome/browser/android/autofill_assistant/ui_controller_android_utils.h"
 #include "chrome/browser/autofill/android/personal_data_manager_android.h"
@@ -62,6 +62,7 @@
 using ::base::android::ConvertUTF8ToJavaString;
 using ::base::android::JavaParamRef;
 using ::base::android::JavaRef;
+using ::base::android::ScopedJavaGlobalRef;
 using ::base::android::ScopedJavaLocalRef;
 using ::base::android::ToJavaArrayOfStrings;
 
@@ -273,18 +274,17 @@
     const base::android::JavaRef<jobject>& jdependencies,
     const base::android::JavaRef<jobject>& joverlay_coordinator) {
   JNIEnv* env = AttachCurrentThread();
-  auto jactivity = Java_AutofillAssistantUiController_findAppropriateActivity(
-      env, web_contents->GetJavaWebContents());
-  if (!jactivity) {
+  if (!Java_AutofillAssistantUiController_shouldCreateNewInstance(
+          env, web_contents->GetJavaWebContents(), jdependencies)) {
     return nullptr;
   }
-  return std::make_unique<UiControllerAndroid>(env, jactivity, jdependencies,
+
+  return std::make_unique<UiControllerAndroid>(env, jdependencies,
                                                joverlay_coordinator);
 }
 
 UiControllerAndroid::UiControllerAndroid(
     JNIEnv* env,
-    const base::android::JavaRef<jobject>& jactivity,
     const base::android::JavaRef<jobject>& jdependencies,
     const base::android::JavaRef<jobject>& joverlay_coordinator)
     : overlay_delegate_(this),
@@ -296,11 +296,11 @@
       jstatic_dependencies_(
           Java_AssistantDependencies_getStaticDependencies(env,
                                                            jdependencies)) {
-  java_object_ = Java_AutofillAssistantUiController_create(
-      env, jactivity,
+  java_object_ = Java_AutofillAssistantUiController_Constructor(
+      env, reinterpret_cast<intptr_t>(this), jdependencies,
       /* allowTabSwitching= */
       base::FeatureList::IsEnabled(features::kAutofillAssistantChromeEntry),
-      reinterpret_cast<intptr_t>(this), jdependencies, joverlay_coordinator);
+      joverlay_coordinator);
   header_model_ = std::make_unique<AssistantHeaderModel>(
       Java_AssistantModel_getHeaderModel(env, GetModel()));
 
@@ -1208,11 +1208,8 @@
       AttachCurrentThread(), java_object_);
 }
 
-base::android::ScopedJavaGlobalRef<jobject>
-UiControllerAndroid::GetInfoPageUtil() const {
-  return base::android::ScopedJavaGlobalRef<jobject>(
-      Java_AssistantStaticDependencies_getInfoPageUtil(AttachCurrentThread(),
-                                                       jstatic_dependencies_));
+ScopedJavaGlobalRef<jobject> UiControllerAndroid::GetInfoPageUtil() const {
+  return Dependencies::GetInfoPageUtil(jstatic_dependencies_);
 }
 
 void UiControllerAndroid::OnCollectUserDataOptionsChanged(
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index f145030..562fadc0 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -56,7 +56,6 @@
   // instance or until WillShutdown is called.
   UiControllerAndroid(
       JNIEnv* env,
-      const base::android::JavaRef<jobject>& jactivity,
       const base::android::JavaRef<jobject>& jdependencies,
       const base::android::JavaRef<jobject>& joverlay_coordinator);
 
diff --git a/chrome/browser/ash/arc/auth/arc_auth_service.cc b/chrome/browser/ash/arc/auth/arc_auth_service.cc
index 996b81b..ea344c3 100644
--- a/chrome/browser/ash/arc/auth/arc_auth_service.cc
+++ b/chrome/browser/ash/arc/auth/arc_auth_service.cc
@@ -18,6 +18,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/singleton.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/account_manager/account_manager_util.h"
@@ -183,6 +184,26 @@
                           persistent_error);
 }
 
+void CompleteFetchPrimaryAccountInfoWithMetrics(
+    ArcAuthService::RequestPrimaryAccountInfoCallback callback,
+    mojom::ArcAuthCodeStatus status,
+    mojom::AccountInfoPtr account_info) {
+  base::UmaHistogramEnumeration(
+      kArcAuthRequestAccountInfoResultPrimaryHistogramName, status);
+  std::move(callback).Run(std::move(status), std::move(account_info));
+}
+
+void CompleteFetchSecondaryAccountInfoWithMetrics(
+    ArcAuthService::RequestAccountInfoCallback callback,
+    mojom::ArcAuthCodeStatus status,
+    mojom::AccountInfoPtr account_info,
+    bool persistent_error) {
+  base::UmaHistogramEnumeration(
+      kArcAuthRequestAccountInfoResultSecondaryHistogramName, status);
+  std::move(callback).Run(std::move(status), std::move(account_info),
+                          persistent_error);
+}
+
 }  // namespace
 
 // static
@@ -369,7 +390,10 @@
 void ArcAuthService::RequestPrimaryAccountInfo(
     RequestPrimaryAccountInfoCallback callback) {
   // This is the provisioning flow.
-  FetchPrimaryAccountInfo(true /* initial_signin */, std::move(callback));
+  FetchPrimaryAccountInfo(
+      true /* initial_signin */,
+      base::BindOnce(&CompleteFetchPrimaryAccountInfoWithMetrics,
+                     std::move(callback)));
 }
 
 void ArcAuthService::RequestAccountInfo(const std::string& account_name,
@@ -380,7 +404,10 @@
 
   // Check if |account_name| points to a Secondary Account.
   if (!IsPrimaryOrDeviceLocalAccount(identity_manager_, account_name)) {
-    FetchSecondaryAccountInfo(account_name, std::move(callback));
+    FetchSecondaryAccountInfo(
+        account_name,
+        base::BindOnce(&CompleteFetchSecondaryAccountInfoWithMetrics,
+                       std::move(callback)));
     return;
   }
 
@@ -389,8 +416,10 @@
   // has persistent error.
   FetchPrimaryAccountInfo(
       false /* initial_signin */,
-      base::BindOnce(&OnFetchPrimaryAccountInfoCompleted, std::move(callback),
-                     false /* persistent_error */));
+      base::BindOnce(
+          &CompleteFetchPrimaryAccountInfoWithMetrics,
+          base::BindOnce(&OnFetchPrimaryAccountInfoCompleted,
+                         std::move(callback), false /* persistent_error */)));
 }
 
 void ArcAuthService::FetchPrimaryAccountInfo(
diff --git a/chrome/browser/ash/arc/auth/arc_auth_service.h b/chrome/browser/ash/arc/auth/arc_auth_service.h
index 6af2442..a6de7f6 100644
--- a/chrome/browser/ash/arc/auth/arc_auth_service.h
+++ b/chrome/browser/ash/arc/auth/arc_auth_service.h
@@ -40,6 +40,11 @@
 class ArcBridgeService;
 class ArcFetcherBase;
 
+constexpr char kArcAuthRequestAccountInfoResultPrimaryHistogramName[] =
+    "Arc.Auth.RequestAccountInfoResult.Primary";
+constexpr char kArcAuthRequestAccountInfoResultSecondaryHistogramName[] =
+    "Arc.Auth.RequestAccountInfoResult.Secondary";
+
 // Implementation of ARC authorization.
 class ArcAuthService : public KeyedService,
                        public mojom::AuthHost,
diff --git a/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc b/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
index 357a8828..7d4e5f70 100644
--- a/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
+++ b/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
@@ -587,6 +587,7 @@
 
 IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
                        ReAuthenticatePrimaryAccountSucceeds) {
+  base::HistogramTester tester;
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
   test_url_loader_factory()->AddResponse(arc::kAuthTokenExchangeEndPoint,
                                          GetFakeAuthTokenResponse());
@@ -604,10 +605,14 @@
   EXPECT_FALSE(auth_instance().account_info()->enrollment_token);
   EXPECT_FALSE(auth_instance().account_info()->is_managed);
   EXPECT_FALSE(auth_instance().sign_in_persistent_error());
+  tester.ExpectUniqueSample(
+      kArcAuthRequestAccountInfoResultPrimaryHistogramName,
+      mojom::ArcAuthCodeStatus::SUCCESS, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
                        RetryAuthTokenExchangeRequestOnUnauthorizedError) {
+  base::HistogramTester tester;
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
 
   base::RunLoop run_loop;
@@ -626,10 +631,14 @@
   run_loop.Run();
 
   ASSERT_TRUE(auth_instance().account_info());
+  tester.ExpectUniqueSample(
+      kArcAuthRequestAccountInfoResultPrimaryHistogramName,
+      mojom::ArcAuthCodeStatus::SUCCESS, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
                        ReAuthenticatePrimaryAccountFailsForInvalidAccount) {
+  base::HistogramTester tester;
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
   test_url_loader_factory()->AddResponse(arc::kAuthTokenExchangeEndPoint,
                                          std::string() /* response */,
@@ -642,9 +651,13 @@
   EXPECT_FALSE(auth_instance().account_info());
   EXPECT_EQ(mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR,
             auth_instance().auth_code_status());
+  tester.ExpectUniqueSample(
+      kArcAuthRequestAccountInfoResultPrimaryHistogramName,
+      mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, FetchSecondaryAccountInfoSucceeds) {
+  base::HistogramTester tester;
   // Add a Secondary Account.
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
   SeedAccountInfo(kSecondaryAccountEmail);
@@ -665,10 +678,14 @@
   EXPECT_FALSE(auth_instance().account_info()->enrollment_token);
   EXPECT_FALSE(auth_instance().account_info()->is_managed);
   EXPECT_FALSE(auth_instance().sign_in_persistent_error());
+  tester.ExpectUniqueSample(
+      kArcAuthRequestAccountInfoResultSecondaryHistogramName,
+      mojom::ArcAuthCodeStatus::SUCCESS, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
                        FetchSecondaryAccountInfoFailsForInvalidAccounts) {
+  base::HistogramTester tester;
   // Add a Secondary Account.
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
   SeedAccountInfo(kSecondaryAccountEmail);
@@ -684,10 +701,14 @@
   EXPECT_FALSE(auth_instance().account_info());
   EXPECT_EQ(mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR,
             auth_instance().auth_code_status());
+  tester.ExpectUniqueSample(
+      kArcAuthRequestAccountInfoResultSecondaryHistogramName,
+      mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
                        FetchSecondaryAccountInfoInvalidRefreshToken) {
+  base::HistogramTester tester;
   const AccountInfo account_info = SetupGaiaAccount(kSecondaryAccountEmail);
   SetInvalidRefreshTokenForAccount(account_info.account_id);
   test_url_loader_factory()->AddResponse(arc::kAuthTokenExchangeEndPoint,
@@ -703,10 +724,14 @@
   EXPECT_EQ(mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR,
             auth_instance().auth_code_status());
   EXPECT_TRUE(auth_instance().sign_in_persistent_error());
+  tester.ExpectUniqueSample(
+      kArcAuthRequestAccountInfoResultSecondaryHistogramName,
+      mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
                        FetchSecondaryAccountRefreshTokenHasPersistentError) {
+  base::HistogramTester tester;
   const AccountInfo account_info = SetupGaiaAccount(kSecondaryAccountEmail);
   UpdatePersistentErrorOfRefreshTokenForAccount(
       account_info.account_id,
@@ -723,11 +748,15 @@
   EXPECT_EQ(mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR,
             auth_instance().auth_code_status());
   EXPECT_TRUE(auth_instance().sign_in_persistent_error());
+  tester.ExpectUniqueSample(
+      kArcAuthRequestAccountInfoResultSecondaryHistogramName,
+      mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(
     ArcAuthServiceTest,
     FetchSecondaryAccountInfoReturnsErrorForNotFoundAccounts) {
+  base::HistogramTester tester;
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
   // Don't add account with kSecondaryAccountEmail.
 
@@ -740,6 +769,9 @@
   EXPECT_EQ(mojom::ArcAuthCodeStatus::CHROME_ACCOUNT_NOT_FOUND,
             auth_instance().auth_code_status());
   EXPECT_TRUE(auth_instance().sign_in_persistent_error());
+  tester.ExpectUniqueSample(
+      kArcAuthRequestAccountInfoResultSecondaryHistogramName,
+      mojom::ArcAuthCodeStatus::CHROME_ACCOUNT_NOT_FOUND, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, FetchGoogleAccountsFromArc) {
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor.h b/chrome/browser/metrics/chrome_metrics_service_accessor.h
index 62ce6fc..a7a6f9e 100644
--- a/chrome/browser/metrics/chrome_metrics_service_accessor.h
+++ b/chrome/browser/metrics/chrome_metrics_service_accessor.h
@@ -34,7 +34,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 namespace autofill_assistant {
-class ClientAndroid;
+class AssistantFieldTrialUtilChrome;
 }  // namespace autofill_assistant
 
 namespace domain_reliability {
@@ -113,7 +113,7 @@
   static void SetMetricsAndCrashReportingForTesting(const bool* value);
 
  private:
-  friend class autofill_assistant::ClientAndroid;
+  friend class autofill_assistant::AssistantFieldTrialUtilChrome;
   friend class ::CrashesDOMHandler;
   friend class ::FlashDOMHandler;
   friend class BreadcrumbsStatus;
diff --git a/chrome/browser/resources/bookmarks/BUILD.gn b/chrome/browser/resources/bookmarks/BUILD.gn
index 7c380a9..aa4eb82 100644
--- a/chrome/browser/resources/bookmarks/BUILD.gn
+++ b/chrome/browser/resources/bookmarks/BUILD.gn
@@ -9,7 +9,6 @@
 import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 import("../tools/optimize_webui.gni")
-import("bookmarks.gni")
 
 preprocess_folder = "preprocessed"
 tsc_folder = "tsc"
@@ -52,20 +51,57 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
+  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = non_web_component_files
+  in_files = [
+    "actions.ts",
+    "api_listener.ts",
+    "bookmarks.ts",
+    "browser_proxy.ts",
+    "constants.ts",
+    "debouncer.ts",
+    "dialog_focus_manager.ts",
+    "dnd_manager.ts",
+    "mouse_focus_behavior.ts",
+    "reducers.ts",
+    "store_client_mixin.ts",
+    "store.ts",
+    "types.ts",
+    "util.ts",
+  ]
 }
 
 preprocess_if_expr("preprocess_generated") {
   deps = [ ":web_components" ]
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = web_component_files
+  in_files = [
+    "app.ts",
+    "command_manager.ts",
+    "edit_dialog.ts",
+    "folder_node.ts",
+    "item.ts",
+    "list.ts",
+    "router.ts",
+    "shared_style.ts",
+    "shared_vars.ts",
+    "toolbar.ts",
+  ]
 }
 
 html_to_js("web_components") {
-  js_files = web_component_files
+  js_files = [
+    "app.ts",
+    "command_manager.ts",
+    "edit_dialog.ts",
+    "folder_node.ts",
+    "item.ts",
+    "list.ts",
+    "router.ts",
+    "shared_style.ts",
+    "shared_vars.ts",
+    "toolbar.ts",
+  ]
 }
 
 grit("resources") {
@@ -90,7 +126,32 @@
   out_dir = "$target_gen_dir/$tsc_folder"
   composite = true
   tsconfig_base = "tsconfig_base.json"
-  in_files = web_component_files + non_web_component_files
+  in_files = [
+    "actions.ts",
+    "api_listener.ts",
+    "app.ts",
+    "bookmarks.ts",
+    "browser_proxy.ts",
+    "command_manager.ts",
+    "constants.ts",
+    "debouncer.ts",
+    "dialog_focus_manager.ts",
+    "dnd_manager.ts",
+    "edit_dialog.ts",
+    "folder_node.ts",
+    "item.ts",
+    "list.ts",
+    "mouse_focus_behavior.ts",
+    "reducers.ts",
+    "router.ts",
+    "shared_vars.ts",
+    "shared_style.ts",
+    "store.ts",
+    "store_client_mixin.ts",
+    "toolbar.ts",
+    "types.ts",
+    "util.ts",
+  ]
   definitions = [
     "//tools/typescript/definitions/bookmarks.d.ts",
     "//tools/typescript/definitions/bookmark_manager_private.d.ts",
diff --git a/chrome/browser/resources/bookmarks/bookmarks.gni b/chrome/browser/resources/bookmarks/bookmarks.gni
deleted file mode 100644
index 65fa3bc..0000000
--- a/chrome/browser/resources/bookmarks/bookmarks.gni
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright 2021 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.
-
-# List of files that don't need to be passed to html_to_js().
-non_web_component_files = [
-  "actions.ts",
-  "api_listener.ts",
-  "bookmarks.ts",
-  "browser_proxy.ts",
-  "constants.ts",
-  "debouncer.ts",
-  "dialog_focus_manager.ts",
-  "dnd_manager.ts",
-  "mouse_focus_behavior.ts",
-  "reducers.ts",
-  "store_client_mixin.ts",
-  "store.ts",
-  "types.ts",
-  "util.ts",
-]
-
-# List of files that should be passed to html_to_js().
-web_component_files = [
-  "app.ts",
-  "command_manager.ts",
-  "edit_dialog.ts",
-  "folder_node.ts",
-  "item.ts",
-  "list.ts",
-  "router.ts",
-  "shared_style.ts",
-  "shared_vars.ts",
-  "toolbar.ts",
-]
diff --git a/chrome/browser/resources/downloads/BUILD.gn b/chrome/browser/resources/downloads/BUILD.gn
index e991634..2a1c5e8cd 100644
--- a/chrome/browser/resources/downloads/BUILD.gn
+++ b/chrome/browser/resources/downloads/BUILD.gn
@@ -9,7 +9,6 @@
 import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 import("../tools/optimize_webui.gni")
-import("downloads.gni")
 
 preprocess_folder = "preprocessed"
 
@@ -54,16 +53,28 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
+  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = non_web_component_files
+  in_files = [
+    "browser_proxy.ts",
+    "constants.ts",
+    "data.ts",
+    "downloads.ts",
+    "icon_loader.ts",
+    "search_service.ts",
+  ]
 }
 
 preprocess_if_expr("preprocess_web_components") {
   deps = [ ":web_components" ]
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = web_component_files
+  in_files = [
+    "icons.ts",
+    "item.ts",
+    "manager.ts",
+    "toolbar.ts",
+  ]
 }
 
 copy("copy_mojo") {
@@ -90,7 +101,12 @@
 }
 
 html_to_js("web_components") {
-  js_files = web_component_files
+  js_files = [
+    "icons.ts",
+    "item.ts",
+    "manager.ts",
+    "toolbar.ts",
+  ]
 }
 
 ts_library("build_ts") {
@@ -98,8 +114,19 @@
   out_dir = "$target_gen_dir/tsc"
   composite = true
   tsconfig_base = "tsconfig_base.json"
-  in_files = web_component_files + non_web_component_files +
-             [ "downloads.mojom-webui.js" ]
+  in_files = [
+    "browser_proxy.ts",
+    "constants.ts",
+    "data.ts",
+    "downloads.mojom-webui.js",
+    "downloads.ts",
+    "icon_loader.ts",
+    "icons.ts",
+    "item.ts",
+    "manager.ts",
+    "search_service.ts",
+    "toolbar.ts",
+  ]
   deps = [
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
diff --git a/chrome/browser/resources/downloads/downloads.gni b/chrome/browser/resources/downloads/downloads.gni
deleted file mode 100644
index e48b3784..0000000
--- a/chrome/browser/resources/downloads/downloads.gni
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2021 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.
-
-# List of files that don't need to be passed to html_to_js().
-non_web_component_files = [
-  "browser_proxy.ts",
-  "constants.ts",
-  "data.ts",
-  "downloads.ts",
-  "icon_loader.ts",
-  "search_service.ts",
-]
-
-# List of files that should be passed to html_to_js().
-web_component_files = [
-  "icons.ts",
-  "item.ts",
-  "manager.ts",
-  "toolbar.ts",
-]
diff --git a/chrome/browser/resources/extensions/BUILD.gn b/chrome/browser/resources/extensions/BUILD.gn
index 6e80a69..81705a7 100644
--- a/chrome/browser/resources/extensions/BUILD.gn
+++ b/chrome/browser/resources/extensions/BUILD.gn
@@ -11,7 +11,6 @@
 import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 import("../tools/optimize_webui.gni")
-import("extensions.gni")
 
 assert(enable_extensions, "enable extensions check failed")
 
@@ -52,20 +51,101 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
+  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = non_web_component_files
+  in_files = [
+    "drag_and_drop_handler.ts",
+    "extensions.ts",
+    "item_mixin.ts",
+    "item_util.ts",
+    "keyboard_shortcut_delegate.ts",
+    "navigation_helper.ts",
+    "service.ts",
+    "shortcut_util.ts",
+  ]
+
+  if (is_chromeos_ash) {
+    in_files += [ "kiosk_browser_proxy.ts" ]
+  }
 }
 
 preprocess_if_expr("preprocess_generated") {
   public_deps = [ ":web_components" ]
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = web_component_files
+  in_files = [
+    "code_section.ts",
+    "activity_log/activity_log_history_item.ts",
+    "activity_log/activity_log_history.ts",
+    "activity_log/activity_log.ts",
+    "activity_log/activity_log_stream_item.ts",
+    "activity_log/activity_log_stream.ts",
+    "detail_view.ts",
+    "drop_overlay.ts",
+    "error_page.ts",
+    "host_permissions_toggle_list.ts",
+    "icons.ts",
+    "install_warnings_dialog.ts",
+    "item.ts",
+    "item_list.ts",
+    "keyboard_shortcuts.ts",
+    "load_error.ts",
+    "manager.ts",
+    "options_dialog.ts",
+    "pack_dialog_alert.ts",
+    "pack_dialog.ts",
+    "runtime_host_permissions.ts",
+    "runtime_hosts_dialog.ts",
+    "shared_style.ts",
+    "shared_vars.ts",
+    "shortcut_input.ts",
+    "sidebar.ts",
+    "toggle_row.ts",
+    "toolbar.ts",
+  ]
+
+  if (is_chromeos_ash) {
+    in_files += [ "kiosk_dialog.ts" ]
+  }
 }
 
-html_to_js("web_components") {
-  js_files = web_component_files
+group("web_components") {
+  public_deps = [
+    ":web_components_local",
+    "activity_log:web_components",
+  ]
+}
+
+html_to_js("web_components_local") {
+  js_files = [
+    "code_section.ts",
+    "detail_view.ts",
+    "drop_overlay.ts",
+    "error_page.ts",
+    "host_permissions_toggle_list.ts",
+    "icons.ts",
+    "install_warnings_dialog.ts",
+    "item.ts",
+    "item_list.ts",
+    "keyboard_shortcuts.ts",
+    "load_error.ts",
+    "manager.ts",
+    "options_dialog.ts",
+    "pack_dialog.ts",
+    "pack_dialog_alert.ts",
+    "runtime_host_permissions.ts",
+    "runtime_hosts_dialog.ts",
+    "shared_style.ts",
+    "shared_vars.ts",
+    "shortcut_input.ts",
+    "sidebar.ts",
+    "toggle_row.ts",
+    "toolbar.ts",
+  ]
+
+  if (is_chromeos_ash) {
+    js_files += [ "kiosk_dialog.ts" ]
+  }
 }
 
 grit("resources") {
@@ -89,7 +169,44 @@
   root_dir = "$target_gen_dir/$preprocess_folder"
   out_dir = "$target_gen_dir/tsc"
   tsconfig_base = "tsconfig_base.json"
-  in_files = web_component_files + non_web_component_files
+  in_files = [
+    "activity_log/activity_log_history_item.ts",
+    "activity_log/activity_log_history.ts",
+    "activity_log/activity_log.ts",
+    "activity_log/activity_log_stream_item.ts",
+    "activity_log/activity_log_stream.ts",
+    "code_section.ts",
+    "detail_view.ts",
+    "drag_and_drop_handler.ts",
+    "drop_overlay.ts",
+    "error_page.ts",
+    "extensions.ts",
+    "host_permissions_toggle_list.ts",
+    "icons.ts",
+    "install_warnings_dialog.ts",
+    "item_mixin.ts",
+    "item.ts",
+    "item_list.ts",
+    "item_util.ts",
+    "keyboard_shortcut_delegate.ts",
+    "keyboard_shortcuts.ts",
+    "load_error.ts",
+    "manager.ts",
+    "navigation_helper.ts",
+    "options_dialog.ts",
+    "pack_dialog_alert.ts",
+    "pack_dialog.ts",
+    "runtime_host_permissions.ts",
+    "runtime_hosts_dialog.ts",
+    "service.ts",
+    "shared_style.ts",
+    "shared_vars.ts",
+    "shortcut_input.ts",
+    "shortcut_util.ts",
+    "sidebar.ts",
+    "toggle_row.ts",
+    "toolbar.ts",
+  ]
   definitions = [
     "//tools/typescript/definitions/activity_log_private.d.ts",
     "//tools/typescript/definitions/chrome_event.d.ts",
@@ -100,6 +217,10 @@
   ]
 
   if (is_chromeos_ash) {
+    in_files += [
+      "kiosk_browser_proxy.ts",
+      "kiosk_dialog.ts",
+    ]
     definitions += [ "//tools/typescript/definitions/chrome_send.d.ts" ]
   }
 
diff --git a/chrome/browser/resources/extensions/activity_log/BUILD.gn b/chrome/browser/resources/extensions/activity_log/BUILD.gn
new file mode 100644
index 0000000..7745d6b7
--- /dev/null
+++ b/chrome/browser/resources/extensions/activity_log/BUILD.gn
@@ -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.
+
+import("//tools/polymer/html_to_js.gni")
+
+html_to_js("web_components") {
+  js_files = [
+    "activity_log.ts",
+    "activity_log_history.ts",
+    "activity_log_history_item.ts",
+    "activity_log_stream.ts",
+    "activity_log_stream_item.ts",
+  ]
+}
diff --git a/chrome/browser/resources/extensions/extensions.gni b/chrome/browser/resources/extensions/extensions.gni
deleted file mode 100644
index 07319d1..0000000
--- a/chrome/browser/resources/extensions/extensions.gni
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright 2021 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("//build/config/chromeos/ui_mode.gni")
-
-# List of files that don't need to be passed to html_to_js().
-non_web_component_files = [
-  "drag_and_drop_handler.ts",
-  "extensions.ts",
-  "item_mixin.ts",
-  "item_util.ts",
-  "keyboard_shortcut_delegate.ts",
-  "navigation_helper.ts",
-  "service.ts",
-  "shortcut_util.ts",
-]
-
-if (is_chromeos_ash) {
-  non_web_component_files += [ "kiosk_browser_proxy.ts" ]
-}
-
-# List of files that should be passed to html_to_js().
-web_component_files = [
-  "activity_log/activity_log.ts",
-  "activity_log/activity_log_history.ts",
-  "activity_log/activity_log_history_item.ts",
-  "activity_log/activity_log_stream.ts",
-  "activity_log/activity_log_stream_item.ts",
-  "code_section.ts",
-  "detail_view.ts",
-  "drop_overlay.ts",
-  "error_page.ts",
-  "host_permissions_toggle_list.ts",
-  "icons.ts",
-  "install_warnings_dialog.ts",
-  "item.ts",
-  "item_list.ts",
-  "keyboard_shortcuts.ts",
-  "load_error.ts",
-  "manager.ts",
-  "options_dialog.ts",
-  "pack_dialog.ts",
-  "pack_dialog_alert.ts",
-  "runtime_host_permissions.ts",
-  "runtime_hosts_dialog.ts",
-  "shared_style.ts",
-  "shared_vars.ts",
-  "shortcut_input.ts",
-  "sidebar.ts",
-  "toggle_row.ts",
-  "toolbar.ts",
-]
-
-if (is_chromeos_ash) {
-  web_component_files += [ "kiosk_dialog.ts" ]
-}
diff --git a/chrome/browser/resources/history/BUILD.gn b/chrome/browser/resources/history/BUILD.gn
index b8a4118a..1957cd2 100644
--- a/chrome/browser/resources/history/BUILD.gn
+++ b/chrome/browser/resources/history/BUILD.gn
@@ -9,7 +9,6 @@
 import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 import("../tools/optimize_webui.gni")
-import("history.gni")
 
 preprocess_folder = "preprocessed"
 
@@ -67,16 +66,47 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
+  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = non_web_component_files
+  in_files = [
+    "browser_service.ts",
+    "constants.ts",
+    "externs.ts",
+    "history_clusters/browser_proxy.ts",
+    "history_clusters/metrics_proxy.ts",
+    "history_clusters/open_window_proxy.ts",
+    "history.ts",
+    "lazy_load.ts",
+    "query_manager.ts",
+    "searched_label.ts",
+  ]
 }
 
 preprocess_if_expr("preprocess_generated") {
   deps = [ ":web_components" ]
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = web_component_files
+  in_files = [
+    "app.ts",
+    "history_clusters/cluster.ts",
+    "history_clusters/clusters.ts",
+    "history_clusters/page_favicon.ts",
+    "history_clusters/search_query.ts",
+    "history_clusters/shared_style.ts",
+    "history_clusters/shared_vars.ts",
+    "history_clusters/top_visit.ts",
+    "history_clusters/url_visit.ts",
+    "history_item.ts",
+    "history_list.ts",
+    "history_toolbar.ts",
+    "router.ts",
+    "shared_icons.ts",
+    "shared_style.ts",
+    "shared_vars.ts",
+    "side_bar.ts",
+    "synced_device_card.ts",
+    "synced_device_manager.ts",
+  ]
 }
 
 grit("resources") {
@@ -96,15 +126,65 @@
   output_dir = "$root_gen_dir/chrome"
 }
 
-html_to_js("web_components") {
-  js_files = web_component_files
+html_to_js("web_components_local") {
+  js_files = [
+    "app.ts",
+    "history_item.ts",
+    "history_list.ts",
+    "history_toolbar.ts",
+    "router.ts",
+    "shared_icons.ts",
+    "shared_style.ts",
+    "shared_vars.ts",
+    "side_bar.ts",
+    "synced_device_card.ts",
+    "synced_device_manager.ts",
+  ]
+}
+
+group("web_components") {
+  public_deps = [
+    ":web_components_local",
+    "history_clusters:web_components",
+  ]
 }
 
 ts_library("build_ts") {
   root_dir = "$target_gen_dir/$preprocess_folder"
   out_dir = "$target_gen_dir/tsc"
   tsconfig_base = "tsconfig_base.json"
-  in_files = web_component_files + non_web_component_files
+  in_files = [
+    "app.ts",
+    "browser_service.ts",
+    "constants.ts",
+    "externs.ts",
+    "history_clusters/browser_proxy.ts",
+    "history_clusters/cluster.ts",
+    "history_clusters/clusters.ts",
+    "history_clusters/history_clusters.mojom-webui.js",
+    "history_clusters/metrics_proxy.ts",
+    "history_clusters/open_window_proxy.ts",
+    "history_clusters/page_favicon.ts",
+    "history_clusters/search_query.ts",
+    "history_clusters/shared_style.ts",
+    "history_clusters/shared_vars.ts",
+    "history_clusters/top_visit.ts",
+    "history_clusters/url_visit.ts",
+    "history_item.ts",
+    "history_list.ts",
+    "history_toolbar.ts",
+    "history.ts",
+    "lazy_load.ts",
+    "query_manager.ts",
+    "router.ts",
+    "searched_label.ts",
+    "shared_icons.ts",
+    "shared_style.ts",
+    "shared_vars.ts",
+    "side_bar.ts",
+    "synced_device_card.ts",
+    "synced_device_manager.ts",
+  ]
   deps = [
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
diff --git a/chrome/browser/resources/history/history.gni b/chrome/browser/resources/history/history.gni
deleted file mode 100644
index 96c38f5..0000000
--- a/chrome/browser/resources/history/history.gni
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2021 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.
-
-# List of files that don't need to be passed to html_to_js().
-non_web_component_files = [
-  "browser_service.ts",
-  "constants.ts",
-  "externs.ts",
-  "history_clusters/browser_proxy.ts",
-  "history_clusters/metrics_proxy.ts",
-  "history_clusters/open_window_proxy.ts",
-  "history.ts",
-  "lazy_load.ts",
-  "query_manager.ts",
-  "searched_label.ts",
-]
-
-# List of files that should be passed to html_to_js().
-web_component_files = [
-  "app.ts",
-  "history_clusters/cluster.ts",
-  "history_clusters/clusters.ts",
-  "history_clusters/page_favicon.ts",
-  "history_clusters/search_query.ts",
-  "history_clusters/shared_style.ts",
-  "history_clusters/shared_vars.ts",
-  "history_clusters/top_visit.ts",
-  "history_clusters/url_visit.ts",
-  "history_item.ts",
-  "history_list.ts",
-  "history_toolbar.ts",
-  "router.ts",
-  "shared_icons.ts",
-  "shared_style.ts",
-  "shared_vars.ts",
-  "side_bar.ts",
-  "synced_device_card.ts",
-  "synced_device_manager.ts",
-]
diff --git a/chrome/browser/resources/history/history_clusters/BUILD.gn b/chrome/browser/resources/history/history_clusters/BUILD.gn
new file mode 100644
index 0000000..47a7bc9b
--- /dev/null
+++ b/chrome/browser/resources/history/history_clusters/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2021 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("//tools/polymer/html_to_js.gni")
+
+html_to_js("web_components") {
+  js_files = [
+    "cluster.ts",
+    "clusters.ts",
+    "page_favicon.ts",
+    "search_query.ts",
+    "shared_style.ts",
+    "shared_vars.ts",
+    "top_visit.ts",
+    "url_visit.ts",
+  ]
+}
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
index 6ba6552..215a4074 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
@@ -193,21 +193,6 @@
     margin-inline-start: 12px;
   }
 
-  .tooltip-primary-account {
-    margin-inline-end: 12px;
-    margin-inline-start: 12px;
-  }
-
-  .no-accounts-message {
-    color: var(--cr-secondary-text-color);
-    padding: 20px 50px;
-    text-align: center;
-  }
-
-  .settings-box.border-bottom {
-    border-bottom: var(--cr-separator-line);
-  }
-
   #removeConfirmationButton {
     --active-shadow-action-rgb: var(--google-red-refresh-500-rgb);
     --bg-action: var(--google-red-600);
@@ -324,6 +309,14 @@
               aria-hidden="true">[[item.email]]</div>
         </div>
       </div>
+      <!-- Display ARC status -->
+      <template is="dom-if" if="[[isArcAccountRestrictionsEnabled_]]">
+        <span class="arc-availability secondary"
+            hidden$="[[item.isAvailableInArc]]">
+          <!-- TODO(crbug.com/1260909): Use real strings -->
+          Not shared with Android apps
+        </span>
+      </template>
       <template is="dom-if"
           if="[[shouldShowReauthenticationButton_(item)]]">
         <cr-button title="[[getAccountManagerSignedOutTitle_(item)]]"
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
index 37d8b566..48fd8e6 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
@@ -100,6 +100,18 @@
     },
 
     /**
+     * @return {boolean} True if `kArcAccountRestrictionsEnabled` feature is
+     *     enabled, false otherwise.
+     * @private
+     */
+    isArcAccountRestrictionsEnabled_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('arcAccountRestrictionsEnabled');
+      },
+    },
+
+    /**
      * Used by DeepLinkingBehavior to focus this page's deep links.
      * @type {!Set<!chromeos.settings.mojom.Setting>}
      */
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager_browser_proxy.js b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager_browser_proxy.js
index 760be34..ab3c36ae 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager_browser_proxy.js
@@ -18,6 +18,7 @@
  *   email: string,
  *   pic: string,
  *   organization: (string|undefined),
+ *   isAvailableInArc: boolean,
  * }}
  */
 export let Account;
diff --git a/chrome/browser/resources/signin/profile_picker/BUILD.gn b/chrome/browser/resources/signin/profile_picker/BUILD.gn
index 2d607f9..70a6bff8 100644
--- a/chrome/browser/resources/signin/profile_picker/BUILD.gn
+++ b/chrome/browser/resources/signin/profile_picker/BUILD.gn
@@ -10,7 +10,6 @@
 import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 import("../../tools/optimize_webui.gni")
-import("profile_picker.gni")
 
 assert(!is_chromeos_ash)
 
@@ -70,20 +69,37 @@
 }
 
 preprocess_if_expr("preprocess") {
-  in_folder = "."
+  in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = non_web_component_files
-}
-
-html_to_js("web_components") {
-  js_files = web_component_files
+  in_files = [
+    "ensure_lazy_loaded.ts",
+    "icons.ts",
+    "lazy_load.ts",
+    "manage_profiles_browser_proxy.ts",
+    "navigation_mixin.ts",
+    "policy_helper.ts",
+    "profile_picker.ts",
+  ]
 }
 
 preprocess_if_expr("preprocess_generated") {
   deps = [ ":web_components" ]
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = web_component_files
+  in_files = [
+    "profile_picker_app.ts",
+    "profile_picker_main_view.ts",
+    "profile_card.ts",
+    "profile_card_menu.ts",
+    "profile_creation_flow/local_profile_customization.ts",
+    "profile_creation_flow/profile_type_choice.ts",
+    "profile_picker_shared_css.ts",
+    "profile_creation_flow/shared_css.ts",
+    "profile_switch.ts",
+  ]
+  if (is_chromeos_lacros) {
+    in_files += [ "profile_creation_flow/account_selection_lacros.ts" ]
+  }
 }
 
 grit("resources") {
@@ -108,7 +124,27 @@
   out_dir = "$target_gen_dir/tsc"
   composite = true
   tsconfig_base = "tsconfig_base.json"
-  in_files = web_component_files + non_web_component_files
+  in_files = [
+    "ensure_lazy_loaded.ts",
+    "icons.ts",
+    "lazy_load.ts",
+    "manage_profiles_browser_proxy.ts",
+    "navigation_mixin.ts",
+    "policy_helper.ts",
+    "profile_card.ts",
+    "profile_card_menu.ts",
+    "profile_creation_flow/local_profile_customization.ts",
+    "profile_creation_flow/profile_type_choice.ts",
+    "profile_creation_flow/shared_css.ts",
+    "profile_picker_app.ts",
+    "profile_picker.ts",
+    "profile_picker_main_view.ts",
+    "profile_picker_shared_css.ts",
+    "profile_switch.ts",
+  ]
+  if (is_chromeos_lacros) {
+    in_files += [ "profile_creation_flow/account_selection_lacros.ts" ]
+  }
   definitions = [
     "//tools/typescript/definitions/chrome_send.d.ts",
     "//tools/typescript/definitions/metrics_private.d.ts",
@@ -124,3 +160,21 @@
     ":preprocess_generated",
   ]
 }
+
+group("web_components") {
+  public_deps = [
+    ":web_components_local",
+    "profile_creation_flow:web_components",
+  ]
+}
+
+html_to_js("web_components_local") {
+  js_files = [
+    "profile_card.ts",
+    "profile_card_menu.ts",
+    "profile_picker_app.ts",
+    "profile_picker_main_view.ts",
+    "profile_picker_shared_css.ts",
+    "profile_switch.ts",
+  ]
+}
diff --git a/chrome/browser/resources/signin/profile_picker/profile_creation_flow/BUILD.gn b/chrome/browser/resources/signin/profile_picker/profile_creation_flow/BUILD.gn
new file mode 100644
index 0000000..e1550d1
--- /dev/null
+++ b/chrome/browser/resources/signin/profile_picker/profile_creation_flow/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2020 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("//build/config/chromeos/ui_mode.gni")
+import("//tools/polymer/html_to_js.gni")
+
+html_to_js("web_components") {
+  js_files = [
+    "profile_type_choice.ts",
+    "local_profile_customization.ts",
+    "shared_css.ts",
+  ]
+  if (is_chromeos_lacros) {
+    js_files += [ "account_selection_lacros.ts" ]
+  }
+}
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker.gni b/chrome/browser/resources/signin/profile_picker/profile_picker.gni
deleted file mode 100644
index 6b8ce7e..0000000
--- a/chrome/browser/resources/signin/profile_picker/profile_picker.gni
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright 2021 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("//build/config/chromeos/ui_mode.gni")
-
-# List of files that don't need to be passed to html_to_js().
-non_web_component_files = [
-  "ensure_lazy_loaded.ts",
-  "icons.ts",
-  "lazy_load.ts",
-  "manage_profiles_browser_proxy.ts",
-  "navigation_mixin.ts",
-  "policy_helper.ts",
-  "profile_picker.ts",
-]
-
-# List of files that should be passed to html_to_js().
-web_component_files = [
-  "profile_card_menu.ts",
-  "profile_card.ts",
-  "profile_creation_flow/local_profile_customization.ts",
-  "profile_creation_flow/profile_type_choice.ts",
-  "profile_creation_flow/shared_css.ts",
-  "profile_picker_app.ts",
-  "profile_picker_main_view.ts",
-  "profile_picker_shared_css.ts",
-  "profile_switch.ts",
-]
-
-if (is_chromeos_lacros) {
-  web_component_files += [ "profile_creation_flow/account_selection_lacros.ts" ]
-}
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc
index a4b434e..5fd5c3f 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc
@@ -429,9 +429,17 @@
   helper_.CheckAppInListNotLocallyInstalled("SiteA");
 }
 
+// TODO(crbug.com/1282608): Test is flaky on Mac.
+#if defined(OS_MAC)
+#define MAYBE_WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA \
+  DISABLED_WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA
+#else
+#define MAYBE_WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA \
+  WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA
+#endif
 IN_PROC_BROWSER_TEST_F(
     TwoClientWebAppsIntegrationTestMacWinLinux,
-    WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA) {
+    MAYBE_WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
diff --git a/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.cc b/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.cc
index 94f91b4..8f41575 100644
--- a/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.cc
@@ -57,8 +57,8 @@
         return;
       case apps::mojom::Readiness::kDisabledByPolicy:
         if (update.ShowInShelfChanged()) {
-          OnShowInShelfChanged(app_id, (update.ShowInShelf() ==
-                                        apps::mojom::OptionalBool::kTrue));
+          OnShowInShelfChangedForAppDisabledByPolicy(
+              app_id, update.ShowInShelf() == apps::mojom::OptionalBool::kTrue);
         } else {
           delegate()->OnAppUpdated(browser_context(), app_id,
                                    /*reload_icon=*/true);
@@ -74,15 +74,19 @@
   if (update.PolicyIdChanged())
     delegate()->OnAppInstalled(browser_context(), app_id);
 
-  if (update.PausedChanged()) {
-    delegate()->OnAppUpdated(browser_context(), app_id, /*reload_icon=*/true);
-    return;
+  if (update.ShowInShelfChanged()) {
+    if (update.Readiness() == apps::mojom::Readiness::kDisabledByPolicy) {
+      OnShowInShelfChangedForAppDisabledByPolicy(
+          app_id, update.ShowInShelf() == apps::mojom::OptionalBool::kTrue);
+    } else {
+      delegate()->OnAppShowInShelfChanged(
+          browser_context(), app_id,
+          update.ShowInShelf() != apps::mojom::OptionalBool::kFalse);
+    }
   }
 
-  if (update.ShowInShelfChanged() &&
-      update.Readiness() == apps::mojom::Readiness::kDisabledByPolicy) {
-    OnShowInShelfChanged(
-        app_id, (update.ShowInShelf() == apps::mojom::OptionalBool::kTrue));
+  if (update.PausedChanged()) {
+    delegate()->OnAppUpdated(browser_context(), app_id, /*reload_icon=*/true);
     return;
   }
 
@@ -95,8 +99,9 @@
   Observe(nullptr);
 }
 
-void ShelfAppServiceAppUpdater::OnShowInShelfChanged(const std::string& app_id,
-                                                     bool show_in_shelf) {
+void ShelfAppServiceAppUpdater::OnShowInShelfChangedForAppDisabledByPolicy(
+    const std::string& app_id,
+    bool show_in_shelf) {
   std::set<std::string>::const_iterator it = installed_apps_.find(app_id);
   if (show_in_shelf) {
     if (it == installed_apps_.end()) {
diff --git a/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.h b/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.h
index f20f96a..b8ca7a5 100644
--- a/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.h
+++ b/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.h
@@ -34,7 +34,8 @@
       apps::AppRegistryCache* cache) override;
 
  private:
-  void OnShowInShelfChanged(const std::string& app_id, bool show_in_shelf);
+  void OnShowInShelfChangedForAppDisabledByPolicy(const std::string& app_id,
+                                                  bool show_in_shelf);
   std::set<std::string> installed_apps_;
 };
 
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
index 3034c91..65bc8f6 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
@@ -862,6 +862,12 @@
 void ChromeShelfController::OnAppInstalled(
     content::BrowserContext* browser_context,
     const std::string& app_id) {
+  if (IsAppPinned(app_id) &&
+      ShelfControllerHelper::IsAppHiddenFromShelf(profile(), app_id)) {
+    ScopedPinSyncDisabler scoped_pin_sync_disabler = GetScopedPinSyncDisabler();
+    UnpinShelfItemInternal(ash::ShelfID(app_id));
+  }
+
   // When the app is pinned to the shelf, or added to the shelf, the app
   // probably isn't ready in AppService, so set the title, and load the icon
   // again on callback when the app is ready in AppService.
@@ -936,6 +942,64 @@
   }
 }
 
+void ChromeShelfController::OnAppShowInShelfChanged(
+    content::BrowserContext* browser_context,
+    const std::string& app_id,
+    bool show_in_shelf) {
+  if (browser_context != profile())
+    return;
+
+  ScopedPinSyncDisabler scoped_pin_sync_disabler = GetScopedPinSyncDisabler();
+
+  // If the app should be hidden from shelf, make sure it gets unpinned.
+  if (!show_in_shelf) {
+    if (IsAppPinned(app_id))
+      UnpinShelfItemInternal(ash::ShelfID(app_id));
+    return;
+  }
+
+  // If the app status changed to "shown in shelf", pin the app if user prefs
+  // (or policy) indicate that the app should be pinned.
+  const std::vector<ash::ShelfID> pinned_apps =
+      shelf_prefs_->GetPinnedAppsFromSync(shelf_controller_helper_.get());
+
+  // Find the app index within pinned apps.
+  int index = -1;
+  for (size_t i = 0; i < pinned_apps.size(); ++i) {
+    if (pinned_apps[i].app_id == app_id) {
+      index = i;
+      break;
+    }
+  }
+
+  // The app should not be pinned - nothing left to do.
+  if (index == -1)
+    return;
+
+  // Update apps icon if applicable.
+  OnAppUpdated(profile(), app_id, /*reload_icon=*/true);
+
+  // Calculate the target app index within the model - find the last app in
+  // `pinned_apps` that precedes `app_id`, and is in shelf model.
+  int target_index_in_model = 0;
+  for (int i = index - 1; i >= 0; --i) {
+    int index_in_model = model_->ItemIndexByID(pinned_apps[i]);
+    if (index_in_model >= 0 &&
+        ItemTypeIsPinned(model_->items()[index_in_model])) {
+      target_index_in_model = index_in_model + 1;
+      break;
+    }
+  }
+
+  EnsureAppPinnedInModelAtIndex(app_id, model_->ItemIndexByAppID(app_id),
+                                target_index_in_model);
+
+  // Set the pinned by policy flag.
+  const int final_index = model_->ItemIndexByAppID(app_id);
+  if (final_index >= 0)
+    UpdatePinnedByPolicyForItemAtIndex(final_index);
+}
+
 void ChromeShelfController::OnAppUninstalledPrepared(
     content::BrowserContext* browser_context,
     const std::string& app_id,
@@ -1160,9 +1224,6 @@
   // cyclically trigger sync changes (eg. ShelfItemAdded calls SyncPinPosition).
   ScopedPinSyncDisabler scoped_pin_sync_disabler = GetScopedPinSyncDisabler();
 
-  apps::AppServiceProxy* proxy =
-      apps::AppServiceProxyFactory::GetForProfile(profile());
-
   const std::vector<ash::ShelfID> pinned_apps =
       shelf_prefs_->GetPinnedAppsFromSync(shelf_controller_helper_.get());
 
@@ -1172,17 +1233,13 @@
   // pin, move existing pin to current position specified by |index| or create
   // the new pin at that position.
   for (const auto& pref_shelf_id : pinned_apps) {
+    const std::string app_id = pref_shelf_id.app_id;
     // Do not show apps in the shelf if they are explicitly forbidden.
-    bool hide = false;
-    proxy->AppRegistryCache().ForOneApp(
-        pref_shelf_id.app_id, [&hide](const apps::AppUpdate& update) {
-          hide = update.ShowInShelf() == apps::mojom::OptionalBool::kFalse;
-        });
-    if (hide)
+    if (ShelfControllerHelper::IsAppHiddenFromShelf(profile(), app_id))
       continue;
 
     // Update apps icon if applicable.
-    OnAppUpdated(profile(), pref_shelf_id.app_id, /*reload_icon=*/true);
+    OnAppUpdated(profile(), app_id, /*reload_icon=*/true);
 
     // Find existing pin or app from the right of current |index|.
     int app_index = index;
@@ -1191,37 +1248,14 @@
       if (item.id == pref_shelf_id)
         break;
     }
-    if (app_index < model_->item_count()) {
-      DCHECK_EQ(model_->ItemIndexByID(pref_shelf_id), app_index);
 
-      // Found existing pin or running app.
-      const ash::ShelfItem item = model_->items()[app_index];
-      if (ItemTypeIsPinned(item)) {
-        // Just move to required position or keep it inplace.
-        model_->Move(app_index, index);
-      } else {
-        PinRunningAppInternal(index, item.id);
-      }
-      DCHECK_EQ(model_->ItemIndexByID(item.id), index);
+    const bool item_pinned = EnsureAppPinnedInModelAtIndex(
+        app_id,
+        /*current_index=*/app_index < model_->item_count() ? app_index : -1,
+        /*target_index=*/index);
+
+    if (item_pinned)
       ++index;
-    } else {
-      DCHECK_EQ(model_->ItemIndexByID(pref_shelf_id), -1);
-      // app_id may be kChromeAppId. This happens when sync happens,
-      // but Lacros becomes the primary browser so that the browser
-      // shortcut is unpinned. Do nothing then.
-      if (pref_shelf_id.app_id != kChromeAppId) {
-        // We need to create a new pin for a synced app.
-        ash::ShelfItem item;
-        std::unique_ptr<ash::ShelfItemDelegate> item_delegate;
-        bool success = shelf_item_factory()->CreateShelfItemForAppId(
-            pref_shelf_id.app_id, &item, &item_delegate);
-        if (success) {
-          InsertAppItem(std::move(item_delegate), ash::STATUS_CLOSED, index,
-                        ash::TYPE_PINNED_APP, /*title=*/std::u16string());
-          ++index;
-        }
-      }
-    }
   }
 
   // At second step remove any pin to the right from the current index.
@@ -1236,16 +1270,60 @@
   UpdatePolicyPinnedAppsFromPrefs();
 }
 
-void ChromeShelfController::UpdatePolicyPinnedAppsFromPrefs() {
-  for (int index = 0; index < model_->item_count(); index++) {
-    ash::ShelfItem item = model_->items()[index];
-    const bool pinned_by_policy =
-        GetPinnableForAppID(item.id.app_id, profile()) ==
-        AppListControllerDelegate::PIN_FIXED;
-    if (item.pinned_by_policy != pinned_by_policy) {
-      item.pinned_by_policy = pinned_by_policy;
-      model_->Set(index, item);
+bool ChromeShelfController::EnsureAppPinnedInModelAtIndex(
+    const std::string& app_id,
+    int current_index,
+    int target_index) {
+  // Passing current app index in model as an argument is an optimization in
+  // case this method is used while looping over items that avoids extra pass
+  // over items in the model to find the app index.
+  DCHECK_EQ(current_index, model_->ItemIndexByAppID(app_id));
+
+  if (current_index >= 0) {
+    const ash::ShelfItem item = model_->items()[current_index];
+    if (ItemTypeIsPinned(item)) {
+      model_->Move(current_index, target_index);
+    } else {
+      PinRunningAppInternal(target_index, item.id);
     }
+    DCHECK_EQ(model_->ItemIndexByID(item.id), target_index);
+    return true;
+  }
+
+  // app_id may be kChromeAppId. This happens when sync happens,
+  // but Lacros becomes the primary browser so that the browser
+  // shortcut is unpinned. Do nothing then.
+  if (app_id == kChromeAppId)
+    return false;
+
+  // We need to create a new pin for a synced app.
+  ash::ShelfItem item;
+  std::unique_ptr<ash::ShelfItemDelegate> item_delegate;
+  if (!shelf_item_factory()->CreateShelfItemForAppId(app_id, &item,
+                                                     &item_delegate)) {
+    return false;
+  }
+
+  InsertAppItem(std::move(item_delegate), ash::STATUS_CLOSED, target_index,
+                ash::TYPE_PINNED_APP,
+                /*title=*/std::u16string());
+  return true;
+}
+
+void ChromeShelfController::UpdatePolicyPinnedAppsFromPrefs() {
+  for (int index = 0; index < model_->item_count(); index++)
+    UpdatePinnedByPolicyForItemAtIndex(index);
+}
+
+void ChromeShelfController::UpdatePinnedByPolicyForItemAtIndex(
+    int model_index) {
+  ash::ShelfItem item = model_->items()[model_index];
+  const bool pinned_by_policy =
+      GetPinnableForAppID(item.id.app_id, profile()) ==
+      AppListControllerDelegate::PIN_FIXED;
+  if (item.pinned_by_policy != pinned_by_policy) {
+    item.pinned_by_policy = pinned_by_policy;
+    model_->Set(model_index, item);
   }
 }
 
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.h b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.h
index f1d4a7b..83e0307 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.h
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.h
@@ -293,6 +293,9 @@
   void OnAppUpdated(content::BrowserContext* browser_context,
                     const std::string& app_id,
                     bool reload_icon) override;
+  void OnAppShowInShelfChanged(content::BrowserContext* browser_context,
+                               const std::string& app_id,
+                               bool show_in_shelf) override;
   void OnAppUninstalledPrepared(content::BrowserContext* browser_context,
                                 const std::string& app_id,
                                 bool by_migration) override;
@@ -352,12 +355,27 @@
   // Re-syncs shelf model.
   void UpdatePinnedAppsFromSync();
 
+  // Makes sure that the app with `app_id` is pinned at `target_index` within
+  // shelf model if possible. `current_index` is the current app index in the
+  // shelf model.
+  // If the app is not in the model, the value should be -1.
+  // Returns whether the app got pinned - for example, this may fail for chrome
+  // app if it's not present in the shelf, or if creating a shelf item for the
+  // app fails.
+  bool EnsureAppPinnedInModelAtIndex(const std::string& app_id,
+                                     int current_index,
+                                     int target_index);
+
   // Schedules re-sync of shelf model.
   void ScheduleUpdatePinnedAppsFromSync();
 
   // Update the policy-pinned flag for each shelf item.
   void UpdatePolicyPinnedAppsFromPrefs();
 
+  // Updates the policy-pinned flag for shelf item at `model_index` in shelf
+  // model.
+  void UpdatePinnedByPolicyForItemAtIndex(int model_index);
+
   // Returns the shelf item status for the given |app_id|, which can be either
   // STATUS_RUNNING (if there is such an app) or STATUS_CLOSED.
   ash::ShelfItemStatus GetAppState(const std::string& app_id);
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
index 8a11f410..4fb5faa 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
@@ -5384,6 +5384,161 @@
   EXPECT_EQ(ash::AppStatus::kReady, model_->items()[1].app_status);
 }
 
+TEST_P(ChromeShelfControllerTest, PinnedAppsRespectShownInShelfState) {
+  InitShelfController();
+  // Pin a test app.
+  AddExtension(extension1_.get());
+
+  PinAppWithIDToShelf(extension1_->id());
+  EXPECT_EQ(2, model_->item_count());
+  EXPECT_TRUE(shelf_controller_->IsAppPinned(extension1_->id()));
+  EXPECT_EQ(1, model_->ItemIndexByAppID(extension1_->id()));
+
+  // Update the app so it's considered not shown in shelf, and verify it's no
+  // longer pinned.
+  UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+  EXPECT_FALSE(shelf_controller_->IsAppPinned(extension1_->id()));
+
+  // Update the app so it's allowed in shelf again, verify it gets pinned.
+  UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kTrue);
+  EXPECT_TRUE(model_->IsAppPinned(extension1_->id()));
+  EXPECT_TRUE(model_->AllowedToSetAppPinState(extension1_->id(), false));
+  EXPECT_EQ(1, model_->ItemIndexByAppID(extension1_->id()));
+}
+
+TEST_P(ChromeShelfControllerTest, AppIndexAfterUnhidingFirstPinnedApp) {
+  InitShelfController();
+  // Pin a test app.
+  AddExtension(extension1_.get());
+
+  PinAppWithIDToShelf(extension1_->id());
+  EXPECT_EQ(2, model_->item_count());
+  EXPECT_TRUE(shelf_controller_->IsAppPinned(extension1_->id()));
+  EXPECT_EQ(1, model_->ItemIndexByAppID(extension1_->id()));
+  model_->Move(1, 0);
+  EXPECT_EQ(0, model_->ItemIndexByAppID(extension1_->id()));
+
+  // Update the app so it's considered not shown in shelf, and verify it's no
+  // longer pinned.
+  UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+  EXPECT_FALSE(shelf_controller_->IsAppPinned(extension1_->id()));
+
+  // Update the app so it's allowed in shelf again, verify it gets pinned.
+  UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kTrue);
+  EXPECT_TRUE(model_->IsAppPinned(extension1_->id()));
+  EXPECT_TRUE(model_->AllowedToSetAppPinState(extension1_->id(), false));
+  EXPECT_EQ(0, model_->ItemIndexByAppID(extension1_->id()));
+}
+
+TEST_P(ChromeShelfControllerTest,
+       AppIndexAfterUnhidingtPinnedAppWithOtherHiddenApps) {
+  InitShelfController();
+  // Pin test apps.
+  AddExtension(extension1_.get());
+  AddExtension(extension2_.get());
+  AddExtension(extension5_.get());
+  AddExtension(extension6_.get());
+
+  PinAppWithIDToShelf(extension1_->id());
+  PinAppWithIDToShelf(extension2_->id());
+  PinAppWithIDToShelf(extension5_->id());
+  PinAppWithIDToShelf(extension6_->id());
+  EXPECT_EQ(5, model_->item_count());
+  EXPECT_TRUE(shelf_controller_->IsAppPinned(extension2_->id()));
+  EXPECT_EQ(2, model_->ItemIndexByAppID(extension2_->id()));
+
+  // Update all test apps so they're considered not shown in shelf, and verify
+  // it's no longer pinned.
+  UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+  UpdateAppRegistryCache(profile(), extension2_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+  UpdateAppRegistryCache(profile(), extension5_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+  EXPECT_FALSE(shelf_controller_->IsAppPinned(extension2_->id()));
+
+  // Update app 2 so it's allowed in shelf again, verify it gets pinned.
+  UpdateAppRegistryCache(profile(), extension2_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kTrue);
+  EXPECT_TRUE(model_->IsAppPinned(extension2_->id()));
+  EXPECT_TRUE(model_->AllowedToSetAppPinState(extension2_->id(), false));
+  EXPECT_EQ(1, model_->ItemIndexByAppID(extension2_->id()));
+  EXPECT_EQ(2, model_->ItemIndexByAppID(extension6_->id()));
+}
+
+TEST_P(ChromeShelfControllerTest, AppsHiddenFromShelfDontGetPinnedByPolicy) {
+  AddExtension(extension1_.get());
+
+  // Pin a test app by policy.
+  base::ListValue policy_value;
+  AppendPrefValue(&policy_value, extension1_->id());
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kPolicyPinnedLauncherApps,
+      base::Value::ToUniquePtrValue(policy_value.Clone()));
+
+  InitShelfController();
+  EXPECT_EQ(2, model_->item_count());
+  EXPECT_TRUE(model_->IsAppPinned(extension1_->id()));
+  EXPECT_EQ(1, model_->ItemIndexByAppID(extension1_->id()));
+
+  // Update the app so it's considered not shown in shelf, and verify it's no
+  // longer pinned.
+  UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+  EXPECT_FALSE(model_->IsAppPinned(extension1_->id()));
+
+  // Update the app so it's allowed in shelf again, verify it gets pinned.
+  UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kTrue);
+  EXPECT_TRUE(model_->IsAppPinned(extension1_->id()));
+  EXPECT_FALSE(model_->AllowedToSetAppPinState(extension1_->id(), false));
+  EXPECT_EQ(1, model_->ItemIndexByAppID(extension1_->id()));
+}
+
+TEST_P(ChromeShelfControllerTest, AppHiddenFromShelfNotPinnedOnInstall) {
+  AddExtension(extension1_.get());
+  InitShelfController();
+  PinAppWithIDToShelf(extension1_->id());
+  EXPECT_EQ(2, model_->item_count());
+  EXPECT_TRUE(model_->IsAppPinned(extension1_->id()));
+  EXPECT_EQ(1, model_->ItemIndexByAppID(extension1_->id()));
+
+  // Block the extension so it gets removed from shelf.
+  UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/true,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+  EXPECT_FALSE(model_->IsAppPinned(extension1_->id()));
+
+  // Unblock the extension, but mark it as not shown in shelf - verify it
+  // doesn't get pinned/added to shelf.
+  UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kFalse);
+  EXPECT_FALSE(model_->IsAppPinned(extension1_->id()));
+
+  // Allow the app to be shown in shelf, and verify it gets pinned again.
+  UpdateAppRegistryCache(profile(), extension1_->id(), /*block=*/false,
+                         /*pause=*/false,
+                         /*show_in_shelf=*/apps::mojom::OptionalBool::kTrue);
+  EXPECT_TRUE(model_->IsAppPinned(extension1_->id()));
+  EXPECT_TRUE(model_->AllowedToSetAppPinState(extension1_->id(), false));
+  EXPECT_EQ(1, model_->ItemIndexByAppID(extension1_->id()));
+}
+
 INSTANTIATE_TEST_SUITE_P(
     /* no label */,
     ChromeShelfControllerTest,
diff --git a/chrome/browser/ui/ash/shelf/shelf_app_updater.h b/chrome/browser/ui/ash/shelf/shelf_app_updater.h
index 82b04ff..8391464 100644
--- a/chrome/browser/ui/ash/shelf/shelf_app_updater.h
+++ b/chrome/browser/ui/ash/shelf/shelf_app_updater.h
@@ -21,6 +21,10 @@
     virtual void OnAppUpdated(content::BrowserContext* browser_context,
                               const std::string& app_id,
                               bool reload_icon) {}
+    virtual void OnAppShowInShelfChanged(
+        content::BrowserContext* browser_context,
+        const std::string& app_id,
+        bool show_in_shelf) {}
     virtual void OnAppUninstalledPrepared(
         content::BrowserContext* browser_context,
         const std::string& app_id,
diff --git a/chrome/browser/ui/ash/shelf/shelf_controller_helper.cc b/chrome/browser/ui/ash/shelf/shelf_controller_helper.cc
index 365a0678..8a44d9c8 100644
--- a/chrome/browser/ui/ash/shelf/shelf_controller_helper.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_controller_helper.cc
@@ -119,6 +119,22 @@
   return status;
 }
 
+// static
+bool ShelfControllerHelper::IsAppHiddenFromShelf(Profile* profile,
+                                                 const std::string& app_id) {
+  if (!apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile))
+    return false;
+
+  bool hidden = false;
+  apps::AppServiceProxyFactory::GetForProfile(profile)
+      ->AppRegistryCache()
+      .ForOneApp(app_id, [&hidden](const apps::AppUpdate& update) {
+        hidden = update.ShowInShelf() == apps::mojom::OptionalBool::kFalse;
+      });
+
+  return hidden;
+}
+
 std::string ShelfControllerHelper::GetAppID(content::WebContents* tab) {
   DCHECK(tab);
   return apps::GetInstanceAppIdForWebContents(tab).value_or(std::string());
diff --git a/chrome/browser/ui/ash/shelf/shelf_controller_helper.h b/chrome/browser/ui/ash/shelf/shelf_controller_helper.h
index 21b71d77..d75aa2fe 100644
--- a/chrome/browser/ui/ash/shelf/shelf_controller_helper.h
+++ b/chrome/browser/ui/ash/shelf/shelf_controller_helper.h
@@ -41,6 +41,10 @@
   static ash::AppStatus GetAppStatus(Profile* profile,
                                      const std::string& app_id);
 
+  // Helper function to return whether the app with `app_id` should explicitly
+  // be hidden from shelf, as indicated by `AppUpdate::ShowInShelf()` app state.
+  static bool IsAppHiddenFromShelf(Profile* profile, const std::string& app_id);
+
   // Returns the app id of the specified tab, or an empty string if there is
   // no app. All known profiles will be queried for this.
   virtual std::string GetAppID(content::WebContents* tab);
diff --git a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
index a432e07..e265c0e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
@@ -14,6 +14,7 @@
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/enterprise/util/managed_browser_utils.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
@@ -143,6 +144,11 @@
     return *this;
   }
 
+  AccountBuilder& SetIsAvailableInArc(bool value) {
+    account_.SetBoolKey("isAvailableInArc", value);
+    return *this;
+  }
+
   // Should be called only once.
   base::DictionaryValue Build() {
     // Check that values were set.
@@ -154,6 +160,9 @@
     DCHECK(account_.FindBoolKey("isSignedIn"));
     DCHECK(account_.FindBoolKey("unmigrated"));
     DCHECK(account_.FindStringKey("pic"));
+    if (ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled()) {
+      DCHECK(account_.FindBoolKey("isAvailableInArc"));
+    }
     // "organization" is an optional field.
 
     return std::move(account_);
@@ -168,13 +177,18 @@
 AccountManagerUIHandler::AccountManagerUIHandler(
     account_manager::AccountManager* account_manager,
     account_manager::AccountManagerFacade* account_manager_facade,
-    signin::IdentityManager* identity_manager)
+    signin::IdentityManager* identity_manager,
+    ash::AccountAppsAvailability* account_apps_availability)
     : account_manager_(account_manager),
       account_manager_facade_(account_manager_facade),
       identity_manager_(identity_manager) {
   DCHECK(account_manager_);
   DCHECK(account_manager_facade_);
   DCHECK(identity_manager_);
+  if (ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled()) {
+    account_apps_availability_ = account_apps_availability;
+    DCHECK(account_apps_availability_);
+  }
 }
 
 AccountManagerUIHandler::~AccountManagerUIHandler() = default;
@@ -232,13 +246,30 @@
     base::Value callback_id,
     const std::vector<std::pair<::account_manager::Account, bool>>&
         account_dummy_token_list) {
+  if (ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled()) {
+    account_apps_availability_->GetAccountsAvailableInArc(
+        base::BindOnce(&AccountManagerUIHandler::FinishHandleGetAccounts,
+                       weak_factory_.GetWeakPtr(), std::move(callback_id),
+                       std::move(account_dummy_token_list)));
+    return;
+  }
+  FinishHandleGetAccounts(std::move(callback_id),
+                          std::move(account_dummy_token_list),
+                          base::flat_set<account_manager::Account>());
+}
+
+void AccountManagerUIHandler::FinishHandleGetAccounts(
+    base::Value callback_id,
+    const std::vector<std::pair<::account_manager::Account, bool>>&
+        account_dummy_token_list,
+    const base::flat_set<account_manager::Account>& arc_accounts) {
   user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile_);
   DCHECK(user);
 
   base::DictionaryValue gaia_device_account;
-  base::ListValue accounts =
-      GetSecondaryGaiaAccounts(account_dummy_token_list, user->GetAccountId(),
-                               profile_->IsChild(), &gaia_device_account);
+  base::ListValue accounts = GetSecondaryGaiaAccounts(
+      account_dummy_token_list, arc_accounts, user->GetAccountId(),
+      profile_->IsChild(), &gaia_device_account);
 
   AccountBuilder device_account;
   if (user->IsActiveDirectoryUser()) {
@@ -249,6 +280,9 @@
         .SetFullName(base::UTF16ToUTF8(user->GetDisplayName()))
         .SetIsSignedIn(true)
         .SetUnmigrated(false);
+    if (ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled()) {
+      device_account.SetIsAvailableInArc(true);
+    }
     gfx::ImageSkia default_icon =
         *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
             IDR_LOGIN_DEFAULT_USER);
@@ -288,6 +322,7 @@
 base::ListValue AccountManagerUIHandler::GetSecondaryGaiaAccounts(
     const std::vector<std::pair<::account_manager::Account, bool>>&
         account_dummy_token_list,
+    const base::flat_set<account_manager::Account>& arc_accounts,
     const AccountId device_account_id,
     const bool is_child_user,
     base::DictionaryValue* device_account) {
@@ -319,6 +354,9 @@
         .SetIsSignedIn(!identity_manager_
                             ->HasAccountWithRefreshTokenInPersistentErrorState(
                                 maybe_account_info.account_id));
+    if (ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled()) {
+      account.SetIsAvailableInArc(arc_accounts.contains(stored_account));
+    }
 
     if (!maybe_account_info.account_image.IsEmpty()) {
       account.SetPic(
@@ -410,11 +448,17 @@
 void AccountManagerUIHandler::OnJavascriptAllowed() {
   account_manager_facade_observation_.Observe(account_manager_facade_);
   identity_manager_observation_.Observe(identity_manager_);
+  if (account_apps_availability_) {
+    account_apps_availability_observation_.Observe(account_apps_availability_);
+  }
 }
 
 void AccountManagerUIHandler::OnJavascriptDisallowed() {
   account_manager_facade_observation_.Reset();
   identity_manager_observation_.Reset();
+  if (account_apps_availability_) {
+    account_apps_availability_observation_.Reset();
+  }
 }
 
 // |AccountManagerFacade::Observer| overrides. Note: We need to listen on
@@ -458,6 +502,16 @@
   }
 }
 
+void AccountManagerUIHandler::OnAccountAvailableInArc(
+    const ::account_manager::Account& account) {
+  RefreshUI();
+}
+
+void AccountManagerUIHandler::OnAccountUnavailableInArc(
+    const ::account_manager::Account& account) {
+  RefreshUI();
+}
+
 void AccountManagerUIHandler::RefreshUI() {
   FireWebUIListener("accounts-changed");
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h
index 76992b0..eb11b64 100644
--- a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "components/account_id/account_id.h"
 #include "components/account_manager_core/account.h"
@@ -24,7 +25,8 @@
 class AccountManagerUIHandler
     : public ::settings::SettingsPageUIHandler,
       public account_manager::AccountManagerFacade::Observer,
-      public signin::IdentityManager::Observer {
+      public signin::IdentityManager::Observer,
+      public ash::AccountAppsAvailability::Observer {
  public:
   // Accepts non-owning pointers to |account_manager::AccountManager|,
   // |AccountManagerFacade| and |IdentityManager|. Both of these must outlive
@@ -32,7 +34,8 @@
   AccountManagerUIHandler(
       account_manager::AccountManager* account_manager,
       account_manager::AccountManagerFacade* account_manager_facade,
-      signin::IdentityManager* identity_manager);
+      signin::IdentityManager* identity_manager,
+      ash::AccountAppsAvailability* account_apps_availability);
 
   AccountManagerUIHandler(const AccountManagerUIHandler&) = delete;
   AccountManagerUIHandler& operator=(const AccountManagerUIHandler&) = delete;
@@ -57,8 +60,15 @@
       const CoreAccountInfo& account_info,
       const GoogleServiceAuthError& error) override;
 
+  // |ash::AccountAppsAvailability::Observer| overrides.
+  void OnAccountAvailableInArc(
+      const ::account_manager::Account& account) override;
+  void OnAccountUnavailableInArc(
+      const ::account_manager::Account& account) override;
+
  private:
   friend class AccountManagerUIHandlerTest;
+  friend class AccountManagerUIHandlerTestWithArcAccountRestrictions;
 
   void SetProfileForTesting(Profile* profile);
 
@@ -87,6 +97,12 @@
       const std::vector<std::pair<::account_manager::Account, bool>>&
           account_dummy_token_list);
 
+  void FinishHandleGetAccounts(
+      base::Value callback_id,
+      const std::vector<std::pair<::account_manager::Account, bool>>&
+          account_dummy_token_list,
+      const base::flat_set<account_manager::Account>& arc_accounts);
+
   // Returns secondary Gaia accounts from |stored_accounts| list. If the Device
   // Account is a Gaia account, populates |device_account| with information
   // about that account, otherwise does not modify |device_account|.
@@ -96,6 +112,7 @@
   base::ListValue GetSecondaryGaiaAccounts(
       const std::vector<std::pair<::account_manager::Account, bool>>&
           account_dummy_token_list,
+      const base::flat_set<account_manager::Account>& arc_accounts,
       const AccountId device_account_id,
       const bool is_child_user,
       base::DictionaryValue* device_account);
@@ -114,6 +131,10 @@
   // A non-owning pointer to |IdentityManager|.
   signin::IdentityManager* const identity_manager_;
 
+  // A non-owning pointer to |AccountAppsAvailability| which is a KeyedService
+  // and should outlive this class.
+  ash::AccountAppsAvailability* account_apps_availability_ = nullptr;
+
   // An observer for |AccountManagerFacade|. Automatically deregisters when
   // |this| is destructed.
   base::ScopedObservation<account_manager::AccountManagerFacade,
@@ -126,6 +147,12 @@
                           signin::IdentityManager::Observer>
       identity_manager_observation_{this};
 
+  // An observer for |ash::AccountAppsAvailability|. Registered on
+  // |OnJavascriptAllowed| and deregistered on |OnJavascriptDisallowed|.
+  base::ScopedObservation<ash::AccountAppsAvailability,
+                          ash::AccountAppsAvailability::Observer>
+      account_apps_availability_observation_{this};
+
   base::WeakPtrFactory<AccountManagerUIHandler> weak_factory_{this};
 };
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler_browsertest.cc b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler_browsertest.cc
index 7c06f7fd..4ef4b9c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler_browsertest.cc
@@ -8,7 +8,10 @@
 #include <ostream>
 
 #include "ash/components/account_manager/account_manager_factory.h"
+#include "ash/constants/ash_features.h"
 #include "base/test/bind.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability_factory.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
@@ -112,10 +115,12 @@
       AccountManager* account_manager,
       account_manager::AccountManagerFacade* account_manager_facade,
       signin::IdentityManager* identity_manager,
+      ash::AccountAppsAvailability* apps_availability,
       content::WebUI* web_ui)
       : AccountManagerUIHandler(account_manager,
                                 account_manager_facade,
-                                identity_manager) {
+                                identity_manager,
+                                apps_availability) {
     set_web_ui(web_ui);
   }
 
@@ -135,6 +140,31 @@
       delete;
 
   void SetUpOnMainThread() override {
+    // Split the setup so it can be called from the inherited classes.
+    SetUpEnvironment();
+
+    auto* account_manager_facade =
+        ::GetAccountManagerFacade(profile_->GetPath().value());
+
+    handler_ = std::make_unique<TestingAccountManagerUIHandler>(
+        account_manager_, account_manager_facade, identity_manager_, nullptr,
+        &web_ui_);
+    handler_->SetProfileForTesting(profile_.get());
+    handler_->RegisterMessages();
+    handler_->AllowJavascriptForTesting();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void TearDownOnMainThread() override {
+    handler_.reset();
+    ProfileHelper::Get()->RemoveUserFromListForTesting(primary_account_id_);
+    profile_.reset();
+    base::RunLoop().RunUntilIdle();
+    user_manager_enabler_.reset();
+  }
+
+  // Sets up profile and user manager. Should be called only once on test setup.
+  void SetUpEnvironment() {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     TestingProfile::Builder profile_builder;
     profile_builder.SetPath(temp_dir_.GetPath().AppendASCII("TestProfile"));
@@ -149,17 +179,17 @@
     const user_manager::User* user;
     if (GetDeviceAccountInfo().user_type ==
         user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY) {
-      user = GetFakeUserManager()->AddUserWithAffiliationAndTypeAndProfile(
+      user = user_manager->AddUserWithAffiliationAndTypeAndProfile(
           AccountId::AdFromUserEmailObjGuid(GetDeviceAccountInfo().email,
                                             GetDeviceAccountInfo().id),
           true, user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY,
           profile_.get());
     } else if (GetDeviceAccountInfo().user_type ==
                user_manager::UserType::USER_TYPE_CHILD) {
-      user = GetFakeUserManager()->AddChildUser(AccountId::FromUserEmailGaiaId(
+      user = user_manager->AddChildUser(AccountId::FromUserEmailGaiaId(
           GetDeviceAccountInfo().email, GetDeviceAccountInfo().id));
     } else {
-      user = GetFakeUserManager()->AddUserWithAffiliationAndTypeAndProfile(
+      user = user_manager->AddUserWithAffiliationAndTypeAndProfile(
           AccountId::FromUserEmailGaiaId(GetDeviceAccountInfo().email,
                                          GetDeviceAccountInfo().id),
           true, GetDeviceAccountInfo().user_type, profile_.get());
@@ -181,24 +211,6 @@
         ::account_manager::AccountKey{GetDeviceAccountInfo().id,
                                       GetDeviceAccountInfo().account_type},
         GetDeviceAccountInfo().email, GetDeviceAccountInfo().token);
-
-    auto* account_manager_facade =
-        ::GetAccountManagerFacade(profile_->GetPath().value());
-
-    handler_ = std::make_unique<TestingAccountManagerUIHandler>(
-        account_manager_, account_manager_facade, identity_manager_, &web_ui_);
-    handler_->SetProfileForTesting(profile_.get());
-    handler_->RegisterMessages();
-    handler_->AllowJavascriptForTesting();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void TearDownOnMainThread() override {
-    handler_.reset();
-    ProfileHelper::Get()->RemoveUserFromListForTesting(primary_account_id_);
-    profile_.reset();
-    base::RunLoop().RunUntilIdle();
-    user_manager_enabler_.reset();
   }
 
   void UpsertAccount(std::string email) {
@@ -242,16 +254,12 @@
 
   DeviceAccountInfo GetDeviceAccountInfo() const { return GetParam(); }
 
+  TestingProfile* profile() { return profile_.get(); }
   content::TestWebUI* web_ui() { return &web_ui_; }
   signin::IdentityManager* identity_manager() { return identity_manager_; }
   AccountManager* account_manager() { return account_manager_; }
 
  private:
-  FakeChromeUserManager* GetFakeUserManager() const {
-    return static_cast<FakeChromeUserManager*>(
-        user_manager::UserManager::Get());
-  }
-
   std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<TestingProfile> profile_;
@@ -398,5 +406,112 @@
                       GetGaiaDeviceAccountInfo(),
                       GetChildDeviceAccountInfo()));
 
+class AccountManagerUIHandlerTestWithArcAccountRestrictions
+    : public AccountManagerUIHandlerTest {
+ public:
+  AccountManagerUIHandlerTestWithArcAccountRestrictions() {
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{chromeos::features::kArcAccountRestrictions,
+                              chromeos::features::kLacrosSupport},
+        /*disabled_features=*/{});
+  }
+
+  void SetUpOnMainThread() override {
+    SetUpEnvironment();
+
+    auto* account_manager_facade =
+        ::GetAccountManagerFacade(profile()->GetPath().value());
+
+    account_apps_availability_ =
+        ash::AccountAppsAvailabilityFactory::GetForProfile(profile());
+
+    handler_ = std::make_unique<TestingAccountManagerUIHandler>(
+        account_manager(), account_manager_facade, identity_manager(),
+        account_apps_availability_, web_ui());
+    handler_->SetProfileForTesting(profile());
+    handler_->RegisterMessages();
+    handler_->AllowJavascriptForTesting();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void TearDownOnMainThread() override {
+    handler_.reset();
+    AccountManagerUIHandlerTest::TearDownOnMainThread();
+  }
+
+  ash::AccountAppsAvailability* account_apps_availability() {
+    return account_apps_availability_;
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+  ash::AccountAppsAvailability* account_apps_availability_;
+  std::unique_ptr<TestingAccountManagerUIHandler> handler_;
+};
+
+IN_PROC_BROWSER_TEST_P(AccountManagerUIHandlerTestWithArcAccountRestrictions,
+                       CheckIsAvailableInArcValue) {
+  const std::string secondary_email_1 = "secondary1@example.com";
+  const std::string secondary_email_2 = "secondary2@example.com";
+  UpsertAccount(secondary_email_1);
+  UpsertAccount(secondary_email_2);
+  const std::vector<::account_manager::Account> account_manager_accounts =
+      GetAccountsFromAccountManager();
+  ASSERT_EQ(3UL, account_manager_accounts.size());
+
+  // Wait for accounts to propagate to IdentityManager.
+  base::RunLoop().RunUntilIdle();
+
+  for (const auto& account : account_manager_accounts) {
+    if (account.raw_email == secondary_email_1) {
+      account_apps_availability()->SetIsAccountAvailableInArc(account, true);
+    } else if (account.raw_email == secondary_email_2) {
+      account_apps_availability()->SetIsAccountAvailableInArc(account, false);
+    }
+  }
+
+  // Call "getAccounts".
+  base::Value args(base::Value::Type::LIST);
+  args.Append(kHandleFunctionName);
+  web_ui()->HandleReceivedMessage(kGetAccountsMessage,
+                                  &base::Value::AsListValue(args));
+
+  // Wait for the async calls to finish.
+  base::RunLoop().RunUntilIdle();
+
+  const content::TestWebUI::CallData& call_data = *web_ui()->call_data().back();
+  EXPECT_EQ("cr.webUIResponse", call_data.function_name());
+  EXPECT_EQ(kHandleFunctionName, call_data.arg1()->GetString());
+  ASSERT_TRUE(call_data.arg2()->GetBool());
+
+  // Get results from JS callback.
+  const base::span<const base::Value> result = call_data.arg3()->GetList();
+  ASSERT_EQ(account_manager_accounts.size(), result.size());
+
+  // The value for the device account should be always `true`.
+  const base::Value& device_account = result[0];
+  EXPECT_TRUE(device_account.FindBoolKey("isAvailableInArc").value());
+
+  // Check secondary accounts.
+  for (const base::Value& account : result) {
+    if (ValueOrEmpty(account.FindStringKey("id")) == GetDeviceAccountInfo().id)
+      continue;
+
+    std::string email = ValueOrEmpty(account.FindStringKey("email"));
+    if (email == secondary_email_1) {
+      EXPECT_TRUE(account.FindBoolKey("isAvailableInArc").value());
+    } else if (email == secondary_email_2) {
+      EXPECT_FALSE(account.FindBoolKey("isAvailableInArc").value());
+    }
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AccountManagerUIHandlerTestWithArcAccountRestrictionsSuite,
+    AccountManagerUIHandlerTestWithArcAccountRestrictions,
+    ::testing::Values(GetActiveDirectoryDeviceAccountInfo(),
+                      GetGaiaDeviceAccountInfo(),
+                      GetChildDeviceAccountInfo()));
+
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/people_section.cc b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
index 54ef8f1..4fef17a 100644
--- a/chrome/browser/ui/webui/settings/chromeos/people_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
@@ -14,6 +14,8 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability_factory.h"
 #include "chrome/browser/ash/account_manager/account_manager_util.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
@@ -270,6 +272,9 @@
                                  ui::GetChromeOSDeviceName()));
   html_source->AddBoolean("lacrosEnabled",
                           crosapi::browser_util::IsLacrosEnabled());
+  html_source->AddBoolean(
+      "arcAccountRestrictionsEnabled",
+      ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled());
 }
 
 void AddLockScreenPageStrings(content::WebUIDataSource* html_source,
@@ -584,6 +589,8 @@
         ::GetAccountManagerFacade(profile->GetPath().value());
     DCHECK(account_manager_facade_);
     account_manager_facade_observation_.Observe(account_manager_facade_);
+    account_apps_availability_ =
+        ash::AccountAppsAvailabilityFactory::GetForProfile(profile);
     FetchAccounts();
   }
 
@@ -704,7 +711,8 @@
   if (account_manager_facade_) {
     web_ui->AddMessageHandler(
         std::make_unique<chromeos::settings::AccountManagerUIHandler>(
-            account_manager_, account_manager_facade_, identity_manager_));
+            account_manager_, account_manager_facade_, identity_manager_,
+            account_apps_availability_));
   }
 
   if (chromeos::features::IsSyncSettingsCategorizationEnabled())
diff --git a/chrome/browser/ui/webui/settings/chromeos/people_section.h b/chrome/browser/ui/webui/settings/chromeos/people_section.h
index 3a73dd14..e1825483 100644
--- a/chrome/browser/ui/webui/settings/chromeos/people_section.h
+++ b/chrome/browser/ui/webui/settings/chromeos/people_section.h
@@ -31,6 +31,10 @@
 class SyncService;
 }  // namespace syncer
 
+namespace ash {
+class AccountAppsAvailability;
+}
+
 namespace chromeos {
 
 namespace settings {
@@ -80,6 +84,7 @@
 
   account_manager::AccountManager* account_manager_ = nullptr;
   account_manager::AccountManagerFacade* account_manager_facade_ = nullptr;
+  ash::AccountAppsAvailability* account_apps_availability_ = nullptr;
   syncer::SyncService* sync_service_;
   SupervisedUserService* supervised_user_service_;
   signin::IdentityManager* identity_manager_;
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index f4d5237e..002cea9 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -107,6 +107,8 @@
 #include "ash/components/phonehub/phone_hub_manager.h"
 #include "ash/constants/ash_features.h"
 #include "ash/webui/eche_app_ui/eche_app_manager.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability_factory.h"
 #include "chrome/browser/ash/account_manager/account_manager_util.h"
 #include "chrome/browser/ash/android_sms/android_sms_app_manager.h"
 #include "chrome/browser/ash/android_sms/android_sms_service_factory.h"
@@ -390,7 +392,8 @@
     web_ui()->AddMessageHandler(
         std::make_unique<chromeos::settings::AccountManagerUIHandler>(
             account_manager, account_manager_facade,
-            IdentityManagerFactory::GetForProfile(profile)));
+            IdentityManagerFactory::GetForProfile(profile),
+            ash::AccountAppsAvailabilityFactory::GetForProfile(profile)));
   }
 
   // MultideviceHandler is required in browser settings to show a special note
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 88e0644c..d2b0112f 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1640302449-81030c8426c8d0c744edbdbde63594487901e986.profdata
+chrome-linux-main-1640409372-cf6d5d40c0a8012f9e1f71cf674d654b40a7080b.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 70f7635..388d74e 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1640302449-7809dc2723d445ef7e933d29339e3c78b5abc025.profdata
+chrome-mac-main-1640409372-d83ca4bf802c7c0c7c8b40a641a18886de7e87b0.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 50748c98..eea4ea8 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1640302449-3b2fc3ac3e1cdab27574b175a4f759335878804e.profdata
+chrome-win32-main-1640409372-514502749e1a2ad331ef978a55df7fea5c3a68ae.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 5e38951..2c5a2a3 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1640302449-2a54af480ef24c2cdf5ed9d930fa6af35d54489c.profdata
+chrome-win64-main-1640409372-d64940908d3fdf998752f5620f8808caa1369544.profdata
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 2a7bf83..c220b53e 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -662,6 +662,12 @@
       "$target_gen_dir/signin/resources.grdp",
     ]
   }
+
+  if (is_chromeos_ash) {
+    deps += [ "chromeos/personalization_app:build_grdp" ]
+    grdp_files +=
+        [ "$target_gen_dir/chromeos/personalization_app/resources.grdp" ]
+  }
 }
 
 # TypeScript related targets
diff --git a/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn b/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
new file mode 100644
index 0000000..6a5ee3e
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
@@ -0,0 +1,51 @@
+# Copyright 2021 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("//build/config/chromeos/ui_mode.gni")
+import("//tools/typescript/ts_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+generate_grd("build_grdp") {
+  grd_prefix = "webui_chromeos_personalization_app"
+  out_grd = "$target_gen_dir/resources.grdp"
+
+  deps = [ ":build_ts" ]
+  manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
+  resource_path_prefix = "chromeos/personalization_app"
+}
+
+ts_library("build_ts") {
+  root_dir = "."
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  path_mappings = [
+    "chrome://personalization/*|" +
+        rebase_path(
+            "$root_gen_dir/ash/webui/personalization_app/resources/tsc/*",
+            target_gen_dir),
+    "chrome://webui-test/*|" +
+        rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
+                    target_gen_dir),
+  ]
+  in_files = [
+    "google_photos_collection_element_test.js",
+    "google_photos_photos_by_album_id_element_test.js",
+    "local_images_element_test.js",
+    "personalization_app_component_test.ts",
+    "personalization_app_controller_test.js",
+    "personalization_app_test_utils.ts",
+    "personalization_breadcrumb_element_test.js",
+    "personalization_main_element_test.js",
+    "personalization_router_element_test.ts",
+    "personalization_toast_element_test.js",
+    "test_personalization_store.js",
+    "test_wallpaper_interface_provider.ts",
+    "wallpaper_collections_element_test.js",
+    "wallpaper_fullscreen_element_test.ts",
+    "wallpaper_images_element_test.ts",
+    "wallpaper_selected_element_test.ts",
+  ]
+  deps = [ "//ash/webui/personalization_app/resources:build_ts" ]
+  extra_deps = [ "../..:generate_definitions" ]
+}
diff --git a/chrome/test/data/webui/chromeos/personalization_app/OWNERS b/chrome/test/data/webui/chromeos/personalization_app/OWNERS
new file mode 100644
index 0000000..ebb61881
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/personalization_app/OWNERS
@@ -0,0 +1 @@
+file://ash/webui/personalization_app/OWNERS
diff --git a/chrome/test/data/webui/chromeos/personalization_app/google_photos_collection_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/google_photos_collection_element_test.js
index 3b18a1b..add9ca4 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/google_photos_collection_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/google_photos_collection_element_test.js
@@ -5,8 +5,8 @@
 import {GooglePhotosCollection} from 'chrome://personalization/trusted/wallpaper/google_photos_collection_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
-import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-import {waitAfterNextRender} from '../../test_util.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
@@ -56,7 +56,7 @@
     personalizationStore.data.wallpaper.loading.googlePhotos.photos = false;
 
     googlePhotosCollectionElement =
-        initElement(GooglePhotosCollection.is, {hidden: false});
+        initElement(GooglePhotosCollection, {hidden: false});
     await waitAfterNextRender(googlePhotosCollectionElement);
 
     // Zero state should be absent.
@@ -87,7 +87,7 @@
     personalizationStore.data.wallpaper.loading.googlePhotos.photos = false;
 
     googlePhotosCollectionElement =
-        initElement(GooglePhotosCollection.is, {hidden: false});
+        initElement(GooglePhotosCollection, {hidden: false});
     await waitAfterNextRender(googlePhotosCollectionElement);
 
     // Zero state should be absent.
@@ -187,7 +187,7 @@
     personalizationStore.data.wallpaper.loading.googlePhotos.photos = false;
 
     googlePhotosCollectionElement =
-        initElement(GooglePhotosCollection.is, {hidden: false});
+        initElement(GooglePhotosCollection, {hidden: false});
     await waitAfterNextRender(googlePhotosCollectionElement);
 
     // Photos tab should be absent.
diff --git a/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.js
index 25e5b5b..b786c80 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/google_photos_photos_by_album_id_element_test.js
@@ -4,8 +4,8 @@
 
 import {GooglePhotosPhotosByAlbumId, promisifyWallpaperControllerFunctionsForTesting} from 'chrome://personalization/trusted/wallpaper/google_photos_photos_by_album_id_element.js';
 
-import {assertEquals} from '../../chai_assert.js';
-import {waitAfterNextRender} from '../../test_util.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
+import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
@@ -55,7 +55,7 @@
         .photosByAlbumId = {};
 
     googlePhotosPhotosByAlbumIdElement =
-        initElement(GooglePhotosPhotosByAlbumId.is, {hidden: false});
+        initElement(GooglePhotosPhotosByAlbumId, {hidden: false});
     await waitAfterNextRender(googlePhotosPhotosByAlbumIdElement);
 
     // Initially no album id selected. Photos should be absent.
diff --git a/chrome/test/data/webui/chromeos/personalization_app/local_images_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/local_images_element_test.js
index 49c1f90..ed59e89 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/local_images_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/local_images_element_test.js
@@ -4,8 +4,8 @@
 
 import {LocalImages} from 'chrome://personalization/trusted/wallpaper/local_images_element.js';
 
-import {assertEquals, assertTrue} from '../../chai_assert.js';
-import {flushTasks, waitAfterNextRender} from '../../test_util.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
@@ -59,7 +59,7 @@
       data: {}
     };
 
-    localImagesElement = initElement(LocalImages.is, {hidden: false});
+    localImagesElement = initElement(LocalImages, {hidden: false});
     await waitAfterNextRender(localImagesElement);
 
     // Iron-list creates some extra dom elements as a scroll buffer and
@@ -104,7 +104,7 @@
           data: {}
         };
 
-        localImagesElement = initElement(LocalImages.is, {hidden: false});
+        localImagesElement = initElement(LocalImages, {hidden: false});
 
         const ironList =
             localImagesElement.shadowRoot.querySelector('iron-list');
@@ -156,7 +156,7 @@
           data: {'LocalImage0.png': false, 'LocalImage1.png': false},
         };
 
-        localImagesElement = initElement(LocalImages.is, {hidden: false});
+        localImagesElement = initElement(LocalImages, {hidden: false});
         await waitAfterNextRender(localImagesElement);
 
         // iron-list pre-creates some extra DOM elements but marks them as
@@ -190,7 +190,7 @@
       data: {'LocalImage0.png': false, 'LocalImage1.png': false},
     };
 
-    localImagesElement = initElement(LocalImages.is, {hidden: false});
+    localImagesElement = initElement(LocalImages, {hidden: false});
     await waitAfterNextRender(localImagesElement);
 
     // iron-list pre-creates some extra DOM elements but marks them as
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js
index b143461a..2cbf217 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js
@@ -14,8 +14,8 @@
 
 var PersonalizationAppComponentBrowserTest = class extends PolymerTest {
   get browsePreload() {
-    return 'chrome://personalization/test_loader.html?' +
-        'module=chromeos/personalization_app/' +
+    return 'chrome://personalization/test_loader.html?host=webui-test' +
+        '&module=chromeos/personalization_app/' +
         'personalization_app_component_test.js';
   }
 
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts
similarity index 96%
rename from chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.js
rename to chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts
index 132b3c5..91a7a2e 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import 'chrome://personalization/strings.m.js';
-import '../../mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {GooglePhotosCollectionTest} from './google_photos_collection_element_test.js';
 import {GooglePhotosPhotosByAlbumIdTest} from './google_photos_photos_by_album_id_element_test.js';
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_browsertest.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_browsertest.js
index f74d7f0..6abf5b05 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_browsertest.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_browsertest.js
@@ -14,8 +14,8 @@
 
 var PersonalizationAppControllerBrowserTest = class extends PolymerTest {
   get browsePreload() {
-    return 'chrome://personalization/test_loader.html?' +
-        'module=chromeos/personalization_app/' +
+    return 'chrome://personalization/test_loader.html?host=webui-test' +
+        '&module=chromeos/personalization_app/' +
         'personalization_app_controller_test.js';
   }
 
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.js
index 4265c7c..bcd5bfb 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_controller_test.js
@@ -3,14 +3,14 @@
 // found in the LICENSE file.
 
 import 'chrome://personalization/strings.m.js';
-import '../../mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {WallpaperCollection} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
 import * as wallpaperAction from 'chrome://personalization/trusted/wallpaper/wallpaper_actions.js';
 import {fetchCollections, fetchGooglePhotosAlbum, fetchLocalData, getLocalImages, initializeBackdropData, initializeGooglePhotosData, selectWallpaper} from 'chrome://personalization/trusted/wallpaper/wallpaper_controller.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
-import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {TestPersonalizationStore} from './test_personalization_store.js';
 import {TestWallpaperProvider} from './test_wallpaper_interface_provider.js';
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test_utils.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test_utils.ts
similarity index 64%
rename from chrome/test/data/webui/chromeos/personalization_app/personalization_app_test_utils.js
rename to chrome/test/data/webui/chromeos/personalization_app/personalization_app_test_utils.ts
index f522de2..7ce5122 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test_utils.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test_utils.ts
@@ -7,27 +7,24 @@
  * SWA.
  */
 
-import {emptyState} from 'chrome://personalization/trusted/personalization_state.js';
+import {emptyState, PersonalizationState} from 'chrome://personalization/trusted/personalization_state.js';
 import {setWallpaperProviderForTesting} from 'chrome://personalization/trusted/wallpaper/wallpaper_interface_provider.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {flush, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {assertTrue} from '../../chai_assert.js';
-import {flushTasks} from '../../test_util.js';
+import {assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {TestPersonalizationStore} from './test_personalization_store.js';
 import {TestWallpaperProvider} from './test_wallpaper_interface_provider.js';
 
 /**
  * Constructs the given element with properties and appends it to body.
- * TODO(cowmoo) make generic and cast to specific polymer types.
- * @param {!string} tag
- * @param {!Object} properties
- * @returns {!HTMLElement}
  */
-export function initElement(tag, properties = {}) {
-  const element = /** @type {!HTMLElement} **/ (document.createElement(tag));
+export function initElement<T extends PolymerElement>(
+    cls: {new (): T; is: string}, properties = {}): T {
+  const element = document.createElement(cls.is) as T & HTMLElement;
   for (const [key, value] of Object.entries(properties)) {
-    element[key] = value;
+    (element as any)[key] = value;
   }
   document.body.appendChild(element);
   flush();
@@ -38,13 +35,12 @@
  * Tear down an element. Make sure the iframe load callback
  * has completed to avoid weird race condition with loading.
  * @see {b/185905694, crbug/466089}
- * @param {*} element
  */
-export async function teardownElement(element) {
+export async function teardownElement(element: HTMLElement|null) {
   if (!element) {
     return;
   }
-  const iframe = await element.iframePromise_;
+  const iframe = await (element as any).iframePromise_;
   if (iframe) {
     iframe.remove();
     await flushTasks();
@@ -56,11 +52,8 @@
 /**
  * Sets up the test wallpaper provider, test personalization store, and clears
  * the page.
- * @param {!PersonalizationState} initialState
- * @return {{wallpaperProvider: !TestWallpaperProvider, personalizationStore:
- *     !TestPersonalizationStore}}
  */
-export function baseSetup(initialState = emptyState()) {
+export function baseSetup(initialState: PersonalizationState = emptyState()) {
   const wallpaperProvider = new TestWallpaperProvider();
   setWallpaperProviderForTesting(wallpaperProvider);
   const personalizationStore = new TestPersonalizationStore(initialState);
@@ -69,7 +62,7 @@
   return {wallpaperProvider, personalizationStore};
 }
 
-function getDebugString(w) {
+function getDebugString(w: any) {
   if (w === window) {
     return w.location.href;
   }
@@ -80,10 +73,8 @@
  * Helper function to test if two window objects are the same.
  * Plain |assertEquals| fails when it attempts to get a debug string
  * representation of cross-origin iframe window.
- * @param {!Object} x
- * @param {!Object} y
  */
-export function assertWindowObjectsEqual(x, y) {
+export function assertWindowObjectsEqual(x: object|null, y: object|null) {
   assertTrue(
       x === y,
       `Window objects are not identical: ${getDebugString(x)}, ${
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.js
index 539e82d7..b4e068a0 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_breadcrumb_element_test.js
@@ -9,8 +9,8 @@
 import {Paths} from 'chrome://personalization/trusted/personalization_router_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
-import {assertEquals, assertTrue} from '../../chai_assert.js';
-import {flushTasks, waitAfterNextRender} from '../../test_util.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {baseSetup, initElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
@@ -70,7 +70,7 @@
   });
 
   test('shows wallpaper label by default', async () => {
-    breadcrumbElement = initElement(PersonalizationBreadcrumb.is);
+    breadcrumbElement = initElement(PersonalizationBreadcrumb);
 
     const breadcrumbContainer =
         breadcrumbElement.shadowRoot.getElementById('breadcrumbContainer');
@@ -81,7 +81,7 @@
   test('shows collection name when collection is selected', async () => {
     const collection = wallpaperProvider.collections[0];
     breadcrumbElement = initElement(
-        PersonalizationBreadcrumb.is,
+        PersonalizationBreadcrumb,
         {'path': Paths.CollectionImages, 'collectionId': collection.id});
 
     personalizationStore.data.wallpaper.backdrop.collections =
@@ -111,7 +111,7 @@
         [googlePhotosAlbum];
     personalizationStore.notifyObservers();
 
-    breadcrumbElement = initElement(PersonalizationBreadcrumb.is, {
+    breadcrumbElement = initElement(PersonalizationBreadcrumb, {
       'path': Paths.GooglePhotosCollection,
       'googlePhotosAlbumId': googlePhotosAlbum.id
     });
@@ -131,7 +131,7 @@
     loadTimeData.overrideValues({'googlePhotosLabel': 'Google Photos'});
 
     breadcrumbElement = initElement(
-        PersonalizationBreadcrumb.is, {'path': Paths.GooglePhotosCollection});
+        PersonalizationBreadcrumb, {'path': Paths.GooglePhotosCollection});
 
     const breadcrumbContainer =
         breadcrumbElement.shadowRoot.getElementById('breadcrumbContainer');
@@ -143,8 +143,8 @@
   });
 
   test('show label when local images subpage is loaded', async () => {
-    breadcrumbElement = initElement(
-        PersonalizationBreadcrumb.is, {'path': Paths.LocalCollection});
+    breadcrumbElement =
+        initElement(PersonalizationBreadcrumb, {'path': Paths.LocalCollection});
 
     personalizationStore.data.wallpaper.local.images =
         wallpaperProvider.localImages;
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.js
index 69e1110..c575d8a 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.js
@@ -4,7 +4,7 @@
 
 import {PersonalizationMain} from 'chrome://personalization/trusted/personalization_main_element.js';
 
-import {assertEquals} from '../../chai_assert.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
 import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
@@ -32,7 +32,7 @@
   });
 
   test('displays content', async () => {
-    personalizationMainElement = initElement(PersonalizationMain.is);
+    personalizationMainElement = initElement(PersonalizationMain);
     assertEquals(
         'Personalization',
         personalizationMainElement.shadowRoot.querySelector('h1').innerText);
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_router_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_router_element_test.ts
similarity index 73%
rename from chrome/test/data/webui/chromeos/personalization_app/personalization_router_element_test.js
rename to chrome/test/data/webui/chromeos/personalization_app/personalization_router_element_test.ts
index 7b2dcd9d..96ac0f4 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_router_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_router_element_test.ts
@@ -8,19 +8,12 @@
 import {initElement} from './personalization_app_test_utils.js';
 
 export function PersonalizationRouterTest() {
-  /** @type {?HTMLElement} */
-  let personalizationRouterElement = null;
-
-  teardown(async () => {
-    personalizationRouterElement = null;
-  });
-
   test('redirects if hub feature off on root page', async () => {
     loadTimeData.overrideValues({'isPersonalizationHubEnabled': false});
-    const reloadCalledPromise = new Promise((resolve) => {
+    const reloadCalledPromise = new Promise<void>((resolve) => {
       PersonalizationRouter.reloadAtWallpaper = resolve;
     });
-    initElement(PersonalizationRouter.is);
+    initElement(PersonalizationRouter);
     await reloadCalledPromise;
   });
 }
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.js
index 304d955..4b7f8e7 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.js
@@ -5,8 +5,8 @@
 import {PersonalizationActionName} from 'chrome://personalization/trusted/personalization_actions.js';
 import {PersonalizationToastElement} from 'chrome://personalization/trusted/personalization_toast_element.js';
 
-import {assertEquals, assertTrue} from '../../chai_assert.js';
-import {flushTasks, waitAfterNextRender} from '../../test_util.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
@@ -21,7 +21,7 @@
   setup(() => {
     const mocks = baseSetup();
     personalizationStore = mocks.personalizationStore;
-    personalizationToastElement = initElement(PersonalizationToastElement.is);
+    personalizationToastElement = initElement(PersonalizationToastElement);
   });
 
   teardown(async () => {
diff --git a/chrome/test/data/webui/chromeos/personalization_app/test_personalization_store.js b/chrome/test/data/webui/chromeos/personalization_app/test_personalization_store.js
index d4217421..a10f2db9 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/test_personalization_store.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/test_personalization_store.js
@@ -14,7 +14,7 @@
 import {reduce} from 'chrome://personalization/trusted/personalization_reducers.js';
 import {emptyState} from 'chrome://personalization/trusted/personalization_state.js';
 import {PersonalizationStore} from 'chrome://personalization/trusted/personalization_store.js';
-import {TestStore} from '../../test_store.js';
+import {TestStore} from 'chrome://webui-test/test_store.js';
 
 export class TestPersonalizationStore extends TestStore {
   constructor(data) {
diff --git a/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.js b/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
similarity index 67%
rename from chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.js
rename to chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
index 120c5f1..8564f043 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {OnlineImageType, WallpaperLayout, WallpaperType} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
+import {CurrentWallpaper, OnlineImageType, WallpaperCollection, WallpaperImage, WallpaperLayout, WallpaperObserverInterface, WallpaperObserverRemote, WallpaperProviderInterface, WallpaperType} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-
-import {assertTrue} from '../../chai_assert.js';
-import {TestBrowserProxy} from '../../test_browser_proxy.js';
+import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
+import {assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 /**
  * @implements {WallpaperProviderInterface}
  * @extends {TestBrowserProxy}
  */
-export class TestWallpaperProvider extends TestBrowserProxy {
+export class TestWallpaperProvider extends TestBrowserProxy implements
+    WallpaperProviderInterface {
   constructor() {
     super([
       'makeTransparent',
@@ -35,8 +36,6 @@
 
     /**
      * URLs are not real but must have the correct origin to pass CSP checks.
-     * @private
-     * @type {?Array<!WallpaperCollection>}
      */
     this.collections_ = [
       {
@@ -53,8 +52,6 @@
 
     /**
      * URLs are not real but must have the correct origin to pass CSP checks.
-     * @private
-     * @type {?Array<!WallpaperImage>}
      */
     this.images_ = [
       {
@@ -80,19 +77,13 @@
       },
     ];
 
-    /** @type {?Array<!mojoBase.mojom.FilePath>} */
     this.localImages = [{path: 'LocalImage0.png'}, {path: 'LocalImage1.png'}];
 
-    /** @type {!Object<string, string>} */
     this.localImageData = {
       'LocalImage0.png': 'data://localimage0data',
       'LocalImage1.png': 'data://localimage1data',
     };
 
-    /**
-     * @public
-     * @type {?CurrentWallpaper}
-     */
     this.currentWallpaper = {
       attribution: ['Image 0'],
       layout: WallpaperLayout.kCenter,
@@ -100,65 +91,47 @@
       type: WallpaperType.kOnline,
       url: {url: 'https://images.googleusercontent.com/0'},
     };
-
-    /** @public */
-    this.selectWallpaperResponse = true;
-
-    /** @public */
-    this.selectLocalImageResponse = true;
-
-    /** @public */
-    this.updateDailyRefreshWallpaperResponse = true;
-
-    /** @public */
-    this.isInTabletModeResponse = true;
-
-    /** @public */
-    this.wallpaperObserverUpdateTimeout = 0;
-
-    /**
-     * @public
-     * @type {?WallpaperObserverInterface}
-     */
-    this.wallpaperObserverRemote = null;
   }
 
-  /**
-   * @return {?Array<!WallpaperCollection>}
-   */
-  get collections() {
+  private collections_: WallpaperCollection[]|null;
+  private images_: WallpaperImage[]|null;
+  localImages: FilePath[];
+  localImageData: Record<string, string>;
+  currentWallpaper: CurrentWallpaper;
+  selectWallpaperResponse = true;
+  selectLocalImageResponse = true;
+  updateDailyRefreshWallpaperResponse = true;
+  isInTabletModeResponse = true;
+  wallpaperObserverUpdateTimeout = 0;
+  wallpaperObserverRemote: WallpaperObserverInterface|null = null;
+
+  get collections(): WallpaperCollection[]|null {
     return this.collections_;
   }
 
-  /**
-   * @return {?Array<!WallpaperImage>}
-   */
-  get images() {
+  get images(): WallpaperImage[]|null {
     return this.images_;
   }
 
-  /** @override */
   makeTransparent() {
     this.methodCalled('makeTransparent');
   }
 
-  /** @override */
   fetchCollections() {
     this.methodCalled('fetchCollections');
     return Promise.resolve({collections: this.collections_});
   }
 
-  /** @override */
-  fetchImagesForCollection(collectionId) {
+  fetchImagesForCollection(collectionId: string) {
     this.methodCalled('fetchImagesForCollection', collectionId);
     assertTrue(
-        !!this.collections_.find(({id}) => id === collectionId),
+        !!this.collections_ &&
+            !!this.collections_.find(({id}) => id === collectionId),
         'Must request images for existing wallpaper collection',
     );
     return Promise.resolve({images: this.images_});
   }
 
-  /** @override */
   fetchGooglePhotosCount() {
     this.methodCalled('fetchGooglePhotosCount');
     const count =
@@ -166,56 +139,48 @@
     return Promise.resolve({count: count});
   }
 
-  /** @override */
   getLocalImages() {
     this.methodCalled('getLocalImages');
     return Promise.resolve({images: this.localImages});
   }
 
-  /** @override */
-  getLocalImageThumbnail(filePath) {
+  getLocalImageThumbnail(filePath: FilePath) {
     this.methodCalled('getLocalImageThumbnail', filePath);
-    return Promise.resolve({data: this.localImageData[filePath.path]});
+    return Promise.resolve({data: this.localImageData[filePath.path]!});
   }
 
-  /** @override */
-  setWallpaperObserver(remote) {
+  setWallpaperObserver(remote: WallpaperObserverRemote) {
     this.methodCalled('setWallpaperObserver');
     this.wallpaperObserverRemote = remote;
     window.setTimeout(() => {
-      this.wallpaperObserverRemote.onWallpaperChanged(this.currentWallpaper);
+      this.wallpaperObserverRemote!.onWallpaperChanged(this.currentWallpaper);
     }, this.wallpaperObserverUpdateTimeout);
   }
 
-  /** @override */
-  selectWallpaper(assetId, previewMode) {
+  selectWallpaper(assetId: bigint, previewMode: boolean) {
     this.methodCalled('selectWallpaper', assetId, previewMode);
     return Promise.resolve({success: this.selectWallpaperResponse});
   }
 
-  /** @override */
-  selectLocalImage(id, layout, previewMode) {
-    this.methodCalled('selectLocalImage', id, layout, previewMode);
+  selectLocalImage(
+      path: FilePath, layout: WallpaperLayout, previewMode: boolean) {
+    this.methodCalled('selectLocalImage', path, layout, previewMode);
     return Promise.resolve({success: this.selectLocalImageResponse});
   }
 
-  /** @override */
-  setCustomWallpaperLayout(layout) {
+  setCustomWallpaperLayout(layout: WallpaperLayout) {
     this.methodCalled('setCustomWallpaperLayout', layout);
   }
 
-  /** @override */
-  setDailyRefreshCollectionId(collectionId) {
+  setDailyRefreshCollectionId(collectionId: string) {
     this.methodCalled('setDailyRefreshCollectionId', collectionId);
   }
 
-  /** @override */
   getDailyRefreshCollectionId() {
     this.methodCalled('getDailyRefreshCollectionId');
-    return Promise.resolve({collectionId: this.collections_[0].id});
+    return Promise.resolve({collectionId: this.collections_![0]!.id});
   }
 
-  /** @override */
   updateDailyRefreshWallpaper() {
     this.methodCalled('updateDailyRefreshWallpaper');
     return Promise.resolve({success: this.updateDailyRefreshWallpaperResponse});
@@ -226,21 +191,15 @@
     return Promise.resolve({tabletMode: this.isInTabletModeResponse});
   }
 
-  /** @override */
   confirmPreviewWallpaper() {
     this.methodCalled('confirmPreviewWallpaper');
   }
 
-  /** @override */
   cancelPreviewWallpaper() {
     this.methodCalled('cancelPreviewWallpaper');
   }
 
-  /**
-   * @param {!Array<!WallpaperCollection>}
-   *     collections
-   */
-  setCollections(collections) {
+  setCollections(collections: WallpaperCollection[]) {
     this.collections_ = collections;
   }
 
@@ -248,10 +207,7 @@
     this.collections_ = null;
   }
 
-  /**
-   * @param {Array<!WallpaperImage>} images
-   */
-  setImages(images) {
+  setImages(images: WallpaperImage[]) {
     this.images_ = images;
   }
 
diff --git a/chrome/test/data/webui/chromeos/personalization_app/tsconfig_base.json b/chrome/test/data/webui/chromeos/personalization_app/tsconfig_base.json
new file mode 100644
index 0000000..60d2cf74
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/personalization_app/tsconfig_base.json
@@ -0,0 +1,10 @@
+{
+  "extends": "../../../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "allowJs": true,
+    "typeRoots": [
+       "../../../../../../third_party/node/node_modules/@types"
+    ]
+  }
+}
+
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js
index 343fbd0..8cd2d336 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js
@@ -6,8 +6,8 @@
 import {emptyState} from 'chrome://personalization/trusted/personalization_state.js';
 import {promisifyIframeFunctionsForTesting, WallpaperCollections} from 'chrome://personalization/trusted/wallpaper/wallpaper_collections_element.js';
 
-import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-import {waitAfterNextRender} from '../../test_util.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {assertWindowObjectsEqual, baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
@@ -37,7 +37,7 @@
   test('sends wallpaper collections when loaded', async () => {
     const {sendCollections: sendCollectionsPromise} =
         promisifyIframeFunctionsForTesting();
-    wallpaperCollectionsElement = initElement(WallpaperCollections.is);
+    wallpaperCollectionsElement = initElement(WallpaperCollections);
 
     personalizationStore.data.wallpaper.loading = {
       ...personalizationStore.data.wallpaper.loading,
@@ -63,7 +63,7 @@
     const {sendGooglePhotosCount: sendGooglePhotosCountPromise} =
         promisifyIframeFunctionsForTesting();
 
-    wallpaperCollectionsElement = initElement(WallpaperCollections.is);
+    wallpaperCollectionsElement = initElement(WallpaperCollections);
 
     personalizationStore.data.wallpaper.googlePhotos.count = 1234;
     personalizationStore.data.wallpaper.loading.googlePhotos.count = false;
@@ -86,7 +86,7 @@
     const {sendGooglePhotosPhotos: sendGooglePhotosPhotosPromise} =
         promisifyIframeFunctionsForTesting();
 
-    wallpaperCollectionsElement = initElement(WallpaperCollections.is);
+    wallpaperCollectionsElement = initElement(WallpaperCollections);
 
     personalizationStore.data.wallpaper.googlePhotos.photos =
         Array.from({length: kMaximumGooglePhotosPreviews + 1})
@@ -120,7 +120,7 @@
       images: {},
     };
 
-    wallpaperCollectionsElement = initElement(WallpaperCollections.is);
+    wallpaperCollectionsElement = initElement(WallpaperCollections);
     // Wait for initial load to complete.
     await promisifyIframeFunctionsForTesting().sendImageCounts;
 
@@ -164,7 +164,7 @@
     const {sendLocalImages: sendLocalImagesPromise} =
         promisifyIframeFunctionsForTesting();
 
-    wallpaperCollectionsElement = initElement(WallpaperCollections.is);
+    wallpaperCollectionsElement = initElement(WallpaperCollections);
 
     personalizationStore.data.wallpaper.loading = {
       ...personalizationStore.data.wallpaper.loading,
@@ -195,7 +195,7 @@
       sendLocalImages: sendLocalImagesPromise
     } = promisifyIframeFunctionsForTesting();
 
-    wallpaperCollectionsElement = initElement(WallpaperCollections.is);
+    wallpaperCollectionsElement = initElement(WallpaperCollections);
 
     personalizationStore.data.wallpaper.loading = {
       ...personalizationStore.data.wallpaper.loading,
@@ -230,7 +230,7 @@
   });
 
   test('shows error when fails to load', async () => {
-    wallpaperCollectionsElement = initElement(WallpaperCollections.is);
+    wallpaperCollectionsElement = initElement(WallpaperCollections);
 
     // No error displayed while loading.
     let error =
@@ -265,7 +265,7 @@
     const {sendCollections: sendCollectionsPromise} =
         promisifyIframeFunctionsForTesting();
 
-    wallpaperCollectionsElement = initElement(WallpaperCollections.is);
+    wallpaperCollectionsElement = initElement(WallpaperCollections);
 
     const [_, collections] = await sendCollectionsPromise;
     assertDeepEquals(wallpaperProvider.collections, collections);
@@ -313,7 +313,7 @@
         const {sendLocalImages, sendLocalImageData} =
             promisifyIframeFunctionsForTesting();
 
-        wallpaperCollectionsElement = initElement(WallpaperCollections.is);
+        wallpaperCollectionsElement = initElement(WallpaperCollections);
 
         await sendLocalImages;
 
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.ts
similarity index 71%
rename from chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.js
rename to chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.ts
index 97fcb1d6..78a48b5 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_fullscreen_element_test.ts
@@ -4,28 +4,23 @@
 
 /** @fileoverview Test suite for wallpaper-fullscreen component.  */
 
-import {WallpaperLayout, WallpaperType} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
+import {CurrentWallpaper, WallpaperLayout, WallpaperType} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
+import {DisplayableImage} from 'chrome://personalization/trusted/personalization_reducers.js';
 import {WallpaperFullscreen} from 'chrome://personalization/trusted/wallpaper/wallpaper_fullscreen_element.js';
-
-import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
-import {flushTasks, waitAfterNextRender} from '../../test_util.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {baseSetup, initElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
 import {TestWallpaperProvider} from './test_wallpaper_interface_provider.js';
 
 export function WallpaperFullscreenTest() {
-  /** @type {?HTMLElement} */
-  let wallpaperFullscreenElement = null;
+  let wallpaperFullscreenElement: WallpaperFullscreen|null = null;
+  let wallpaperProvider: TestWallpaperProvider;
+  let personalizationStore: TestPersonalizationStore;
 
-  /** @type {?TestWallpaperProvider} */
-  let wallpaperProvider = null;
-
-  /** @type {?TestPersonalizationStore} */
-  let personalizationStore = null;
-
-  /** @type {!CurrentWallpaper} */
-  const currentSelectedCustomImage = {
+  const currentSelectedCustomImage: CurrentWallpaper = {
     attribution: ['Custom image'],
     layout: WallpaperLayout.kCenter,
     key: 'testing',
@@ -33,8 +28,8 @@
     url: {url: 'data://testing'},
   };
 
-  /** @type {!DisplayableImage} */
-  const pendingSelectedCustomImage = {path: '/local/image/path.jpg'};
+  const pendingSelectedCustomImage:
+      DisplayableImage = {path: '/local/image/path.jpg'};
 
   setup(() => {
     const mocks = baseSetup();
@@ -54,25 +49,25 @@
 
   function mockFullscreenApis() {
     const container =
-        wallpaperFullscreenElement.shadowRoot.getElementById('container');
+        wallpaperFullscreenElement!.shadowRoot!.getElementById('container');
 
-    let fullscreenElement = null;
+    let fullscreenElement: HTMLElement|null = null;
 
-    const requestFullscreenPromise = new Promise((resolve) => {
-      container.requestFullscreen = () => {
+    const requestFullscreenPromise = new Promise<void>((resolve) => {
+      container!.requestFullscreen = () => {
         fullscreenElement = container;
-        container.dispatchEvent(new Event('fullscreenchange'));
+        container!.dispatchEvent(new Event('fullscreenchange'));
         resolve();
         return requestFullscreenPromise;
       };
     });
 
-    wallpaperFullscreenElement.getFullscreenElement = () => fullscreenElement;
-    const exitFullscreenPromise = new Promise((resolve) => {
-      wallpaperFullscreenElement.exitFullscreen = () => {
+    wallpaperFullscreenElement!.getFullscreenElement = () => fullscreenElement;
+    const exitFullscreenPromise = new Promise<void>((resolve) => {
+      wallpaperFullscreenElement!.exitFullscreen = () => {
         assertTrue(!!fullscreenElement);
         fullscreenElement = null;
-        container.dispatchEvent(new Event('fullscreenchange'));
+        container!.dispatchEvent(new Event('fullscreenchange'));
         resolve();
         return exitFullscreenPromise;
       };
@@ -82,14 +77,14 @@
   }
 
   test('toggles element visibility on full screen change', async () => {
-    wallpaperFullscreenElement = initElement(WallpaperFullscreen.is);
+    wallpaperFullscreenElement = initElement(WallpaperFullscreen);
     const {requestFullscreenPromise, exitFullscreenPromise} =
         mockFullscreenApis();
     await waitAfterNextRender(wallpaperFullscreenElement);
 
     const container =
-        wallpaperFullscreenElement.shadowRoot.getElementById('container');
-    assertTrue(container.hidden);
+        wallpaperFullscreenElement.shadowRoot!.getElementById('container');
+    assertTrue(container!.hidden);
 
     personalizationStore.data.wallpaper.fullscreen = true;
     personalizationStore.data.wallpaper.currentSelected =
@@ -98,23 +93,23 @@
 
     await requestFullscreenPromise;
 
-    assertFalse(container.hidden);
+    assertFalse(container!.hidden);
 
     personalizationStore.data.wallpaper.fullscreen = false;
     personalizationStore.notifyObservers();
 
     await exitFullscreenPromise;
 
-    assertTrue(container.hidden);
+    assertTrue(container!.hidden);
   });
 
   test('sets default layout option on when entering preview', async () => {
-    wallpaperFullscreenElement = initElement(WallpaperFullscreen.is);
+    wallpaperFullscreenElement = initElement(WallpaperFullscreen);
     const {requestFullscreenPromise, exitFullscreenPromise} =
         mockFullscreenApis();
     await waitAfterNextRender(wallpaperFullscreenElement);
 
-    assertEquals(null, wallpaperFullscreenElement.selectedLayout_);
+    assertEquals(null, wallpaperFullscreenElement['selectedLayout_']);
 
     personalizationStore.data.wallpaper.fullscreen = true;
     personalizationStore.data.wallpaper.currentSelected =
@@ -125,18 +120,18 @@
 
     assertEquals(
         WallpaperLayout.kCenterCropped,
-        wallpaperFullscreenElement.selectedLayout_);
+        wallpaperFullscreenElement['selectedLayout_']);
 
     personalizationStore.data.wallpaper.fullscreen = false;
     personalizationStore.notifyObservers();
 
     await exitFullscreenPromise;
 
-    assertEquals(null, wallpaperFullscreenElement.selectedLayout_);
+    assertEquals(null, wallpaperFullscreenElement['selectedLayout_']);
   });
 
   test('sets fullscreen class on body when entering fullscreen', async () => {
-    wallpaperFullscreenElement = initElement(WallpaperFullscreen.is);
+    wallpaperFullscreenElement = initElement(WallpaperFullscreen);
     const {requestFullscreenPromise, exitFullscreenPromise} =
         mockFullscreenApis();
     await waitAfterNextRender(wallpaperFullscreenElement);
@@ -160,7 +155,7 @@
   });
 
   test('exits full screen on exit button click', async () => {
-    wallpaperFullscreenElement = initElement(WallpaperFullscreen.is);
+    wallpaperFullscreenElement = initElement(WallpaperFullscreen);
     const {requestFullscreenPromise, exitFullscreenPromise} =
         mockFullscreenApis();
     await waitAfterNextRender(wallpaperFullscreenElement);
@@ -173,19 +168,19 @@
     await requestFullscreenPromise;
 
     const exitButton =
-        wallpaperFullscreenElement.shadowRoot.getElementById('exit');
-    exitButton.click();
+        wallpaperFullscreenElement.shadowRoot!.getElementById('exit');
+    exitButton!.click();
 
     await exitFullscreenPromise;
   });
 
   test('shows layout options for custom images', async () => {
-    wallpaperFullscreenElement = initElement(WallpaperFullscreen.is);
+    wallpaperFullscreenElement = initElement(WallpaperFullscreen);
     await waitAfterNextRender(wallpaperFullscreenElement);
 
     assertEquals(
         null,
-        wallpaperFullscreenElement.shadowRoot.getElementById('layoutButtons'));
+        wallpaperFullscreenElement.shadowRoot!.getElementById('layoutButtons'));
 
     personalizationStore.data.wallpaper.pendingSelected =
         pendingSelectedCustomImage;
@@ -193,12 +188,12 @@
 
     await waitAfterNextRender(wallpaperFullscreenElement);
 
-    assertTrue(!!wallpaperFullscreenElement.shadowRoot.getElementById(
+    assertTrue(!!wallpaperFullscreenElement.shadowRoot!.getElementById(
         'layoutButtons'));
   });
 
   test('clicking layout option selects image with new layout', async () => {
-    wallpaperFullscreenElement = initElement(WallpaperFullscreen.is);
+    wallpaperFullscreenElement = initElement(WallpaperFullscreen);
     const {requestFullscreenPromise} = mockFullscreenApis();
     await waitAfterNextRender(wallpaperFullscreenElement);
 
@@ -211,9 +206,9 @@
 
     await requestFullscreenPromise;
 
-    wallpaperFullscreenElement.shadowRoot
-        .querySelector('cr-button[data-layout="FILL"]')
-        .click();
+    let button = wallpaperFullscreenElement.shadowRoot!.querySelector(
+                     'cr-button[data-layout="FILL"]')! as HTMLButtonElement;
+    button.click();
 
     const [fillImage, fillLayout, fillPreviewMode] =
         await wallpaperProvider.whenCalled('selectLocalImage');
@@ -223,9 +218,9 @@
     assertEquals(WallpaperLayout.kCenterCropped, fillLayout);
     assertTrue(fillPreviewMode);
 
-    wallpaperFullscreenElement.shadowRoot
-        .querySelector('cr-button[data-layout="CENTER"]')
-        .click();
+    button = wallpaperFullscreenElement.shadowRoot!.querySelector(
+                 'cr-button[data-layout="CENTER"]') as HTMLButtonElement;
+    button.click();
 
     const [centerImage, centerLayout, centerPreviewMode] =
         await wallpaperProvider.whenCalled('selectLocalImage');
@@ -236,7 +231,7 @@
   });
 
   test('aria pressed set for chosen layout option', async () => {
-    wallpaperFullscreenElement = initElement(WallpaperFullscreen.is);
+    wallpaperFullscreenElement = initElement(WallpaperFullscreen);
     const {requestFullscreenPromise} = mockFullscreenApis();
     await waitAfterNextRender(wallpaperFullscreenElement);
 
@@ -254,24 +249,24 @@
         WallpaperLayout.kCenter,
         personalizationStore.data.wallpaper.currentSelected.layout);
 
-    const center = wallpaperFullscreenElement.shadowRoot.querySelector(
+    const center = wallpaperFullscreenElement.shadowRoot!.querySelector(
         'cr-button[data-layout="CENTER"]');
-    const fill = wallpaperFullscreenElement.shadowRoot.querySelector(
+    const fill = wallpaperFullscreenElement.shadowRoot!.querySelector(
         'cr-button[data-layout="FILL"]');
 
-    assertEquals('false', center.getAttribute('aria-pressed'));
-    assertEquals('true', fill.getAttribute('aria-pressed'));
+    assertEquals('false', center!.getAttribute('aria-pressed'));
+    assertEquals('true', fill!.getAttribute('aria-pressed'));
 
-    wallpaperFullscreenElement.selectedLayout_ = WallpaperLayout.kCenter;
+    wallpaperFullscreenElement['selectedLayout_'] = WallpaperLayout.kCenter;
     await waitAfterNextRender(wallpaperFullscreenElement);
 
-    assertEquals('true', center.getAttribute('aria-pressed'));
-    assertEquals('false', fill.getAttribute('aria-pressed'));
+    assertEquals('true', center!.getAttribute('aria-pressed'));
+    assertEquals('false', fill!.getAttribute('aria-pressed'));
   });
 
   test('clicking set as wallpaper confirms wallpaper', async () => {
-    wallpaperFullscreenElement = initElement(WallpaperFullscreen.is);
-    const {requestFullscreenPromise} = mockFullscreenApis();
+    wallpaperFullscreenElement = initElement(WallpaperFullscreen);
+    mockFullscreenApis();
     await waitAfterNextRender(wallpaperFullscreenElement);
 
     personalizationStore.data.wallpaper.fullscreen = true;
@@ -280,15 +275,16 @@
       type: WallpaperType.kDaily,
     };
     personalizationStore.data.wallpaper.dailyRefresh.collectionId =
-        wallpaperProvider.collections[0].id;
+        wallpaperProvider.collections![0]!.id;
     personalizationStore.data.wallpaper.pendingSelected =
-        wallpaperProvider.images[1];
+        wallpaperProvider.images![1];
     personalizationStore.notifyObservers();
 
     await waitAfterNextRender(wallpaperFullscreenElement);
 
     const setAsWallpaperButton =
-        wallpaperFullscreenElement.shadowRoot.getElementById('confirm');
+        wallpaperFullscreenElement.shadowRoot!.getElementById('confirm') as
+        HTMLButtonElement;
     setAsWallpaperButton.click();
 
     await wallpaperProvider.whenCalled('confirmPreviewWallpaper');
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
similarity index 77%
rename from chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.js
rename to chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
index 1e3f92cb..937872a 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
@@ -2,26 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {ImageTile} from 'chrome://personalization/common/constants.js';
 import {WallpaperLayout, WallpaperType} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
 import {PersonalizationRouter} from 'chrome://personalization/trusted/personalization_router_element.js';
 import {getDarkLightImageTiles, getRegularImageTiles, promisifyImagesIframeFunctionsForTesting, WallpaperImages} from 'chrome://personalization/trusted/wallpaper/wallpaper_images_element.js';
-
-import {assertDeepEquals, assertEquals, assertFalse} from '../../chai_assert.js';
-import {flushTasks, waitAfterNextRender} from '../../test_util.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {assertDeepEquals, assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {assertWindowObjectsEqual, baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
 import {TestWallpaperProvider} from './test_wallpaper_interface_provider.js';
 
 export function WallpaperImagesTest() {
-  /** @type {?HTMLElement} */
-  let wallpaperImagesElement = null;
-
-  /** @type {?TestWallpaperProvider} */
-  let wallpaperProvider = null;
-
-  /** @type {?TestPersonalizationStore} */
-  let personalizationStore = null;
+  let wallpaperImagesElement: WallpaperImages|null = null;
+  let wallpaperProvider: TestWallpaperProvider;
+  let personalizationStore: TestPersonalizationStore;
 
   setup(() => {
     const mocks = baseSetup();
@@ -45,13 +41,14 @@
         wallpaperProvider.currentWallpaper;
 
     wallpaperImagesElement =
-        initElement(WallpaperImages.is, {collectionId: 'id_0'});
+        initElement(WallpaperImages, {collectionId: 'id_0'});
 
-    const iframe =
-        wallpaperImagesElement.shadowRoot.getElementById('images-iframe');
+    const iframe = wallpaperImagesElement.shadowRoot!.getElementById(
+                       'images-iframe') as HTMLIFrameElement;
 
     // Wait for iframe to receive data.
-    let [targetWindow, data] = await sendCurrentWallpaperAssetIdPromise;
+    let [targetWindow, data] =
+        await sendCurrentWallpaperAssetIdPromise as [Window, bigint | null];
 
     assertEquals(iframe.contentWindow, targetWindow);
     assertDeepEquals(
@@ -72,7 +69,8 @@
     personalizationStore.notifyObservers();
 
     // Wait for iframe to receive data.
-    [targetWindow, data] = await sendCurrentWallpaperAssetIdPromise;
+    [targetWindow, data] =
+        await sendCurrentWallpaperAssetIdPromise as [Window, bigint | null];
     assertEquals(iframe.contentWindow, targetWindow);
     assertDeepEquals(
         BigInt(personalizationStore.data.wallpaper.currentSelected.key), data);
@@ -92,7 +90,8 @@
     personalizationStore.notifyObservers();
 
     // Wait for iframe to receive data.
-    [targetWindow, data] = await sendCurrentWallpaperAssetIdPromise;
+    [targetWindow, data] =
+        await sendCurrentWallpaperAssetIdPromise as [Window, bigint | null];
     assertEquals(iframe.contentWindow, targetWindow);
     assertEquals(undefined, data);
   });
@@ -116,13 +115,14 @@
     let {sendImageTiles: sendImageTilesPromise} =
         promisifyImagesIframeFunctionsForTesting();
     wallpaperImagesElement =
-        initElement(WallpaperImages.is, {collectionId: 'id_0'});
+        initElement(WallpaperImages, {collectionId: 'id_0'});
 
-    const iframe =
-        wallpaperImagesElement.shadowRoot.getElementById('images-iframe');
+    const iframe = wallpaperImagesElement.shadowRoot!.getElementById(
+                       'images-iframe') as HTMLIFrameElement;
 
     // Wait for iframe to receive data.
-    let [targetWindow, data] = await sendImageTilesPromise;
+    let [targetWindow, data] =
+        await sendImageTilesPromise as [Window, ImageTile[]];
     assertEquals(iframe.contentWindow, targetWindow);
     assertDeepEquals(
         getRegularImageTiles(
@@ -137,7 +137,7 @@
     wallpaperImagesElement.collectionId = 'id_1';
 
     // Wait for iframe to receive new data.
-    [targetWindow, data] = await sendImageTilesPromise;
+    [targetWindow, data] = await sendImageTilesPromise as [Window, ImageTile[]];
 
     await waitAfterNextRender(wallpaperImagesElement);
 
@@ -170,23 +170,24 @@
     let {sendImageTiles: sendImageTilesPromise} =
         promisifyImagesIframeFunctionsForTesting();
     wallpaperImagesElement =
-        initElement(WallpaperImages.is, {collectionId: 'id_0'});
+        initElement(WallpaperImages, {collectionId: 'id_0'});
 
-    const iframe =
-        wallpaperImagesElement.shadowRoot.getElementById('images-iframe');
+    const iframe = wallpaperImagesElement.shadowRoot!.getElementById(
+                       'images-iframe') as HTMLIFrameElement;
 
     // Wait for iframe to receive data.
-    let [targetWindow, data] = await sendImageTilesPromise;
+    let [targetWindow, data] =
+        await sendImageTilesPromise as [Window, ImageTile[]];
     assertEquals(iframe.contentWindow, targetWindow);
     const tiles = getDarkLightImageTiles(
         false, personalizationStore.data.wallpaper.backdrop.images['id_0']);
     assertDeepEquals(tiles, data);
-    assertEquals(data[0].preview.length, 2);
+    assertEquals(data[0]!.preview.length, 2);
     // Check that light variant comes before dark variant.
     assertEquals(
-        data[0].preview[0].url, 'https://images.googleusercontent.com/1');
+        data[0]!.preview[0]!.url, 'https://images.googleusercontent.com/1');
     assertEquals(
-        data[0].preview[1].url, 'https://images.googleusercontent.com/0');
+        data[0]!.preview[1]!.url, 'https://images.googleusercontent.com/0');
     // Wait for a render to happen.
     await waitAfterNextRender(wallpaperImagesElement);
     assertFalse(iframe.hidden);
@@ -196,7 +197,7 @@
     wallpaperImagesElement.collectionId = 'id_1';
 
     // Wait for iframe to receive new data.
-    [targetWindow, data] = await sendImageTilesPromise;
+    [targetWindow, data] = await sendImageTilesPromise as [Window, ImageTile[]];
 
     await waitAfterNextRender(wallpaperImagesElement);
 
@@ -210,7 +211,7 @@
   });
 
   test('navigates back to main page on loading failure', async () => {
-    const reloadPromise = new Promise((resolve) => {
+    const reloadPromise = new Promise<void>((resolve) => {
       PersonalizationRouter.reloadAtWallpaper = resolve;
     });
 
@@ -223,7 +224,7 @@
     };
 
     wallpaperImagesElement =
-        initElement(WallpaperImages.is, {collectionId: 'id_0'});
+        initElement(WallpaperImages, {collectionId: 'id_0'});
 
     // Simulate finish loading. Images still null. Should bail and reload
     // application.
@@ -234,7 +235,7 @@
   });
 
   test('navigates back to main page on unknown collection id', async () => {
-    const reloadPromise = new Promise((resolve) => {
+    const reloadPromise = new Promise<void>((resolve) => {
       PersonalizationRouter.reloadAtWallpaper = resolve;
     });
 
@@ -249,7 +250,7 @@
     };
 
     wallpaperImagesElement =
-        initElement(WallpaperImages.is, {collectionId: 'unknown_id'});
+        initElement(WallpaperImages, {collectionId: 'unknown_id'});
 
     await reloadPromise;
   });
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts
similarity index 74%
rename from chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.js
rename to chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts
index 57139813..e00b3b7e 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts
@@ -9,22 +9,17 @@
 import {WallpaperActionName} from 'chrome://personalization/trusted/wallpaper/wallpaper_actions.js';
 import {mockTimeoutForTesting, WallpaperSelected} from 'chrome://personalization/trusted/wallpaper/wallpaper_selected_element.js';
 
-import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertNotReached, assertTrue} from '../../chai_assert.js';
-import {flushTasks, waitAfterNextRender} from '../../test_util.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {baseSetup, initElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
 import {TestWallpaperProvider} from './test_wallpaper_interface_provider.js';
 
 export function WallpaperSelectedTest() {
-  /** @type {?HTMLElement} */
-  let wallpaperSelectedElement = null;
-
-  /** @type {?TestWallpaperProvider} */
-  let wallpaperProvider = null;
-
-  /** @type {?TestPersonalizationStore} */
-  let personalizationStore = null;
+  let wallpaperSelectedElement: WallpaperSelected|null;
+  let wallpaperProvider: TestWallpaperProvider;
+  let personalizationStore: TestPersonalizationStore;
 
   setup(() => {
     const mocks = baseSetup();
@@ -48,17 +43,17 @@
           selected: 1,
           setImage: 0,
         };
-        wallpaperSelectedElement = initElement(WallpaperSelected.is);
+        wallpaperSelectedElement = initElement(WallpaperSelected);
 
         assertEquals(
-            null, wallpaperSelectedElement.shadowRoot.querySelector('img'));
+            null, wallpaperSelectedElement.shadowRoot!.querySelector('img'));
 
         assertEquals(
             null,
-            wallpaperSelectedElement.shadowRoot.getElementById(
+            wallpaperSelectedElement.shadowRoot!.getElementById(
                 'textContainer'));
 
-        const placeholder = wallpaperSelectedElement.shadowRoot.getElementById(
+        const placeholder = wallpaperSelectedElement.shadowRoot!.getElementById(
             'imagePlaceholder');
 
         assertTrue(!!placeholder);
@@ -74,7 +69,7 @@
         personalizationStore.notifyObservers();
         await waitAfterNextRender(wallpaperSelectedElement);
 
-        assertEquals('none', placeholder.style.display);
+        assertEquals('none', placeholder!.style.display);
 
         // Sent a request to update user wallpaper. Loading placeholder should
         // come back.
@@ -86,12 +81,12 @@
         personalizationStore.notifyObservers();
         await waitAfterNextRender(wallpaperSelectedElement);
 
-        assertEquals('', placeholder.style.display);
+        assertEquals('', placeholder!.style.display);
       });
 
   test('sets wallpaper image in store on first load', async () => {
     personalizationStore.expectAction(WallpaperActionName.SET_SELECTED_IMAGE);
-    wallpaperSelectedElement = initElement(WallpaperSelected.is);
+    wallpaperSelectedElement = initElement(WallpaperSelected);
     const action = await personalizationStore.waitForAction(
         WallpaperActionName.SET_SELECTED_IMAGE);
     assertDeepEquals(wallpaperProvider.currentWallpaper, action.image);
@@ -101,32 +96,33 @@
     personalizationStore.data.wallpaper.currentSelected =
         wallpaperProvider.currentWallpaper;
 
-    wallpaperSelectedElement = initElement(WallpaperSelected.is);
+    wallpaperSelectedElement = initElement(WallpaperSelected);
     await waitAfterNextRender(wallpaperSelectedElement);
 
-    const img = wallpaperSelectedElement.shadowRoot.querySelector('img');
+    const img = wallpaperSelectedElement.shadowRoot!.querySelector('img');
     assertEquals(
         `chrome://image/?${wallpaperProvider.currentWallpaper.url.url}`,
-        img.src);
+        img!.src);
 
     const textContainerElements =
-        wallpaperSelectedElement.shadowRoot.querySelectorAll(
+        wallpaperSelectedElement.shadowRoot!.querySelectorAll(
             '#textContainer span');
 
     // First span tag is 'Currently Set' text.
-    assertEquals('currentlySet', textContainerElements[0].id);
+    assertEquals('currentlySet', textContainerElements[0]!.id);
     assertEquals(
         wallpaperSelectedElement.i18n('currentlySet'),
-        textContainerElements[0].textContent);
+        textContainerElements[0]!.textContent);
 
     // Following text elements are for the photo attribution text.
-    const attributionLines = Array.from(textContainerElements).slice(1);
+    const attributionLines =
+        Array.from(textContainerElements).slice(1) as HTMLElement[];
 
     assertEquals(
         wallpaperProvider.currentWallpaper.attribution.length,
         attributionLines.length);
     wallpaperProvider.currentWallpaper.attribution.forEach((line, i) => {
-      assertEquals(line, attributionLines[i].innerText);
+      assertEquals(line, attributionLines[i]!.innerText);
     });
   });
 
@@ -137,14 +133,14 @@
       assetId: BigInt(100),
     };
     personalizationStore.data.wallpaper.loading.selected = false;
-    wallpaperSelectedElement = initElement(WallpaperSelected.is);
+    wallpaperSelectedElement = initElement(WallpaperSelected);
     await waitAfterNextRender(wallpaperSelectedElement);
 
     const title =
-        wallpaperSelectedElement.shadowRoot.getElementById('imageTitle');
+        wallpaperSelectedElement.shadowRoot!.getElementById('imageTitle');
     assertEquals(
         wallpaperSelectedElement.i18n('unknownImageAttribution'),
-        title.textContent.trim());
+        title!.textContent!.trim());
   });
 
   test('removes high resolution suffix from image url', async () => {
@@ -154,12 +150,12 @@
       assetId: BigInt(100),
     };
     personalizationStore.data.wallpaper.loading.selected = false;
-    wallpaperSelectedElement = initElement(WallpaperSelected.is);
+    wallpaperSelectedElement = initElement(WallpaperSelected);
     await waitAfterNextRender(wallpaperSelectedElement);
 
-    const img = wallpaperSelectedElement.shadowRoot.querySelector('img');
+    const img = wallpaperSelectedElement.shadowRoot!.querySelector('img');
     assertEquals(
-        'chrome://image/?https://images.googleusercontent.com/abc12', img.src);
+        'chrome://image/?https://images.googleusercontent.com/abc12', img!.src);
   });
 
   test('updates image when store is updated', async () => {
@@ -167,10 +163,11 @@
         wallpaperProvider.currentWallpaper;
     personalizationStore.data.wallpaper.loading.selected = false;
 
-    wallpaperSelectedElement = initElement(WallpaperSelected.is);
+    wallpaperSelectedElement = initElement(WallpaperSelected);
     await waitAfterNextRender(wallpaperSelectedElement);
 
-    const img = wallpaperSelectedElement.shadowRoot.querySelector('img');
+    const img = wallpaperSelectedElement.shadowRoot!.querySelector('img') as
+        HTMLImageElement;
     assertEquals(
         `chrome://image/?${wallpaperProvider.currentWallpaper.url.url}`,
         img.src);
@@ -187,7 +184,7 @@
   });
 
   test('shows placeholders when image fails to load', async () => {
-    wallpaperSelectedElement = initElement(WallpaperSelected.is);
+    wallpaperSelectedElement = initElement(WallpaperSelected);
     await waitAfterNextRender(wallpaperSelectedElement);
 
     // Still loading.
@@ -197,7 +194,7 @@
     await waitAfterNextRender(wallpaperSelectedElement);
 
     const placeholder =
-        wallpaperSelectedElement.shadowRoot.getElementById('imagePlaceholder');
+        wallpaperSelectedElement.shadowRoot!.getElementById('imagePlaceholder');
     assertTrue(!!placeholder);
 
     // Loading finished and still no current wallpaper.
@@ -207,21 +204,21 @@
 
     // Dom-if will set display: none if the element is hidden. Make sure it is
     // not hidden.
-    assertNotEquals('none', placeholder.style.display);
+    assertNotEquals('none', placeholder!.style.display);
     assertEquals(
-        null, wallpaperSelectedElement.shadowRoot.querySelector('img'));
+        null, wallpaperSelectedElement.shadowRoot!.querySelector('img'));
   });
 
   test('sets selected wallpaper data in store on changed', async () => {
     // Make sure state starts as expected.
     assertDeepEquals(emptyState(), personalizationStore.data);
 
-    wallpaperSelectedElement = initElement(WallpaperSelected.is);
+    wallpaperSelectedElement = initElement(WallpaperSelected);
 
     await wallpaperProvider.whenCalled('setWallpaperObserver');
 
     personalizationStore.expectAction(WallpaperActionName.SET_SELECTED_IMAGE);
-    wallpaperProvider.wallpaperObserverRemote.onWallpaperChanged(
+    wallpaperProvider.wallpaperObserverRemote!.onWallpaperChanged(
         wallpaperProvider.currentWallpaper);
 
     const {image} = await personalizationStore.waitForAction(
@@ -237,11 +234,11 @@
       assetId: BigInt(100),
     };
     personalizationStore.data.wallpaper.loading.selected = false;
-    wallpaperSelectedElement = initElement(WallpaperSelected.is);
+    wallpaperSelectedElement = initElement(WallpaperSelected);
     await waitAfterNextRender(wallpaperSelectedElement);
 
-    const img = wallpaperSelectedElement.shadowRoot.querySelector('img');
-    assertEquals('data:image/png;base64,abc=', img.src);
+    const img = wallpaperSelectedElement.shadowRoot!.querySelector('img');
+    assertEquals('data:image/png;base64,abc=', img!.src);
   });
 
   test('shows daily refresh option on the collection view', async () => {
@@ -253,16 +250,16 @@
     personalizationStore.data.wallpaper.loading.selected = false;
 
     wallpaperSelectedElement =
-        initElement(WallpaperSelected.is, {'path': Paths.CollectionImages});
+        initElement(WallpaperSelected, {'path': Paths.CollectionImages});
     await waitAfterNextRender(wallpaperSelectedElement);
 
     const dailyRefresh =
-        wallpaperSelectedElement.shadowRoot.getElementById('dailyRefresh');
+        wallpaperSelectedElement.shadowRoot!.getElementById('dailyRefresh');
     assertTrue(!!dailyRefresh);
 
     const refreshWallpaper =
-        wallpaperSelectedElement.shadowRoot.getElementById('refreshWallpaper');
-    assertTrue(refreshWallpaper.hidden);
+        wallpaperSelectedElement.shadowRoot!.getElementById('refreshWallpaper');
+    assertTrue(refreshWallpaper!.hidden);
   });
 
   test(
@@ -274,33 +271,33 @@
           assetId: BigInt(100),
         };
         personalizationStore.data.wallpaper.loading.selected = false;
-        const collection_id = wallpaperProvider.collections[0].id;
+        const collection_id = wallpaperProvider.collections![0]!.id;
         personalizationStore.data.wallpaper.dailyRefresh = {
           collectionId: collection_id,
         };
 
         wallpaperSelectedElement = initElement(
-            WallpaperSelected.is,
+            WallpaperSelected,
             {'path': Paths.CollectionImages, 'collectionId': collection_id});
         personalizationStore.notifyObservers();
 
         await waitAfterNextRender(wallpaperSelectedElement);
 
         const newRefreshWallpaper =
-            wallpaperSelectedElement.shadowRoot.getElementById(
+            wallpaperSelectedElement.shadowRoot!.getElementById(
                 'refreshWallpaper');
-        assertFalse(newRefreshWallpaper.hidden);
+        assertFalse(newRefreshWallpaper!.hidden);
       });
 
   test('sets current image to null after timeout', async () => {
-    let timeoutCallback;
+    let timeoutCallback: Function;
     mockTimeoutForTesting({
       setTimeout(callback, delay) {
         assertEquals(120 * 1000, delay);
-        timeoutCallback = callback;
+        timeoutCallback = callback as Function;
         return 1234;
       },
-      clearTimeout(id) {
+      clearTimeout() {
         assertNotReached('Should not clear timeout');
       },
     });
@@ -308,12 +305,12 @@
     wallpaperProvider.wallpaperObserverUpdateTimeout = 100;
 
     wallpaperSelectedElement =
-        initElement(WallpaperSelected.is, {'path': Paths.CollectionImages});
+        initElement(WallpaperSelected, {'path': Paths.CollectionImages});
     await waitAfterNextRender(wallpaperSelectedElement);
 
     personalizationStore.expectAction(WallpaperActionName.SET_SELECTED_IMAGE);
 
-    timeoutCallback.call(wallpaperSelectedElement);
+    timeoutCallback!.call(wallpaperSelectedElement);
 
     const action = await personalizationStore.waitForAction(
         WallpaperActionName.SET_SELECTED_IMAGE);
@@ -322,9 +319,9 @@
 
   test('cancels timeout after receiving first image', async () => {
     const timeoutId = 1234;
-    const clearTimeoutPromise = new Promise(resolve => {
+    const clearTimeoutPromise = new Promise<void>(resolve => {
       mockTimeoutForTesting({
-        setTimeout(callback, delay) {
+        setTimeout() {
           return timeoutId;
         },
         clearTimeout(id) {
@@ -335,10 +332,10 @@
     });
 
     wallpaperSelectedElement =
-        initElement(WallpaperSelected.is, {'path': Paths.CollectionImages});
+        initElement(WallpaperSelected, {'path': Paths.CollectionImages});
     await waitAfterNextRender(wallpaperSelectedElement);
 
-    wallpaperProvider.wallpaperObserverRemote.onWallpaperChanged(
+    wallpaperProvider.wallpaperObserverRemote!.onWallpaperChanged(
         wallpaperProvider.currentWallpaper);
 
     await clearTimeoutPromise;
@@ -348,12 +345,12 @@
     personalizationStore.data.wallpaper.fullscreen = true;
 
     wallpaperSelectedElement =
-        initElement(WallpaperSelected.is, {'path': Paths.CollectionImages});
+        initElement(WallpaperSelected, {'path': Paths.CollectionImages});
     await waitAfterNextRender(wallpaperSelectedElement);
 
     personalizationStore.resetLastAction();
 
-    await wallpaperProvider.wallpaperObserverRemote.onWallpaperChanged(
+    wallpaperProvider.wallpaperObserverRemote!.onWallpaperChanged(
         wallpaperProvider.currentWallpaper);
     await waitAfterNextRender(wallpaperSelectedElement);
 
@@ -364,7 +361,7 @@
 
     personalizationStore.expectAction(WallpaperActionName.SET_SELECTED_IMAGE);
 
-    wallpaperProvider.wallpaperObserverRemote.onWallpaperChanged(
+    wallpaperProvider.wallpaperObserverRemote!.onWallpaperChanged(
         wallpaperProvider.currentWallpaper);
 
     const action = await personalizationStore.waitForAction(
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 9c440d8..479ca2c04 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -56,6 +56,50 @@
     'OSSettingsDevicePageV3Test', 'All',
     () => mocha.grep('/^((?!arrow_key_arrangement_disabled).)*$/').run());
 
+// TODO(crbug.com/1275568): move this to the generic test lists below after the
+// feature is launched.
+var OSSettingsPeoplePageAccountManagerV3Test =
+    class extends OSSettingsV3BrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/people_page_account_manager_test.m.js';
+  }
+
+  /** @override */
+  get featureList() {
+    return {
+      disabled: [
+        'chromeos::features::kArcAccountRestrictions',
+        'chromeos::features::kLacrosSupport'
+      ]
+    };
+  }
+};
+
+TEST_F('OSSettingsPeoplePageAccountManagerV3Test', 'All', () => mocha.run());
+
+var OSSettingsPeoplePageAccountManagerWithArcAccountRestrictionsEnabledV3Test =
+    class extends OSSettingsV3BrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://os-settings/test_loader.html?module=settings/chromeos/people_page_account_manager_test.m.js';
+  }
+
+  /** @override */
+  get featureList() {
+    return {
+      enabled: [
+        'chromeos::features::kArcAccountRestrictions',
+        'chromeos::features::kLacrosSupport'
+      ]
+    };
+  }
+};
+
+TEST_F(
+    'OSSettingsPeoplePageAccountManagerWithArcAccountRestrictionsEnabledV3Test',
+    'All', () => mocha.run());
+
 var OSSettingsDevicePageKeyboardArrangementDisabledV3Test =
     class extends OSSettingsV3BrowserTest {
   /** @override */
@@ -320,7 +364,6 @@
  ['NearbyShareReceiveDialog', 'nearby_share_receive_dialog_tests.m.js'],
  ['ParentalControlsPage', 'parental_controls_page_test.m.js'],
  ['PeoplePage', 'os_people_page_test.m.js'],
- ['PeoplePageAccountManager', 'people_page_account_manager_test.m.js'],
  ['PeoplePageChangePicture', 'people_page_change_picture_test.m.js'],
  [
    'PeoplePageQuickUnlock',
diff --git a/chrome/test/data/webui/settings/chromeos/people_page_account_manager_test.js b/chrome/test/data/webui/settings/chromeos/people_page_account_manager_test.js
index 78c52e4b..67ebe0d 100644
--- a/chrome/test/data/webui/settings/chromeos/people_page_account_manager_test.js
+++ b/chrome/test/data/webui/settings/chromeos/people_page_account_manager_test.js
@@ -40,6 +40,7 @@
           fullName: 'Device Account',
           email: 'admin@domain.com',
           pic: 'data:image/png;base64,abc123',
+          isAvailableInArc: true,
           organization: 'Family Link',
         },
         {
@@ -51,6 +52,7 @@
           fullName: 'Secondary Account 1',
           email: 'user1@example.com',
           pic: '',
+          isAvailableInArc: true,
         },
         {
           id: '789',
@@ -61,6 +63,7 @@
           fullName: 'Secondary Account 2',
           email: 'user2@example.com',
           pic: '',
+          isAvailableInArc: true,
         },
         {
           id: '1010',
@@ -71,6 +74,7 @@
           fullName: 'Secondary Account 3',
           email: 'user3@example.com',
           pic: '',
+          isAvailableInArc: false,
         }
       ]);
     }
@@ -231,6 +235,13 @@
       // Click on 'Remove account'
       accountManager.$$('cr-action-menu').querySelector('button').click();
 
+      if (loadTimeData.getBoolean('lacrosEnabled')) {
+        const confirmationDialog =
+            accountManager.$$('#removeConfirmationDialog');
+        assertTrue(!!confirmationDialog);
+        confirmationDialog.querySelector('#removeConfirmationButton').click();
+      }
+
       const account = await browserProxy.whenCalled('removeAccount');
       assertEquals('456', account.id);
       // Add account button should be in focus now.
@@ -278,6 +289,21 @@
         // Managed badge should be shown for managed accounts.
         assertFalse(managedBadge.hidden);
     });
+
+    test('ArcAvailabilityIsShownForSecondaryAccounts', async function() {
+      if (!loadTimeData.getBoolean('arcAccountRestrictionsEnabled')) {
+        return;
+      }
+
+      await browserProxy.whenCalled('getAccounts');
+      Polymer.dom.flush();
+
+      accountList.items.forEach((item, i) => {
+        const notAvailableInArc =
+            accountManager.root.querySelectorAll('.arc-availability')[i];
+        assertEquals(item.isAvailableInArc, notAvailableInArc.hidden);
+      });
+    });
   });
 
   suite('AccountManagerUnmanagedAccountTests', function() {
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index c9e294d..c1ac463 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-14413.0.0
\ No newline at end of file
+14417.0.0
\ No newline at end of file
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index f619f860..bcc4357f 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-98-4758.0-1639998454-benchmark-98.0.4758.20-r1.orderfile.xz
+chromeos-chrome-orderfile-field-98-4758.0-1639998454-benchmark-98.0.4758.21-r1.orderfile.xz
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 6fbfc77..9ffeab6 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "4.67",
-  "log_list_timestamp": "2021-12-23T01:35:39Z",
+  "version": "4.68",
+  "log_list_timestamp": "2021-12-24T01:34:39Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/policy/test_support/BUILD.gn b/components/policy/test_support/BUILD.gn
index b8d7860..48760ac 100644
--- a/components/policy/test_support/BUILD.gn
+++ b/components/policy/test_support/BUILD.gn
@@ -12,18 +12,38 @@
     "client_storage.h",
     "embedded_policy_test_server.cc",
     "embedded_policy_test_server.h",
+    "failing_request_handler.cc",
+    "failing_request_handler.h",
     "policy_storage.cc",
     "policy_storage.h",
     "request_handler_for_api_authorization.cc",
     "request_handler_for_api_authorization.h",
+    "request_handler_for_auto_enrollment.cc",
+    "request_handler_for_auto_enrollment.h",
     "request_handler_for_chrome_desktop_report.cc",
     "request_handler_for_chrome_desktop_report.h",
+    "request_handler_for_device_attribute_update.cc",
+    "request_handler_for_device_attribute_update.h",
+    "request_handler_for_device_attribute_update_permission.cc",
+    "request_handler_for_device_attribute_update_permission.h",
+    "request_handler_for_device_initial_enrollment_state.cc",
+    "request_handler_for_device_initial_enrollment_state.h",
+    "request_handler_for_device_state_retrieval.cc",
+    "request_handler_for_device_state_retrieval.h",
     "request_handler_for_policy.cc",
     "request_handler_for_policy.h",
+    "request_handler_for_psm_auto_enrollment.cc",
+    "request_handler_for_psm_auto_enrollment.h",
     "request_handler_for_register_browser.cc",
     "request_handler_for_register_browser.h",
+    "request_handler_for_register_cert_based.cc",
+    "request_handler_for_register_cert_based.h",
     "request_handler_for_register_device_and_user.cc",
     "request_handler_for_register_device_and_user.h",
+    "request_handler_for_remote_commands.cc",
+    "request_handler_for_remote_commands.h",
+    "request_handler_for_status_upload.cc",
+    "request_handler_for_status_upload.h",
     "signature_provider.cc",
     "signature_provider.h",
     "test_server_helpers.cc",
@@ -46,6 +66,7 @@
     "//google_apis:google_apis",
     "//net",
     "//net:test_support",
+    "//third_party/private_membership:private_membership_proto",
     "//third_party/re2:re2",
   ]
 }
@@ -57,11 +78,21 @@
     "embedded_policy_test_server_test_base.cc",
     "embedded_policy_test_server_test_base.h",
     "embedded_policy_test_server_unittest.cc",
+    "failing_request_handler_unittest.cc",
     "request_handler_for_api_authorization_unittest.cc",
+    "request_handler_for_auto_enrollment_unittest.cc",
     "request_handler_for_chrome_desktop_report_unittest.cc",
+    "request_handler_for_device_attribute_update_permission_unittest.cc",
+    "request_handler_for_device_attribute_update_unittest.cc",
+    "request_handler_for_device_initial_enrollment_state_unittest.cc",
+    "request_handler_for_device_state_retrieval_unittest.cc",
     "request_handler_for_policy_unittest.cc",
+    "request_handler_for_psm_auto_enrollment_unittest.cc",
     "request_handler_for_register_browser_unittest.cc",
+    "request_handler_for_register_cert_based_unittest.cc",
     "request_handler_for_register_device_and_user_unittest.cc",
+    "request_handler_for_remote_commands_unittest.cc",
+    "request_handler_for_status_upload_unittest.cc",
     "signature_provider_unittest.cc",
   ]
 
diff --git a/components/policy/test_support/client_storage.cc b/components/policy/test_support/client_storage.cc
index 8c628c3..524cece 100644
--- a/components/policy/test_support/client_storage.cc
+++ b/components/policy/test_support/client_storage.cc
@@ -5,6 +5,8 @@
 #include "components/policy/test_support/client_storage.h"
 
 #include "base/check.h"
+#include "base/containers/contains.h"
+#include "crypto/sha2.h"
 
 namespace policy {
 
@@ -57,8 +59,32 @@
   return it == clients_.end() ? nullptr : &it->second;
 }
 
+const ClientStorage::ClientInfo* ClientStorage::LookupByStateKey(
+    const std::string& state_key) const {
+  for (auto const& [device_id, client_info] : clients_) {
+    if (base::Contains(client_info.state_keys, state_key))
+      return &client_info;
+  }
+  return nullptr;
+}
+
 size_t ClientStorage::GetNumberOfRegisteredClients() const {
   return clients_.size();
 }
 
+std::vector<std::string> ClientStorage::GetMatchingStateKeyHashes(
+    uint64_t modulus,
+    uint64_t remainder) const {
+  std::vector<std::string> hashes;
+  for (const auto& [device_id, client_info] : clients_) {
+    // This does not actually divide hashes by |modulus| and verifies that
+    // |remainder| is correct as current tests do not rely on this behavior.
+    // This is difficult to implement since 32-byte hashes do not fit into
+    // regular integer types and thus long-arithmetic approach is needed.
+    for (const std::string& key : client_info.state_keys)
+      hashes.push_back(crypto::SHA256HashString(key));
+  }
+  return hashes;
+}
+
 }  // namespace policy
diff --git a/components/policy/test_support/client_storage.h b/components/policy/test_support/client_storage.h
index 74c6701..2b2058a 100644
--- a/components/policy/test_support/client_storage.h
+++ b/components/policy/test_support/client_storage.h
@@ -54,9 +54,18 @@
   // no such a client.
   const ClientInfo* GetClientOrNull(const std::string& device_id) const;
 
+  // Returns the client info associated with |state_key| or nullptr if there is
+  // no such a client.
+  const ClientInfo* LookupByStateKey(const std::string& state_key) const;
+
   // Returns the number of clients registered.
   size_t GetNumberOfRegisteredClients() const;
 
+  // Returns hashes for all state keys registered with the server, which, when
+  // divied by |modulus|, result in the specified |remainder|.
+  std::vector<std::string> GetMatchingStateKeyHashes(uint64_t modulus,
+                                                     uint64_t remainder) const;
+
  private:
   // Key: device ids.
   std::map<std::string, ClientInfo> clients_;
diff --git a/components/policy/test_support/embedded_policy_test_server.cc b/components/policy/test_support/embedded_policy_test_server.cc
index dc10123..6245380 100644
--- a/components/policy/test_support/embedded_policy_test_server.cc
+++ b/components/policy/test_support/embedded_policy_test_server.cc
@@ -4,24 +4,34 @@
 
 #include "components/policy/test_support/embedded_policy_test_server.h"
 
+#include <memory>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/logging.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/failing_request_handler.h"
 #include "components/policy/test_support/policy_storage.h"
 #include "components/policy/test_support/request_handler_for_api_authorization.h"
+#include "components/policy/test_support/request_handler_for_auto_enrollment.h"
 #include "components/policy/test_support/request_handler_for_chrome_desktop_report.h"
+#include "components/policy/test_support/request_handler_for_device_attribute_update.h"
+#include "components/policy/test_support/request_handler_for_device_attribute_update_permission.h"
+#include "components/policy/test_support/request_handler_for_device_initial_enrollment_state.h"
+#include "components/policy/test_support/request_handler_for_device_state_retrieval.h"
 #include "components/policy/test_support/request_handler_for_policy.h"
+#include "components/policy/test_support/request_handler_for_psm_auto_enrollment.h"
 #include "components/policy/test_support/request_handler_for_register_browser.h"
+#include "components/policy/test_support/request_handler_for_register_cert_based.h"
 #include "components/policy/test_support/request_handler_for_register_device_and_user.h"
+#include "components/policy/test_support/request_handler_for_remote_commands.h"
+#include "components/policy/test_support/request_handler_for_status_upload.h"
 #include "components/policy/test_support/test_server_helpers.h"
 #include "net/http/http_status_code.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 
-using ::net::test_server::BasicHttpResponse;
 using ::net::test_server::EmbeddedTestServer;
 using ::net::test_server::HttpRequest;
 using ::net::test_server::HttpResponse;
@@ -36,8 +46,8 @@
   if (!response)
     return nullptr;
 
-  BasicHttpResponse* basic_response =
-      static_cast<BasicHttpResponse*>(response.get());
+  CustomHttpResponse* basic_response =
+      static_cast<CustomHttpResponse*>(response.get());
   if (basic_response->code() == net::HTTP_OK) {
     DLOG(INFO) << "Request succeeded: " << url;
   } else {
@@ -65,14 +75,34 @@
       policy_storage_(std::make_unique<PolicyStorage>()) {
   RegisterHandler(std::make_unique<RequestHandlerForApiAuthorization>(
       client_storage_.get(), policy_storage_.get()));
+  RegisterHandler(std::make_unique<RequestHandlerForAutoEnrollment>(
+      client_storage_.get(), policy_storage_.get()));
   RegisterHandler(std::make_unique<RequestHandlerForChromeDesktopReport>(
       client_storage_.get(), policy_storage_.get()));
+  RegisterHandler(std::make_unique<RequestHandlerForDeviceAttributeUpdate>(
+      client_storage_.get(), policy_storage_.get()));
+  RegisterHandler(
+      std::make_unique<RequestHandlerForDeviceAttributeUpdatePermission>(
+          client_storage_.get(), policy_storage_.get()));
+  RegisterHandler(
+      std::make_unique<RequestHandlerForDeviceInitialEnrollmentState>(
+          client_storage_.get(), policy_storage_.get()));
+  RegisterHandler(std::make_unique<RequestHandlerForDeviceStateRetrieval>(
+      client_storage_.get(), policy_storage_.get()));
   RegisterHandler(std::make_unique<RequestHandlerForPolicy>(
       client_storage_.get(), policy_storage_.get()));
+  RegisterHandler(std::make_unique<RequestHandlerForPsmAutoEnrollment>(
+      client_storage_.get(), policy_storage_.get()));
   RegisterHandler(std::make_unique<RequestHandlerForRegisterBrowser>(
       client_storage_.get(), policy_storage_.get()));
+  RegisterHandler(std::make_unique<RequestHandlerForRegisterCertBased>(
+      client_storage_.get(), policy_storage_.get()));
   RegisterHandler(std::make_unique<RequestHandlerForRegisterDeviceAndUser>(
       client_storage_.get(), policy_storage_.get()));
+  RegisterHandler(std::make_unique<RequestHandlerForRemoteCommands>(
+      client_storage_.get(), policy_storage_.get()));
+  RegisterHandler(std::make_unique<RequestHandlerForStatusUpload>(
+      client_storage_.get(), policy_storage_.get()));
 
   http_server_.RegisterDefaultHandler(base::BindRepeating(
       &EmbeddedPolicyTestServer::HandleRequest, base::Unretained(this)));
@@ -94,6 +124,13 @@
       std::move(request_handler);
 }
 
+void EmbeddedPolicyTestServer::ConfigureRequestError(
+    const std::string& request_type,
+    net::HttpStatusCode error_code) {
+  RegisterHandler(std::make_unique<FailingRequestHandler>(
+      client_storage_.get(), policy_storage_.get(), request_type, error_code));
+}
+
 std::unique_ptr<HttpResponse> EmbeddedPolicyTestServer::HandleRequest(
     const HttpRequest& request) {
   GURL url = request.GetURL();
diff --git a/components/policy/test_support/embedded_policy_test_server.h b/components/policy/test_support/embedded_policy_test_server.h
index 7e236faf..4b05eb01 100644
--- a/components/policy/test_support/embedded_policy_test_server.h
+++ b/components/policy/test_support/embedded_policy_test_server.h
@@ -13,6 +13,7 @@
 #include "base/memory/raw_ptr.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_response.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -78,6 +79,11 @@
   void RegisterHandler(std::unique_ptr<EmbeddedPolicyTestServer::RequestHandler>
                            request_handler);
 
+  // Configures requests of a given |request_type| to always fail with
+  // |error_code|.
+  void ConfigureRequestError(const std::string& request_type,
+                             net::HttpStatusCode error_code);
+
  private:
   // Default request handler.
   std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
diff --git a/components/policy/test_support/failing_request_handler.cc b/components/policy/test_support/failing_request_handler.cc
new file mode 100644
index 0000000..0c39f976
--- /dev/null
+++ b/components/policy/test_support/failing_request_handler.cc
@@ -0,0 +1,34 @@
+// Copyright 2021 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/policy/test_support/failing_request_handler.h"
+
+#include "components/policy/test_support/test_server_helpers.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+namespace policy {
+
+FailingRequestHandler::FailingRequestHandler(ClientStorage* client_storage,
+                                             PolicyStorage* policy_storage,
+                                             const std::string& request_type,
+                                             net::HttpStatusCode error_code)
+    : EmbeddedPolicyTestServer::RequestHandler(client_storage, policy_storage),
+      request_type_(request_type),
+      error_code_(error_code) {}
+
+FailingRequestHandler::~FailingRequestHandler() = default;
+
+std::string FailingRequestHandler::RequestType() {
+  return request_type_;
+}
+
+std::unique_ptr<net::test_server::HttpResponse>
+FailingRequestHandler::HandleRequest(
+    const net::test_server::HttpRequest& request) {
+  return CreateHttpResponse(error_code_, "Preconfigured error");
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/failing_request_handler.h b/components/policy/test_support/failing_request_handler.h
new file mode 100644
index 0000000..7b5f284
--- /dev/null
+++ b/components/policy/test_support/failing_request_handler.h
@@ -0,0 +1,36 @@
+// Copyright 2021 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_POLICY_TEST_SUPPORT_FAILING_REQUEST_HANDLER_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_FAILING_REQUEST_HANDLER_H_
+
+#include "components/policy/test_support/embedded_policy_test_server.h"
+#include "net/http/http_status_code.h"
+
+namespace policy {
+
+// Handler that always returns specified error code for a given request type.
+class FailingRequestHandler : public EmbeddedPolicyTestServer::RequestHandler {
+ public:
+  FailingRequestHandler(ClientStorage* client_storage,
+                        PolicyStorage* policy_storage,
+                        const std::string& request_type,
+                        net::HttpStatusCode error_code);
+  FailingRequestHandler(FailingRequestHandler&& handler) = delete;
+  FailingRequestHandler& operator=(FailingRequestHandler&& handler) = delete;
+  ~FailingRequestHandler() override;
+
+  // EmbeddedPolicyTestServer::RequestHandler:
+  std::string RequestType() override;
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) override;
+
+ private:
+  std::string request_type_;
+  net::HttpStatusCode error_code_;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_FAILING_REQUEST_HANDLER_H_
diff --git a/components/policy/test_support/failing_request_handler_unittest.cc b/components/policy/test_support/failing_request_handler_unittest.cc
new file mode 100644
index 0000000..373352bf
--- /dev/null
+++ b/components/policy/test_support/failing_request_handler_unittest.cc
@@ -0,0 +1,47 @@
+// Copyright 2021 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/policy/test_support/failing_request_handler.h"
+
+#include <utility>
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/test_support/embedded_policy_test_server_test_base.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "net/http/http_status_code.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+constexpr char kDeviceId[] = "fake_device_id";
+
+}  // namespace
+
+class FailingRequestHandlerTest : public EmbeddedPolicyTestServerTestBase {
+ public:
+  FailingRequestHandlerTest() = default;
+  ~FailingRequestHandlerTest() override = default;
+
+  void SetUp() override {
+    EmbeddedPolicyTestServerTestBase::SetUp();
+
+    SetRequestTypeParam(dm_protocol::kValueRequestRegister);
+    SetAppType(dm_protocol::kValueAppType);
+    SetDeviceIdParam(kDeviceId);
+    SetDeviceType(dm_protocol::kValueDeviceType);
+  }
+};
+
+TEST_F(FailingRequestHandlerTest, HandleRequest) {
+  test_server()->ConfigureRequestError(dm_protocol::kValueRequestRegister,
+                                       net::HTTP_METHOD_NOT_ALLOWED);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_METHOD_NOT_ALLOWED);
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/policy_storage.cc b/components/policy/test_support/policy_storage.cc
index 793eaf2..26c3bc7 100644
--- a/components/policy/test_support/policy_storage.cc
+++ b/components/policy/test_support/policy_storage.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "components/policy/test_support/policy_storage.h"
+#include "crypto/sha2.h"
 
 namespace policy {
 
@@ -27,4 +28,51 @@
   policy_payloads_[policy_type] = policy_payload;
 }
 
+void PolicyStorage::SetPsmEntry(const std::string& brand_serial_id,
+                                const PolicyStorage::PsmEntry& psm_entry) {
+  psm_entries_[brand_serial_id] = psm_entry;
+}
+
+const PolicyStorage::PsmEntry* PolicyStorage::GetPsmEntry(
+    const std::string& brand_serial_id) const {
+  auto it = psm_entries_.find(brand_serial_id);
+  if (it == psm_entries_.end())
+    return nullptr;
+  return &it->second;
+}
+
+void PolicyStorage::SetInitialEnrollmentState(
+    const std::string& brand_serial_id,
+    const PolicyStorage::InitialEnrollmentState& initial_enrollment_state) {
+  initial_enrollment_states_[brand_serial_id] = initial_enrollment_state;
+}
+
+const PolicyStorage::InitialEnrollmentState*
+PolicyStorage::GetInitialEnrollmentState(
+    const std::string& brand_serial_id) const {
+  auto it = initial_enrollment_states_.find(brand_serial_id);
+  if (it == initial_enrollment_states_.end())
+    return nullptr;
+  return &it->second;
+}
+
+std::vector<std::string> PolicyStorage::GetMatchingSerialHashes(
+    uint64_t modulus,
+    uint64_t remainder) const {
+  std::vector<std::string> hashes;
+  for (const auto& [serial, enrollment_state] : initial_enrollment_states_) {
+    uint64_t hash = 0;
+    char hstr[sizeof(hash)];
+    crypto::SHA256HashString(serial, &hstr, sizeof(hash));
+    // Convert hash string to uint64_t using big-endian byte-order.
+    for (size_t i = 0; i < sizeof(hash); i++) {
+      uint64_t byte = static_cast<unsigned char>(hstr[i]);
+      hash += byte << (sizeof(hash) - i - 1) * CHAR_BIT;
+    }
+    if (hash % modulus == remainder)
+      hashes.emplace_back(hstr, sizeof(uint64_t));
+  }
+  return hashes;
+}
+
 }  // namespace policy
diff --git a/components/policy/test_support/policy_storage.h b/components/policy/test_support/policy_storage.h
index 2b878e6..e8a840a6 100644
--- a/components/policy/test_support/policy_storage.h
+++ b/components/policy/test_support/policy_storage.h
@@ -7,13 +7,14 @@
 
 #include <stdint.h>
 
-#include <map>
 #include <memory>
-#include <set>
 #include <string>
 #include <utility>
 
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
 #include "base/time/time.h"
+#include "components/policy/proto/device_management_backend.pb.h"
 #include "components/policy/test_support/signature_provider.h"
 
 namespace policy {
@@ -59,7 +60,9 @@
     service_account_identity_ = service_account_identity;
   }
 
-  const std::set<std::string>& managed_users() const { return managed_users_; }
+  const base::flat_set<std::string>& managed_users() const {
+    return managed_users_;
+  }
   void add_managed_user(const std::string& managed_user) {
     managed_users_.insert(managed_user);
   }
@@ -80,11 +83,56 @@
   base::Time timestamp() const { return timestamp_; }
   void set_timestamp(const base::Time& timestamp) { timestamp_ = timestamp; }
 
+  bool allow_set_device_attributes() { return allow_set_device_attributes_; }
+  void set_allow_set_device_attributes(bool allow_set_device_attributes) {
+    allow_set_device_attributes_ = allow_set_device_attributes;
+  }
+
+  struct DeviceState {
+    std::string management_domain;
+    enterprise_management::DeviceStateRetrievalResponse::RestoreMode
+        restore_mode;
+  };
+
+  const DeviceState& device_state() { return device_state_; }
+  void set_device_state(const DeviceState& device_state) {
+    device_state_ = device_state;
+  }
+
+  struct PsmEntry {
+    int psm_execution_result;
+    int64_t psm_determination_timestamp;
+  };
+
+  void SetPsmEntry(const std::string& brand_serial_id,
+                   const PsmEntry& psm_entry);
+
+  const PsmEntry* GetPsmEntry(const std::string& brand_serial_id) const;
+
+  struct InitialEnrollmentState {
+    enterprise_management::DeviceInitialEnrollmentStateResponse::
+        InitialEnrollmentMode initial_enrollment_mode;
+    std::string management_domain;
+  };
+
+  void SetInitialEnrollmentState(
+      const std::string& brand_serial_id,
+      const InitialEnrollmentState& initial_enrollment_state);
+
+  const InitialEnrollmentState* GetInitialEnrollmentState(
+      const std::string& brand_serial_id) const;
+
+  // Returns hashes for brand serial IDs whose initial enrollment state is
+  // registered on the server. Only hashes, which, when divied by |modulus|,
+  // result in the specified |remainder|, are returned.
+  std::vector<std::string> GetMatchingSerialHashes(uint64_t modulus,
+                                                   uint64_t remainder) const;
+
  private:
   // Maps policy types to a serialized proto representing the policies to be
   // applied for the type (e.g. CloudPolicySettings,
   // ChromeDeviceSettingsProto).
-  std::map<std::string, std::string> policy_payloads_;
+  base::flat_map<std::string, std::string> policy_payloads_;
 
   std::unique_ptr<SignatureProvider> signature_provider_;
 
@@ -92,13 +140,24 @@
 
   std::string service_account_identity_;
 
-  std::set<std::string> managed_users_;
+  base::flat_set<std::string> managed_users_;
 
   std::string policy_user_;
 
   std::string policy_invalidation_topic_;
 
   base::Time timestamp_;
+
+  bool allow_set_device_attributes_ = true;
+
+  DeviceState device_state_;
+
+  // Maps brand serial ID to PsmEntry.
+  base::flat_map<std::string, PsmEntry> psm_entries_;
+
+  // Maps brand serial ID to InitialEnrollmentState.
+  base::flat_map<std::string, InitialEnrollmentState>
+      initial_enrollment_states_;
 };
 
 }  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_api_authorization_unittest.cc b/components/policy/test_support/request_handler_for_api_authorization_unittest.cc
index 77c4d659..99d6818 100644
--- a/components/policy/test_support/request_handler_for_api_authorization_unittest.cc
+++ b/components/policy/test_support/request_handler_for_api_authorization_unittest.cc
@@ -44,9 +44,9 @@
 
   EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
   ASSERT_TRUE(HasResponseBody());
-  EXPECT_EQ(
-      GetDeviceManagementResponse().service_api_access_response().auth_code(),
-      kRobotApiAuthCode);
+  auto response = GetDeviceManagementResponse();
+  EXPECT_EQ(response.service_api_access_response().auth_code(),
+            kRobotApiAuthCode);
 }
 
 }  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_auto_enrollment.cc b/components/policy/test_support/request_handler_for_auto_enrollment.cc
new file mode 100644
index 0000000..6c4a505
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_auto_enrollment.cc
@@ -0,0 +1,83 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_auto_enrollment.h"
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/test_server_helpers.h"
+#include "net/base/url_util.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+void AddHashes(const std::vector<std::string>& hashes,
+               em::DeviceAutoEnrollmentResponse* response) {
+  for (const std::string& hash : hashes)
+    *response->add_hashes() = hash;
+}
+
+}  // namespace
+
+RequestHandlerForAutoEnrollment::RequestHandlerForAutoEnrollment(
+    ClientStorage* client_storage,
+    PolicyStorage* policy_storage)
+    : EmbeddedPolicyTestServer::RequestHandler(client_storage, policy_storage) {
+}
+
+RequestHandlerForAutoEnrollment::~RequestHandlerForAutoEnrollment() = default;
+
+std::string RequestHandlerForAutoEnrollment::RequestType() {
+  return dm_protocol::kValueRequestAutoEnrollment;
+}
+
+std::unique_ptr<HttpResponse> RequestHandlerForAutoEnrollment::HandleRequest(
+    const HttpRequest& request) {
+  em::DeviceManagementRequest device_management_request;
+  device_management_request.ParseFromString(request.content);
+  const em::DeviceAutoEnrollmentRequest& enrollment_request =
+      device_management_request.auto_enrollment_request();
+
+  em::DeviceManagementResponse device_management_response;
+  em::DeviceAutoEnrollmentResponse* enrollment_response =
+      device_management_response.mutable_auto_enrollment_response();
+  switch (enrollment_request.modulus()) {
+    case 1:
+      if (enrollment_request.enrollment_check_type() ==
+          enterprise_management::DeviceAutoEnrollmentRequest::
+              ENROLLMENT_CHECK_TYPE_FRE) {
+        AddHashes(
+            client_storage()->GetMatchingStateKeyHashes(
+                enrollment_request.modulus(), enrollment_request.remainder()),
+            enrollment_response);
+      } else if (enrollment_request.enrollment_check_type() ==
+                 enterprise_management::DeviceAutoEnrollmentRequest::
+                     ENROLLMENT_CHECK_TYPE_FORCED_ENROLLMENT) {
+        AddHashes(
+            policy_storage()->GetMatchingSerialHashes(
+                enrollment_request.modulus(), enrollment_request.remainder()),
+            enrollment_response);
+      }
+      break;
+    case 32:
+      enrollment_response->set_expected_modulus(1);
+      break;
+  }
+
+  return CreateHttpResponse(net::HTTP_OK,
+                            device_management_response.SerializeAsString());
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_auto_enrollment.h b/components/policy/test_support/request_handler_for_auto_enrollment.h
new file mode 100644
index 0000000..9e13d65
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_auto_enrollment.h
@@ -0,0 +1,32 @@
+// Copyright 2021 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_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_AUTO_ENROLLMENT_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_AUTO_ENROLLMENT_H_
+
+#include "components/policy/test_support/embedded_policy_test_server.h"
+
+namespace policy {
+
+// Handler for request type `enterprise_check`.
+class RequestHandlerForAutoEnrollment
+    : public EmbeddedPolicyTestServer::RequestHandler {
+ public:
+  RequestHandlerForAutoEnrollment(ClientStorage* client_storage,
+                                  PolicyStorage* policy_storage);
+  RequestHandlerForAutoEnrollment(RequestHandlerForAutoEnrollment&& handler) =
+      delete;
+  RequestHandlerForAutoEnrollment& operator=(
+      RequestHandlerForAutoEnrollment&& handler) = delete;
+  ~RequestHandlerForAutoEnrollment() override;
+
+  // EmbeddedPolicyTestServer::RequestHandler:
+  std::string RequestType() override;
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) override;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_AUTO_ENROLLMENT_H_
diff --git a/components/policy/test_support/request_handler_for_auto_enrollment_unittest.cc b/components/policy/test_support/request_handler_for_auto_enrollment_unittest.cc
new file mode 100644
index 0000000..80ceb08b
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_auto_enrollment_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2021 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 "base/strings/strcat.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/embedded_policy_test_server_test_base.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/request_handler_for_register_browser.h"
+#include "device_management_backend.pb.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+using testing::IsEmpty;
+using testing::UnorderedElementsAreArray;
+
+constexpr char kDeviceId[] = "fake_device_id";
+constexpr char kBrandSerial[] = "ABC21345748";
+constexpr char kTrimmedSHA256HashForBrandSerial[] =
+    "\x8C\xB0\x9C\xC2\x11\x70\x9B\xB1";
+constexpr char kStateKey1[] = "fake_state_key_1";
+constexpr char kStateKey2[] = "fake_state_key_2";
+constexpr char kSHA256HashForStateKey1[] =
+    "\xB0\x58\x21\x15\x1E\xF5\xEE\x95\x50\xE7\x7D\xB5\x62\x8F\x44\x5A\xE2\x83"
+    "\xB1\xD1\x2C\x87\x22\x85\x50\xF9\x9C\x33\x34\x0F\x42\x13";
+constexpr char kSHA256HashForStateKey2[] =
+    "\xBC\x11\x2A\x4D\x1A\x7F\xA8\x66\xCA\x4F\xF4\xD8\xC3\x0B\xC3\x5B\x83\x0A"
+    "\x82\xF1\x2C\x0C\x1A\xBE\x34\xA7\xAD\xF6\x29\x88\x18\x6D";
+
+}  // namespace
+
+class RequestHandlerForAutoEnrollmentTest
+    : public EmbeddedPolicyTestServerTestBase {
+ protected:
+  RequestHandlerForAutoEnrollmentTest() = default;
+  ~RequestHandlerForAutoEnrollmentTest() override = default;
+
+  void SetUp() override {
+    EmbeddedPolicyTestServerTestBase::SetUp();
+
+    SetRequestTypeParam(dm_protocol::kValueRequestAutoEnrollment);
+    SetAppType(dm_protocol::kValueAppType);
+    SetDeviceIdParam(kDeviceId);
+    SetDeviceType(dm_protocol::kValueDeviceType);
+  }
+};
+
+TEST_F(RequestHandlerForAutoEnrollmentTest, HandleRequest_ForcedEnrollment) {
+  policy_storage()->SetInitialEnrollmentState(
+      kBrandSerial, PolicyStorage::InitialEnrollmentState{});
+
+  em::DeviceManagementRequest device_management_request;
+  em::DeviceAutoEnrollmentRequest* enrollment_request =
+      device_management_request.mutable_auto_enrollment_request();
+  enrollment_request->set_enrollment_check_type(
+      em::DeviceAutoEnrollmentRequest::ENROLLMENT_CHECK_TYPE_FORCED_ENROLLMENT);
+  // This matches any serial hash since dividing by 1 always gives remainder 0.
+  enrollment_request->set_modulus(1);
+  enrollment_request->set_remainder(0);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_THAT(response.auto_enrollment_response().hashes(),
+              UnorderedElementsAreArray({kTrimmedSHA256HashForBrandSerial}));
+}
+
+TEST_F(RequestHandlerForAutoEnrollmentTest,
+       HandleRequest_ForcedEnrollmentMismatchingRemainder) {
+  policy_storage()->SetInitialEnrollmentState(
+      kBrandSerial, PolicyStorage::InitialEnrollmentState{});
+
+  em::DeviceManagementRequest device_management_request;
+  em::DeviceAutoEnrollmentRequest* enrollment_request =
+      device_management_request.mutable_auto_enrollment_request();
+  enrollment_request->set_enrollment_check_type(
+      em::DeviceAutoEnrollmentRequest::ENROLLMENT_CHECK_TYPE_FORCED_ENROLLMENT);
+  // Set impossible remainder to ensure than no serial hash matches it.
+  enrollment_request->set_modulus(1);
+  enrollment_request->set_remainder(1);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_THAT(response.auto_enrollment_response().hashes(), IsEmpty());
+}
+
+TEST_F(RequestHandlerForAutoEnrollmentTest, HandleRequest_ForcedReEnrollment) {
+  ClientStorage::ClientInfo client_info;
+  client_info.device_id = kDeviceId;
+  client_info.state_keys.push_back(kStateKey1);
+  client_info.state_keys.push_back(kStateKey2);
+  client_storage()->RegisterClient(client_info);
+
+  em::DeviceManagementRequest device_management_request;
+  em::DeviceAutoEnrollmentRequest* enrollment_request =
+      device_management_request.mutable_auto_enrollment_request();
+  enrollment_request->set_enrollment_check_type(
+      em::DeviceAutoEnrollmentRequest::ENROLLMENT_CHECK_TYPE_FRE);
+  // This matches any serial hash since dividing by 1 always gives remainder 0.
+  enrollment_request->set_modulus(1);
+  enrollment_request->set_remainder(0);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_THAT(response.auto_enrollment_response().hashes(),
+              UnorderedElementsAreArray(
+                  {kSHA256HashForStateKey1, kSHA256HashForStateKey2}));
+}
+
+TEST_F(RequestHandlerForAutoEnrollmentTest, HandleRequest_Modulus32) {
+  em::DeviceManagementRequest device_management_request;
+  em::DeviceAutoEnrollmentRequest* enrollment_request =
+      device_management_request.mutable_auto_enrollment_request();
+  enrollment_request->set_modulus(32);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_EQ(response.auto_enrollment_response().expected_modulus(), 1);
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_chrome_desktop_report_unittest.cc b/components/policy/test_support/request_handler_for_chrome_desktop_report_unittest.cc
index 9241ce1..119e824 100644
--- a/components/policy/test_support/request_handler_for_chrome_desktop_report_unittest.cc
+++ b/components/policy/test_support/request_handler_for_chrome_desktop_report_unittest.cc
@@ -42,8 +42,8 @@
 
   EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
   ASSERT_TRUE(HasResponseBody());
-  EXPECT_TRUE(
-      GetDeviceManagementResponse().has_chrome_desktop_report_response());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_TRUE(response.has_chrome_desktop_report_response());
 }
 
 }  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_device_attribute_update.cc b/components/policy/test_support/request_handler_for_device_attribute_update.cc
new file mode 100644
index 0000000..1d98d40
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_attribute_update.cc
@@ -0,0 +1,45 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_device_attribute_update.h"
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/test_server_helpers.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+RequestHandlerForDeviceAttributeUpdate::RequestHandlerForDeviceAttributeUpdate(
+    ClientStorage* client_storage,
+    PolicyStorage* policy_storage)
+    : EmbeddedPolicyTestServer::RequestHandler(client_storage, policy_storage) {
+}
+
+RequestHandlerForDeviceAttributeUpdate::
+    ~RequestHandlerForDeviceAttributeUpdate() = default;
+
+std::string RequestHandlerForDeviceAttributeUpdate::RequestType() {
+  return dm_protocol::kValueRequestDeviceAttributeUpdate;
+}
+
+std::unique_ptr<HttpResponse>
+RequestHandlerForDeviceAttributeUpdate::HandleRequest(
+    const HttpRequest& request) {
+  em::DeviceManagementResponse response;
+  response.mutable_device_attribute_update_response()->set_result(
+      em::DeviceAttributeUpdateResponse::ATTRIBUTE_UPDATE_SUCCESS);
+  return CreateHttpResponse(net::HTTP_OK, response.SerializeAsString());
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_device_attribute_update.h b/components/policy/test_support/request_handler_for_device_attribute_update.h
new file mode 100644
index 0000000..36783bf
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_attribute_update.h
@@ -0,0 +1,32 @@
+// Copyright 2021 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_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_ATTRIBUTE_UPDATE_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_ATTRIBUTE_UPDATE_H_
+
+#include "components/policy/test_support/embedded_policy_test_server.h"
+
+namespace policy {
+
+// Handler for request type `device_attribute_update`.
+class RequestHandlerForDeviceAttributeUpdate
+    : public EmbeddedPolicyTestServer::RequestHandler {
+ public:
+  RequestHandlerForDeviceAttributeUpdate(ClientStorage* client_storage,
+                                         PolicyStorage* policy_storage);
+  RequestHandlerForDeviceAttributeUpdate(
+      RequestHandlerForDeviceAttributeUpdate&& handler) = delete;
+  RequestHandlerForDeviceAttributeUpdate& operator=(
+      RequestHandlerForDeviceAttributeUpdate&& handler) = delete;
+  ~RequestHandlerForDeviceAttributeUpdate() override;
+
+  // EmbeddedPolicyTestServer::RequestHandler:
+  std::string RequestType() override;
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) override;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_ATTRIBUTE_UPDATE_H_
diff --git a/components/policy/test_support/request_handler_for_device_attribute_update_permission.cc b/components/policy/test_support/request_handler_for_device_attribute_update_permission.cc
new file mode 100644
index 0000000..4d846b30
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_attribute_update_permission.cc
@@ -0,0 +1,55 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_device_attribute_update_permission.h"
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/test_server_helpers.h"
+#include "net/base/url_util.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+RequestHandlerForDeviceAttributeUpdatePermission::
+    RequestHandlerForDeviceAttributeUpdatePermission(
+        ClientStorage* client_storage,
+        PolicyStorage* policy_storage)
+    : EmbeddedPolicyTestServer::RequestHandler(client_storage, policy_storage) {
+}
+
+RequestHandlerForDeviceAttributeUpdatePermission::
+    ~RequestHandlerForDeviceAttributeUpdatePermission() = default;
+
+std::string RequestHandlerForDeviceAttributeUpdatePermission::RequestType() {
+  return dm_protocol::kValueRequestDeviceAttributeUpdatePermission;
+}
+
+std::unique_ptr<HttpResponse>
+RequestHandlerForDeviceAttributeUpdatePermission::HandleRequest(
+    const HttpRequest& request) {
+  em::DeviceManagementResponse device_management_response;
+  em::DeviceAttributeUpdatePermissionResponse::ResultType result =
+      policy_storage()->allow_set_device_attributes()
+          ? em::DeviceAttributeUpdatePermissionResponse::
+                ATTRIBUTE_UPDATE_ALLOWED
+          : em::DeviceAttributeUpdatePermissionResponse::
+                ATTRIBUTE_UPDATE_DISALLOWED;
+  device_management_response
+      .mutable_device_attribute_update_permission_response()
+      ->set_result(result);
+  return CreateHttpResponse(net::HTTP_OK,
+                            device_management_response.SerializeAsString());
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_device_attribute_update_permission.h b/components/policy/test_support/request_handler_for_device_attribute_update_permission.h
new file mode 100644
index 0000000..4ee54f5
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_attribute_update_permission.h
@@ -0,0 +1,33 @@
+// Copyright 2021 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_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_ATTRIBUTE_UPDATE_PERMISSION_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_ATTRIBUTE_UPDATE_PERMISSION_H_
+
+#include "components/policy/test_support/embedded_policy_test_server.h"
+
+namespace policy {
+
+// Handler for request type `device_attribute_update_permission`.
+class RequestHandlerForDeviceAttributeUpdatePermission
+    : public EmbeddedPolicyTestServer::RequestHandler {
+ public:
+  RequestHandlerForDeviceAttributeUpdatePermission(
+      ClientStorage* client_storage,
+      PolicyStorage* policy_storage);
+  RequestHandlerForDeviceAttributeUpdatePermission(
+      RequestHandlerForDeviceAttributeUpdatePermission&& handler) = delete;
+  RequestHandlerForDeviceAttributeUpdatePermission& operator=(
+      RequestHandlerForDeviceAttributeUpdatePermission&& handler) = delete;
+  ~RequestHandlerForDeviceAttributeUpdatePermission() override;
+
+  // EmbeddedPolicyTestServer::RequestHandler:
+  std::string RequestType() override;
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) override;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_ATTRIBUTE_UPDATE_PERMISSION_H_
diff --git a/components/policy/test_support/request_handler_for_device_attribute_update_permission_unittest.cc b/components/policy/test_support/request_handler_for_device_attribute_update_permission_unittest.cc
new file mode 100644
index 0000000..64444c2
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_attribute_update_permission_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2021 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 "base/strings/strcat.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/embedded_policy_test_server_test_base.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/request_handler_for_register_browser.h"
+#include "device_management_backend.pb.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+constexpr char kDeviceId[] = "fake_device_id";
+
+}  // namespace
+
+class RequestHandlerForDeviceAttributeUpdatePermissionTest
+    : public EmbeddedPolicyTestServerTestBase {
+ protected:
+  RequestHandlerForDeviceAttributeUpdatePermissionTest() = default;
+  ~RequestHandlerForDeviceAttributeUpdatePermissionTest() override = default;
+
+  void SetUp() override {
+    EmbeddedPolicyTestServerTestBase::SetUp();
+
+    SetRequestTypeParam(
+        dm_protocol::kValueRequestDeviceAttributeUpdatePermission);
+    SetAppType(dm_protocol::kValueAppType);
+    SetDeviceIdParam(kDeviceId);
+    SetDeviceType(dm_protocol::kValueDeviceType);
+  }
+};
+
+TEST_F(RequestHandlerForDeviceAttributeUpdatePermissionTest,
+       HandleRequest_Allowed) {
+  policy_storage()->set_allow_set_device_attributes(true);
+
+  em::DeviceManagementRequest device_management_request;
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_EQ(
+      response.device_attribute_update_permission_response().result(),
+      em::DeviceAttributeUpdatePermissionResponse::ATTRIBUTE_UPDATE_ALLOWED);
+}
+
+TEST_F(RequestHandlerForDeviceAttributeUpdatePermissionTest,
+       HandleRequest_Disallowed) {
+  policy_storage()->set_allow_set_device_attributes(false);
+
+  em::DeviceManagementRequest device_management_request;
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_EQ(
+      response.device_attribute_update_permission_response().result(),
+      em::DeviceAttributeUpdatePermissionResponse::ATTRIBUTE_UPDATE_DISALLOWED);
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_device_attribute_update_unittest.cc b/components/policy/test_support/request_handler_for_device_attribute_update_unittest.cc
new file mode 100644
index 0000000..325715b
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_attribute_update_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2021 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 "base/strings/strcat.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/embedded_policy_test_server_test_base.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/request_handler_for_register_browser.h"
+#include "device_management_backend.pb.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+constexpr char kDeviceId[] = "fake_device_id";
+
+}  // namespace
+
+class RequestHandlerForDeviceAttributeUpdateTest
+    : public EmbeddedPolicyTestServerTestBase {
+ protected:
+  RequestHandlerForDeviceAttributeUpdateTest() = default;
+  ~RequestHandlerForDeviceAttributeUpdateTest() override = default;
+
+  void SetUp() override {
+    EmbeddedPolicyTestServerTestBase::SetUp();
+
+    SetRequestTypeParam(dm_protocol::kValueRequestDeviceAttributeUpdate);
+    SetAppType(dm_protocol::kValueAppType);
+    SetDeviceIdParam(kDeviceId);
+    SetDeviceType(dm_protocol::kValueDeviceType);
+  }
+};
+
+TEST_F(RequestHandlerForDeviceAttributeUpdateTest, HandleRequest) {
+  em::DeviceManagementRequest device_management_request;
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_EQ(response.device_attribute_update_response().result(),
+            em::DeviceAttributeUpdateResponse::ATTRIBUTE_UPDATE_SUCCESS);
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_device_initial_enrollment_state.cc b/components/policy/test_support/request_handler_for_device_initial_enrollment_state.cc
new file mode 100644
index 0000000..3e47e31d
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_initial_enrollment_state.cc
@@ -0,0 +1,58 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_device_initial_enrollment_state.h"
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/test_server_helpers.h"
+#include "net/base/url_util.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+RequestHandlerForDeviceInitialEnrollmentState::
+    RequestHandlerForDeviceInitialEnrollmentState(ClientStorage* client_storage,
+                                                  PolicyStorage* policy_storage)
+    : EmbeddedPolicyTestServer::RequestHandler(client_storage, policy_storage) {
+}
+
+RequestHandlerForDeviceInitialEnrollmentState::
+    ~RequestHandlerForDeviceInitialEnrollmentState() = default;
+
+std::string RequestHandlerForDeviceInitialEnrollmentState::RequestType() {
+  return dm_protocol::kValueRequestInitialEnrollmentStateRetrieval;
+}
+
+std::unique_ptr<HttpResponse>
+RequestHandlerForDeviceInitialEnrollmentState::HandleRequest(
+    const HttpRequest& request) {
+  em::DeviceManagementRequest device_management_request;
+  device_management_request.ParseFromString(request.content);
+  const em::DeviceInitialEnrollmentStateRequest& state_request =
+      device_management_request.device_initial_enrollment_state_request();
+
+  const PolicyStorage::InitialEnrollmentState* state =
+      policy_storage()->GetInitialEnrollmentState(
+          state_request.brand_code() + "_" + state_request.serial_number());
+  em::DeviceManagementResponse device_management_response;
+  em::DeviceInitialEnrollmentStateResponse* state_response =
+      device_management_response
+          .mutable_device_initial_enrollment_state_response();
+  state_response->set_initial_enrollment_mode(state->initial_enrollment_mode);
+  state_response->set_management_domain(state->management_domain);
+  return CreateHttpResponse(net::HTTP_OK,
+                            device_management_response.SerializeAsString());
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_device_initial_enrollment_state.h b/components/policy/test_support/request_handler_for_device_initial_enrollment_state.h
new file mode 100644
index 0000000..9205a90
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_initial_enrollment_state.h
@@ -0,0 +1,32 @@
+// Copyright 2021 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_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_INITIAL_ENROLLMENT_STATE_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_INITIAL_ENROLLMENT_STATE_H_
+
+#include "components/policy/test_support/embedded_policy_test_server.h"
+
+namespace policy {
+
+// Handler for request type `device_state_retrieval`.
+class RequestHandlerForDeviceInitialEnrollmentState
+    : public EmbeddedPolicyTestServer::RequestHandler {
+ public:
+  RequestHandlerForDeviceInitialEnrollmentState(ClientStorage* client_storage,
+                                                PolicyStorage* policy_storage);
+  RequestHandlerForDeviceInitialEnrollmentState(
+      RequestHandlerForDeviceInitialEnrollmentState&& handler) = delete;
+  RequestHandlerForDeviceInitialEnrollmentState& operator=(
+      RequestHandlerForDeviceInitialEnrollmentState&& handler) = delete;
+  ~RequestHandlerForDeviceInitialEnrollmentState() override;
+
+  // EmbeddedPolicyTestServer::RequestHandler:
+  std::string RequestType() override;
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) override;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_INITIAL_ENROLLMENT_STATE_H_
diff --git a/components/policy/test_support/request_handler_for_device_initial_enrollment_state_unittest.cc b/components/policy/test_support/request_handler_for_device_initial_enrollment_state_unittest.cc
new file mode 100644
index 0000000..cb04786
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_initial_enrollment_state_unittest.cc
@@ -0,0 +1,77 @@
+// Copyright 2021 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 "base/strings/strcat.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/embedded_policy_test_server_test_base.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/request_handler_for_register_browser.h"
+#include "device_management_backend.pb.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+constexpr char kDeviceId[] = "fake_device_id";
+constexpr char kBrandCode[] = "Google Pixel";
+constexpr char kSerialNumber[] = "AXD123145";
+constexpr char kManagementDomain[] = "example.com";
+
+}  // namespace
+
+class RequestHandlerForDeviceInitialEnrollmentStateTest
+    : public EmbeddedPolicyTestServerTestBase {
+ protected:
+  RequestHandlerForDeviceInitialEnrollmentStateTest() = default;
+  ~RequestHandlerForDeviceInitialEnrollmentStateTest() override = default;
+
+  void SetUp() override {
+    EmbeddedPolicyTestServerTestBase::SetUp();
+
+    SetRequestTypeParam(
+        dm_protocol::kValueRequestInitialEnrollmentStateRetrieval);
+    SetAppType(dm_protocol::kValueAppType);
+    SetDeviceIdParam(kDeviceId);
+    SetDeviceType(dm_protocol::kValueDeviceType);
+  }
+};
+
+TEST_F(RequestHandlerForDeviceInitialEnrollmentStateTest, HandleRequest) {
+  policy_storage()->SetInitialEnrollmentState(
+      base::StrCat({kBrandCode, "_", kSerialNumber}),
+      PolicyStorage::InitialEnrollmentState{
+          .initial_enrollment_mode = em::DeviceInitialEnrollmentStateResponse::
+              INITIAL_ENROLLMENT_MODE_ZERO_TOUCH_ENFORCED,
+          .management_domain = kManagementDomain});
+
+  em::DeviceManagementRequest device_management_request;
+  em::DeviceInitialEnrollmentStateRequest* enrollment_request =
+      device_management_request
+          .mutable_device_initial_enrollment_state_request();
+  enrollment_request->set_brand_code(kBrandCode);
+  enrollment_request->set_serial_number(kSerialNumber);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_EQ(response.device_initial_enrollment_state_response()
+                .initial_enrollment_mode(),
+            em::DeviceInitialEnrollmentStateResponse::
+                INITIAL_ENROLLMENT_MODE_ZERO_TOUCH_ENFORCED);
+  EXPECT_EQ(
+      response.device_initial_enrollment_state_response().management_domain(),
+      kManagementDomain);
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_device_state_retrieval.cc b/components/policy/test_support/request_handler_for_device_state_retrieval.cc
new file mode 100644
index 0000000..fb5a929
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_state_retrieval.cc
@@ -0,0 +1,62 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_device_state_retrieval.h"
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/test_server_helpers.h"
+#include "net/base/url_util.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+RequestHandlerForDeviceStateRetrieval::RequestHandlerForDeviceStateRetrieval(
+    ClientStorage* client_storage,
+    PolicyStorage* policy_storage)
+    : EmbeddedPolicyTestServer::RequestHandler(client_storage, policy_storage) {
+}
+
+RequestHandlerForDeviceStateRetrieval::
+    ~RequestHandlerForDeviceStateRetrieval() = default;
+
+std::string RequestHandlerForDeviceStateRetrieval::RequestType() {
+  return dm_protocol::kValueRequestDeviceStateRetrieval;
+}
+
+std::unique_ptr<HttpResponse>
+RequestHandlerForDeviceStateRetrieval::HandleRequest(
+    const HttpRequest& request) {
+  em::DeviceManagementRequest device_management_request;
+  device_management_request.ParseFromString(request.content);
+  const std::string& server_backed_state_key =
+      device_management_request.device_state_retrieval_request()
+          .server_backed_state_key();
+
+  em::DeviceManagementResponse device_management_response;
+  if (client_storage()->LookupByStateKey(server_backed_state_key)) {
+    em::DeviceStateRetrievalResponse* device_state_retrieval_response =
+        device_management_response.mutable_device_state_retrieval_response();
+    PolicyStorage::DeviceState device_state = policy_storage()->device_state();
+    if (!device_state.management_domain.empty()) {
+      device_state_retrieval_response->set_management_domain(
+          device_state.management_domain);
+    }
+    device_state_retrieval_response->set_restore_mode(
+        device_state.restore_mode);
+  }
+  return CreateHttpResponse(net::HTTP_OK,
+                            device_management_response.SerializeAsString());
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_device_state_retrieval.h b/components/policy/test_support/request_handler_for_device_state_retrieval.h
new file mode 100644
index 0000000..c5765a3
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_state_retrieval.h
@@ -0,0 +1,32 @@
+// Copyright 2021 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_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_STATE_RETRIEVAL_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_STATE_RETRIEVAL_H_
+
+#include "components/policy/test_support/embedded_policy_test_server.h"
+
+namespace policy {
+
+// Handler for request type `device_state_retrieval`.
+class RequestHandlerForDeviceStateRetrieval
+    : public EmbeddedPolicyTestServer::RequestHandler {
+ public:
+  RequestHandlerForDeviceStateRetrieval(ClientStorage* client_storage,
+                                        PolicyStorage* policy_storage);
+  RequestHandlerForDeviceStateRetrieval(
+      RequestHandlerForDeviceStateRetrieval&& handler) = delete;
+  RequestHandlerForDeviceStateRetrieval& operator=(
+      RequestHandlerForDeviceStateRetrieval&& handler) = delete;
+  ~RequestHandlerForDeviceStateRetrieval() override;
+
+  // EmbeddedPolicyTestServer::RequestHandler:
+  std::string RequestType() override;
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) override;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_DEVICE_STATE_RETRIEVAL_H_
diff --git a/components/policy/test_support/request_handler_for_device_state_retrieval_unittest.cc b/components/policy/test_support/request_handler_for_device_state_retrieval_unittest.cc
new file mode 100644
index 0000000..edc202a
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_device_state_retrieval_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright 2021 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 "base/strings/strcat.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/embedded_policy_test_server_test_base.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/request_handler_for_register_browser.h"
+#include "device_management_backend.pb.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+constexpr char kDeviceId[] = "fake_device_id";
+constexpr char kStateKey1[] = "fake_state_key_1";
+constexpr char kStateKey2[] = "fake_state_key_2";
+constexpr char kManagementDomain[] = "example.com";
+
+}  // namespace
+
+class RequestHandlerForDeviceStateRetrievalTest
+    : public EmbeddedPolicyTestServerTestBase {
+ protected:
+  RequestHandlerForDeviceStateRetrievalTest() = default;
+  ~RequestHandlerForDeviceStateRetrievalTest() override = default;
+
+  void SetUp() override {
+    EmbeddedPolicyTestServerTestBase::SetUp();
+
+    SetRequestTypeParam(dm_protocol::kValueRequestDeviceStateRetrieval);
+    SetAppType(dm_protocol::kValueAppType);
+    SetDeviceIdParam(kDeviceId);
+    SetDeviceType(dm_protocol::kValueDeviceType);
+  }
+};
+
+TEST_F(RequestHandlerForDeviceStateRetrievalTest, HandleRequest) {
+  ClientStorage::ClientInfo client_info;
+  client_info.device_id = kDeviceId;
+  client_info.state_keys.push_back(kStateKey1);
+  client_info.state_keys.push_back(kStateKey2);
+  client_storage()->RegisterClient(client_info);
+  policy_storage()->set_device_state(PolicyStorage::DeviceState{
+      .management_domain = kManagementDomain,
+      .restore_mode = enterprise_management::DeviceStateRetrievalResponse::
+          RESTORE_MODE_REENROLLMENT_ZERO_TOUCH});
+
+  em::DeviceManagementRequest device_management_request;
+  em::DeviceStateRetrievalRequest* request =
+      device_management_request.mutable_device_state_retrieval_request();
+  request->set_server_backed_state_key(kStateKey2);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_EQ(
+      response.device_state_retrieval_response().restore_mode(),
+      em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ZERO_TOUCH);
+  EXPECT_EQ(response.device_state_retrieval_response().management_domain(),
+            kManagementDomain);
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_psm_auto_enrollment.cc b/components/policy/test_support/request_handler_for_psm_auto_enrollment.cc
new file mode 100644
index 0000000..df820f83
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_psm_auto_enrollment.cc
@@ -0,0 +1,94 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_psm_auto_enrollment.h"
+
+#include "base/containers/contains.h"
+#include "base/strings/stringprintf.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/test_server_helpers.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+constexpr const char* kPsmMembershipEncryptedTestIds[] = {
+    "54455354/111111",  // Brand code "TEST" (as hex), serial number "111111".
+};
+
+}  // namespace
+
+RequestHandlerForPsmAutoEnrollment::RequestHandlerForPsmAutoEnrollment(
+    ClientStorage* client_storage,
+    PolicyStorage* policy_storage)
+    : EmbeddedPolicyTestServer::RequestHandler(client_storage, policy_storage) {
+}
+
+RequestHandlerForPsmAutoEnrollment::~RequestHandlerForPsmAutoEnrollment() =
+    default;
+
+std::string RequestHandlerForPsmAutoEnrollment::RequestType() {
+  return dm_protocol::kValueRequestPsmHasDeviceState;
+}
+
+std::unique_ptr<HttpResponse> RequestHandlerForPsmAutoEnrollment::HandleRequest(
+    const HttpRequest& request) {
+  em::DeviceManagementRequest device_management_request;
+  device_management_request.ParseFromString(request.content);
+  const em::PrivateSetMembershipRequest& psm_request =
+      device_management_request.private_set_membership_request();
+
+  em::DeviceManagementResponse device_management_response;
+  em::PrivateSetMembershipResponse* psm_response =
+      device_management_response.mutable_private_set_membership_response();
+  const auto& rlwe_request = psm_request.rlwe_request();
+  if (rlwe_request.has_oprf_request()) {
+    if (rlwe_request.oprf_request().encrypted_ids_size() == 0) {
+      return CreateHttpResponse(
+          net::HTTP_BAD_REQUEST,
+          "PSM RLWE OPRF request must contain encrypted_ids field");
+    }
+    psm_response->mutable_rlwe_response()
+        ->mutable_oprf_response()
+        ->add_doubly_encrypted_ids()
+        ->set_queried_encrypted_id(
+            rlwe_request.oprf_request().encrypted_ids(0));
+  } else if (rlwe_request.has_query_request()) {
+    if (rlwe_request.query_request().queries_size() == 0) {
+      return CreateHttpResponse(
+          net::HTTP_BAD_REQUEST,
+          "PSM RLWE query request must contain queries field");
+    }
+    auto* pir_response = psm_response->mutable_rlwe_response()
+                             ->mutable_query_response()
+                             ->add_pir_responses();
+    const auto& encrypted_id =
+        rlwe_request.query_request().queries(0).queried_encrypted_id();
+    pir_response->set_queried_encrypted_id(encrypted_id);
+    pir_response->mutable_pir_response()->set_plaintext_entry_size(
+        base::Contains(kPsmMembershipEncryptedTestIds, encrypted_id)
+            ? kPirResponseHasMembership
+            : kPirResponseHasNoMembership);
+  } else {
+    return CreateHttpResponse(
+        net::HTTP_BAD_REQUEST,
+        "PSM RLWE oprf_request, or query_request fields must be filled");
+  }
+
+  return CreateHttpResponse(net::HTTP_OK,
+                            device_management_response.SerializeAsString());
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_psm_auto_enrollment.h b/components/policy/test_support/request_handler_for_psm_auto_enrollment.h
new file mode 100644
index 0000000..23b732af
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_psm_auto_enrollment.h
@@ -0,0 +1,37 @@
+// Copyright 2021 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_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_PSM_AUTO_ENROLLMENT_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_PSM_AUTO_ENROLLMENT_H_
+
+#include "components/policy/test_support/embedded_policy_test_server.h"
+
+namespace policy {
+
+// Handler for request type `enterprise_psm_check`.
+class RequestHandlerForPsmAutoEnrollment
+    : public EmbeddedPolicyTestServer::RequestHandler {
+ public:
+  enum PirResponse {
+    kPirResponseHasMembership = 1,
+    kPirResponseHasNoMembership = 2,
+  };
+
+  RequestHandlerForPsmAutoEnrollment(ClientStorage* client_storage,
+                                     PolicyStorage* policy_storage);
+  RequestHandlerForPsmAutoEnrollment(
+      RequestHandlerForPsmAutoEnrollment&& handler) = delete;
+  RequestHandlerForPsmAutoEnrollment& operator=(
+      RequestHandlerForPsmAutoEnrollment&& handler) = delete;
+  ~RequestHandlerForPsmAutoEnrollment() override;
+
+  // EmbeddedPolicyTestServer::RequestHandler:
+  std::string RequestType() override;
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) override;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_PSM_AUTO_ENROLLMENT_H_
diff --git a/components/policy/test_support/request_handler_for_psm_auto_enrollment_unittest.cc b/components/policy/test_support/request_handler_for_psm_auto_enrollment_unittest.cc
new file mode 100644
index 0000000..6c7fab5
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_psm_auto_enrollment_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_psm_auto_enrollment.h"
+#include "base/strings/strcat.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/embedded_policy_test_server_test_base.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/request_handler_for_register_browser.h"
+#include "device_management_backend.pb.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+constexpr char kDeviceId[] = "fake_device_id";
+constexpr char kEncryptedId1[] = "fake/ecrypted-id";
+constexpr char kEncryptedId2[] = "54455354/111111";
+
+}  // namespace
+
+class RequestHandlerForPsmAutoEnrollmentTest
+    : public EmbeddedPolicyTestServerTestBase {
+ protected:
+  RequestHandlerForPsmAutoEnrollmentTest() = default;
+  ~RequestHandlerForPsmAutoEnrollmentTest() override = default;
+
+  void SetUp() override {
+    EmbeddedPolicyTestServerTestBase::SetUp();
+
+    SetRequestTypeParam(dm_protocol::kValueRequestPsmHasDeviceState);
+    SetAppType(dm_protocol::kValueAppType);
+    SetDeviceIdParam(kDeviceId);
+    SetDeviceType(dm_protocol::kValueDeviceType);
+  }
+};
+
+TEST_F(RequestHandlerForPsmAutoEnrollmentTest, HandleRequest_OprfRequest) {
+  em::DeviceManagementRequest device_management_request;
+  em::PrivateSetMembershipRequest* request =
+      device_management_request.mutable_private_set_membership_request();
+  request->mutable_rlwe_request()->mutable_oprf_request()->add_encrypted_ids(
+      kEncryptedId1);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  ASSERT_EQ(response.private_set_membership_response()
+                .rlwe_response()
+                .oprf_response()
+                .doubly_encrypted_ids_size(),
+            1);
+  EXPECT_EQ(response.private_set_membership_response()
+                .rlwe_response()
+                .oprf_response()
+                .doubly_encrypted_ids(0)
+                .queried_encrypted_id(),
+            kEncryptedId1);
+}
+
+TEST_F(RequestHandlerForPsmAutoEnrollmentTest,
+       HandleRequest_QueryRequestNoMembership) {
+  em::DeviceManagementRequest device_management_request;
+  em::PrivateSetMembershipRequest* request =
+      device_management_request.mutable_private_set_membership_request();
+  request->mutable_rlwe_request()
+      ->mutable_query_request()
+      ->add_queries()
+      ->set_queried_encrypted_id(kEncryptedId1);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  ASSERT_EQ(response.private_set_membership_response()
+                .rlwe_response()
+                .query_response()
+                .pir_responses_size(),
+            1);
+  EXPECT_EQ(response.private_set_membership_response()
+                .rlwe_response()
+                .query_response()
+                .pir_responses(0)
+                .queried_encrypted_id(),
+            kEncryptedId1);
+  EXPECT_EQ(response.private_set_membership_response()
+                .rlwe_response()
+                .query_response()
+                .pir_responses(0)
+                .pir_response()
+                .plaintext_entry_size(),
+            RequestHandlerForPsmAutoEnrollment::kPirResponseHasNoMembership);
+}
+
+TEST_F(RequestHandlerForPsmAutoEnrollmentTest,
+       HandleRequest_QueryRequestHasMembership) {
+  em::DeviceManagementRequest device_management_request;
+  em::PrivateSetMembershipRequest* request =
+      device_management_request.mutable_private_set_membership_request();
+  request->mutable_rlwe_request()
+      ->mutable_query_request()
+      ->add_queries()
+      ->set_queried_encrypted_id(kEncryptedId2);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  ASSERT_EQ(response.private_set_membership_response()
+                .rlwe_response()
+                .query_response()
+                .pir_responses_size(),
+            1);
+  EXPECT_EQ(response.private_set_membership_response()
+                .rlwe_response()
+                .query_response()
+                .pir_responses(0)
+                .queried_encrypted_id(),
+            kEncryptedId2);
+  EXPECT_EQ(response.private_set_membership_response()
+                .rlwe_response()
+                .query_response()
+                .pir_responses(0)
+                .pir_response()
+                .plaintext_entry_size(),
+            RequestHandlerForPsmAutoEnrollment::kPirResponseHasMembership);
+}
+
+TEST_F(RequestHandlerForPsmAutoEnrollmentTest,
+       HandleRequest_MissingRequestFields) {
+  em::DeviceManagementRequest device_management_request;
+  device_management_request.mutable_private_set_membership_request();
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_BAD_REQUEST);
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_register_cert_based.cc b/components/policy/test_support/request_handler_for_register_cert_based.cc
new file mode 100644
index 0000000..eded203
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_register_cert_based.cc
@@ -0,0 +1,66 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_register_cert_based.h"
+
+#include <set>
+#include <string>
+
+#include "base/guid.h"
+#include "base/notreached.h"
+#include "base/strings/stringprintf.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/request_handler_for_register_device_and_user.h"
+#include "components/policy/test_support/test_server_helpers.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+RequestHandlerForRegisterCertBased::RequestHandlerForRegisterCertBased(
+    ClientStorage* client_storage,
+    PolicyStorage* policy_storage)
+    : RequestHandlerForRegisterDeviceAndUser(client_storage, policy_storage) {}
+
+RequestHandlerForRegisterCertBased::~RequestHandlerForRegisterCertBased() =
+    default;
+
+std::string RequestHandlerForRegisterCertBased::RequestType() {
+  return dm_protocol::kValueRequestCertBasedRegister;
+}
+
+std::unique_ptr<HttpResponse> RequestHandlerForRegisterCertBased::HandleRequest(
+    const HttpRequest& request) {
+  em::DeviceManagementRequest device_management_request;
+  device_management_request.ParseFromString(request.content);
+  const em::SignedData& signed_req =
+      device_management_request.certificate_based_register_request()
+          .signed_request();
+  em::CertificateBasedDeviceRegistrationData parsed_req;
+  std::string data = signed_req.data().substr(
+      0, signed_req.data().size() - signed_req.extra_data_bytes());
+  if (!parsed_req.ParseFromString(data))
+    return CreateHttpResponse(net::HTTP_BAD_REQUEST, "Invalid request");
+  if (parsed_req.certificate_type() !=
+      em::CertificateBasedDeviceRegistrationData::
+          ENTERPRISE_ENROLLMENT_CERTIFICATE) {
+    return CreateHttpResponse(net::HTTP_FORBIDDEN,
+                              "Invalid certificate type for registration");
+  }
+  const em::DeviceRegisterRequest& register_request =
+      parsed_req.device_register_request();
+
+  return RegisterDeviceAndSendResponse(request, register_request, "");
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_register_cert_based.h b/components/policy/test_support/request_handler_for_register_cert_based.h
new file mode 100644
index 0000000..2810842
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_register_cert_based.h
@@ -0,0 +1,33 @@
+// Copyright 2021 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_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_REGISTER_CERT_BASED_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_REGISTER_CERT_BASED_H_
+
+#include "components/policy/test_support/embedded_policy_test_server.h"
+#include "components/policy/test_support/request_handler_for_register_device_and_user.h"
+
+namespace policy {
+
+// Handler for request type `certificate_based_register`.
+class RequestHandlerForRegisterCertBased
+    : public RequestHandlerForRegisterDeviceAndUser {
+ public:
+  RequestHandlerForRegisterCertBased(ClientStorage* client_storage,
+                                     PolicyStorage* policy_storage);
+  RequestHandlerForRegisterCertBased(
+      RequestHandlerForRegisterCertBased&& handler) = delete;
+  RequestHandlerForRegisterCertBased& operator=(
+      RequestHandlerForRegisterCertBased&& handler) = delete;
+  ~RequestHandlerForRegisterCertBased() override;
+
+  // EmbeddedPolicyTestServer::RequestHandler:
+  std::string RequestType() override;
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) override;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_REGISTER_CERT_BASED_H_
diff --git a/components/policy/test_support/request_handler_for_register_cert_based_unittest.cc b/components/policy/test_support/request_handler_for_register_cert_based_unittest.cc
new file mode 100644
index 0000000..a3fb930f
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_register_cert_based_unittest.cc
@@ -0,0 +1,123 @@
+// Copyright 2021 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 "base/strings/strcat.h"
+#include "components/policy/test_support/request_handler_for_register_browser.h"
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/embedded_policy_test_server_test_base.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "device_management_backend.pb.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+namespace {
+
+using testing::IsEmpty;
+
+constexpr char kDeviceId[] = "fake_device_id";
+constexpr char kMachineModel[] = "iPhone 10";
+constexpr char kBrandCode[] = "iPhone";
+constexpr char kMachineId[] = "11123";
+constexpr char kExtraData[] = "fake_extra_data";
+
+}  // namespace
+
+class RequestHandlerForRegisterCertBasedTest
+    : public EmbeddedPolicyTestServerTestBase {
+ protected:
+  RequestHandlerForRegisterCertBasedTest() = default;
+  ~RequestHandlerForRegisterCertBasedTest() override = default;
+
+  void SetUp() override {
+    EmbeddedPolicyTestServerTestBase::SetUp();
+
+    SetRequestTypeParam(dm_protocol::kValueRequestCertBasedRegister);
+    SetAppType(dm_protocol::kValueAppType);
+    SetDeviceIdParam(kDeviceId);
+    SetDeviceType(dm_protocol::kValueDeviceType);
+  }
+};
+
+TEST_F(RequestHandlerForRegisterCertBasedTest, HandleRequest_Success) {
+  em::CertificateBasedDeviceRegistrationData register_data;
+  register_data.set_certificate_type(
+      em::CertificateBasedDeviceRegistrationData::
+          ENTERPRISE_ENROLLMENT_CERTIFICATE);
+  em::DeviceRegisterRequest* register_request =
+      register_data.mutable_device_register_request();
+  register_request->set_machine_model(kMachineModel);
+  register_request->set_type(em::DeviceRegisterRequest::USER);
+  register_request->set_brand_code(kBrandCode);
+  register_request->set_machine_id(kMachineId);
+
+  em::DeviceManagementRequest device_management_request;
+  em::CertificateBasedDeviceRegisterRequest* cert_request =
+      device_management_request.mutable_certificate_based_register_request();
+  cert_request->mutable_signed_request()->set_data(
+      register_data.SerializeAsString() + kExtraData);
+  cert_request->mutable_signed_request()->set_extra_data_bytes(
+      std::string(kExtraData).length());
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_FALSE(response.register_response().device_management_token().empty());
+  EXPECT_FALSE(response.register_response().machine_name().empty());
+  EXPECT_EQ(response.register_response().enrollment_type(),
+            em::DeviceRegisterResponse::ENTERPRISE);
+
+  ASSERT_EQ(client_storage()->GetNumberOfRegisteredClients(), 1u);
+  const ClientStorage::ClientInfo* client_info =
+      client_storage()->GetClientOrNull(kDeviceId);
+  ASSERT_NE(client_info, nullptr);
+  EXPECT_EQ(client_info->device_id, kDeviceId);
+  EXPECT_EQ(client_info->device_token,
+            response.register_response().device_management_token());
+  EXPECT_EQ(client_info->machine_name,
+            response.register_response().machine_name());
+  EXPECT_FALSE(client_info->username.has_value());
+  EXPECT_FALSE(client_info->allowed_policy_types.empty());
+}
+
+TEST_F(RequestHandlerForRegisterCertBasedTest, HandleRequest_InvalidData) {
+  em::DeviceManagementRequest device_management_request;
+  em::CertificateBasedDeviceRegisterRequest* cert_request =
+      device_management_request.mutable_certificate_based_register_request();
+  cert_request->mutable_signed_request()->set_data(kExtraData);
+  cert_request->mutable_signed_request()->set_extra_data_bytes(0);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_BAD_REQUEST);
+}
+
+TEST_F(RequestHandlerForRegisterCertBasedTest,
+       HandleRequest_MissingCertificateType) {
+  em::CertificateBasedDeviceRegistrationData register_data;
+  em::DeviceManagementRequest device_management_request;
+  em::CertificateBasedDeviceRegisterRequest* cert_request =
+      device_management_request.mutable_certificate_based_register_request();
+  cert_request->mutable_signed_request()->set_data(
+      register_data.SerializeAsString());
+  cert_request->mutable_signed_request()->set_extra_data_bytes(0);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_FORBIDDEN);
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_register_device_and_user.cc b/components/policy/test_support/request_handler_for_register_device_and_user.cc
index f62b4802..d26022d0 100644
--- a/components/policy/test_support/request_handler_for_register_device_and_user.cc
+++ b/components/policy/test_support/request_handler_for_register_device_and_user.cc
@@ -54,6 +54,33 @@
   }
 }
 
+std::unique_ptr<HttpResponse> ValidatePsmFields(
+    const em::DeviceRegisterRequest& register_request,
+    const PolicyStorage* policy_storage) {
+  const PolicyStorage::PsmEntry* psm_entry = policy_storage->GetPsmEntry(
+      register_request.brand_code() + "_" + register_request.machine_id());
+  if (!psm_entry)
+    return nullptr;
+
+  if (!register_request.has_psm_execution_result() ||
+      !register_request.has_psm_determination_timestamp_ms()) {
+    return CreateHttpResponse(net::HTTP_BAD_REQUEST,
+                              "DeviceRegisterRequest must have all required "
+                              "PSM execution fields.");
+  }
+
+  if (register_request.psm_execution_result() !=
+          psm_entry->psm_execution_result ||
+      psm_entry->psm_determination_timestamp !=
+          register_request.psm_determination_timestamp_ms()) {
+    return CreateHttpResponse(
+        net::HTTP_BAD_REQUEST,
+        "DeviceRegisterRequest must have all correct PSM execution values");
+  }
+
+  return nullptr;
+}
+
 }  // namespace
 
 RequestHandlerForRegisterDeviceAndUser::RequestHandlerForRegisterDeviceAndUser(
@@ -79,7 +106,7 @@
   if (!GetGoogleLoginFromRequest(request, &google_login))
     return CreateHttpResponse(net::HTTP_UNAUTHORIZED, "User not authorized.");
 
-  const std::set<std::string>& managed_users =
+  const base::flat_set<std::string>& managed_users =
       policy_storage()->managed_users();
   if (managed_users.empty()) {
     return CreateHttpResponse(net::HTTP_INTERNAL_SERVER_ERROR,
@@ -97,6 +124,19 @@
   const em::DeviceRegisterRequest& register_request =
       device_management_request.register_request();
 
+  std::unique_ptr<HttpResponse> error_response =
+      ValidatePsmFields(register_request, policy_storage());
+  if (error_response)
+    return error_response;
+
+  return RegisterDeviceAndSendResponse(request, register_request, policy_user);
+}
+
+std::unique_ptr<HttpResponse>
+RequestHandlerForRegisterDeviceAndUser::RegisterDeviceAndSendResponse(
+    const HttpRequest& request,
+    const em::DeviceRegisterRequest& register_request,
+    const std::string& policy_user) {
   std::string device_id =
       KeyValueFromUrl(request.GetURL(), dm_protocol::kParamDeviceID);
   std::string device_token = base::GUID::GenerateRandomV4().AsLowercaseString();
diff --git a/components/policy/test_support/request_handler_for_register_device_and_user.h b/components/policy/test_support/request_handler_for_register_device_and_user.h
index f7c59b7f..1aba73a 100644
--- a/components/policy/test_support/request_handler_for_register_device_and_user.h
+++ b/components/policy/test_support/request_handler_for_register_device_and_user.h
@@ -6,6 +6,9 @@
 #define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_REGISTER_DEVICE_AND_USER_H_
 
 #include "components/policy/test_support/embedded_policy_test_server.h"
+#include "device_management_backend.pb.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
 
 namespace policy {
 
@@ -26,6 +29,12 @@
   std::string RequestType() override;
   std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
       const net::test_server::HttpRequest& request) override;
+
+ protected:
+  std::unique_ptr<net::test_server::HttpResponse> RegisterDeviceAndSendResponse(
+      const net::test_server::HttpRequest& request,
+      const enterprise_management::DeviceRegisterRequest& register_request,
+      const std::string& policy_user);
 };
 
 }  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_register_device_and_user_unittest.cc b/components/policy/test_support/request_handler_for_register_device_and_user_unittest.cc
index 461ca69..51cb782 100644
--- a/components/policy/test_support/request_handler_for_register_device_and_user_unittest.cc
+++ b/components/policy/test_support/request_handler_for_register_device_and_user_unittest.cc
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/strings/strcat.h"
 #include "components/policy/test_support/request_handler_for_register_browser.h"
 
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/test_support/client_storage.h"
 #include "components/policy/test_support/embedded_policy_test_server_test_base.h"
 #include "components/policy/test_support/policy_storage.h"
+#include "device_management_backend.pb.h"
 #include "net/http/http_status_code.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -23,6 +25,8 @@
 constexpr char kAllowedUserOAuthToken[] = "oauth-token-for-user";
 constexpr char kDisallowedUserOAuthToken[] = "oauth-token-for-invalid-user";
 constexpr char kMachineModel[] = "iPhone 10";
+constexpr char kBrandCode[] = "iPhone";
+constexpr char kMachineId[] = "11123";
 
 }  // namespace
 
@@ -79,12 +83,23 @@
   policy_storage()->add_managed_user(kAllowedUserEmail);
   SetGoogleLoginTokenHeader(kAllowedUserOAuthToken);
   policy_storage()->set_policy_user(kAllowedUserEmail);
+  policy_storage()->SetPsmEntry(
+      base::StrCat({kBrandCode, "_", kMachineId}),
+      PolicyStorage::PsmEntry{
+          .psm_execution_result =
+              em::DeviceRegisterRequest::PSM_RESULT_SUCCESSFUL_WITH_STATE,
+          .psm_determination_timestamp = 42});
 
   em::DeviceManagementRequest device_management_request;
   em::DeviceRegisterRequest* register_request =
       device_management_request.mutable_register_request();
   register_request->set_machine_model(kMachineModel);
   register_request->set_type(em::DeviceRegisterRequest::USER);
+  register_request->set_brand_code(kBrandCode);
+  register_request->set_machine_id(kMachineId);
+  register_request->set_psm_execution_result(
+      em::DeviceRegisterRequest::PSM_RESULT_SUCCESSFUL_WITH_STATE);
+  register_request->set_psm_determination_timestamp_ms(42);
   SetPayload(device_management_request);
 
   StartRequestAndWait();
@@ -113,4 +128,117 @@
   EXPECT_FALSE(client_info->allowed_policy_types.empty());
 }
 
+TEST_F(RequestHandlerForRegisterDeviceAndUserTest,
+       HandleRequest_NoPsmExecutionResult) {
+  policy_storage()->add_managed_user(kAllowedUserEmail);
+  SetGoogleLoginTokenHeader(kAllowedUserOAuthToken);
+  policy_storage()->set_policy_user(kAllowedUserEmail);
+  policy_storage()->SetPsmEntry(
+      base::StrCat({kBrandCode, "_", kMachineId}),
+      PolicyStorage::PsmEntry{
+          .psm_execution_result =
+              em::DeviceRegisterRequest::PSM_RESULT_SUCCESSFUL_WITH_STATE,
+          .psm_determination_timestamp = 42});
+
+  em::DeviceManagementRequest device_management_request;
+  em::DeviceRegisterRequest* register_request =
+      device_management_request.mutable_register_request();
+  register_request->set_machine_model(kMachineModel);
+  register_request->set_type(em::DeviceRegisterRequest::USER);
+  register_request->set_brand_code(kBrandCode);
+  register_request->set_machine_id(kMachineId);
+  register_request->set_psm_determination_timestamp_ms(42);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_BAD_REQUEST);
+}
+
+TEST_F(RequestHandlerForRegisterDeviceAndUserTest,
+       HandleRequest_NoPsmDeterminationTimestamp) {
+  policy_storage()->add_managed_user(kAllowedUserEmail);
+  SetGoogleLoginTokenHeader(kAllowedUserOAuthToken);
+  policy_storage()->set_policy_user(kAllowedUserEmail);
+  policy_storage()->SetPsmEntry(
+      base::StrCat({kBrandCode, "_", kMachineId}),
+      PolicyStorage::PsmEntry{
+          .psm_execution_result =
+              em::DeviceRegisterRequest::PSM_RESULT_SUCCESSFUL_WITH_STATE,
+          .psm_determination_timestamp = 42});
+
+  em::DeviceManagementRequest device_management_request;
+  em::DeviceRegisterRequest* register_request =
+      device_management_request.mutable_register_request();
+  register_request->set_machine_model(kMachineModel);
+  register_request->set_type(em::DeviceRegisterRequest::USER);
+  register_request->set_brand_code(kBrandCode);
+  register_request->set_machine_id(kMachineId);
+  register_request->set_psm_execution_result(
+      em::DeviceRegisterRequest::PSM_RESULT_SUCCESSFUL_WITH_STATE);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_BAD_REQUEST);
+}
+
+TEST_F(RequestHandlerForRegisterDeviceAndUserTest,
+       HandleRequest_MismatchingPsmExecutionResult) {
+  policy_storage()->add_managed_user(kAllowedUserEmail);
+  SetGoogleLoginTokenHeader(kAllowedUserOAuthToken);
+  policy_storage()->set_policy_user(kAllowedUserEmail);
+  policy_storage()->SetPsmEntry(
+      base::StrCat({kBrandCode, "_", kMachineId}),
+      PolicyStorage::PsmEntry{
+          .psm_execution_result =
+              em::DeviceRegisterRequest::PSM_RESULT_SUCCESSFUL_WITH_STATE,
+          .psm_determination_timestamp = 42});
+
+  em::DeviceManagementRequest device_management_request;
+  em::DeviceRegisterRequest* register_request =
+      device_management_request.mutable_register_request();
+  register_request->set_machine_model(kMachineModel);
+  register_request->set_type(em::DeviceRegisterRequest::USER);
+  register_request->set_brand_code(kBrandCode);
+  register_request->set_machine_id(kMachineId);
+  register_request->set_psm_execution_result(
+      em::DeviceRegisterRequest::PSM_RESULT_SUCCESSFUL_WITHOUT_STATE);
+  register_request->set_psm_determination_timestamp_ms(42);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_BAD_REQUEST);
+}
+
+TEST_F(RequestHandlerForRegisterDeviceAndUserTest,
+       HandleRequest_MismatchingPsmDeterminationTimestamp) {
+  policy_storage()->add_managed_user(kAllowedUserEmail);
+  SetGoogleLoginTokenHeader(kAllowedUserOAuthToken);
+  policy_storage()->set_policy_user(kAllowedUserEmail);
+  policy_storage()->SetPsmEntry(
+      base::StrCat({kBrandCode, "_", kMachineId}),
+      PolicyStorage::PsmEntry{
+          .psm_execution_result =
+              em::DeviceRegisterRequest::PSM_RESULT_SUCCESSFUL_WITH_STATE,
+          .psm_determination_timestamp = 42});
+
+  em::DeviceManagementRequest device_management_request;
+  em::DeviceRegisterRequest* register_request =
+      device_management_request.mutable_register_request();
+  register_request->set_machine_model(kMachineModel);
+  register_request->set_type(em::DeviceRegisterRequest::USER);
+  register_request->set_brand_code(kBrandCode);
+  register_request->set_machine_id(kMachineId);
+  register_request->set_psm_execution_result(
+      em::DeviceRegisterRequest::PSM_RESULT_SUCCESSFUL_WITHOUT_STATE);
+  register_request->set_psm_determination_timestamp_ms(24);
+  SetPayload(device_management_request);
+
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_BAD_REQUEST);
+}
+
 }  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_remote_commands.cc b/components/policy/test_support/request_handler_for_remote_commands.cc
new file mode 100644
index 0000000..e719c1a
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_remote_commands.cc
@@ -0,0 +1,42 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_remote_commands.h"
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/test_server_helpers.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+RequestHandlerForRemoteCommands::RequestHandlerForRemoteCommands(
+    ClientStorage* client_storage,
+    PolicyStorage* policy_storage)
+    : EmbeddedPolicyTestServer::RequestHandler(client_storage, policy_storage) {
+}
+
+RequestHandlerForRemoteCommands::~RequestHandlerForRemoteCommands() = default;
+
+std::string RequestHandlerForRemoteCommands::RequestType() {
+  return dm_protocol::kValueRequestRemoteCommands;
+}
+
+std::unique_ptr<HttpResponse> RequestHandlerForRemoteCommands::HandleRequest(
+    const HttpRequest& request) {
+  em::DeviceManagementResponse response;
+  response.mutable_remote_command_response();
+  return CreateHttpResponse(net::HTTP_OK, response.SerializeAsString());
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_remote_commands.h b/components/policy/test_support/request_handler_for_remote_commands.h
new file mode 100644
index 0000000..ceca731
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_remote_commands.h
@@ -0,0 +1,32 @@
+// Copyright 2021 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_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_REMOTE_COMMANDS_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_REMOTE_COMMANDS_H_
+
+#include "components/policy/test_support/embedded_policy_test_server.h"
+
+namespace policy {
+
+// Handler for request type `remote_commands`.
+class RequestHandlerForRemoteCommands
+    : public EmbeddedPolicyTestServer::RequestHandler {
+ public:
+  RequestHandlerForRemoteCommands(ClientStorage* client_storage,
+                                  PolicyStorage* policy_storage);
+  RequestHandlerForRemoteCommands(RequestHandlerForRemoteCommands&& handler) =
+      delete;
+  RequestHandlerForRemoteCommands& operator=(
+      RequestHandlerForRemoteCommands&& handler) = delete;
+  ~RequestHandlerForRemoteCommands() override;
+
+  // EmbeddedPolicyTestServer::RequestHandler:
+  std::string RequestType() override;
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) override;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_REMOTE_COMMANDS_H_
diff --git a/components/policy/test_support/request_handler_for_remote_commands_unittest.cc b/components/policy/test_support/request_handler_for_remote_commands_unittest.cc
new file mode 100644
index 0000000..a789fdc4
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_remote_commands_unittest.cc
@@ -0,0 +1,48 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_remote_commands.h"
+
+#include <utility>
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/test_support/embedded_policy_test_server_test_base.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "net/http/http_status_code.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+constexpr char kDeviceId[] = "fake_device_id";
+
+}  // namespace
+
+class RequestHandlerForRemoteCommandsTest
+    : public EmbeddedPolicyTestServerTestBase {
+ public:
+  RequestHandlerForRemoteCommandsTest() = default;
+  ~RequestHandlerForRemoteCommandsTest() override = default;
+
+  void SetUp() override {
+    EmbeddedPolicyTestServerTestBase::SetUp();
+
+    SetRequestTypeParam(dm_protocol::kValueRequestRemoteCommands);
+    SetAppType(dm_protocol::kValueAppType);
+    SetDeviceIdParam(kDeviceId);
+    SetDeviceType(dm_protocol::kValueDeviceType);
+  }
+};
+
+TEST_F(RequestHandlerForRemoteCommandsTest, HandleRequest) {
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_TRUE(response.has_remote_command_response());
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_status_upload.cc b/components/policy/test_support/request_handler_for_status_upload.cc
new file mode 100644
index 0000000..32679ac
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_status_upload.cc
@@ -0,0 +1,43 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_status_upload.h"
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/test_support/client_storage.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "components/policy/test_support/test_server_helpers.h"
+#include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+
+using net::test_server::HttpRequest;
+using net::test_server::HttpResponse;
+
+namespace em = enterprise_management;
+
+namespace policy {
+
+RequestHandlerForStatusUpload::RequestHandlerForStatusUpload(
+    ClientStorage* client_storage,
+    PolicyStorage* policy_storage)
+    : EmbeddedPolicyTestServer::RequestHandler(client_storage, policy_storage) {
+}
+
+RequestHandlerForStatusUpload::~RequestHandlerForStatusUpload() = default;
+
+std::string RequestHandlerForStatusUpload::RequestType() {
+  return dm_protocol::kValueRequestUploadStatus;
+}
+
+std::unique_ptr<HttpResponse> RequestHandlerForStatusUpload::HandleRequest(
+    const HttpRequest& request) {
+  em::DeviceManagementResponse response;
+  response.mutable_device_status_report_response();
+  response.mutable_session_status_report_response();
+  return CreateHttpResponse(net::HTTP_OK, response.SerializeAsString());
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_status_upload.h b/components/policy/test_support/request_handler_for_status_upload.h
new file mode 100644
index 0000000..f48f539c
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_status_upload.h
@@ -0,0 +1,32 @@
+// Copyright 2021 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_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_STATUS_UPLOAD_H_
+#define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_STATUS_UPLOAD_H_
+
+#include "components/policy/test_support/embedded_policy_test_server.h"
+
+namespace policy {
+
+// Handler for request type `status_upload`.
+class RequestHandlerForStatusUpload
+    : public EmbeddedPolicyTestServer::RequestHandler {
+ public:
+  RequestHandlerForStatusUpload(ClientStorage* client_storage,
+                                PolicyStorage* policy_storage);
+  RequestHandlerForStatusUpload(RequestHandlerForStatusUpload&& handler) =
+      delete;
+  RequestHandlerForStatusUpload& operator=(
+      RequestHandlerForStatusUpload&& handler) = delete;
+  ~RequestHandlerForStatusUpload() override;
+
+  // EmbeddedPolicyTestServer::RequestHandler:
+  std::string RequestType() override;
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) override;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_STATUS_UPLOAD_H_
diff --git a/components/policy/test_support/request_handler_for_status_upload_unittest.cc b/components/policy/test_support/request_handler_for_status_upload_unittest.cc
new file mode 100644
index 0000000..f2a16a58
--- /dev/null
+++ b/components/policy/test_support/request_handler_for_status_upload_unittest.cc
@@ -0,0 +1,49 @@
+// Copyright 2021 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/policy/test_support/request_handler_for_status_upload.h"
+
+#include <utility>
+
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/test_support/embedded_policy_test_server_test_base.h"
+#include "components/policy/test_support/policy_storage.h"
+#include "net/http/http_status_code.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+constexpr char kDeviceId[] = "fake_device_id";
+
+}  // namespace
+
+class RequestHandlerForStatusUploadTest
+    : public EmbeddedPolicyTestServerTestBase {
+ public:
+  RequestHandlerForStatusUploadTest() = default;
+  ~RequestHandlerForStatusUploadTest() override = default;
+
+  void SetUp() override {
+    EmbeddedPolicyTestServerTestBase::SetUp();
+
+    SetRequestTypeParam(dm_protocol::kValueRequestUploadStatus);
+    SetAppType(dm_protocol::kValueAppType);
+    SetDeviceIdParam(kDeviceId);
+    SetDeviceType(dm_protocol::kValueDeviceType);
+  }
+};
+
+TEST_F(RequestHandlerForStatusUploadTest, HandleRequest) {
+  StartRequestAndWait();
+
+  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
+  ASSERT_TRUE(HasResponseBody());
+  auto response = GetDeviceManagementResponse();
+  EXPECT_TRUE(response.has_device_status_report_response());
+  EXPECT_TRUE(response.has_session_status_report_response());
+}
+
+}  // namespace policy
diff --git a/components/policy/test_support/test_server_helpers.cc b/components/policy/test_support/test_server_helpers.cc
index 758cd12..ec73939 100644
--- a/components/policy/test_support/test_server_helpers.cc
+++ b/components/policy/test_support/test_server_helpers.cc
@@ -5,8 +5,10 @@
 #include "components/policy/test_support/test_server_helpers.h"
 
 #include <utility>
+#include "base/ranges/algorithm.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "net/base/url_util.h"
+#include "net/http/http_status_code.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "third_party/re2/src/re2/re2.h"
@@ -17,6 +19,28 @@
 using ::net::test_server::HttpRequest;
 using ::net::test_server::HttpResponse;
 
+namespace {
+
+// C++ does not offer a mechanism to check if a given status code is present in
+// net::HttpStatusCode enum. To allow distinguishing standard HTTP status code
+// from custom ones, we define this array that will contain all standard codes.
+constexpr net::HttpStatusCode kStandardHttpStatusCodes[] = {
+#define HTTP_STATUS(label, code, reason) net::HttpStatusCode(code),
+#include "net/http/http_status_code_list.h"
+#undef HTTP_STATUS
+};
+
+}  // namespace
+
+void CustomHttpResponse::SendResponse(
+    base::WeakPtr<net::test_server::HttpResponseDelegate> delegate) {
+  std::string reason = "Custom";
+  if (base::ranges::lower_bound(kStandardHttpStatusCodes, code()))
+    reason = BasicHttpResponse::reason();
+  delegate->SendHeadersContentAndFinish(code(), reason, BuildHeaders(),
+                                        content());
+}
+
 std::string KeyValueFromUrl(GURL url, const std::string& key) {
   std::string value;
   return net::GetValueForKeyInQuery(url, key, &value) ? value : std::string();
@@ -60,7 +84,7 @@
 
 std::unique_ptr<HttpResponse> CreateHttpResponse(net::HttpStatusCode code,
                                                  const std::string& content) {
-  auto response = std::make_unique<BasicHttpResponse>();
+  auto response = std::make_unique<CustomHttpResponse>();
   response->set_content_type("text/plain");
   response->set_code(code);
   response->set_content(content);
diff --git a/components/policy/test_support/test_server_helpers.h b/components/policy/test_support/test_server_helpers.h
index cf6ec00..4804a03 100644
--- a/components/policy/test_support/test_server_helpers.h
+++ b/components/policy/test_support/test_server_helpers.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "net/http/http_status_code.h"
+#include "net/test/embedded_test_server/http_response.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -20,6 +21,13 @@
 
 namespace policy {
 
+// HTTP Response that supports custom HTTP status codes.
+class CustomHttpResponse : public net::test_server::BasicHttpResponse {
+ public:
+  void SendResponse(
+      base::WeakPtr<net::test_server::HttpResponseDelegate> delegate) override;
+};
+
 // Returns the value associated with `key` in `url`'s query or empty string if
 // `key` is not present.
 std::string KeyValueFromUrl(GURL url, const std::string& key);
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc
index bee1db1..d38dbd4 100644
--- a/content/browser/back_forward_cache_features_browsertest.cc
+++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -3752,10 +3752,10 @@
 
   // 1) Navigate to a page and start using SpeechSynthesis.
   EXPECT_TRUE(NavigateToURL(shell(), url_a));
-  RenderFrameHostImpl* rfh_a = current_frame_host();
-  RenderFrameDeletedObserver rhf_a_deleted(rfh_a);
+  RenderFrameHostImplWrapper rfh_a(current_frame_host());
+  RenderFrameDeletedObserver rfh_a_deleted(rfh_a.get());
 
-  EXPECT_TRUE(ExecJs(rfh_a, R"(
+  EXPECT_TRUE(ExecJs(rfh_a.get(), R"(
     new Promise(async resolve => {
     var u = new SpeechSynthesisUtterance(" ");
     speechSynthesis.speak(u);
@@ -3767,7 +3767,7 @@
   EXPECT_TRUE(NavigateToURL(shell(), url_b));
 
   // The page uses SpeechSynthesis so it should be deleted.
-  rhf_a_deleted.WaitUntilDeleted();
+  rfh_a_deleted.WaitUntilDeleted();
 
   // 3) Go back to the page with SpeechSynthesis.
   ASSERT_TRUE(HistoryGoBack(web_contents()));
@@ -3777,6 +3777,50 @@
       {}, FROM_HERE);
 }
 
+// This test is not important for Chrome OS if TTS is called in content. For
+// more details refer (content/browser/speech/tts_platform_impl.cc).
+#if defined(OS_CHROMEOS)
+#define MAYBE_CacheIfSpeechSynthesisCancelled \
+  DISABLED_CacheIfSpeechSynthesisCancelled
+#else
+#define MAYBE_CacheIfSpeechSynthesisCancelled CacheIfSpeechSynthesisCancelled
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       MAYBE_CacheIfSpeechSynthesisCancelled) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  // 1) Navigate to a page and start using SpeechSynthesis.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImplWrapper rfh_a(current_frame_host());
+
+  EXPECT_TRUE(ExecJs(rfh_a.get(), R"(
+    new Promise(async resolve => {
+    var u = new SpeechSynthesisUtterance(" ");
+    speechSynthesis.speak(u);
+    resolve();
+    });
+    )"));
+  // Register a pagehide handler to cancel SpeechSynthesis. It will no longer
+  // block bfcache.
+  EXPECT_TRUE(ExecJs(rfh_a.get(), R"(
+    window.onpagehide = function(e) {
+      new Promise(resolve => {
+      speechSynthesis.cancel();
+      resolve();
+      });
+    };
+  )"));
+
+  // 2) Navigate away.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b));
+
+  // 3) Go back to the page with SpeechSynthesis.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectRestored(FROM_HERE);
+}
+
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
                        DoesNotCacheIfRunFileChooserIsInvoked) {
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/content/browser/fenced_frame/fenced_frame.cc b/content/browser/fenced_frame/fenced_frame.cc
index 754a66d..ce120e01 100644
--- a/content/browser/fenced_frame/fenced_frame.cc
+++ b/content/browser/fenced_frame/fenced_frame.cc
@@ -111,13 +111,6 @@
   return proxy_to_inner_main_frame_;
 }
 
-void FencedFrame::OnFrameTreeNodeDestroyed(
-    FrameTreeNode* outer_delegate_frame_tree_node) {
-  DCHECK_EQ(outer_delegate_frame_tree_node_, outer_delegate_frame_tree_node);
-  owner_render_frame_host_->DestroyFencedFrame(*this);
-  // Don't use `this` after this point, as it is destroyed.
-}
-
 void FencedFrame::CreateProxyAndAttachToOuterFrameTree() {
   // The fenced frame should not already be attached.
   DCHECK(!outer_delegate_frame_tree_node_);
@@ -143,11 +136,6 @@
       ->set_inner_tree_main_frame_tree_node_id(
           frame_tree_->root()->frame_tree_node_id());
 
-  // We observe the outer node because when it is destroyed by its parent
-  // RenderFrameHostImpl, we respond to its destruction by destroying ourself
-  // and the inner fenced frame FrameTree.
-  outer_delegate_frame_tree_node_->AddObserver(this);
-
   FrameTreeNode* inner_root = frame_tree_->root();
   proxy_to_inner_main_frame_ =
       inner_root->render_manager()->CreateOuterDelegateProxy(
diff --git a/content/browser/fenced_frame/fenced_frame.h b/content/browser/fenced_frame/fenced_frame.h
index f1c6efda..68aaee5 100644
--- a/content/browser/fenced_frame/fenced_frame.h
+++ b/content/browser/fenced_frame/fenced_frame.h
@@ -10,7 +10,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/safe_ref.h"
-#include "content/browser/renderer_host/frame_tree_node.h"
+#include "content/browser/renderer_host/frame_tree.h"
 #include "content/browser/renderer_host/navigation_controller_delegate.h"
 #include "content/common/content_export.h"
 #include "content/common/frame.mojom.h"
@@ -32,7 +32,6 @@
 // on `RenderFrameHostImpl`.
 class CONTENT_EXPORT FencedFrame : public blink::mojom::FencedFrameOwnerHost,
                                    public FrameTree::Delegate,
-                                   public FrameTreeNode::Observer,
                                    public NavigationControllerDelegate {
  public:
   explicit FencedFrame(
@@ -57,18 +56,6 @@
   int GetOuterDelegateFrameTreeNodeId() override;
   bool IsPortal() override;
 
-  // FrameTreeNode::Observer.
-  // We are monitoring the destruction of the outer delegate dummy
-  // FrameTreeNode. That node is a direct child of `owner_render_frame_host_`,
-  // so in order to make the lifetime of `this` fenced frame perfectly match
-  // that of a traditional child node, we tie ourselves directly to its
-  // destruction.
-  void OnFrameTreeNodeDestroyed(FrameTreeNode*) override;
-
-  // TODO(crbug.com/1123606): Make FencedFrame a NavigationControllerDelegate
-  // to suppress certain events about the fenced frame from being exposed to the
-  // outer WebContents.
-
   RenderFrameProxyHost* GetProxyToInnerMainFrame();
 
   // Returns the devtools frame token of the fenced frame's inner FrameTree's
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
index e1b7cf7e..c63e213 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
@@ -358,7 +358,14 @@
   loop3.Run();
 }
 
-TEST_F(IndexedDBDispatcherHostTest, OpenNewConnectionWhileUpgrading) {
+// TODO(crbug.com/1282613): Test is flaky on Mac in debug.
+#if defined(OS_MAC) && !defined(NDEBUG)
+#define MAYBE_OpenNewConnectionWhileUpgrading \
+  DISABLED_OpenNewConnectionWhileUpgrading
+#else
+#define MAYBE_OpenNewConnectionWhileUpgrading OpenNewConnectionWhileUpgrading
+#endif
+TEST_F(IndexedDBDispatcherHostTest, MAYBE_OpenNewConnectionWhileUpgrading) {
   const int64_t kDBVersion = 1;
   const int64_t kTransactionId = 1;
   const int64_t kObjectStoreId = 10;
diff --git a/content/browser/renderer_host/frame_tree_node.cc b/content/browser/renderer_host/frame_tree_node.cc
index 01a84ac..49f824f 100644
--- a/content/browser/renderer_host/frame_tree_node.cc
+++ b/content/browser/renderer_host/frame_tree_node.cc
@@ -18,6 +18,7 @@
 #include "base/strings/string_util.h"
 #include "base/timer/elapsed_timer.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
+#include "content/browser/fenced_frame/fenced_frame.h"
 #include "content/browser/renderer_host/navigation_controller_impl.h"
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/renderer_host/navigator.h"
@@ -138,6 +139,44 @@
   blame_context_.Initialize();
 }
 
+void FrameTreeNode::DestroyInnerFrameTreeIfExists() {
+  // If `this` is an dummy outer delegate node, then we really are representing
+  // an inner FrameTree for one of the following consumers:
+  //   - `Portal`
+  //   - `FencedFrame`
+  //   - `GuestView`
+  // If we are representing a `FencedFrame` object, we need to destroy it
+  // alongside ourself. `Portals` and `GuestView` however, *currently* have a
+  // more complex lifetime and are dealt with separately.
+  bool is_outer_dummy_node = false;
+  if (current_frame_host() &&
+      current_frame_host()->inner_tree_main_frame_tree_node_id() !=
+          FrameTreeNode::kFrameTreeNodeInvalidId) {
+    is_outer_dummy_node = true;
+  }
+
+  if (is_outer_dummy_node) {
+    DCHECK(parent());
+    // Try and find the `FencedFrame` that `this` represents.
+    std::vector<FencedFrame*> fenced_frames = parent()->GetFencedFrames();
+    FencedFrame* doomed_fenced_frame = nullptr;
+    for (FencedFrame* fenced_frame : fenced_frames) {
+      if (frame_tree_node_id() ==
+          fenced_frame->GetOuterDelegateFrameTreeNodeId()) {
+        doomed_fenced_frame = fenced_frame;
+        break;
+      }
+    }
+
+    // `doomed_fenced_frame` might not actually exist, because some outer dummy
+    // `FrameTreeNode`s might correspond to `Portal`s, which do not have their
+    // lifetime managed in the same way as `FencedFrames`.
+    if (doomed_fenced_frame) {
+      parent()->DestroyFencedFrame(*doomed_fenced_frame);
+    }
+  }
+}
+
 FrameTreeNode::~FrameTreeNode() {
   // There should always be a current RenderFrameHost except during prerender
   // activation. Prerender activation moves the current RenderFrameHost from
@@ -176,6 +215,8 @@
 
   frame_tree_->FrameRemoved(this);
 
+  DestroyInnerFrameTreeIfExists();
+
   // Do not dispatch notification for the root frame as ~WebContentsImpl already
   // dispatches it for now.
   // TODO(https://crbug.com/1170277): This is only needed because the FrameTree
diff --git a/content/browser/renderer_host/frame_tree_node.h b/content/browser/renderer_host/frame_tree_node.h
index ad01d27f..94b0aec 100644
--- a/content/browser/renderer_host/frame_tree_node.h
+++ b/content/browser/renderer_host/frame_tree_node.h
@@ -529,6 +529,10 @@
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessPermissionsPolicyBrowserTest,
                            ContainerPolicySandboxDynamic);
 
+  // Called by the destructor. When `this` is an outer dummy FrameTreeNode
+  // representing an inner FrameTree, this method destroys said inner FrameTree.
+  void DestroyInnerFrameTreeIfExists();
+
   class OpenerDestroyedObserver;
 
   // The |notification_type| parameter is used for histograms only.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 3b3a2ce..7560d8e 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -9631,12 +9631,6 @@
         GetProcess()->GetBrowserContext(), this);
   }
   speech_synthesis_impl_->AddReceiver(std::move(receiver));
-
-  // Blocklist SpeechSynthesis for BackForwardCache, because currently we do not
-  // handle speech synthesis after placing the page in BackForwardCache.
-  // TODO(sreejakshetty): Make SpeechSynthesis compatible with BackForwardCache.
-  OnBackForwardCacheDisablingFeatureUsed(
-      BackForwardCacheDisablingFeature::kSpeechSynthesis);
 }
 
 void RenderFrameHostImpl::GetSensorProvider(
diff --git a/content/browser/speech/speech_synthesis_impl.cc b/content/browser/speech/speech_synthesis_impl.cc
index d5f8f9ce..252a8d8 100644
--- a/content/browser/speech/speech_synthesis_impl.cc
+++ b/content/browser/speech/speech_synthesis_impl.cc
@@ -4,7 +4,6 @@
 
 #include "content/browser/speech/speech_synthesis_impl.h"
 
-#include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/speech/tts_utterance_impl.h"
 #include "content/public/browser/web_contents.h"
 
@@ -92,7 +91,9 @@
 SpeechSynthesisImpl::SpeechSynthesisImpl(BrowserContext* browser_context,
                                          RenderFrameHostImpl* rfh)
     : browser_context_(browser_context),
-      web_contents_(WebContents::FromRenderFrameHost((rfh))) {
+      web_contents_(WebContents::FromRenderFrameHost((rfh))),
+      feature_handle_(rfh->RegisterBackForwardCacheDisablingNonStickyFeature(
+          blink::scheduler::WebSchedulerTrackedFeature::kSpeechSynthesis)) {
   DCHECK(browser_context_);
   DCHECK(web_contents_);
   TtsController::GetInstance()->AddVoicesChangedDelegate(this);
@@ -155,6 +156,7 @@
 
 void SpeechSynthesisImpl::Cancel() {
   TtsController::GetInstance()->Stop();
+  feature_handle_.reset();
 }
 
 void SpeechSynthesisImpl::OnVoicesChanged() {
diff --git a/content/browser/speech/speech_synthesis_impl.h b/content/browser/speech/speech_synthesis_impl.h
index 984f1787..8c42656 100644
--- a/content/browser/speech/speech_synthesis_impl.h
+++ b/content/browser/speech/speech_synthesis_impl.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_SPEECH_SPEECH_SYNTHESIS_IMPL_H_
 
 #include "base/memory/raw_ptr.h"
+#include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/public/browser/tts_controller.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
@@ -49,6 +50,11 @@
   raw_ptr<BrowserContext> browser_context_;
   raw_ptr<WebContents> web_contents_;
 
+  // When the handle is set, the page is not eligible for back/forward cache.
+  // When the handle is reset, the page is no longer blocked for back/forward
+  // cache because of speech synthesis.
+  RenderFrameHostImpl::BackForwardCacheDisablingFeatureHandle feature_handle_;
+
   mojo::ReceiverSet<blink::mojom::SpeechSynthesis> receiver_set_;
   mojo::RemoteSet<blink::mojom::SpeechSynthesisVoiceListObserver> observer_set_;
 };
diff --git a/docs/mac_build_instructions.md b/docs/mac_build_instructions.md
index 632ba03..26ca10fb 100644
--- a/docs/mac_build_instructions.md
+++ b/docs/mac_build_instructions.md
@@ -27,10 +27,10 @@
     [official builds](https://source.chromium.org/search?q=MAC_BINARIES_LABEL&ss=chromium),
     so that version is guaranteed to work. Building with a newer SDK usually
     works too (please fix or file a bug if it doesn't).
-    
+
     Building with an older SDK might also work, but if it doesn't then we won't
     accept changes for making it work.
-    
+
     The easiest way to get the newest SDK is to use the newest version of Xcode,
     which often requires using the newest version of macOS. We don't use Xcode
     itself much, so if you're know what you're doing, you can likely get the
@@ -196,9 +196,15 @@
 
 --disable-features="DialMediaRouteProvider"
 
-## Running test targets
+## Build and run test targets
 
-You can run the tests in the same way. You can also limit which tests are
+You can build a test in the same way, e.g.:
+
+```shell
+$ autoninja -C out/Default unit_tests
+```
+
+and can run the tests in the same way. You can also limit which tests are
 run using the `--gtest_filter` arg, e.g.:
 
 ```
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 27ae863..c8fc3b7 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-d687eaaf847091f9f025ff475d48853c17691a2b
\ No newline at end of file
+53d49efafb8ed70e6d31bf686eb2fe24a5bf730f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 91b22f1..344f29f 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-8bebcdae5487683f4df6bb60ac959c1d0e7acd30
\ No newline at end of file
+cef0866a9bf6abf2010c4e07ba259fb25bf6f9e1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index d20b0d2d..818fca0 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-7a2008e3cb5ec1cee9ff812a42b95e20604c695a
\ No newline at end of file
+8ade5d64d5d14a97375026aa1a416f4678620a68
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 838bdb8a..13475e6c 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-466307de1c7100a60b12bc10f40fde7b22662340
\ No newline at end of file
+a3c3575bfab7878f45c93df3c6049a32de3f08a4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index de2b9c6..8ccc5c2 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-f435402ce4682ad3b3667653aace2c9db00e6ed5
\ No newline at end of file
+27ee2bff8dd1de236091eb66d139a66afb428ab1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 166a0e0..4790634 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-7b5790b8aac0bf4f2da6f720de61a962f4022542
\ No newline at end of file
+494cdd5b63a036b344c903d744c9769d1dfc326a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index c50f48d3..7a3bca0 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-0cd8e06b23629dac4169c94d9d94065898086a11
\ No newline at end of file
+708f18a2f6cf69aa6e8b05b0e039df953d4d7c8f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index dbed826a..d4b94541 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-5500664aaa190546ec63a235b0caf6b7693f0532
\ No newline at end of file
+5a03413203b56de4f505fecfd9afe1c526e5b218
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 066078f..0d4b6201 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-df7bdfb2e7d68a9db0a49a63263edd0fd5ccd5f8
\ No newline at end of file
+a7ecdf3e6b07d834a0b20ad283bc2142d815ff2c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index a4c204e..0e1c27c24 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-55950588a1c642c91d612439a44975d4921bba12
\ No newline at end of file
+81835cf8eb0fcec38e2dba185c8d38a087b134cf
\ No newline at end of file
diff --git a/media/base/status_codes.h b/media/base/status_codes.h
index d48bd8e..2c9bff8 100644
--- a/media/base/status_codes.h
+++ b/media/base/status_codes.h
@@ -53,11 +53,6 @@
   // DecodeStatus => Status conversion.
   kDecodeErrorDoNotUse = 0x010F,
 
-  // Windows Errors: 0x02
-  kWindowsWrappedHresult = 0x0201,
-  kWindowsApiNotAvailible = 0x0202,
-  kWindowsD3D11Error = 0x0203,
-
   // MojoDecoder Errors: 0x04
   kMojoDecoderNoWrappedDecoder = 0x0401,
   kMojoDecoderStoppedBeforeInitDone = 0x0402,
diff --git a/media/base/win/BUILD.gn b/media/base/win/BUILD.gn
index 15ab5db..bcc1f9d5 100644
--- a/media/base/win/BUILD.gn
+++ b/media/base/win/BUILD.gn
@@ -41,14 +41,6 @@
   all_dependent_configs = [ ":delay_load_mf" ]
 }
 
-source_set("hresult_status_helper") {
-  sources = [
-    "hresult_status_helper.cc",
-    "hresult_status_helper.h",
-  ]
-  deps = [ "//media" ]
-}
-
 source_set("mf_cdm_proxy") {
   sources = [ "media_foundation_cdm_proxy.h" ]
   deps = [ "//base" ]
diff --git a/media/base/win/hresult_status_helper.cc b/media/base/win/hresult_status_helper.cc
index fe141bb..b8218fa 100644
--- a/media/base/win/hresult_status_helper.cc
+++ b/media/base/win/hresult_status_helper.cc
@@ -9,18 +9,18 @@
 
 namespace media {
 
-Status HresultToStatus(HRESULT hresult,
-                       const char* message,
-                       StatusCode code,
-                       const base::Location& location) {
+D3D11Status HresultToStatus(HRESULT hresult,
+                            D3D11Status::Codes code,
+                            const char* message,
+                            const base::Location& location) {
   if (SUCCEEDED(hresult))
-    return OkStatus();
+    return D3D11Status::Codes::kOk;
 
   std::string sys_err = logging::SystemErrorCodeToString(hresult);
   if (!base::IsStringUTF8AllowingNoncharacters(sys_err))
     sys_err = "System error string is invalid";
 
-  return Status(code, message == nullptr ? "HRESULT" : message, location)
+  return D3D11Status(code, message == nullptr ? "HRESULT" : message, location)
       .WithData("value", sys_err);
 }
 
diff --git a/media/base/win/hresult_status_helper.h b/media/base/win/hresult_status_helper.h
deleted file mode 100644
index 67ffe3f..0000000
--- a/media/base/win/hresult_status_helper.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_BASE_WIN_HRESULT_STATUS_HELPER_H_
-#define MEDIA_BASE_WIN_HRESULT_STATUS_HELPER_H_
-
-#include <wrl/client.h>
-
-#include "media/base/status.h"
-
-namespace media {
-
-// Generate a status from an HRESULT. If message is provided, it will add the
-// HRESULT hex value as a data value to the status. Otherwise, the hex value
-// will be included in the error message itself.
-Status HresultToStatus(
-    HRESULT hresult,
-    const char* message = nullptr,
-    StatusCode code = StatusCode::kWindowsWrappedHresult,
-    const base::Location& location = base::Location::Current());
-
-}  // namespace media
-
-#endif  // MEDIA_BASE_WIN_HRESULT_STATUS_HELPER_H_
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 30267d96..47adf4f4 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -185,6 +185,7 @@
       "windows/d3d11_h264_accelerator.h",
       "windows/d3d11_picture_buffer.cc",
       "windows/d3d11_picture_buffer.h",
+      "windows/d3d11_status.cc",
       "windows/d3d11_status.h",
       "windows/d3d11_texture_selector.cc",
       "windows/d3d11_texture_selector.h",
@@ -209,8 +210,6 @@
       "windows/dxva_picture_buffer_win.h",
       "windows/dxva_video_decode_accelerator_win.cc",
       "windows/dxva_video_decode_accelerator_win.h",
-      "windows/hresult_status_debug_device.cc",
-      "windows/hresult_status_debug_device.h",
       "windows/init_guid.cc",
       "windows/media_foundation_video_encode_accelerator_win.cc",
       "windows/media_foundation_video_encode_accelerator_win.h",
@@ -221,7 +220,6 @@
     public_deps += [ "//media/base/win:media_foundation_util" ]
     deps += [
       "//gpu/ipc/common:common",
-      "//media/base/win:hresult_status_helper",
       "//media/parsers",
       "//third_party/angle:includes",
       "//ui/display",
diff --git a/media/gpu/windows/d3d11_copying_texture_wrapper.cc b/media/gpu/windows/d3d11_copying_texture_wrapper.cc
index 1a4f0306..76e98a9 100644
--- a/media/gpu/windows/d3d11_copying_texture_wrapper.cc
+++ b/media/gpu/windows/d3d11_copying_texture_wrapper.cc
@@ -8,7 +8,6 @@
 
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "media/base/status_codes.h"
-#include "media/base/win/hresult_status_helper.h"
 #include "media/gpu/windows/d3d11_com_defs.h"
 #include "ui/gl/hdr_metadata_helper_win.h"
 
@@ -52,9 +51,8 @@
   HRESULT hr = video_processor_->CreateVideoProcessorOutputView(
       output_texture_.Get(), &output_view_desc, &output_view);
   if (!SUCCEEDED(hr)) {
-    return D3D11Status(
-               D3D11Status::Codes::kCreateVideoProcessorOutputViewFailed)
-        .AddCause(HresultToStatus(hr));
+    return HresultToStatus(
+        hr, D3D11Status::Codes::kCreateVideoProcessorOutputViewFailed);
   }
 
   D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_view_desc = {0};
@@ -65,8 +63,8 @@
   hr = video_processor_->CreateVideoProcessorInputView(
       texture_.Get(), &input_view_desc, &input_view);
   if (!SUCCEEDED(hr)) {
-    return D3D11Status(D3D11Status::Codes::kCreateVideoProcessorInputViewFailed)
-        .AddCause(HresultToStatus(hr));
+    return HresultToStatus(
+        hr, D3D11Status::Codes::kCreateVideoProcessorInputViewFailed);
   }
 
   D3D11_VIDEO_PROCESSOR_STREAM streams = {0};
@@ -106,8 +104,7 @@
                                            1,  // stream_count
                                            &streams);
   if (!SUCCEEDED(hr)) {
-    return D3D11Status(D3D11Status::Codes::kVideoProcessorBltFailed)
-        .AddCause(HresultToStatus(hr));
+    return HresultToStatus(hr, D3D11Status::Codes::kVideoProcessorBltFailed);
   }
 
   return output_texture_wrapper_->ProcessTexture(copy_color_space, mailbox_dest,
diff --git a/media/gpu/windows/d3d11_decoder_configurator.cc b/media/gpu/windows/d3d11_decoder_configurator.cc
index 01b91c77..4466a0a 100644
--- a/media/gpu/windows/d3d11_decoder_configurator.cc
+++ b/media/gpu/windows/d3d11_decoder_configurator.cc
@@ -12,10 +12,10 @@
 #include "media/base/media_log.h"
 #include "media/base/media_switches.h"
 #include "media/base/status_codes.h"
-#include "media/base/win/hresult_status_helper.h"
 #include "media/base/win/mf_helpers.h"
 #include "media/gpu/windows/av1_guids.h"
 #include "media/gpu/windows/d3d11_copying_texture_wrapper.h"
+#include "media/gpu/windows/d3d11_status.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gl/direct_composition_surface_win.h"
 
@@ -129,13 +129,13 @@
   HRESULT hr =
       device->CreateTexture2D(&output_texture_desc_, nullptr, &texture);
   if (FAILED(hr)) {
-    return D3D11Status(D3D11Status::Codes::kCreateDecoderOutputTextureFailed)
-        .AddCause(HresultToStatus(hr));
+    return HresultToStatus(
+        hr, D3D11Status::Codes::kCreateDecoderOutputTextureFailed);
   }
   hr = SetDebugName(texture.Get(), "D3D11Decoder_ConfiguratorOutput");
   if (FAILED(hr)) {
-    return D3D11Status(D3D11Status::Codes::kCreateDecoderOutputTextureFailed)
-        .AddCause(HresultToStatus(hr));
+    return HresultToStatus(
+        hr, D3D11Status::Codes::kCreateDecoderOutputTextureFailed);
   }
   return texture;
 }
diff --git a/media/gpu/windows/d3d11_picture_buffer.cc b/media/gpu/windows/d3d11_picture_buffer.cc
index 6a83b4c..ca680d03 100644
--- a/media/gpu/windows/d3d11_picture_buffer.cc
+++ b/media/gpu/windows/d3d11_picture_buffer.cc
@@ -15,7 +15,6 @@
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "media/base/media_log.h"
-#include "media/base/win/hresult_status_helper.h"
 #include "media/base/win/mf_helpers.h"
 #include "third_party/angle/include/EGL/egl.h"
 #include "third_party/angle/include/EGL/eglext.h"
@@ -66,8 +65,8 @@
 
   if (!SUCCEEDED(hr)) {
     MEDIA_LOG(ERROR, media_log_) << "Failed to CreateVideoDecoderOutputView";
-    return D3D11Status(D3D11Status::Codes::kCreateDecoderOutputViewFailed)
-        .AddCause(HresultToStatus(hr));
+    return HresultToStatus(hr,
+                           D3D11Status::Codes::kCreateDecoderOutputViewFailed);
   }
 
   return D3D11Status::Codes::kOk;
diff --git a/media/gpu/windows/d3d11_status.cc b/media/gpu/windows/d3d11_status.cc
new file mode 100644
index 0000000..5d425ed1
--- /dev/null
+++ b/media/gpu/windows/d3d11_status.cc
@@ -0,0 +1,27 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/windows/d3d11_status.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+
+namespace media {
+
+D3D11Status HresultToStatus(HRESULT hresult,
+                            D3D11Status::Codes code,
+                            const char* message,
+                            const base::Location& location) {
+  if (SUCCEEDED(hresult))
+    return D3D11Status::Codes::kOk;
+
+  std::string sys_err = logging::SystemErrorCodeToString(hresult);
+  if (!base::IsStringUTF8AllowingNoncharacters(sys_err))
+    sys_err = "System error string is invalid";
+
+  return D3D11Status(code, message == nullptr ? "HRESULT" : message, location)
+      .WithData("value", sys_err);
+}
+
+}  // namespace media
diff --git a/media/gpu/windows/d3d11_status.h b/media/gpu/windows/d3d11_status.h
index f408b71..65db9ac1 100644
--- a/media/gpu/windows/d3d11_status.h
+++ b/media/gpu/windows/d3d11_status.h
@@ -5,6 +5,8 @@
 #ifndef MEDIA_GPU_WINDOWS_D3D11_STATUS_H_
 #define MEDIA_GPU_WINDOWS_D3D11_STATUS_H_
 
+#include <wrl/client.h>
+
 #include "media/base/status.h"
 
 namespace media {
@@ -80,6 +82,12 @@
 
 using D3D11Status = TypedStatus<D3D11StatusTraits>;
 
+D3D11Status HresultToStatus(
+    HRESULT hresult,
+    D3D11Status::Codes code,
+    const char* message = nullptr,
+    const base::Location& location = base::Location::Current());
+
 }  // namespace media
 
 #endif  // MEDIA_GPU_WINDOWS_D3D11_STATUS_H_
diff --git a/media/gpu/windows/d3d11_texture_wrapper.cc b/media/gpu/windows/d3d11_texture_wrapper.cc
index eaa5eab..1a1b806 100644
--- a/media/gpu/windows/d3d11_texture_wrapper.cc
+++ b/media/gpu/windows/d3d11_texture_wrapper.cc
@@ -16,7 +16,6 @@
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/shared_image_backing_d3d.h"
 #include "media/base/bind_to_current_loop.h"
-#include "media/base/win/hresult_status_helper.h"
 #include "media/base/win/mf_helpers.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "ui/gl/gl_image.h"
@@ -85,8 +84,7 @@
   if (FAILED(hr)) {
     keyed_mutex_acquired_ = false;
     DPLOG(ERROR) << "Unable to acquire the key mutex, error: " << hr;
-    return D3D11Status(D3D11Status::Codes::kAcquireKeyedMutexFailed)
-        .AddCause(HresultToStatus(hr));
+    return HresultToStatus(hr, D3D11Status::Codes::kAcquireKeyedMutexFailed);
   }
 
   // Key mutex has been acquired for shared resource.
@@ -105,8 +103,7 @@
     HRESULT hr = keyed_mutex_->ReleaseSync(gpu::kDXGIKeyedMutexAcquireKey);
     if (FAILED(hr)) {
       DPLOG(ERROR) << "Unable to release the keyed mutex, error: " << hr;
-      return D3D11Status(D3D11Status::Codes::kReleaseKeyedMutexFailed)
-          .AddCause(HresultToStatus(hr));
+      return HresultToStatus(hr, D3D11Status::Codes::kReleaseKeyedMutexFailed);
     }
 
     keyed_mutex_acquired_ = false;
@@ -148,8 +145,7 @@
       if (FAILED(hr)) {
         DPLOG(ERROR) << "Failed to get key_mutex from output resource, error "
                      << std::hex << hr;
-        return D3D11Status(D3D11Status::Codes::kGetKeyedMutexFailed)
-            .AddCause(HresultToStatus(hr));
+        return HresultToStatus(hr, D3D11Status::Codes::kGetKeyedMutexFailed);
       }
     }
   }
diff --git a/media/gpu/windows/d3d11_video_decoder.cc b/media/gpu/windows/d3d11_video_decoder.cc
index 6bfc2b9..34a8c9d 100644
--- a/media/gpu/windows/d3d11_video_decoder.cc
+++ b/media/gpu/windows/d3d11_video_decoder.cc
@@ -28,9 +28,9 @@
 #include "media/base/video_decoder_config.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
-#include "media/base/win/hresult_status_helper.h"
 #include "media/gpu/windows/d3d11_av1_accelerator.h"
 #include "media/gpu/windows/d3d11_picture_buffer.h"
+#include "media/gpu/windows/d3d11_status.h"
 #include "media/gpu/windows/d3d11_video_context_wrapper.h"
 #include "media/gpu/windows/d3d11_video_decoder_impl.h"
 #include "media/gpu/windows/d3d11_video_device_format_support.h"
@@ -247,8 +247,8 @@
   auto hr = video_device_->GetVideoDecoderConfigCount(
       decoder_configurator_->DecoderDescriptor(), &config_count);
   if (FAILED(hr)) {
-    return D3D11Status(D3D11Status::Codes::kGetDecoderConfigCountFailed)
-        .AddCause(HresultToStatus(hr));
+    return HresultToStatus(hr,
+                           D3D11Status::Codes::kGetDecoderConfigCountFailed);
   }
 
   if (config_count == 0)
@@ -261,8 +261,7 @@
     hr = video_device_->GetVideoDecoderConfig(
         decoder_configurator_->DecoderDescriptor(), i, &dec_config);
     if (FAILED(hr)) {
-      return D3D11Status(D3D11Status::Codes::kGetDecoderConfigFailed)
-          .AddCause(HresultToStatus(hr));
+      return HresultToStatus(hr, D3D11Status::Codes::kGetDecoderConfigFailed);
     }
 
     if ((config_.codec() == VideoCodec::kVP9 ||
@@ -317,8 +316,7 @@
     return D3D11Status(D3D11Status::Codes::kDecoderCreationFailed);
 
   if (FAILED(hr)) {
-    return D3D11Status(D3D11Status::Codes::kDecoderCreationFailed)
-        .AddCause(HresultToStatus(hr));
+    return HresultToStatus(hr, D3D11Status::Codes::kDecoderCreationFailed);
   }
 
   return {std::move(video_decoder)};
@@ -407,8 +405,7 @@
   hr = device_->QueryInterface(IID_PPV_ARGS(&multi_threaded));
   if (FAILED(hr)) {
     return NotifyError(
-        D3D11Status(D3D11Status::Codes::kQueryID3D11MultithreadFailed)
-            .AddCause(HresultToStatus(hr)));
+        HresultToStatus(hr, D3D11Status::Codes::kQueryID3D11MultithreadFailed));
   }
 
   multi_threaded->SetMultithreadProtected(TRUE);
diff --git a/media/gpu/windows/d3d11_video_processor_proxy.cc b/media/gpu/windows/d3d11_video_processor_proxy.cc
index 1f06e54b56..a31aa66 100644
--- a/media/gpu/windows/d3d11_video_processor_proxy.cc
+++ b/media/gpu/windows/d3d11_video_processor_proxy.cc
@@ -4,10 +4,59 @@
 
 #include "media/gpu/windows/d3d11_video_processor_proxy.h"
 
-#include "media/gpu/windows/hresult_status_debug_device.h"
+#include "media/base/media_serializers.h"
 #include "ui/gfx/color_space_win.h"
 
 namespace media {
+namespace {
+
+// Only define this method in debug mode.
+#if !defined(NDEBUG)
+
+void AddDebugMessages(D3D11Status* error, ComD3D11Device device) {
+  // MSDN says that this needs to be casted twice, then GetMessage should
+  // be called with a malloc.
+  Microsoft::WRL::ComPtr<ID3D11Debug> debug_layer;
+  if (!SUCCEEDED(device.As(&debug_layer)))
+    return;
+
+  Microsoft::WRL::ComPtr<ID3D11InfoQueue> message_layer;
+  if (!SUCCEEDED(debug_layer.As(&message_layer)))
+    return;
+
+  uint64_t messages_count = message_layer->GetNumStoredMessages();
+  if (messages_count == 0)
+    return;
+
+  std::vector<std::string> messages(messages_count, "");
+  for (uint64_t i = 0; i < messages_count; i++) {
+    SIZE_T size;
+    message_layer->GetMessage(i, nullptr, &size);
+    D3D11_MESSAGE* message = reinterpret_cast<D3D11_MESSAGE*>(malloc(size));
+    if (!message)  // probably OOM - so just stop trying to get more.
+      return;
+    message_layer->GetMessage(i, message, &size);
+    messages.emplace_back(message->pDescription);
+    free(message);
+  }
+
+  error->WithData("debug_info", messages);
+}
+
+D3D11Status DebugStatus(D3D11Status&& status, ComD3D11Device device) {
+  AddDebugMessages(&status, device);
+  return std::move(status);
+}
+
+#else
+
+D3D11Status DebugStatus(D3D11Status&& status, ComD3D11Device device) {
+  return std::move(status);
+}
+
+#endif  // !defined(NDEBUG)
+
+}  // namespace
 
 VideoProcessorProxy::~VideoProcessorProxy() {}
 
@@ -39,9 +88,9 @@
   HRESULT hr = video_device_->CreateVideoProcessorEnumerator(
       &desc, &processor_enumerator_);
   if (!SUCCEEDED(hr)) {
-    return D3D11Status(
-               D3D11Status::Codes::kCreateVideoProcessorEnumeratorFailed)
-        .AddCause(D3D11HresultToStatus(hr, device));
+    return DebugStatus(
+        HresultToStatus(hr, D3D11Status::Codes::kCreateDecoderOutputViewFailed),
+        device);
   }
 
   D3D11_VIDEO_PROCESSOR_CAPS caps = {0};
@@ -53,14 +102,16 @@
   hr = video_device_->CreateVideoProcessor(processor_enumerator_.Get(), 0,
                                            &video_processor_);
   if (!SUCCEEDED(hr)) {
-    return D3D11Status(D3D11Status::Codes::kCreateVideoProcessorFailed)
-        .AddCause(D3D11HresultToStatus(hr, device));
+    return DebugStatus(
+        HresultToStatus(hr, D3D11Status::Codes::kCreateVideoProcessorFailed),
+        device);
   }
 
   hr = device_context_.As(&video_context_);
   if (!SUCCEEDED(hr)) {
-    return D3D11Status(D3D11Status::Codes::kQueryVideoContextFailed)
-        .AddCause(D3D11HresultToStatus(hr, device));
+    return DebugStatus(
+        HresultToStatus(hr, D3D11Status::Codes::kQueryVideoContextFailed),
+        device);
   }
 
   return D3D11Status::Codes::kOk;
diff --git a/media/gpu/windows/hresult_status_debug_device.cc b/media/gpu/windows/hresult_status_debug_device.cc
deleted file mode 100644
index 2534965..0000000
--- a/media/gpu/windows/hresult_status_debug_device.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/gpu/windows/hresult_status_debug_device.h"
-#include "media/base/media_serializers.h"
-#include "media/base/win/hresult_status_helper.h"
-
-namespace media {
-
-// Only define this method in debug mode.
-#if !defined(NDEBUG)
-
-Status AddDebugMessages(Status error, ComD3D11Device device) {
-  // MSDN says that this needs to be casted twice, then GetMessage should
-  // be called with a malloc.
-  Microsoft::WRL::ComPtr<ID3D11Debug> debug_layer;
-  if (!SUCCEEDED(device.As(&debug_layer)))
-    return error;
-
-  Microsoft::WRL::ComPtr<ID3D11InfoQueue> message_layer;
-  if (!SUCCEEDED(debug_layer.As(&message_layer)))
-    return error;
-
-  uint64_t messages_count = message_layer->GetNumStoredMessages();
-  if (messages_count == 0)
-    return error;
-
-  std::vector<std::string> messages(messages_count, "");
-  for (uint64_t i = 0; i < messages_count; i++) {
-    SIZE_T size;
-    message_layer->GetMessage(i, nullptr, &size);
-    D3D11_MESSAGE* message = reinterpret_cast<D3D11_MESSAGE*>(malloc(size));
-    if (!message)  // probably OOM - so just stop trying to get more.
-      return error;
-    message_layer->GetMessage(i, message, &size);
-    messages.emplace_back(message->pDescription);
-    free(message);
-  }
-
-  return std::move(error).WithData("debug_info", messages);
-}
-
-#endif  // !defined(NDEBUG)
-
-Status D3D11HresultToStatus(HRESULT hresult,
-                            ComD3D11Device device,
-                            const char* message,
-                            const base::Location& location) {
-  if (SUCCEEDED(hresult))
-    return OkStatus();
-#if !defined(NDEBUG)
-  return AddDebugMessages(
-      HresultToStatus(hresult, message, StatusCode::kWindowsD3D11Error,
-                      location),
-      device);
-#else   // !defined(NDEBUG)
-  return HresultToStatus(hresult, message, StatusCode::kWindowsD3D11Error,
-                         location);
-#endif  // !defined(NDEBUG)
-}
-
-}  // namespace media
diff --git a/media/gpu/windows/hresult_status_debug_device.h b/media/gpu/windows/hresult_status_debug_device.h
deleted file mode 100644
index b07fdc5..0000000
--- a/media/gpu/windows/hresult_status_debug_device.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_GPU_WINDOWS_HRESULT_STATUS_DEBUG_DEVICE_H_
-#define MEDIA_GPU_WINDOWS_HRESULT_STATUS_DEBUG_DEVICE_H_
-
-#include <d3d11.h>
-#include <wrl/client.h>
-
-#include "media/base/status.h"
-#include "media/gpu/windows/d3d11_com_defs.h"
-
-namespace media {
-
-// In debug mode, this uses |AddDebugMessages()| to give us a detailed error
-// trace from the d3d11 stack. Otherwise, it just generates a Status with the
-// kWindowsD3D11Error code.
-Status D3D11HresultToStatus(
-    HRESULT hresult,
-    ComD3D11Device device,
-    const char* message = nullptr,
-    const base::Location& location = base::Location::Current());
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_WINDOWS_HRESULT_STATUS_DEBUG_DEVICE_H_
\ No newline at end of file
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client.cc b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
index 0f9b8e6..3027fedf 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
@@ -188,6 +188,7 @@
   }
 
   auto new_local_rect = rect_and_block.caret_rect;
+  // TODO(crbug.com/1123630): Avoid paint invalidation on caret movement.
   if (new_local_rect != local_rect_) {
     needs_paint_invalidation_ = true;
     local_rect_ = new_local_rect;
@@ -197,10 +198,10 @@
     new_layout_block->SetShouldCheckForPaintInvalidation();
 }
 
-void CaretDisplayItemClient::SetVisibleIfActive(bool visible) {
-  if (visible == is_visible_if_active_)
+void CaretDisplayItemClient::SetActive(bool active) {
+  if (active == is_active_)
     return;
-  is_visible_if_active_ = visible;
+  is_active_ = active;
   needs_paint_invalidation_ = true;
 }
 
@@ -267,7 +268,7 @@
   }
 
   gfx::Rect paint_rect = ToPixelSnappedRect(drawing_rect);
-  context.FillRect(paint_rect, is_visible_if_active_ ? color_ : Color(),
+  context.FillRect(paint_rect, color_,
                    PaintAutoDarkMode(layout_block_->StyleRef(),
                                      DarkModeFilter::ElementRole::kForeground));
 }
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client.h b/third_party/blink/renderer/core/editing/caret_display_item_client.h
index e97e7c4..3c4813d 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client.h
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client.h
@@ -60,8 +60,7 @@
   // caret for paint invalidation and painting.
   void UpdateStyleAndLayoutIfNeeded(const PositionWithAffinity& caret_position);
 
-  bool IsVisibleIfActive() const { return is_visible_if_active_; }
-  void SetVisibleIfActive(bool visible);
+  void SetActive(bool active);
 
   // Called during LayoutBlock paint invalidation.
   void InvalidatePaint(const LayoutBlock&, const PaintInvalidatorContext&);
@@ -102,7 +101,7 @@
   void InvalidatePaintInCurrentLayoutBlock(const PaintInvalidatorContext&);
   void InvalidatePaintInPreviousLayoutBlock(const PaintInvalidatorContext&);
 
-  // These are updated by updateStyleAndLayoutIfNeeded().
+  // These are updated by UpdateStyleAndLayoutIfNeeded().
   Color color_;
   PhysicalRect local_rect_;
   Member<LayoutBlock> layout_block_;
@@ -115,8 +114,8 @@
 
   const NGPhysicalBoxFragment* box_fragment_ = nullptr;
 
+  bool is_active_ = false;
   bool needs_paint_invalidation_ = false;
-  bool is_visible_if_active_ = true;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc b/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
index 92aef879e..e1b725d3 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
@@ -6,6 +6,7 @@
 
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/renderer/core/editing/frame_caret.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
 #include "third_party/blink/renderer/core/editing/selection_template.h"
@@ -36,19 +37,24 @@
     return GetDocument().View()->GetFrame().Selection();
   }
 
-  const CaretDisplayItemClient& GetCaretDisplayItemClient() const {
-    return Selection().CaretDisplayItemClientForTesting();
+  FrameCaret& GetFrameCaret() { return Selection().FrameCaretForTesting(); }
+
+  bool IsVisibleIfActive() { return GetFrameCaret().IsVisibleIfActive(); }
+  void SetVisibleIfActive(bool v) { GetFrameCaret().SetVisibleIfActive(v); }
+
+  CaretDisplayItemClient& GetCaretDisplayItemClient() {
+    return *GetFrameCaret().display_item_client_;
   }
 
-  const PhysicalRect& CaretLocalRect() const {
+  const PhysicalRect& CaretLocalRect() {
     return GetCaretDisplayItemClient().local_rect_;
   }
 
-  const LayoutBlock* CaretLayoutBlock() const {
+  const LayoutBlock* CaretLayoutBlock() {
     return GetCaretDisplayItemClient().layout_block_;
   }
 
-  const LayoutBlock* PreviousCaretLayoutBlock() const {
+  const LayoutBlock* PreviousCaretLayoutBlock() {
     return GetCaretDisplayItemClient().previous_layout_block_;
   }
 
@@ -70,6 +76,17 @@
     return block;
   }
 
+  RasterInvalidationTracking* CaretRasterInvalidationTracking() const {
+    for (const auto& client : GetDocument()
+                                  .View()
+                                  ->GetPaintArtifactCompositor()
+                                  ->ContentLayerClientsForTesting()) {
+      if (client->Layer().DebugName() == "Caret")
+        return client->GetRasterInvalidator().GetTracking();
+    }
+    return nullptr;
+  }
+
   void UpdateAllLifecyclePhasesForCaretTest() {
     // Partial lifecycle updates should not affect caret paint invalidation.
     GetDocument().View()->UpdateLifecycleToLayoutClean(
@@ -104,10 +121,10 @@
   EXPECT_TRUE(GetCaretDisplayItemClient().IsValid());
   EXPECT_EQ(PhysicalRect(0, 0, 1, 1), CaretLocalRect());
 
-  EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
+  EXPECT_THAT(CaretRasterInvalidationTracking()->Invalidations(),
               UnorderedElementsAre(RasterInvalidationInfo{
                   GetCaretDisplayItemClient().Id(), "Caret",
-                  gfx::Rect(8, 8, 1, 1), PaintInvalidationReason::kAppeared}));
+                  gfx::Rect(0, 0, 1, 1), PaintInvalidationReason::kFullLayer}));
   GetDocument().View()->SetTracksRasterInvalidations(false);
 
   // Move the caret to the end of the text. Should invalidate both the old and
@@ -127,14 +144,11 @@
   EXPECT_GT(delta, 0);
   EXPECT_EQ(PhysicalRect(delta, 0, 1, 1), CaretLocalRect());
 
-  EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
-              UnorderedElementsAre(
-                  RasterInvalidationInfo{GetCaretDisplayItemClient().Id(),
-                                         "Caret", gfx::Rect(8, 8, 1, 1),
-                                         PaintInvalidationReason::kCaret},
-                  RasterInvalidationInfo{GetCaretDisplayItemClient().Id(),
-                                         "Caret", gfx::Rect(8 + delta, 8, 1, 1),
-                                         PaintInvalidationReason::kCaret}));
+  EXPECT_THAT(
+      CaretRasterInvalidationTracking()->Invalidations(),
+      UnorderedElementsAre(RasterInvalidationInfo{
+          GetCaretDisplayItemClient().Id(), "Caret", gfx::Rect(0, 0, 1, 1),
+          PaintInvalidationReason::kPaintProperty}));
   GetDocument().View()->SetTracksRasterInvalidations(false);
 
   // Remove selection. Should invalidate the old caret.
@@ -150,12 +164,8 @@
   // The caret display item client painted nothing, so is not validated.
   EXPECT_FALSE(GetCaretDisplayItemClient().IsValid());
   EXPECT_EQ(PhysicalRect(), CaretLocalRect());
-
-  EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
-              UnorderedElementsAre(RasterInvalidationInfo{
-                  GetCaretDisplayItemClient().Id(), "Caret",
-                  gfx::Rect(8 + delta, 8, 1, 1),
-                  PaintInvalidationReason::kDisappeared}));
+  // The caret composited layer is removed.
+  EXPECT_FALSE(CaretRasterInvalidationTracking());
   GetDocument().View()->SetTracksRasterInvalidations(false);
 }
 
@@ -200,14 +210,11 @@
   EXPECT_FALSE(ShouldPaintCursorCaret(*block1));
   EXPECT_TRUE(ShouldPaintCursorCaret(*block2));
 
-  EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
-              UnorderedElementsAre(
-                  RasterInvalidationInfo{GetCaretDisplayItemClient().Id(),
-                                         "Caret", gfx::Rect(8, 8, 1, 1),
-                                         PaintInvalidationReason::kCaret},
-                  RasterInvalidationInfo{GetCaretDisplayItemClient().Id(),
-                                         "Caret", gfx::Rect(8, 9, 1, 1),
-                                         PaintInvalidationReason::kCaret}));
+  EXPECT_THAT(
+      CaretRasterInvalidationTracking()->Invalidations(),
+      UnorderedElementsAre(RasterInvalidationInfo{
+          GetCaretDisplayItemClient().Id(), "Caret", gfx::Rect(0, 0, 1, 1),
+          PaintInvalidationReason::kPaintProperty}));
   GetDocument().View()->SetTracksRasterInvalidations(false);
 
   // Move the caret back into block1.
@@ -228,14 +235,11 @@
   EXPECT_TRUE(ShouldPaintCursorCaret(*block1));
   EXPECT_FALSE(ShouldPaintCursorCaret(*block2));
 
-  EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
-              UnorderedElementsAre(
-                  RasterInvalidationInfo{GetCaretDisplayItemClient().Id(),
-                                         "Caret", gfx::Rect(8, 8, 1, 1),
-                                         PaintInvalidationReason::kCaret},
-                  RasterInvalidationInfo{GetCaretDisplayItemClient().Id(),
-                                         "Caret", gfx::Rect(8, 9, 1, 1),
-                                         PaintInvalidationReason::kCaret}));
+  EXPECT_THAT(
+      CaretRasterInvalidationTracking()->Invalidations(),
+      UnorderedElementsAre(RasterInvalidationInfo{
+          GetCaretDisplayItemClient().Id(), "Caret", gfx::Rect(0, 0, 1, 1),
+          PaintInvalidationReason::kPaintProperty}));
   GetDocument().View()->SetTracksRasterInvalidations(false);
 }
 
@@ -334,14 +338,40 @@
   EXPECT_GT(delta, 0);
   EXPECT_EQ(PhysicalRect(delta, 0, 1, 1), CaretLocalRect());
 
-  EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
-              UnorderedElementsAre(
-                  RasterInvalidationInfo{GetCaretDisplayItemClient().Id(),
-                                         "Caret", gfx::Rect(8, 8, 1, 1),
-                                         PaintInvalidationReason::kCaret},
-                  RasterInvalidationInfo{GetCaretDisplayItemClient().Id(),
-                                         "Caret", gfx::Rect(8 + delta, 8, 1, 1),
-                                         PaintInvalidationReason::kCaret}));
+  EXPECT_THAT(
+      CaretRasterInvalidationTracking()->Invalidations(),
+      UnorderedElementsAre(RasterInvalidationInfo{
+          GetCaretDisplayItemClient().Id(), "Caret", gfx::Rect(0, 0, 1, 1),
+          PaintInvalidationReason::kPaintProperty}));
+  GetDocument().View()->SetTracksRasterInvalidations(false);
+}
+
+TEST_P(CaretDisplayItemClientTest, BlinkingCaretNoInvalidation) {
+  GetDocument().body()->setContentEditable("true", ASSERT_NO_EXCEPTION);
+  GetDocument().GetPage()->GetFocusController().SetActive(true);
+  GetDocument().GetPage()->GetFocusController().SetFocused(true);
+
+  GetDocument().body()->focus();
+  UpdateAllLifecyclePhasesForCaretTest();
+  EXPECT_EQ(PhysicalRect(0, 0, 1, 1), CaretLocalRect());
+
+  GetDocument().View()->SetTracksRasterInvalidations(true);
+
+  // No paint or raster invalidation when caret is blinking.
+  EXPECT_TRUE(IsVisibleIfActive());
+  SetVisibleIfActive(false);
+  EXPECT_TRUE(GetCaretDisplayItemClient().IsValid());
+  UpdateAllLifecyclePhasesExceptPaint();
+  EXPECT_TRUE(GetCaretDisplayItemClient().IsValid());
+  EXPECT_TRUE(CaretRasterInvalidationTracking()->Invalidations().IsEmpty());
+
+  EXPECT_TRUE(IsVisibleIfActive());
+  SetVisibleIfActive(true);
+  EXPECT_TRUE(GetCaretDisplayItemClient().IsValid());
+  UpdateAllLifecyclePhasesExceptPaint();
+  EXPECT_TRUE(GetCaretDisplayItemClient().IsValid());
+  EXPECT_TRUE(CaretRasterInvalidationTracking()->Invalidations().IsEmpty());
+
   GetDocument().View()->SetTracksRasterInvalidations(false);
 }
 
diff --git a/third_party/blink/renderer/core/editing/frame_caret.cc b/third_party/blink/renderer/core/editing/frame_caret.cc
index 3710622d..c4ac37cd 100644
--- a/third_party/blink/renderer/core/editing/frame_caret.cc
+++ b/third_party/blink/renderer/core/editing/frame_caret.cc
@@ -42,9 +42,31 @@
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
 #include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h"
 
 namespace blink {
 
+namespace {
+
+EffectPaintPropertyNode::State CaretEffectNodeState(
+    bool visible,
+    const TransformPaintPropertyNodeOrAlias& local_transform_space) {
+  EffectPaintPropertyNode::State state;
+  state.opacity = visible ? 1.f : 0.f;
+  state.local_transform_space = &local_transform_space;
+  DEFINE_STATIC_LOCAL(
+      CompositorElementId, element_id,
+      (CompositorElementIdFromUniqueObjectId(
+          NewUniqueObjectId(), CompositorElementIdNamespace::kPrimaryEffect)));
+  state.compositor_element_id = element_id;
+  state.direct_compositing_reasons = CompositingReason::kWillChangeOpacity;
+  return state;
+}
+
+}  // anonymous namespace
+
 FrameCaret::FrameCaret(LocalFrame& frame,
                        const SelectionEditor& selection_editor)
     : selection_editor_(&selection_editor),
@@ -52,7 +74,15 @@
       display_item_client_(MakeGarbageCollected<CaretDisplayItemClient>()),
       caret_blink_timer_(frame.GetTaskRunner(TaskType::kInternalDefault),
                          this,
-                         &FrameCaret::CaretBlinkTimerFired) {}
+                         &FrameCaret::CaretBlinkTimerFired),
+      effect_(EffectPaintPropertyNode::Create(
+          EffectPaintPropertyNode::Root(),
+          CaretEffectNodeState(/*visible*/ true,
+                               TransformPaintPropertyNode::Root()))) {
+#if DCHECK_IS_ON()
+  effect_->SetDebugName("Caret");
+#endif
+}
 
 FrameCaret::~FrameCaret() = default;
 
@@ -96,11 +126,11 @@
 }
 
 void FrameCaret::StopCaretBlinkTimer() {
-  if (caret_blink_timer_.IsActive() ||
-      display_item_client_->IsVisibleIfActive())
+  if (caret_blink_timer_.IsActive() || IsVisibleIfActive())
     ScheduleVisualUpdateForPaintInvalidationIfNeeded();
-  display_item_client_->SetVisibleIfActive(false);
   caret_blink_timer_.Stop();
+  display_item_client_->SetActive(false);
+  SetVisibleIfActive(false);
 }
 
 void FrameCaret::StartBlinkCaret() {
@@ -113,7 +143,8 @@
   if (!blink_interval.is_zero())
     caret_blink_timer_.StartRepeating(blink_interval, FROM_HERE);
 
-  display_item_client_->SetVisibleIfActive(true);
+  display_item_client_->SetActive(true);
+  SetVisibleIfActive(true);
   ScheduleVisualUpdateForPaintInvalidationIfNeeded();
 }
 
@@ -164,8 +195,46 @@
   return display_item_client_->ShouldPaintCaret(box_fragment);
 }
 
+void FrameCaret::SetVisibleIfActive(bool visible) {
+  if (visible == IsVisibleIfActive())
+    return;
+
+  DCHECK(frame_);
+  DCHECK(effect_);
+  if (!frame_->View())
+    return;
+
+  auto change_type = effect_->Update(
+      *effect_->Parent(),
+      CaretEffectNodeState(visible, effect_->LocalTransformSpace()));
+  DCHECK_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues, change_type);
+  if (auto* compositor = frame_->View()->GetPaintArtifactCompositor()) {
+    if (compositor->DirectlyUpdateCompositedOpacityValue(*effect_)) {
+      effect_->CompositorSimpleValuesUpdated();
+      return;
+    }
+  }
+  // Fallback to full update if direct update is not available.
+  frame_->View()->SetPaintArtifactCompositorNeedsUpdate();
+}
+
 void FrameCaret::PaintCaret(GraphicsContext& context,
                             const PhysicalOffset& paint_offset) const {
+  if (effect_->Update(
+          context.GetPaintController().CurrentPaintChunkProperties().Effect(),
+          CaretEffectNodeState(IsVisibleIfActive(),
+                               context.GetPaintController()
+                                   .CurrentPaintChunkProperties()
+                                   .Transform())) !=
+      PaintPropertyChangeType::kUnchanged) {
+    // Needs full PaintArtifactCompositor update if the parent or the local
+    // transform space changed.
+    frame_->View()->SetPaintArtifactCompositorNeedsUpdate();
+  }
+  ScopedPaintChunkProperties scoped_properties(context.GetPaintController(),
+                                               *effect_, *display_item_client_,
+                                               DisplayItem::kCaret);
+
   display_item_client_->PaintCaret(context, paint_offset, DisplayItem::kCaret);
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
       frame_->Selection().IsHandleVisible() && !frame_->Selection().IsHidden())
@@ -202,10 +271,9 @@
 
 void FrameCaret::CaretBlinkTimerFired(TimerBase*) {
   DCHECK(is_caret_enabled_);
-  if (IsCaretBlinkingSuspended() && display_item_client_->IsVisibleIfActive())
+  if (IsCaretBlinkingSuspended() && IsVisibleIfActive())
     return;
-  display_item_client_->SetVisibleIfActive(
-      !display_item_client_->IsVisibleIfActive());
+  SetVisibleIfActive(!IsVisibleIfActive());
   ScheduleVisualUpdateForPaintInvalidationIfNeeded();
 }
 
@@ -219,8 +287,4 @@
   caret_blink_timer_.MoveToNewTaskRunner(std::move(task_runner));
 }
 
-bool FrameCaret::IsVisibleIfActiveForTesting() const {
-  return display_item_client_->IsVisibleIfActive();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/frame_caret.h b/third_party/blink/renderer/core/editing/frame_caret.h
index da9424b..ee1a904 100644
--- a/third_party/blink/renderer/core/editing/frame_caret.h
+++ b/third_party/blink/renderer/core/editing/frame_caret.h
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/editing/forward.h"
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
 #include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
 #include "third_party/blink/renderer/platform/heap/disallow_new_wrapper.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -39,6 +40,7 @@
 namespace blink {
 
 class CaretDisplayItemClient;
+class EffectPaintPropertyNode;
 class FrameCaret;
 class GraphicsContext;
 class LayoutBlock;
@@ -78,11 +80,9 @@
   bool ShouldPaintCaret(const NGPhysicalBoxFragment&) const;
   void PaintCaret(GraphicsContext&, const PhysicalOffset&) const;
 
+  const EffectPaintPropertyNode& CaretEffectNode() const { return *effect_; }
+
   // For unit tests.
-  const CaretDisplayItemClient& CaretDisplayItemClientForTesting() const {
-    return *display_item_client_;
-  }
-  bool IsVisibleIfActiveForTesting() const;
   void RecreateCaretBlinkTimerForTesting(
       scoped_refptr<base::SingleThreadTaskRunner>);
 
@@ -91,18 +91,24 @@
  private:
   friend class FrameCaretTest;
   friend class FrameSelectionTest;
+  friend class CaretDisplayItemClientTest;
 
   const PositionWithAffinity CaretPosition() const;
 
   bool ShouldShowCaret() const;
   void CaretBlinkTimerFired(TimerBase*);
   void UpdateAppearance();
+  void SetVisibleIfActive(bool visible);
+  bool IsVisibleIfActive() const { return effect_->Opacity() != 0; }
 
   const Member<const SelectionEditor> selection_editor_;
   const Member<LocalFrame> frame_;
   const Member<CaretDisplayItemClient> display_item_client_;
-  // TODO(https://crbug.com/668758): Consider using BeginFrame update for this.
+  // TODO(https://crbug.com/1123630): Consider moving the timer into the
+  // compositor thread.
   HeapTaskRunnerTimer<FrameCaret> caret_blink_timer_;
+  // Controls visibility of caret with opacity when the caret is blinking.
+  scoped_refptr<EffectPaintPropertyNode> effect_;
   bool is_caret_enabled_ = false;
   bool should_show_caret_ = false;
   bool is_caret_blinking_suspended_ = false;
diff --git a/third_party/blink/renderer/core/editing/frame_caret_test.cc b/third_party/blink/renderer/core/editing/frame_caret_test.cc
index 91cae51..ca02f820 100644
--- a/third_party/blink/renderer/core/editing/frame_caret_test.cc
+++ b/third_party/blink/renderer/core/editing/frame_caret_test.cc
@@ -25,6 +25,10 @@
     return caret.ShouldShowCaret();
   }
 
+  static bool IsVisibleIfActive(const FrameCaret& caret) {
+    return caret.IsVisibleIfActive();
+  }
+
  private:
   // The caret blink timer doesn't work if IsRunningWebTest() because
   // LayoutTheme::CaretBlinkInterval() returns 0.
@@ -53,26 +57,25 @@
   UpdateAllLifecyclePhasesForTest();
 
   EXPECT_TRUE(caret.IsActive());
-  EXPECT_TRUE(caret.IsVisibleIfActiveForTesting())
+  EXPECT_TRUE(IsVisibleIfActive(caret))
       << "Initially a caret should be in visible cycle.";
 
   task_runner->AdvanceTimeAndRun(kInterval);
-  EXPECT_FALSE(caret.IsVisibleIfActiveForTesting())
-      << "The caret blinks normally.";
+  EXPECT_FALSE(IsVisibleIfActive(caret)) << "The caret blinks normally.";
 
   TypingCommand::InsertLineBreak(GetDocument());
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(caret.IsVisibleIfActiveForTesting())
+  EXPECT_TRUE(IsVisibleIfActive(caret))
       << "The caret should be in visible cycle just after a typing command.";
 
   task_runner->AdvanceTimeAndRun(kInterval - 1);
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_TRUE(caret.IsVisibleIfActiveForTesting())
+  EXPECT_TRUE(IsVisibleIfActive(caret))
       << "The typing command reset the timer. The caret is still visible.";
 
   task_runner->AdvanceTimeAndRun(1);
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_FALSE(caret.IsVisibleIfActiveForTesting())
+  EXPECT_FALSE(IsVisibleIfActive(caret))
       << "The caret should blink after the typing command.";
 }
 
diff --git a/third_party/blink/renderer/core/editing/frame_selection.cc b/third_party/blink/renderer/core/editing/frame_selection.cc
index 130401c..a1dc1570 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection.cc
@@ -112,9 +112,8 @@
 
 FrameSelection::~FrameSelection() = default;
 
-const CaretDisplayItemClient& FrameSelection::CaretDisplayItemClientForTesting()
-    const {
-  return frame_caret_->CaretDisplayItemClientForTesting();
+const EffectPaintPropertyNode& FrameSelection::CaretEffectNode() const {
+  return frame_caret_->CaretEffectNode();
 }
 
 bool FrameSelection::IsAvailable() const {
diff --git a/third_party/blink/renderer/core/editing/frame_selection.h b/third_party/blink/renderer/core/editing/frame_selection.h
index 785d5f07..d97ed563 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.h
+++ b/third_party/blink/renderer/core/editing/frame_selection.h
@@ -41,7 +41,7 @@
 
 namespace blink {
 
-class CaretDisplayItemClient;
+class EffectPaintPropertyNode;
 class Element;
 class InlineTextBox;
 class LayoutBlock;
@@ -291,6 +291,8 @@
   // |VisibleSelection| and selection bounds.
   void MarkCacheDirty();
 
+  const EffectPaintPropertyNode& CaretEffectNode() const;
+
   FrameCaret& FrameCaretForTesting() const { return *frame_caret_; }
 
   LayoutTextSelectionStatus ComputeLayoutSelectionStatus(
@@ -310,8 +312,6 @@
   friend class PaintControllerPaintTestBase;
   friend class SelectionControllerTest;
 
-  const CaretDisplayItemClient& CaretDisplayItemClientForTesting() const;
-
   void NotifyAccessibilityForSelectionChange();
   void NotifyCompositorForSelectionChange();
   void NotifyEventHandlerForSelectionChange();
diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc
index e910a7b..cb31c12f 100644
--- a/third_party/blink/renderer/core/frame/web_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_test.cc
@@ -6424,7 +6424,7 @@
     ASSERT_EQ(selection.end, cc::LayerSelectionBound());
   }
 
-  void RunTest(const char* test_file) {
+  void RunTest(const char* test_file, bool selection_is_caret = false) {
     RegisterMockedHttpURLLoad(test_file);
     web_view_helper_.GetWebView()->MainFrameWidget()->SetFocus(true);
     frame_test_helpers::LoadFrame(
@@ -6446,39 +6446,39 @@
     v8::Local<v8::Context> context =
         v8::Isolate::GetCurrent()->GetCurrentContext();
 
-    const int start_edge_start_in_layer_x = expected_result.Get(context, 1)
-                                                .ToLocalChecked()
-                                                .As<v8::Int32>()
-                                                ->Value();
-    const int start_edge_start_in_layer_y = expected_result.Get(context, 2)
-                                                .ToLocalChecked()
-                                                .As<v8::Int32>()
-                                                ->Value();
-    const int start_edge_end_in_layer_x = expected_result.Get(context, 3)
-                                              .ToLocalChecked()
-                                              .As<v8::Int32>()
-                                              ->Value();
-    const int start_edge_end_in_layer_y = expected_result.Get(context, 4)
-                                              .ToLocalChecked()
-                                              .As<v8::Int32>()
-                                              ->Value();
+    int start_edge_start_in_layer_x = expected_result.Get(context, 1)
+                                          .ToLocalChecked()
+                                          .As<v8::Int32>()
+                                          ->Value();
+    int start_edge_start_in_layer_y = expected_result.Get(context, 2)
+                                          .ToLocalChecked()
+                                          .As<v8::Int32>()
+                                          ->Value();
+    int start_edge_end_in_layer_x = expected_result.Get(context, 3)
+                                        .ToLocalChecked()
+                                        .As<v8::Int32>()
+                                        ->Value();
+    int start_edge_end_in_layer_y = expected_result.Get(context, 4)
+                                        .ToLocalChecked()
+                                        .As<v8::Int32>()
+                                        ->Value();
 
-    const int end_edge_start_in_layer_x = expected_result.Get(context, 6)
-                                              .ToLocalChecked()
-                                              .As<v8::Int32>()
-                                              ->Value();
-    const int end_edge_start_in_layer_y = expected_result.Get(context, 7)
-                                              .ToLocalChecked()
-                                              .As<v8::Int32>()
-                                              ->Value();
-    const int end_edge_end_in_layer_x = expected_result.Get(context, 8)
-                                            .ToLocalChecked()
-                                            .As<v8::Int32>()
-                                            ->Value();
-    const int end_edge_end_in_layer_y = expected_result.Get(context, 9)
-                                            .ToLocalChecked()
-                                            .As<v8::Int32>()
-                                            ->Value();
+    int end_edge_start_in_layer_x = expected_result.Get(context, 6)
+                                        .ToLocalChecked()
+                                        .As<v8::Int32>()
+                                        ->Value();
+    int end_edge_start_in_layer_y = expected_result.Get(context, 7)
+                                        .ToLocalChecked()
+                                        .As<v8::Int32>()
+                                        ->Value();
+    int end_edge_end_in_layer_x = expected_result.Get(context, 8)
+                                      .ToLocalChecked()
+                                      .As<v8::Int32>()
+                                      ->Value();
+    int end_edge_end_in_layer_y = expected_result.Get(context, 9)
+                                      .ToLocalChecked()
+                                      .As<v8::Int32>()
+                                      ->Value();
 
     gfx::PointF hit_point;
 
@@ -6529,9 +6529,21 @@
         v8::Isolate::GetCurrent(),
         expected_result.Get(context, 0).ToLocalChecked());
     ASSERT_TRUE(layer_owner_node_for_start);
-    int id = LayerIdFromNode(layer_tree_host->root_layer(),
-                             layer_owner_node_for_start);
-    EXPECT_EQ(selection.start.layer_id, id);
+    int start_layer_id = LayerIdFromNode(layer_tree_host->root_layer(),
+                                         layer_owner_node_for_start);
+    if (selection_is_caret) {
+      // The selection data are recorded on the caret layer which is the next
+      // layer for the current test cases.
+      start_layer_id++;
+      EXPECT_EQ("Caret",
+                layer_tree_host->LayerById(start_layer_id)->DebugName());
+      // The locations are relative to the caret layer.
+      start_edge_end_in_layer_x -= start_edge_start_in_layer_x;
+      start_edge_end_in_layer_y -= start_edge_start_in_layer_y;
+      start_edge_start_in_layer_x = 0;
+      start_edge_start_in_layer_y = 0;
+    }
+    EXPECT_EQ(start_layer_id, selection.start.layer_id);
 
     EXPECT_NEAR(start_edge_start_in_layer_x, selection.start.edge_start.x(), 1);
     EXPECT_NEAR(start_edge_start_in_layer_y, selection.start.edge_start.y(), 1);
@@ -6541,9 +6553,21 @@
         v8::Isolate::GetCurrent(),
         expected_result.Get(context, 5).ToLocalChecked());
     ASSERT_TRUE(layer_owner_node_for_end);
-    id = LayerIdFromNode(layer_tree_host->root_layer(),
-                         layer_owner_node_for_end);
-    EXPECT_EQ(selection.end.layer_id, id);
+    int end_layer_id = LayerIdFromNode(layer_tree_host->root_layer(),
+                                       layer_owner_node_for_end);
+    if (selection_is_caret) {
+      // The selection data are recorded on the caret layer which is the next
+      // layer for the current test cases.
+      end_layer_id++;
+      EXPECT_EQ(start_layer_id, end_layer_id);
+      // The locations are relative to the caret layer.
+      end_edge_end_in_layer_x -= end_edge_start_in_layer_x;
+      end_edge_end_in_layer_y -= end_edge_start_in_layer_y;
+      end_edge_start_in_layer_x = 0;
+      end_edge_start_in_layer_y = 0;
+    }
+    EXPECT_EQ(end_layer_id, selection.end.layer_id);
+
     EXPECT_NEAR(end_edge_start_in_layer_x, selection.end.edge_start.x(), 1);
     EXPECT_NEAR(end_edge_start_in_layer_y, selection.end.edge_start.y(), 1);
     EXPECT_NEAR(end_edge_end_in_layer_x, selection.end.edge_end.x(), 1);
@@ -6591,6 +6615,10 @@
     RunTest(test_file);
   }
 
+  void RunTestWithCaret(const char* test_file) {
+    RunTest(test_file, /*selection_is_caret*/ true);
+  }
+
   static int LayerIdFromNode(const cc::Layer* root_layer, blink::Node* node) {
     Vector<const cc::Layer*> layers;
     if (node->IsDocumentNode()) {
@@ -6650,10 +6678,10 @@
 }
 TEST_F(CompositedSelectionBoundsTest, Editable) {
   web_view_helper_.GetWebView()->GetSettings()->SetDefaultFontSize(16);
-  RunTest("composited_selection_bounds_editable.html");
+  RunTestWithCaret("composited_selection_bounds_editable.html");
 }
 TEST_F(CompositedSelectionBoundsTest, EditableDiv) {
-  RunTest("composited_selection_bounds_editable_div.html");
+  RunTestWithCaret("composited_selection_bounds_editable_div.html");
 }
 TEST_F(CompositedSelectionBoundsTest, SVGBasic) {
   RunTest("composited_selection_bounds_svg_basic.html");
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc b/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc
index 4af8f28..641823a 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_printer.cc
@@ -48,6 +48,7 @@
       if (LocalFrameView* child_view = child_local_frame->View())
         CollectNodes(*child_view);
     }
+    Traits::AddOtherProperties(frame_view, *this);
   }
 
   void CollectNodes(const LayoutObject& object) {
@@ -85,6 +86,9 @@
     printer.AddNode(properties.ScrollTranslation());
     printer.AddNode(properties.TransformIsolationNode());
   }
+  static void AddOtherProperties(
+      const FrameView& frame_view,
+      PropertyTreePrinter<TransformPaintPropertyNodeOrAlias>& printer) {}
 };
 
 template <>
@@ -106,6 +110,9 @@
     printer.AddNode(properties.OverflowClip());
     printer.AddNode(properties.ClipIsolationNode());
   }
+  static void AddOtherProperties(
+      const LocalFrameView& frame_view,
+      PropertyTreePrinter<ClipPaintPropertyNodeOrAlias>& printer) {}
 };
 
 template <>
@@ -128,6 +135,12 @@
     printer.AddNode(properties.ClipPathMask());
     printer.AddNode(properties.EffectIsolationNode());
   }
+
+  static void AddOtherProperties(
+      const LocalFrameView& frame_view,
+      PropertyTreePrinter<EffectPaintPropertyNodeOrAlias>& printer) {
+    printer.AddNode(&frame_view.GetFrame().Selection().CaretEffectNode());
+  }
 };
 
 template <>
@@ -144,6 +157,10 @@
       PropertyTreePrinter<ScrollPaintPropertyNode>& printer) {
     printer.AddNode(properties.Scroll());
   }
+
+  static void AddOtherProperties(
+      const LocalFrameView& frame_view,
+      PropertyTreePrinter<ScrollPaintPropertyNode>& printer) {}
 };
 
 template <typename PropertyTreeNode>
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 90ffdff..df970de3 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -1596,3 +1596,8 @@
 crbug.com/1266821 virtual/gpu-rasterization/images/23-55.html [ Failure ]
 crbug.com/1266821 virtual/gpu-rasterization/images/55.html [ Failure ]
 crbug.com/1266821 virtual/gpu-rasterization/images/imagemap-focus-ring-zoom.html [ Failure ]
+
+# Invisible antialiased pixel differences. Can't rebaseline ref tests.
+crbug.com/1123630 editing/caret/in-multicol-child.html [ Failure ]
+crbug.com/1123630 paint/caret/multicol_block_children.html [ Failure ]
+crbug.com/1123630 paint/caret/multicol_inline_children.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 48391352..45536fd3 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -543,9 +543,12 @@
 crbug.com/1266900 [ Mac ] fast/events/touch/gesture/right-click-gestures-set-cursor-at-correct-position.html [ Crash Pass ]
 crbug.com/1267924 [ Mac ] external/wpt/css/css-contain/contain-layout-baseline-005.html [ Failure ]
 crbug.com/1267924 [ Mac ] external/wpt/css/css-contain/contain-layout-suppress-baseline-001.html [ Failure ]
+
 # Needs rebaseline on Fuchsia.
 crbug.com/1267498 [ Fuchsia ] paint/invalidation/svg/animated-svg-as-image-transformed-offscreen.html [ Failure ]
 crbug.com/1267498 [ Fuchsia ] compositing/layer-creation/fixed-position-out-of-view.html [ Failure ]
+crbug.com/1267498 [ Fuchsia ] fast/inline/inline-focus-ring.html [ Failure ]
+crbug.com/1267498 [ Fuchsia ] transforms/transformed-caret.html [ Failure ]
 
 # WebGPU tests are only run on GPU bots, so they are skipped by default and run
 # separately from other Web Tests.
@@ -2858,7 +2861,8 @@
 crbug.com/626703 [ Mac11 ] external/wpt/html/syntax/speculative-parsing/generated/document-write/svg-script-src.tentative.sub.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/selectors/invalidation/has-pseudo-class.html [ Failure Crash ]
+crbug.com/626703 external/wpt/css/css-conditional/at-supports-046.html [ Failure ]
+crbug.com/626703 [ Mac10.15 ] external/wpt/css/selectors/invalidation/has-pseudo-class.html [ Crash Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-025.html [ Failure ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html [ Skip Timeout ]
 crbug.com/626703 external/wpt/css/css-text/hyphens/hyphenate-character-001.html [ Failure ]
diff --git a/third_party/blink/web_tests/editing/pasteboard/emacs-ctrl-k-y-001-expected.png b/third_party/blink/web_tests/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
new file mode 100644
index 0000000..675891eb
--- /dev/null
+++ b/third_party/blink/web_tests/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index b612b82..9d3f8b8f 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -76532,6 +76532,19 @@
        {}
       ]
      ],
+     "border-radius-clipping-with-transform-001.html": [
+      "e7d173b3c4e400803ce1c599f3050660d1813b64",
+      [
+       null,
+       [
+        [
+         "/css/css-backgrounds/border-radius-clipping-with-transform-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "border-radius-dynamic-from-no-radius.html": [
       "335548f33ad4bb9d7c42b4b07323c0a398aa28fc",
       [
@@ -85940,45 +85953,6 @@
        {}
       ]
      ],
-     "at-supports-040.html": [
-      "6c4a58f346baa7b7788dd6be5e5926759c279a82",
-      [
-       null,
-       [
-        [
-         "/css/css-conditional/at-supports-001-ref.html",
-         "=="
-        ]
-       ],
-       {}
-      ]
-     ],
-     "at-supports-041.html": [
-      "804fb3c383538bc3959ce51744a6b76e25cb5771",
-      [
-       null,
-       [
-        [
-         "/css/css-conditional/at-supports-001-ref.html",
-         "=="
-        ]
-       ],
-       {}
-      ]
-     ],
-     "at-supports-042.html": [
-      "47241f37a35792c554545b3f8f6365afc835b07d",
-      [
-       null,
-       [
-        [
-         "/css/css-conditional/at-supports-001-ref.html",
-         "=="
-        ]
-       ],
-       {}
-      ]
-     ],
      "at-supports-043.html": [
       "bf74ca6614b93c01bd93b3a98594c4dc9030b159",
       [
@@ -86018,6 +85992,19 @@
        {}
       ]
      ],
+     "at-supports-046.html": [
+      "a239a117968f4a7d2f70f04c841de2c9ca2ece1c",
+      [
+       null,
+       [
+        [
+         "/css/css-conditional/at-supports-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "at-supports-content-001.html": [
       "19c99f504be7766978dc3816d186e7c2dabe126e",
       [
@@ -86096,6 +86083,58 @@
        {}
       ]
      ],
+     "at-supports-selector-001.html": [
+      "5e2d385ad36db9a339c4e9ca1a2bad6d24886ec4",
+      [
+       null,
+       [
+        [
+         "/css/css-conditional/at-supports-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "at-supports-selector-002.html": [
+      "6c4a58f346baa7b7788dd6be5e5926759c279a82",
+      [
+       null,
+       [
+        [
+         "/css/css-conditional/at-supports-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "at-supports-selector-003.html": [
+      "804fb3c383538bc3959ce51744a6b76e25cb5771",
+      [
+       null,
+       [
+        [
+         "/css/css-conditional/at-supports-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "at-supports-selector-004.html": [
+      "47241f37a35792c554545b3f8f6365afc835b07d",
+      [
+       null,
+       [
+        [
+         "/css/css-conditional/at-supports-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "css-supports-001.xht": [
       "3f8782580168132c64e831d55dd959d5b7a9c65d",
       [
@@ -214753,6 +214792,21 @@
         ]
        ]
       },
+      "the-meter-element": {
+       "meter-min-rendering.html": [
+        "ca83fc9565bc2245f9525ce78d48496022bc51e5",
+        [
+         null,
+         [
+          [
+           "/html/semantics/forms/the-meter-element/meter-min-rendering-ref.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ]
+      },
       "the-option-element": {
        "dynamic-content-change-rendering.html": [
         "1108c45e11b4de2f92e66a5475e4b5e346c5fa44",
@@ -240325,6 +240379,10 @@
       "75f7985aa4a9361078c05429b7975c3bf81c77cf",
       []
      ],
+     "border-radius-clipping-with-transform-001-ref.html": [
+      "db3ae40c5b53b045d11d9d740794005d37209b7d",
+      []
+     ],
      "border-radius-dynamic-from-no-radius-ref.html": [
       "e0ab6ce4414b107b4ca6a3fce8d6f5976d9bd7db",
       []
@@ -244435,7 +244493,7 @@
        []
       ],
       "flex-abspos-staticpos-align-content-002-expected.txt": [
-       "9e6e0c3a899b4403b7378a542adcdac436c92cd3",
+       "03063a3bcf60c04a2b4fa9f692af19accba9c1ca",
        []
       ],
       "flex-abspos-staticpos-align-content-003-expected.txt": [
@@ -244443,7 +244501,7 @@
        []
       ],
       "flex-abspos-staticpos-align-content-004-expected.txt": [
-       "ce0ac474aae469c6f47f477dfccfa58a9793aa7d",
+       "6b4e717bdb008a069c92b0afad9d88f8cc66594e",
        []
       ],
       "flex-abspos-staticpos-align-content-005-expected.txt": [
@@ -244451,7 +244509,7 @@
        []
       ],
       "flex-abspos-staticpos-align-content-006-expected.txt": [
-       "4a15c232d40f4b6e79b495c379e2a5fff4dfa083",
+       "95a738433699c0a7db4f406978d5f70e91c4e501",
        []
       ],
       "flex-abspos-staticpos-align-content-007-expected.txt": [
@@ -244459,7 +244517,7 @@
        []
       ],
       "flex-abspos-staticpos-align-content-008-expected.txt": [
-       "f588fc5834665ac5e224f43a62ed5c36111c91f4",
+       "d340b9ad14f261130a23f4c2f62e31b02c64dc3a",
        []
       ],
       "flex-abspos-staticpos-align-content-rtl-001-expected.txt": [
@@ -244483,7 +244541,7 @@
        []
       ],
       "flex-abspos-staticpos-align-self-002-expected.txt": [
-       "58f45d7f93a159ad514c35dce56113b5e7ae7f0d",
+       "9e23c5bf914cdb52a7b4e5da7a3498b283c6e0c3",
        []
       ],
       "flex-abspos-staticpos-align-self-003-expected.txt": [
@@ -244491,7 +244549,7 @@
        []
       ],
       "flex-abspos-staticpos-align-self-004-expected.txt": [
-       "0b386b9a1dcccfe9299b8e4e5cb45df99c10a6cc",
+       "bfac00f41878eb6815bfb329490d78bcc6ba668a",
        []
       ],
       "flex-abspos-staticpos-align-self-005-expected.txt": [
@@ -244499,7 +244557,7 @@
        []
       ],
       "flex-abspos-staticpos-align-self-006-expected.txt": [
-       "d604d8a44aaf705b3142b279da82d1224ada566a",
+       "0014cbbeda698064be13c06d8511b827f7261aa4",
        []
       ],
       "flex-abspos-staticpos-align-self-007-expected.txt": [
@@ -244507,7 +244565,7 @@
        []
       ],
       "flex-abspos-staticpos-align-self-008-expected.txt": [
-       "41cc6efb0ff3e8f6c570221a3c34ca030bffc310",
+       "bfb1cf9262026085b12160bd2b7628a7c4dab046",
        []
       ],
       "flex-abspos-staticpos-align-self-rtl-001-expected.txt": [
@@ -244554,30 +244612,6 @@
        "0abf592d6d476d0e5a9a01b163e3aebf9fffb74d",
        []
       ],
-      "flex-abspos-staticpos-justify-content-002-expected.txt": [
-       "79846dd95405caaf9125c9466dd324a37b388e15",
-       []
-      ],
-      "flex-abspos-staticpos-justify-content-003-expected.txt": [
-       "0c91b0884dec56c9f35a88c0f13badb980c5a0f0",
-       []
-      ],
-      "flex-abspos-staticpos-justify-content-004-expected.txt": [
-       "80f6b31d90e9bc81334d7a49dd199e88db7bc783",
-       []
-      ],
-      "flex-abspos-staticpos-justify-content-006-expected.txt": [
-       "662f4049e95adabe52d43cc28e2e8eb14de12005",
-       []
-      ],
-      "flex-abspos-staticpos-justify-content-007-expected.txt": [
-       "cbc0012bcccc86726507a9bcd5b069df5d91cd92",
-       []
-      ],
-      "flex-abspos-staticpos-justify-content-008-expected.txt": [
-       "905caa8a7c37b04be42260af53cac2e87f5f50be",
-       []
-      ],
       "flex-abspos-staticpos-justify-self-001-ref.html": [
        "d4f75a036ad049006776fe8f5e44b165f4a2867a",
        []
@@ -276309,6 +276343,36 @@
      []
     ]
    },
+   "direct-sockets": {
+    "DIR_METADATA": [
+     "b9d644b2b52ab0c94b02de0ccb94c2a5708155be",
+     []
+    ],
+    "META.yml": [
+     "85c05e8c834847aa6a1d5aa1f1faa8c60a7ea9ca",
+     []
+    ],
+    "OWNERS": [
+     "73c81fa31d15b0276bb6c8e59002a8ab94031899",
+     []
+    ],
+    "README.md": [
+     "71a19de6904fd6fc6ef706a21a08168d65f87158",
+     []
+    ],
+    "open-consume-activation.https.html.headers": [
+     "63b60e490f47f4db77d33d7a4ca2f5b9a4181de8",
+     []
+    ],
+    "open-without-user-gesture.https.html.headers": [
+     "63b60e490f47f4db77d33d7a4ca2f5b9a4181de8",
+     []
+    ],
+    "remotePort-required.https.html.headers": [
+     "63b60e490f47f4db77d33d7a4ca2f5b9a4181de8",
+     []
+    ]
+   },
    "docs": {
     "Dockerfile": [
      "e8ae834bd4fcab002b75bdd171e85eb9e81b9cfb",
@@ -281675,10 +281739,6 @@
       "a69aab487239021088f944b0a8cee2dad4b3d111",
       []
      ],
-     "non-secure-context.window-expected.txt": [
-      "b973089394588a1e183dc4b8dacbdb763ddd465d",
-      []
-     ],
      "resources": {
       "fetcher.html": [
        "000a5cc25bb72b334d41ff05e7b8f22691f48f30",
@@ -281700,7 +281760,11 @@
        "e9e70fca517855a2ddebc91e0516f3f063627709",
        []
       ]
-     }
+     },
+     "websocket.window-expected.txt": [
+      "11e2dcac903cb8fb7f68563036c4b8a4a9c1a9e7",
+      []
+     ]
     },
     "range": {
      "general.window-expected.txt": [
@@ -292075,16 +292139,8 @@
           "673b2fca324aa863afb474b199ce3882ef2dd513",
           []
          ],
-         "manifest.py": [
-          "92947d8d8314ab84cffc6e8b135667ad585c4388",
-          []
-         ],
-         "page-using-manifest.py": [
-          "9e6836c183e8730ce44d2a8ad37a57292e2927ea",
-          []
-         ],
          "resolve-url.js": [
-          "0b8493da6cbcf3455300ad6a5d969e25a9224a33",
+          "f654a894b9d4df94caf46852ac4dc33d11b3d5dd",
           []
          ],
          "resource.py": [
@@ -295110,6 +295166,12 @@
         []
        ]
       },
+      "the-meter-element": {
+       "meter-min-rendering-ref.html": [
+        "f253945968e49902d1f2c2d60d9d8cd230166c6b",
+        []
+       ]
+      },
       "the-option-element": {
        "dynamic-content-change-rendering-ref.html": [
         "453bb70822f2d107705c999a8ae9282e4352bd68",
@@ -297561,7 +297623,7 @@
          []
         ],
         "link-rel-stylesheet-disabled.tentative.sub-expected.txt": [
-         "968eb6788e3f3c7bf15c52c75a9246af9a097a4c",
+         "e36c830e8b933eafde301f2b410c936e3461bb85",
          []
         ],
         "link-rel-stylesheet-nomatch-media.tentative.sub-expected.txt": [
@@ -297569,11 +297631,11 @@
          []
         ],
         "math-font-script-src.tentative.sub-expected.txt": [
-         "7aa48ff91f98581fdb5f46b5e6ecae2788cdee5c",
+         "9ee506ea41db3fd8f9dd4e0a3f78bbdd46c1ccfa",
          []
         ],
         "math-script-src.tentative.sub-expected.txt": [
-         "ec629926535450571f5412cdfe99067b73d4fe0e",
+         "50dd2b882abc55d710816c9cc0221015d4a3536c",
          []
         ],
         "meta-csp-img-src-asterisk.tentative.sub-expected.txt": [
@@ -297581,7 +297643,7 @@
          []
         ],
         "meta-viewport-link-stylesheet-media.tentative.sub-expected.txt": [
-         "576fb8b54260580b3634d8167d21a3c84187d2b0",
+         "bb5a15742ad51cb2984d1021d4aa065e32c3d9cb",
          []
         ],
         "picture-source-br-img.tentative.sub-expected.txt": [
@@ -297601,7 +297663,7 @@
          []
         ],
         "svg-script-src.tentative.sub-expected.txt": [
-         "723d0f0207a7c8439bf58dc0a781d715490397e5",
+         "01ce8430ec750a1032006a5d6bb0991489c268a4",
          []
         ],
         "svg-script-xlinkhref.tentative.sub-expected.txt": [
@@ -298102,7 +298164,7 @@
         []
        ],
        "stash.py": [
-        "91e3e480d254c561b6b307ab486a4522f875bab5",
+        "5ad04e155818a01ba4ad7bf6e9cd728deecef85e",
         []
        ]
       },
@@ -306135,36 +306197,6 @@
      }
     }
    },
-   "raw-sockets": {
-    "DIR_METADATA": [
-     "b9d644b2b52ab0c94b02de0ccb94c2a5708155be",
-     []
-    ],
-    "META.yml": [
-     "d424a0af6ecc77577524a4db9baf706ae4dc943c",
-     []
-    ],
-    "OWNERS": [
-     "73c81fa31d15b0276bb6c8e59002a8ab94031899",
-     []
-    ],
-    "README.md": [
-     "bcebe03c8f8ed9f758dca37debb3dc43a0057679",
-     []
-    ],
-    "open-consume-activation.https.html.headers": [
-     "63b60e490f47f4db77d33d7a4ca2f5b9a4181de8",
-     []
-    ],
-    "open-without-user-gesture.https.html.headers": [
-     "63b60e490f47f4db77d33d7a4ca2f5b9a4181de8",
-     []
-    ],
-    "remotePort-required.https.html.headers": [
-     "63b60e490f47f4db77d33d7a4ca2f5b9a4181de8",
-     []
-    ]
-   },
    "referrer-policy": {
     "4K": {
      "gen": {
@@ -314702,7 +314734,7 @@
       []
      ],
      "urltestdata.json": [
-      "e5e5e76336f678763b375873074d88c8967e25ba",
+      "b39c6e7578c3cdb9a4753fd3f939cb3a91bebbc9",
       []
      ]
     },
@@ -359280,7 +359312,7 @@
        ]
       ],
       "flex-abspos-staticpos-align-content-002.html": [
-       "409f7923f3f3bcb3ea0ad71dcd27520a42bff0d4",
+       "cc94b9eb5e12db90fdb1fca7b96e41fec4fbc034",
        [
         null,
         {}
@@ -359294,7 +359326,7 @@
        ]
       ],
       "flex-abspos-staticpos-align-content-004.html": [
-       "fe07b8d0f53ac75cecb2632c97605f715e0e7bcd",
+       "74ccc0c527a7bc24f480a05000e4e659c497d7fa",
        [
         null,
         {}
@@ -359308,7 +359340,7 @@
        ]
       ],
       "flex-abspos-staticpos-align-content-006.html": [
-       "aacfe85660c3311a0d5fdb307902c26b01bc9185",
+       "e9be819b490a7374ada758a2723930a0fe142a0f",
        [
         null,
         {}
@@ -359322,7 +359354,7 @@
        ]
       ],
       "flex-abspos-staticpos-align-content-008.html": [
-       "66bc6be0880b348250998ffeef1ec8fc4fcc5f3f",
+       "37812f59dffb93c24e4b3b603243dc598c63aee9",
        [
         null,
         {}
@@ -359364,7 +359396,7 @@
        ]
       ],
       "flex-abspos-staticpos-align-self-002.html": [
-       "3c8896bc2778670894cf6e5da690e883ae3b3b6c",
+       "c570a9f79de2bb7466fa0430fb5500bf2c95a4b5",
        [
         null,
         {}
@@ -359378,7 +359410,7 @@
        ]
       ],
       "flex-abspos-staticpos-align-self-004.html": [
-       "b1bb61394912d96d37bfbd7085d481c8cafd0682",
+       "3cf1318d70aa80a2ab6ee214f62464b18dfb44d5",
        [
         null,
         {}
@@ -359392,7 +359424,7 @@
        ]
       ],
       "flex-abspos-staticpos-align-self-006.html": [
-       "60e4c4d1d6c9c854aa0dcb594c58dde2b07f23d6",
+       "3c03636b35ea5e3bcec9a3fa0075205647e6bb91",
        [
         null,
         {}
@@ -359406,7 +359438,7 @@
        ]
       ],
       "flex-abspos-staticpos-align-self-008.html": [
-       "1e692a87611c466a0205feba6ebda8e0db052c5f",
+       "8ea47aaddae4fce18184d7d862ecbbea2446883e",
        [
         null,
         {}
@@ -359476,21 +359508,21 @@
        ]
       ],
       "flex-abspos-staticpos-justify-content-002.html": [
-       "e56c8c7fc5f9fd0e777bd0dd7e967a207154759c",
+       "db6f116fea4d12df3eb248d85e55197c1e5b3520",
        [
         null,
         {}
        ]
       ],
       "flex-abspos-staticpos-justify-content-003.html": [
-       "660dcc247b0f6247405cdd59e7511c65ca6082b3",
+       "fc5a2cdab0fcf8ef88fd672c372667099f7cb3f8",
        [
         null,
         {}
        ]
       ],
       "flex-abspos-staticpos-justify-content-004.html": [
-       "b84cb99be1f8578b098803c0f93b44ef725d2fca",
+       "38066fa32bdea6b4b23e8e263b59408c8775226a",
        [
         null,
         {}
@@ -359504,21 +359536,21 @@
        ]
       ],
       "flex-abspos-staticpos-justify-content-006.html": [
-       "bc07dac374af4685235fad5fc43c93f872f6adfe",
+       "9b22ddd636e2037166f5d339ea8af90aad1a6780",
        [
         null,
         {}
        ]
       ],
       "flex-abspos-staticpos-justify-content-007.html": [
-       "153381b2418809f71e0dab71152d8351abb0823c",
+       "6c51255b8a8903c8638b4e20cc0bac611f3439a7",
        [
         null,
         {}
        ]
       ],
       "flex-abspos-staticpos-justify-content-008.html": [
-       "0ff5aa829acaa915fe2de97f7dddcd2b2b3ed8b7",
+       "b7ec27301f29f17494a34a137776b3e80de85088",
        [
         null,
         {}
@@ -385404,6 +385436,40 @@
      ]
     ]
    },
+   "direct-sockets": {
+    "open-consume-activation.https.html": [
+     "6cbf017712b197886fb2d8d620927ee03506f6d4",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ],
+    "open-securecontext.http.html": [
+     "ae403b6b5849eed23431a834ae7e7d0c609676f9",
+     [
+      null,
+      {}
+     ]
+    ],
+    "open-without-user-gesture.https.html": [
+     "ed9a9e0d2c63428d39d7c2ebbde8fd1d58d9b02b",
+     [
+      null,
+      {}
+     ]
+    ],
+    "remotePort-required.https.html": [
+     "6d738da845ae09b265d1ce4ad93d3ec6528b46b3",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ]
+   },
    "document-policy": {
     "experimental-features": {
      "document-domain": {
@@ -401303,6 +401369,13 @@
       {}
      ]
     ],
+    "en-copyright-windows-1252.tentative.html": [
+     "a519de0e9eb46154124e3d0186800162723756a4",
+     [
+      null,
+      {}
+     ]
+    ],
     "en-euro-windows-1252.tentative.html": [
      "c2835e7d98a5c00d530b0b70c9de94d4f153c4ca",
      [
@@ -414578,10 +414651,228 @@
      ]
     },
     "private-network-access": {
-     "non-secure-context.window.js": [
-      "e81dfb616bb2a05ab66352d4e08987c6f1f275c1",
+     "fetch.https.window.js": [
+      "e87a6396b0b99699d8ca79dee00914a01421ec78",
       [
-       "fetch/private-network-access/non-secure-context.window.html",
+       "fetch/private-network-access/fetch.https.window.html?include=baseline",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/subset-tests-by-key.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/support.js"
+         ],
+         [
+          "script",
+          "resources/ports.sub.js"
+         ],
+         [
+          "variant",
+          "?include=baseline"
+         ],
+         [
+          "variant",
+          "?include=from-local"
+         ],
+         [
+          "variant",
+          "?include=from-private"
+         ],
+         [
+          "variant",
+          "?include=from-public"
+         ],
+         [
+          "variant",
+          "?include=from-treat-as-public"
+         ]
+        ]
+       }
+      ],
+      [
+       "fetch/private-network-access/fetch.https.window.html?include=from-local",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/subset-tests-by-key.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/support.js"
+         ],
+         [
+          "script",
+          "resources/ports.sub.js"
+         ],
+         [
+          "variant",
+          "?include=baseline"
+         ],
+         [
+          "variant",
+          "?include=from-local"
+         ],
+         [
+          "variant",
+          "?include=from-private"
+         ],
+         [
+          "variant",
+          "?include=from-public"
+         ],
+         [
+          "variant",
+          "?include=from-treat-as-public"
+         ]
+        ]
+       }
+      ],
+      [
+       "fetch/private-network-access/fetch.https.window.html?include=from-private",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/subset-tests-by-key.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/support.js"
+         ],
+         [
+          "script",
+          "resources/ports.sub.js"
+         ],
+         [
+          "variant",
+          "?include=baseline"
+         ],
+         [
+          "variant",
+          "?include=from-local"
+         ],
+         [
+          "variant",
+          "?include=from-private"
+         ],
+         [
+          "variant",
+          "?include=from-public"
+         ],
+         [
+          "variant",
+          "?include=from-treat-as-public"
+         ]
+        ]
+       }
+      ],
+      [
+       "fetch/private-network-access/fetch.https.window.html?include=from-public",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/subset-tests-by-key.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/support.js"
+         ],
+         [
+          "script",
+          "resources/ports.sub.js"
+         ],
+         [
+          "variant",
+          "?include=baseline"
+         ],
+         [
+          "variant",
+          "?include=from-local"
+         ],
+         [
+          "variant",
+          "?include=from-private"
+         ],
+         [
+          "variant",
+          "?include=from-public"
+         ],
+         [
+          "variant",
+          "?include=from-treat-as-public"
+         ]
+        ]
+       }
+      ],
+      [
+       "fetch/private-network-access/fetch.https.window.html?include=from-treat-as-public",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/subset-tests-by-key.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/support.js"
+         ],
+         [
+          "script",
+          "resources/ports.sub.js"
+         ],
+         [
+          "variant",
+          "?include=baseline"
+         ],
+         [
+          "variant",
+          "?include=from-local"
+         ],
+         [
+          "variant",
+          "?include=from-private"
+         ],
+         [
+          "variant",
+          "?include=from-public"
+         ],
+         [
+          "variant",
+          "?include=from-treat-as-public"
+         ]
+        ]
+       }
+      ]
+     ],
+     "fetch.window.js": [
+      "e8f1b323dddb62c6525bc2c79c2936e760e93fe8",
+      [
+       "fetch/private-network-access/fetch.window.html",
        {
         "script_metadata": [
          [
@@ -414600,18 +414891,32 @@
        }
       ]
      ],
-     "secure-context.https.window.js": [
-      "f5714bef8bd086c5abfda0e6d6f6cba2cdc0bbf6",
+     "websocket.https.window.js": [
+      "86eabab0a14ad40e2caed0e6788b5b56b54d00d7",
       [
-       "fetch/private-network-access/secure-context.https.window.html",
+       "fetch/private-network-access/websocket.https.window.html",
        {
         "script_metadata": [
          [
           "script",
-          "/common/utils.js"
+          "resources/support.js"
          ],
          [
           "script",
+          "resources/ports.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "websocket.window.js": [
+      "23407ab5ef8bd5fef1dae15db978d479ed76ab7d",
+      [
+       "fetch/private-network-access/websocket.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
           "resources/support.js"
          ],
          [
@@ -479209,40 +479514,6 @@
      ]
     }
    },
-   "raw-sockets": {
-    "open-consume-activation.https.html": [
-     "6cbf017712b197886fb2d8d620927ee03506f6d4",
-     [
-      null,
-      {
-       "testdriver": true
-      }
-     ]
-    ],
-    "open-securecontext.http.html": [
-     "371f13781a4e2bdea9d9fa2545a13b8e4add9ba8",
-     [
-      null,
-      {}
-     ]
-    ],
-    "open-without-user-gesture.https.html": [
-     "ed9a9e0d2c63428d39d7c2ebbde8fd1d58d9b02b",
-     [
-      null,
-      {}
-     ]
-    ],
-    "remotePort-required.https.html": [
-     "6d738da845ae09b265d1ce4ad93d3ec6528b46b3",
-     [
-      null,
-      {
-       "testdriver": true
-      }
-     ]
-    ]
-   },
    "referrer-policy": {
     "4K": {
      "gen": {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-046.html b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-046.html
new file mode 100644
index 0000000..a239a11
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-046.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="UTF-8">
+
+<title>CSS Conditional Test: Unknown Functional Notation as False in @supports</title>
+<meta name="assert"
+      content="Test passes if unknown but grammatical functional notations are treated as false in @supports.">
+<link rel="help" href="https://www.w3.org/TR/css3-conditional/#at-supports">
+<link rel="author" title="Elika J. Etemad" href="http://fantasai.inkedblade.net/contact">
+<link rel="match" href="at-supports-001-ref.html">
+
+<style>
+  div {
+    background: green;
+    height: 10px;
+    width: 100px;
+  }
+
+  div {
+    background: red;
+  }
+
+  @supports not unknown() {
+    .test1.bare { background: green; }
+  }
+  @supports (not (unknown())) {
+    .test1.wrapped { background: green; }
+  }
+
+  @supports not unknown(with stuff) {
+    .test2.bare { background: green; }
+  }
+  @supports (not (unknown(with stuff))) {
+    .test2.wrapped { background: green; }
+  }
+
+  @supports not unknown(!@#% { ... } more() @stuff [ ]) {
+    .test3.bare { background: green; }
+  }
+  @supports (not (unknown(!@#% { ... } more() @stuff [ ]))) {
+    .test3.wrapped { background: green; }
+  }
+
+  .test4, .test5 { background: green; }
+
+  @supports unknown() {
+    .test4.bare { background: red; }
+  }
+  @supports (unknown()) {
+    .test4.wrapped { background: red; }
+  }
+
+  @supports unknown(with stuff) {
+    .test5.bare { background: red; }
+  }
+  @supports (unknown(with stuff)) {
+    .test5.wrapped { background: red; }
+  }
+</style>
+
+<p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+
+<div class="test1 bare"></div>
+<div class="test2 bare"></div>
+<div class="test3 bare"></div>
+<div class="test4 bare"></div>
+<div class="test5 bare"></div>
+<div class="test1 wrapped"></div>
+<div class="test2 wrapped"></div>
+<div class="test3 wrapped"></div>
+<div class="test4 wrapped"></div>
+<div class="test5 wrapped"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-selector-001.html b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-selector-001.html
new file mode 100644
index 0000000..5e2d385
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-selector-001.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>CSS Conditional Test: @supports selector() with compound selector</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Elika J. Etemad" href="http://fantasai.inkedblade.net/contact">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/css-conditional/#at-supports">
+<link rel="match" href="at-supports-001-ref.html">
+<style>
+  div {
+    background-color:red;
+    height:100px;
+    width:100px;
+  }
+  @supports selector(a:link.class#ident) {
+    div { background: green };
+  }
+</style>
+<p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+<div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-040.html b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-selector-002.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-040.html
rename to third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-selector-002.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-041.html b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-selector-003.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-041.html
rename to third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-selector-003.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-042.html b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-selector-004.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-042.html
rename to third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-selector-004.html
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-002-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-002-expected.txt
index 9e6e0c3..03063a3 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-002-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-002-expected.txt
@@ -7,8 +7,8 @@
 <div class="container" style="align-content: last baseline"><div data-offset-x="2" data-offset-y="5"></div></div>
 offsetTop expected 5 but got 3
 FAIL .container > div 4 assert_equals: 
-<div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 3
+<div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="5"></div></div>
+offsetTop expected 5 but got 3
 PASS .container > div 5
 PASS .container > div 6
 PASS .container > div 7
@@ -33,8 +33,8 @@
 <div class="container" style="align-content: last baseline"><div data-offset-x="2" data-offset-y="-3"></div></div>
 offsetTop expected -3 but got -1
 FAIL .container > div 16 assert_equals: 
-<div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -1
+<div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="-3"></div></div>
+offsetTop expected -3 but got -1
 PASS .container > div 17
 PASS .container > div 18
 PASS .container > div 19
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-002.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-002.html
index 409f792..cc94b9eb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-002.html
@@ -61,7 +61,7 @@
     <div class="container" style="align-content: last baseline"><div data-offset-x="2" data-offset-y="5"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="5"></div></div>
     <div class="container" style="align-content: space-around"><div data-offset-x="2" data-offset-y="3"></div></div>
     <div class="container" style="align-content: space-evenly"><div data-offset-x="2" data-offset-y="3"></div></div>
     <div class="container" style="align-content: stretch"><div data-offset-x="2" data-offset-y="3"></div></div>
@@ -87,7 +87,7 @@
     <div class="container" style="align-content: last baseline"><div data-offset-x="2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="-3"></div></div>
     <div class="container" style="align-content: space-around"><div data-offset-x="2" data-offset-y="-1"></div></div>
     <div class="container" style="align-content: space-evenly"><div data-offset-x="2" data-offset-y="-1"></div></div>
     <div class="container" style="align-content: stretch"><div data-offset-x="2" data-offset-y="-1"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-004-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-004-expected.txt
index ce0ac47..6b4e717b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-004-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-004-expected.txt
@@ -7,8 +7,8 @@
 <div class="container" style="align-content: last baseline"><div data-offset-x="10" data-offset-y="5"></div></div>
 offsetTop expected 5 but got 3
 FAIL .container > div 4 assert_equals: 
-<div class="container" style="align-content: space-between"><div data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 3
+<div class="container" style="align-content: space-between"><div data-offset-x="10" data-offset-y="5"></div></div>
+offsetTop expected 5 but got 3
 PASS .container > div 5
 PASS .container > div 6
 PASS .container > div 7
@@ -33,8 +33,8 @@
 <div class="container" style="align-content: last baseline"><div data-offset-x="-2" data-offset-y="-3"></div></div>
 offsetTop expected -3 but got -1
 FAIL .container > div 16 assert_equals: 
-<div class="container" style="align-content: space-between"><div data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -1
+<div class="container" style="align-content: space-between"><div data-offset-x="-2" data-offset-y="-3"></div></div>
+offsetTop expected -3 but got -1
 PASS .container > div 17
 PASS .container > div 18
 PASS .container > div 19
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-004.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-004.html
index fe07b8d..74ccc0c5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-004.html
@@ -61,7 +61,7 @@
     <div class="container" style="align-content: last baseline"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="align-content: space-between"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="align-content: space-between"><div data-offset-x="10" data-offset-y="5"></div></div>
     <div class="container" style="align-content: space-around"><div data-offset-x="10" data-offset-y="3"></div></div>
     <div class="container" style="align-content: space-evenly"><div data-offset-x="10" data-offset-y="3"></div></div>
     <div class="container" style="align-content: stretch"><div data-offset-x="10" data-offset-y="3"></div></div>
@@ -87,7 +87,7 @@
     <div class="container" style="align-content: last baseline"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="align-content: space-between"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="align-content: space-between"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <div class="container" style="align-content: space-around"><div data-offset-x="-2" data-offset-y="-1"></div></div>
     <div class="container" style="align-content: space-evenly"><div data-offset-x="-2" data-offset-y="-1"></div></div>
     <div class="container" style="align-content: stretch"><div data-offset-x="-2" data-offset-y="-1"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-006-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-006-expected.txt
index 4a15c232..95a7384 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-006-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-006-expected.txt
@@ -7,8 +7,8 @@
 <div class="container" style="align-content: last baseline"><div data-offset-x="10" data-offset-y="1"></div></div>
 offsetLeft expected 10 but got 6
 FAIL .container > div 4 assert_equals: 
-<div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 6
+<div class="container" style="align-content: space-between"><div data-offset-x="10" data-offset-y="1"></div></div>
+offsetLeft expected 10 but got 6
 PASS .container > div 5
 PASS .container > div 6
 PASS .container > div 7
@@ -33,8 +33,8 @@
 <div class="container" style="align-content: last baseline"><div data-offset-x="-2" data-offset-y="1"></div></div>
 offsetLeft expected -2 but got 0
 FAIL .container > div 16 assert_equals: 
-<div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 0
+<div class="container" style="align-content: space-between"><div data-offset-x="-2" data-offset-y="1"></div></div>
+offsetLeft expected -2 but got 0
 PASS .container > div 17
 PASS .container > div 18
 PASS .container > div 19
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-006.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-006.html
index aacfe85..e9be819b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-006.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-006.html
@@ -61,7 +61,7 @@
     <div class="container" style="align-content: last baseline"><div data-offset-x="10" data-offset-y="1"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="align-content: space-between"><div data-offset-x="10" data-offset-y="1"></div></div>
     <div class="container" style="align-content: space-around"><div data-offset-x="6" data-offset-y="1"></div></div>
     <div class="container" style="align-content: space-evenly"><div data-offset-x="6" data-offset-y="1"></div></div>
     <div class="container" style="align-content: stretch"><div data-offset-x="6" data-offset-y="1"></div></div>
@@ -87,7 +87,7 @@
     <div class="container" style="align-content: last baseline"><div data-offset-x="-2" data-offset-y="1"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="align-content: space-between"><div data-offset-x="-2" data-offset-y="1"></div></div>
     <div class="container" style="align-content: space-around"><div data-offset-x="0" data-offset-y="1"></div></div>
     <div class="container" style="align-content: space-evenly"><div data-offset-x="0" data-offset-y="1"></div></div>
     <div class="container" style="align-content: stretch"><div data-offset-x="0" data-offset-y="1"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-008-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-008-expected.txt
index f588fc583..d340b9a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-008-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-008-expected.txt
@@ -7,8 +7,8 @@
 <div class="container" style="align-content: last baseline"><div data-offset-x="10" data-offset-y="5"></div></div>
 offsetLeft expected 10 but got 6
 FAIL .container > div 4 assert_equals: 
-<div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="5"></div></div>
-offsetLeft expected 2 but got 6
+<div class="container" style="align-content: space-between"><div data-offset-x="10" data-offset-y="5"></div></div>
+offsetLeft expected 10 but got 6
 PASS .container > div 5
 PASS .container > div 6
 PASS .container > div 7
@@ -33,8 +33,8 @@
 <div class="container" style="align-content: last baseline"><div data-offset-x="-2" data-offset-y="-3"></div></div>
 offsetLeft expected -2 but got 0
 FAIL .container > div 16 assert_equals: 
-<div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="-3"></div></div>
-offsetLeft expected 2 but got 0
+<div class="container" style="align-content: space-between"><div data-offset-x="-2" data-offset-y="-3"></div></div>
+offsetLeft expected -2 but got 0
 PASS .container > div 17
 PASS .container > div 18
 PASS .container > div 19
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-008.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-008.html
index 66bc6be..37812f5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-008.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-content-008.html
@@ -61,7 +61,7 @@
     <div class="container" style="align-content: last baseline"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="align-content: space-between"><div data-offset-x="10" data-offset-y="5"></div></div>
     <div class="container" style="align-content: space-around"><div data-offset-x="6" data-offset-y="5"></div></div>
     <div class="container" style="align-content: space-evenly"><div data-offset-x="6" data-offset-y="5"></div></div>
     <div class="container" style="align-content: stretch"><div data-offset-x="6" data-offset-y="5"></div></div>
@@ -87,7 +87,7 @@
     <div class="container" style="align-content: last baseline"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="align-content: space-between"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="align-content: space-between"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <div class="container" style="align-content: space-around"><div data-offset-x="0" data-offset-y="-3"></div></div>
     <div class="container" style="align-content: space-evenly"><div data-offset-x="0" data-offset-y="-3"></div></div>
     <div class="container" style="align-content: stretch"><div data-offset-x="0" data-offset-y="-3"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-002-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-002-expected.txt
index 58f45d7..9e23c5bf 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-002-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-002-expected.txt
@@ -1,11 +1,7 @@
 This is a testharness.js-based test.
 PASS .container > div 1
-FAIL .container > div 2 assert_equals: 
-<div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 3 assert_equals: 
-<div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
+PASS .container > div 2
+PASS .container > div 3
 PASS .container > div 4
 FAIL .container > div 5 assert_equals: 
 <div class="container"><div style="align-self: last baseline" data-offset-x="2" data-offset-y="5"></div></div>
@@ -18,12 +14,8 @@
 PASS .container > div 11
 PASS .container > div 12
 PASS .container > div 13
-FAIL .container > div 14 assert_equals: 
-<div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 15 assert_equals: 
-<div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
+PASS .container > div 14
+PASS .container > div 15
 PASS .container > div 16
 FAIL .container > div 17 assert_equals: 
 <div class="container"><div style="align-self: last baseline" data-offset-x="2" data-offset-y="-3"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-002.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-002.html
index 3c8896bc..c570a9f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-002.html
@@ -51,8 +51,8 @@
          https://www.w3.org/TR/css-align-3/#propdef-align-self -->
     <!-- auto | normal | stretch -->
     <div class="container"><div style="align-self: auto" data-offset-x="2" data-offset-y="3"></div></div>
-    <div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="5"></div></div>
     <br>
     <!-- <baseline-position> -->
     <div class="container"><div style="align-self: baseline" data-offset-x="2" data-offset-y="1"></div></div>
@@ -75,8 +75,8 @@
          https://www.w3.org/TR/css-align-3/#propdef-align-self -->
     <!-- auto | normal | stretch -->
     <div class="container"><div style="align-self: auto" data-offset-x="2" data-offset-y="-1"></div></div>
-    <div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="-3"></div></div>
     <br>
     <!-- <baseline-position> -->
     <div class="container"><div style="align-self: baseline" data-offset-x="2" data-offset-y="1"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-004-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-004-expected.txt
index 0b386b9..bfac00f4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-004-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-004-expected.txt
@@ -1,11 +1,7 @@
 This is a testharness.js-based test.
 PASS .container > div 1
-FAIL .container > div 2 assert_equals: 
-<div class="container"><div style="align-self: normal" data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 3 assert_equals: 
-<div class="container"><div style="align-self: stretch" data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
+PASS .container > div 2
+PASS .container > div 3
 PASS .container > div 4
 FAIL .container > div 5 assert_equals: 
 <div class="container"><div style="align-self: last baseline" data-offset-x="10" data-offset-y="5"></div></div>
@@ -18,12 +14,8 @@
 PASS .container > div 11
 PASS .container > div 12
 PASS .container > div 13
-FAIL .container > div 14 assert_equals: 
-<div class="container"><div style="align-self: normal" data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 15 assert_equals: 
-<div class="container"><div style="align-self: stretch" data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
+PASS .container > div 14
+PASS .container > div 15
 PASS .container > div 16
 FAIL .container > div 17 assert_equals: 
 <div class="container"><div style="align-self: last baseline" data-offset-x="-2" data-offset-y="-3"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-004.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-004.html
index b1bb6139..3cf1318d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-004.html
@@ -51,8 +51,8 @@
          https://www.w3.org/TR/css-align-3/#propdef-align-self -->
     <!-- auto | normal | stretch -->
     <div class="container"><div style="align-self: auto" data-offset-x="10" data-offset-y="3"></div></div>
-    <div class="container"><div style="align-self: normal" data-offset-x="10" data-offset-y="1"></div></div>
-    <div class="container"><div style="align-self: stretch" data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container"><div style="align-self: normal" data-offset-x="10" data-offset-y="5"></div></div>
+    <div class="container"><div style="align-self: stretch" data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <baseline-position> -->
     <div class="container"><div style="align-self: baseline" data-offset-x="10" data-offset-y="1"></div></div>
@@ -75,8 +75,8 @@
          https://www.w3.org/TR/css-align-3/#propdef-align-self -->
     <!-- auto | normal | stretch -->
     <div class="container"><div style="align-self: auto" data-offset-x="-2" data-offset-y="-1"></div></div>
-    <div class="container"><div style="align-self: normal" data-offset-x="-2" data-offset-y="1"></div></div>
-    <div class="container"><div style="align-self: stretch" data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container"><div style="align-self: normal" data-offset-x="-2" data-offset-y="-3"></div></div>
+    <div class="container"><div style="align-self: stretch" data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <baseline-position> -->
     <div class="container"><div style="align-self: baseline" data-offset-x="-2" data-offset-y="1"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-006-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-006-expected.txt
index d604d8a..0014cbb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-006-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-006-expected.txt
@@ -1,11 +1,7 @@
 This is a testharness.js-based test.
 PASS .container > div 1
-FAIL .container > div 2 assert_equals: 
-<div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 3 assert_equals: 
-<div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
+PASS .container > div 2
+PASS .container > div 3
 FAIL .container > div 4 assert_equals: 
 <div class="container"><div style="align-self: baseline" data-offset-x="2" data-offset-y="1"></div></div>
 offsetLeft expected 2 but got 10
@@ -20,12 +16,8 @@
 PASS .container > div 11
 PASS .container > div 12
 PASS .container > div 13
-FAIL .container > div 14 assert_equals: 
-<div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 15 assert_equals: 
-<div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
+PASS .container > div 14
+PASS .container > div 15
 FAIL .container > div 16 assert_equals: 
 <div class="container"><div style="align-self: baseline" data-offset-x="2" data-offset-y="1"></div></div>
 offsetLeft expected 2 but got -2
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-006.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-006.html
index 60e4c4d1..3c03636b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-006.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-006.html
@@ -51,8 +51,8 @@
          https://www.w3.org/TR/css-align-3/#propdef-align-self -->
     <!-- auto | normal | stretch -->
     <div class="container"><div style="align-self: auto" data-offset-x="6" data-offset-y="1"></div></div>
-    <div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container"><div style="align-self: normal" data-offset-x="10" data-offset-y=""></div></div>
+    <div class="container"><div style="align-self: stretch" data-offset-x="10" data-offset-y="1"></div></div>
     <br>
     <!-- <baseline-position> -->
     <div class="container"><div style="align-self: baseline" data-offset-x="2" data-offset-y="1"></div></div>
@@ -75,8 +75,8 @@
          https://www.w3.org/TR/css-align-3/#propdef-align-self -->
     <!-- auto | normal | stretch -->
     <div class="container"><div style="align-self: auto" data-offset-x="0" data-offset-y="1"></div></div>
-    <div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container"><div style="align-self: normal" data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container"><div style="align-self: stretch" data-offset-x="-2" data-offset-y="1"></div></div>
     <br>
     <!-- <baseline-position> -->
     <div class="container"><div style="align-self: baseline" data-offset-x="2" data-offset-y="1"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-008-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-008-expected.txt
index 41cc6efb..bfb1cf92 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-008-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-008-expected.txt
@@ -1,11 +1,7 @@
 This is a testharness.js-based test.
 PASS .container > div 1
-FAIL .container > div 2 assert_equals: 
-<div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="5"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 3 assert_equals: 
-<div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="5"></div></div>
-offsetLeft expected 2 but got 10
+PASS .container > div 2
+PASS .container > div 3
 FAIL .container > div 4 assert_equals: 
 <div class="container"><div style="align-self: baseline" data-offset-x="2" data-offset-y="5"></div></div>
 offsetLeft expected 2 but got 10
@@ -20,12 +16,8 @@
 PASS .container > div 11
 PASS .container > div 12
 PASS .container > div 13
-FAIL .container > div 14 assert_equals: 
-<div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="-3"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 15 assert_equals: 
-<div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="-3"></div></div>
-offsetLeft expected 2 but got -2
+PASS .container > div 14
+PASS .container > div 15
 FAIL .container > div 16 assert_equals: 
 <div class="container"><div style="align-self: baseline" data-offset-x="2" data-offset-y="-3"></div></div>
 offsetLeft expected 2 but got -2
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-008.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-008.html
index 1e692a87..8ea47aa 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-008.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-008.html
@@ -51,8 +51,8 @@
          https://www.w3.org/TR/css-align-3/#propdef-align-self -->
     <!-- auto | normal | stretch -->
     <div class="container"><div style="align-self: auto" data-offset-x="6" data-offset-y="5"></div></div>
-    <div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="5"></div></div>
-    <div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container"><div style="align-self: normal" data-offset-x="10" data-offset-y="5"></div></div>
+    <div class="container"><div style="align-self: stretch" data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <baseline-position> -->
     <div class="container"><div style="align-self: baseline" data-offset-x="2" data-offset-y="5"></div></div>
@@ -75,8 +75,8 @@
          https://www.w3.org/TR/css-align-3/#propdef-align-self -->
     <!-- auto | normal | stretch -->
     <div class="container"><div style="align-self: auto" data-offset-x="0" data-offset-y="-3"></div></div>
-    <div class="container"><div style="align-self: normal" data-offset-x="2" data-offset-y="-3"></div></div>
-    <div class="container"><div style="align-self: stretch" data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container"><div style="align-self: normal" data-offset-x="-2" data-offset-y="-3"></div></div>
+    <div class="container"><div style="align-self: stretch" data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <baseline-position> -->
     <div class="container"><div style="align-self: baseline" data-offset-x="2" data-offset-y="-3"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-002-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-002-expected.txt
deleted file mode 100644
index 79846dd..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-002-expected.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-This is a testharness.js-based test.
-FAIL .container > div 1 assert_equals: 
-<div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 2 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 3 assert_equals: 
-<div class="container" style="justify-content: space-around"><div data-offset-x="6" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 4 assert_equals: 
-<div class="container" style="justify-content: space-evenly"><div data-offset-x="6" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 5 assert_equals: 
-<div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 6 assert_equals: 
-<div class="container" style="justify-content: center"><div data-offset-x="6" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 7 assert_equals: 
-<div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 8 assert_equals: 
-<div class="container" style="justify-content: end"><div data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 9 assert_equals: 
-<div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 10 assert_equals: 
-<div class="container" style="justify-content: flex-end"><div data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 11 assert_equals: 
-<div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 12 assert_equals: 
-<div class="container" style="justify-content: right"><div data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 13 assert_equals: 
-<div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 14 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 15 assert_equals: 
-<div class="container" style="justify-content: space-around"><div data-offset-x="0" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 16 assert_equals: 
-<div class="container" style="justify-content: space-evenly"><div data-offset-x="0" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 17 assert_equals: 
-<div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 18 assert_equals: 
-<div class="container" style="justify-content: center"><div data-offset-x="0" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 19 assert_equals: 
-<div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 20 assert_equals: 
-<div class="container" style="justify-content: end"><div data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 21 assert_equals: 
-<div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 22 assert_equals: 
-<div class="container" style="justify-content: flex-end"><div data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 23 assert_equals: 
-<div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 24 assert_equals: 
-<div class="container" style="justify-content: right"><div data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-002.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-002.html
index e56c8c7f..db6f116f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-002.html
@@ -49,48 +49,48 @@
     <!-- The various justify-content values, from
          https://www.w3.org/TR/css-align-3/#propdef-align-content -->
     <!-- normal -->
-    <div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="5"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-around"><div data-offset-x="6" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-evenly"><div data-offset-x="6" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: space-around"><div data-offset-x="6" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: space-evenly"><div data-offset-x="6" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="5"></div></div>
     <br>
     <!-- <content-position>, part 1 -->
-    <div class="container" style="justify-content: center"><div data-offset-x="6" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: end"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: center"><div data-offset-x="6" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: end"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <content-position>, part 2 -->
-    <div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: flex-end"><div data-offset-x="10" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: right"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: flex-end"><div data-offset-x="10" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: right"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
   </div>
   <div class="small">
     <!-- The various justify-content values, from
          https://www.w3.org/TR/css-align-3/#propdef-align-content -->
     <!-- normal -->
-    <div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-around"><div data-offset-x="0" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-evenly"><div data-offset-x="0" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: space-around"><div data-offset-x="0" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: space-evenly"><div data-offset-x="0" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-position>, part 1 -->
-    <div class="container" style="justify-content: center"><div data-offset-x="0" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: end"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: center"><div data-offset-x="0" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: end"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-position>, part 2 -->
-    <div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: flex-end"><div data-offset-x="-2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: right"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: flex-end"><div data-offset-x="-2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: right"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
   </div>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-003-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-003-expected.txt
deleted file mode 100644
index 0c91b088..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-003-expected.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-This is a testharness.js-based test.
-PASS .container > div 1
-FAIL .container > div 2 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-PASS .container > div 3
-PASS .container > div 4
-PASS .container > div 5
-PASS .container > div 6
-PASS .container > div 7
-PASS .container > div 8
-PASS .container > div 9
-PASS .container > div 10
-PASS .container > div 11
-PASS .container > div 12
-PASS .container > div 13
-FAIL .container > div 14 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-PASS .container > div 15
-PASS .container > div 16
-PASS .container > div 17
-PASS .container > div 18
-PASS .container > div 19
-PASS .container > div 20
-PASS .container > div 21
-PASS .container > div 22
-PASS .container > div 23
-PASS .container > div 24
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-003.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-003.html
index 660dcc2..fc5a2cd 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-003.html
@@ -52,7 +52,7 @@
     <div class="container" style="justify-content: normal"><div data-offset-x="10" data-offset-y="1"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="10" data-offset-y="1"></div></div>
     <div class="container" style="justify-content: space-around"><div data-offset-x="6" data-offset-y="1"></div></div>
     <div class="container" style="justify-content: space-evenly"><div data-offset-x="6" data-offset-y="1"></div></div>
     <div class="container" style="justify-content: stretch"><div data-offset-x="10" data-offset-y="1"></div></div>
@@ -76,7 +76,7 @@
     <div class="container" style="justify-content: normal"><div data-offset-x="-2" data-offset-y="1"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="-2" data-offset-y="1"></div></div>
     <div class="container" style="justify-content: space-around"><div data-offset-x="0" data-offset-y="1"></div></div>
     <div class="container" style="justify-content: space-evenly"><div data-offset-x="0" data-offset-y="1"></div></div>
     <div class="container" style="justify-content: stretch"><div data-offset-x="-2" data-offset-y="1"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-004-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-004-expected.txt
deleted file mode 100644
index 80f6b31..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-004-expected.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-This is a testharness.js-based test.
-FAIL .container > div 1 assert_equals: 
-<div class="container" style="justify-content: normal"><div data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 2 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 3 assert_equals: 
-<div class="container" style="justify-content: space-around"><div data-offset-x="6" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 4 assert_equals: 
-<div class="container" style="justify-content: space-evenly"><div data-offset-x="6" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 5 assert_equals: 
-<div class="container" style="justify-content: stretch"><div data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 6 assert_equals: 
-<div class="container" style="justify-content: center"><div data-offset-x="6" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 7 assert_equals: 
-<div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 8 assert_equals: 
-<div class="container" style="justify-content: end"><div data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 9 assert_equals: 
-<div class="container" style="justify-content: flex-start"><div data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 10 assert_equals: 
-<div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 11 assert_equals: 
-<div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 12 assert_equals: 
-<div class="container" style="justify-content: right"><div data-offset-x="10" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-FAIL .container > div 13 assert_equals: 
-<div class="container" style="justify-content: normal"><div data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 14 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 15 assert_equals: 
-<div class="container" style="justify-content: space-around"><div data-offset-x="0" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 16 assert_equals: 
-<div class="container" style="justify-content: space-evenly"><div data-offset-x="0" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 17 assert_equals: 
-<div class="container" style="justify-content: stretch"><div data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 18 assert_equals: 
-<div class="container" style="justify-content: center"><div data-offset-x="0" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 19 assert_equals: 
-<div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 20 assert_equals: 
-<div class="container" style="justify-content: end"><div data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 21 assert_equals: 
-<div class="container" style="justify-content: flex-start"><div data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 22 assert_equals: 
-<div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 23 assert_equals: 
-<div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-FAIL .container > div 24 assert_equals: 
-<div class="container" style="justify-content: right"><div data-offset-x="-2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-004.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-004.html
index b84cb99..38066fa3 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-004.html
@@ -49,48 +49,48 @@
     <!-- The various justify-content values, from
          https://www.w3.org/TR/css-align-3/#propdef-align-content -->
     <!-- normal -->
-    <div class="container" style="justify-content: normal"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: normal"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-around"><div data-offset-x="6" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-evenly"><div data-offset-x="6" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: stretch"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="10" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: space-around"><div data-offset-x="6" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: space-evenly"><div data-offset-x="6" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: stretch"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <content-position>, part 1 -->
-    <div class="container" style="justify-content: center"><div data-offset-x="6" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: end"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: center"><div data-offset-x="6" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: end"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <content-position>, part 2 -->
-    <div class="container" style="justify-content: flex-start"><div data-offset-x="10" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: right"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: flex-start"><div data-offset-x="10" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: right"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
   </div>
   <div class="small">
     <!-- The various justify-content values, from
          https://www.w3.org/TR/css-align-3/#propdef-align-content -->
     <!-- normal -->
-    <div class="container" style="justify-content: normal"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: normal"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-around"><div data-offset-x="0" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-evenly"><div data-offset-x="0" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: stretch"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="-2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: space-around"><div data-offset-x="0" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: space-evenly"><div data-offset-x="0" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: stretch"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-position>, part 1 -->
-    <div class="container" style="justify-content: center"><div data-offset-x="0" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: end"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: center"><div data-offset-x="0" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: end"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-position>, part 2 -->
-    <div class="container" style="justify-content: flex-start"><div data-offset-x="-2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: right"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: flex-start"><div data-offset-x="-2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: right"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
   </div>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-006-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-006-expected.txt
deleted file mode 100644
index 662f404..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-006-expected.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-This is a testharness.js-based test.
-FAIL .container > div 1 assert_equals: 
-<div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 2 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 3 assert_equals: 
-<div class="container" style="justify-content: space-around"><div data-offset-x="2" data-offset-y="3"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 4 assert_equals: 
-<div class="container" style="justify-content: space-evenly"><div data-offset-x="2" data-offset-y="3"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 5 assert_equals: 
-<div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 6 assert_equals: 
-<div class="container" style="justify-content: center"><div data-offset-x="2" data-offset-y="3"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 7 assert_equals: 
-<div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 8 assert_equals: 
-<div class="container" style="justify-content: end"><div data-offset-x="2" data-offset-y="5"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 9 assert_equals: 
-<div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 10 assert_equals: 
-<div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="5"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 11 assert_equals: 
-<div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 12 assert_equals: 
-<div class="container" style="justify-content: right"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 13 assert_equals: 
-<div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 14 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 15 assert_equals: 
-<div class="container" style="justify-content: space-around"><div data-offset-x="2" data-offset-y="-1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 16 assert_equals: 
-<div class="container" style="justify-content: space-evenly"><div data-offset-x="2" data-offset-y="-1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 17 assert_equals: 
-<div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 18 assert_equals: 
-<div class="container" style="justify-content: center"><div data-offset-x="2" data-offset-y="-1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 19 assert_equals: 
-<div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 20 assert_equals: 
-<div class="container" style="justify-content: end"><div data-offset-x="2" data-offset-y="-3"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 21 assert_equals: 
-<div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 22 assert_equals: 
-<div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="-3"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 23 assert_equals: 
-<div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 24 assert_equals: 
-<div class="container" style="justify-content: right"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-006.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-006.html
index bc07dac..9b22ddd 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-006.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-006.html
@@ -49,48 +49,48 @@
     <!-- The various justify-content values, from
          https://www.w3.org/TR/css-align-3/#propdef-align-content -->
     <!-- normal -->
-    <div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: normal"><div data-offset-x="10" data-offset-y="1"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-around"><div data-offset-x="2" data-offset-y="3"></div></div>
-    <div class="container" style="justify-content: space-evenly"><div data-offset-x="2" data-offset-y="3"></div></div>
-    <div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-around"><div data-offset-x="10" data-offset-y="3"></div></div>
+    <div class="container" style="justify-content: space-evenly"><div data-offset-x="10" data-offset-y="3"></div></div>
+    <div class="container" style="justify-content: stretch"><div data-offset-x="10" data-offset-y="1"></div></div>
     <br>
     <!-- <content-position>, part 1 -->
-    <div class="container" style="justify-content: center"><div data-offset-x="2" data-offset-y="3"></div></div>
-    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: end"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: center"><div data-offset-x="10" data-offset-y="3"></div></div>
+    <div class="container" style="justify-content: start"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: end"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <content-position>, part 2 -->
-    <div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="5"></div></div>
-    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: right"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: flex-start"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: flex-end"><div data-offset-x="10" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: left"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: right"><div data-offset-x="10" data-offset-y="1"></div></div>
     <br>
   </div>
   <div class="small">
     <!-- The various justify-content values, from
          https://www.w3.org/TR/css-align-3/#propdef-align-content -->
     <!-- normal -->
-    <div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: normal"><div data-offset-x="-2" data-offset-y="1"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-around"><div data-offset-x="2" data-offset-y="-1"></div></div>
-    <div class="container" style="justify-content: space-evenly"><div data-offset-x="2" data-offset-y="-1"></div></div>
-    <div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-around"><div data-offset-x="-2" data-offset-y="-1"></div></div>
+    <div class="container" style="justify-content: space-evenly"><div data-offset-x="-2" data-offset-y="-1"></div></div>
+    <div class="container" style="justify-content: stretch"><div data-offset-x="-2" data-offset-y="1"></div></div>
     <br>
     <!-- <content-position>, part 1 -->
-    <div class="container" style="justify-content: center"><div data-offset-x="2" data-offset-y="-1"></div></div>
-    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: end"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: center"><div data-offset-x="-2" data-offset-y="-1"></div></div>
+    <div class="container" style="justify-content: start"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: end"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-position>, part 2 -->
-    <div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="-3"></div></div>
-    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: right"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: flex-start"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: flex-end"><div data-offset-x="-2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: left"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: right"><div data-offset-x="-2" data-offset-y="1"></div></div>
     <br>
   </div>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-007-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-007-expected.txt
deleted file mode 100644
index cbc0012..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-007-expected.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-This is a testharness.js-based test.
-PASS .container > div 1
-FAIL .container > div 2 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got 5
-PASS .container > div 3
-PASS .container > div 4
-PASS .container > div 5
-PASS .container > div 6
-PASS .container > div 7
-PASS .container > div 8
-PASS .container > div 9
-PASS .container > div 10
-PASS .container > div 11
-PASS .container > div 12
-PASS .container > div 13
-FAIL .container > div 14 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetTop expected 1 but got -3
-PASS .container > div 15
-PASS .container > div 16
-PASS .container > div 17
-PASS .container > div 18
-PASS .container > div 19
-PASS .container > div 20
-PASS .container > div 21
-PASS .container > div 22
-PASS .container > div 23
-PASS .container > div 24
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-007.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-007.html
index 153381b2..6c51255 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-007.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-007.html
@@ -52,7 +52,7 @@
     <div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="5"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="5"></div></div>
     <div class="container" style="justify-content: space-around"><div data-offset-x="2" data-offset-y="3"></div></div>
     <div class="container" style="justify-content: space-evenly"><div data-offset-x="2" data-offset-y="3"></div></div>
     <div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="5"></div></div>
@@ -76,7 +76,7 @@
     <div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="-3"></div></div>
     <div class="container" style="justify-content: space-around"><div data-offset-x="2" data-offset-y="-1"></div></div>
     <div class="container" style="justify-content: space-evenly"><div data-offset-x="2" data-offset-y="-1"></div></div>
     <div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="-3"></div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-008-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-008-expected.txt
deleted file mode 100644
index 905caa8a..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-008-expected.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-This is a testharness.js-based test.
-FAIL .container > div 1 assert_equals: 
-<div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="5"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 2 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 3 assert_equals: 
-<div class="container" style="justify-content: space-around"><div data-offset-x="2" data-offset-y="3"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 4 assert_equals: 
-<div class="container" style="justify-content: space-evenly"><div data-offset-x="2" data-offset-y="3"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 5 assert_equals: 
-<div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="5"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 6 assert_equals: 
-<div class="container" style="justify-content: center"><div data-offset-x="2" data-offset-y="3"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 7 assert_equals: 
-<div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 8 assert_equals: 
-<div class="container" style="justify-content: end"><div data-offset-x="2" data-offset-y="5"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 9 assert_equals: 
-<div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="5"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 10 assert_equals: 
-<div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 11 assert_equals: 
-<div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 12 assert_equals: 
-<div class="container" style="justify-content: right"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got 10
-FAIL .container > div 13 assert_equals: 
-<div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="-3"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 14 assert_equals: 
-<div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 15 assert_equals: 
-<div class="container" style="justify-content: space-around"><div data-offset-x="2" data-offset-y="-1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 16 assert_equals: 
-<div class="container" style="justify-content: space-evenly"><div data-offset-x="2" data-offset-y="-1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 17 assert_equals: 
-<div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="-3"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 18 assert_equals: 
-<div class="container" style="justify-content: center"><div data-offset-x="2" data-offset-y="-1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 19 assert_equals: 
-<div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 20 assert_equals: 
-<div class="container" style="justify-content: end"><div data-offset-x="2" data-offset-y="-3"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 21 assert_equals: 
-<div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="-3"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 22 assert_equals: 
-<div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 23 assert_equals: 
-<div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-FAIL .container > div 24 assert_equals: 
-<div class="container" style="justify-content: right"><div data-offset-x="2" data-offset-y="1"></div></div>
-offsetLeft expected 2 but got -2
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-008.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-008.html
index 0ff5aa8..b7ec273 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-008.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-008.html
@@ -49,48 +49,48 @@
     <!-- The various justify-content values, from
          https://www.w3.org/TR/css-align-3/#propdef-align-content -->
     <!-- normal -->
-    <div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: normal"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-around"><div data-offset-x="2" data-offset-y="3"></div></div>
-    <div class="container" style="justify-content: space-evenly"><div data-offset-x="2" data-offset-y="3"></div></div>
-    <div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="10" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: space-around"><div data-offset-x="10" data-offset-y="3"></div></div>
+    <div class="container" style="justify-content: space-evenly"><div data-offset-x="10" data-offset-y="3"></div></div>
+    <div class="container" style="justify-content: stretch"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <content-position>, part 1 -->
-    <div class="container" style="justify-content: center"><div data-offset-x="2" data-offset-y="3"></div></div>
-    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: end"><div data-offset-x="2" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: center"><div data-offset-x="10" data-offset-y="3"></div></div>
+    <div class="container" style="justify-content: start"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: end"><div data-offset-x="10" data-offset-y="5"></div></div>
     <br>
     <!-- <content-position>, part 2 -->
-    <div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="5"></div></div>
-    <div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: right"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: flex-start"><div data-offset-x="10" data-offset-y="5"></div></div>
+    <div class="container" style="justify-content: flex-end"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: left"><div data-offset-x="10" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: right"><div data-offset-x="10" data-offset-y="1"></div></div>
     <br>
   </div>
   <div class="small">
     <!-- The various justify-content values, from
          https://www.w3.org/TR/css-align-3/#propdef-align-content -->
     <!-- normal -->
-    <div class="container" style="justify-content: normal"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: normal"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-distribution> -->
-    <div class="container" style="justify-content: space-between"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: space-around"><div data-offset-x="2" data-offset-y="-1"></div></div>
-    <div class="container" style="justify-content: space-evenly"><div data-offset-x="2" data-offset-y="-1"></div></div>
-    <div class="container" style="justify-content: stretch"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: space-between"><div data-offset-x="-2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: space-around"><div data-offset-x="-2" data-offset-y="-1"></div></div>
+    <div class="container" style="justify-content: space-evenly"><div data-offset-x="-2" data-offset-y="-1"></div></div>
+    <div class="container" style="justify-content: stretch"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-position>, part 1 -->
-    <div class="container" style="justify-content: center"><div data-offset-x="2" data-offset-y="-1"></div></div>
-    <div class="container" style="justify-content: start"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: end"><div data-offset-x="2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: center"><div data-offset-x="-2" data-offset-y="-1"></div></div>
+    <div class="container" style="justify-content: start"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: end"><div data-offset-x="-2" data-offset-y="-3"></div></div>
     <br>
     <!-- <content-position>, part 2 -->
-    <div class="container" style="justify-content: flex-start"><div data-offset-x="2" data-offset-y="-3"></div></div>
-    <div class="container" style="justify-content: flex-end"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: left"><div data-offset-x="2" data-offset-y="1"></div></div>
-    <div class="container" style="justify-content: right"><div data-offset-x="2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: flex-start"><div data-offset-x="-2" data-offset-y="-3"></div></div>
+    <div class="container" style="justify-content: flex-end"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: left"><div data-offset-x="-2" data-offset-y="1"></div></div>
+    <div class="container" style="justify-content: right"><div data-offset-x="-2" data-offset-y="1"></div></div>
     <br>
   </div>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/selection-contenteditable-011.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/selection-contenteditable-011.html
index bc01d103..5203edb 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/selection-contenteditable-011.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/selection-contenteditable-011.html
@@ -7,7 +7,8 @@
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
   <link rel="help" href="https://www.w3.org/TR/css-pseudo-4/#highlight-selectors">
   <link rel="match" href="reference/selection-contenteditable-011-ref.html">
-
+  <!-- Allow different antialiased pixels on the focus ring. -->
+  <meta name="fuzzy" content="0-5;0-255">
   <meta content="" name="flags">
 
   <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/selection-input-011.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/selection-input-011.html
index 77f7188c..56ea2a0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/selection-input-011.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/selection-input-011.html
@@ -7,7 +7,8 @@
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
   <link rel="help" href="https://www.w3.org/TR/css-pseudo-4/#highlight-selectors">
   <link rel="match" href="reference/selection-input-011-ref.html">
-
+  <!-- Allow different antialiased pixels on the focus ring. -->
+  <meta name="fuzzy" content="0-5;0-255">
   <meta content="" name="flags">
 
   <style>
diff --git a/third_party/blink/web_tests/external/wpt/encoding-detection/en-copyright-windows-1252.tentative.html b/third_party/blink/web_tests/external/wpt/encoding-detection/en-copyright-windows-1252.tentative.html
new file mode 100644
index 0000000..a519de0e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/encoding-detection/en-copyright-windows-1252.tentative.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>en windows-1252 copyright sign</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<p>Copyright © 2021</p>
+<script>
+setup({explicit_done:true});
+onload = function() {
+    test(function() {
+        assert_equals(document.characterSet, "windows-1252", 'Expected windows-1252');
+    }, "Check detection result");
+      done();
+};
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-meter-element/meter-min-rendering-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-meter-element/meter-min-rendering-ref.html
new file mode 100644
index 0000000..f253945
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-meter-element/meter-min-rendering-ref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>Test Reference</title>
+<meter max="1.0" value="0.5">
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-meter-element/meter-min-rendering.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-meter-element/meter-min-rendering.html
new file mode 100644
index 0000000..ca83fc9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-meter-element/meter-min-rendering.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>min is accounted for when rendering a &lt;meter&gt; element</title>
+<link rel=author href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel=author href="https://mozilla.org" title="Mozilla">
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1746758">
+<link rel=match href="meter-min-rendering-ref.html">
+
+<meter min="1.0" max="2.0" value="1.5">
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-disabled.tentative.sub-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-disabled.tentative.sub-expected.txt
index 968eb67..e36c830 100644
--- a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-disabled.tentative.sub-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-disabled.tentative.sub-expected.txt
@@ -1,4 +1,4 @@
 This is a testharness.js-based test.
-FAIL Speculative parsing, document.write(): link-rel-stylesheet-disabled Unhandled rejection: assert_equals: speculative case incorrectly fetched expected "" but got "param-encodingcheck: %C4%9E\r\n"
+FAIL Speculative parsing, document.write(): link-rel-stylesheet-disabled Unhandled rejection: assert_equals: speculative case incorrectly fetched expected "" but got "param-encodingcheck: %C4%9E\r\nAccept: text/css,*/*;q=0.1\r\nReferer: http://web-platform.test:8001/html/syntax/speculative-parsing/generated/document-write/link-rel-stylesheet-disabled.tentative.sub.html"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/math-font-script-src.tentative.sub-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/math-font-script-src.tentative.sub-expected.txt
index 7aa48ff..9ee506e 100644
--- a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/math-font-script-src.tentative.sub-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/math-font-script-src.tentative.sub-expected.txt
@@ -1,4 +1,4 @@
 This is a testharness.js-based test.
-FAIL Speculative parsing, document.write(): math-font-script-src Unhandled rejection: assert_equals: speculative case incorrectly fetched expected "" but got "param-encodingcheck: %C4%9E\r\n"
+FAIL Speculative parsing, document.write(): math-font-script-src Unhandled rejection: assert_equals: speculative case incorrectly fetched expected "" but got "param-encodingcheck: %C4%9E\r\nAccept: */*\r\nReferer: http://web-platform.test:8001/html/syntax/speculative-parsing/generated/document-write/math-font-script-src.tentative.sub.html"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/math-script-src.tentative.sub-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/math-script-src.tentative.sub-expected.txt
index ec62992..50dd2b8 100644
--- a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/math-script-src.tentative.sub-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/math-script-src.tentative.sub-expected.txt
@@ -1,4 +1,4 @@
 This is a testharness.js-based test.
-FAIL Speculative parsing, document.write(): math-script-src Unhandled rejection: assert_equals: speculative case incorrectly fetched expected "" but got "param-encodingcheck: %C4%9E\r\n"
+FAIL Speculative parsing, document.write(): math-script-src Unhandled rejection: assert_equals: speculative case incorrectly fetched expected "" but got "param-encodingcheck: %C4%9E\r\nAccept: */*\r\nReferer: http://web-platform.test:8001/html/syntax/speculative-parsing/generated/document-write/math-script-src.tentative.sub.html"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/meta-viewport-link-stylesheet-media.tentative.sub-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/meta-viewport-link-stylesheet-media.tentative.sub-expected.txt
index 576fb8b..bb5a157 100644
--- a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/meta-viewport-link-stylesheet-media.tentative.sub-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/meta-viewport-link-stylesheet-media.tentative.sub-expected.txt
@@ -1,4 +1,4 @@
 This is a testharness.js-based test.
-FAIL Speculative parsing, document.write(): meta-viewport-link-stylesheet-media Unhandled rejection: assert_equals: speculative case incorrectly fetched expected "" but got "param-encodingcheck: %C4%9E\r\n"
+FAIL Speculative parsing, document.write(): meta-viewport-link-stylesheet-media Unhandled rejection: assert_equals: speculative case incorrectly fetched expected "" but got "param-encodingcheck: %C4%9E\r\nAccept: text/css,*/*;q=0.1\r\nReferer: http://web-platform.test:8001/html/syntax/speculative-parsing/generated/document-write/meta-viewport-link-stylesheet-media.tentative.sub.html"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/svg-script-src.tentative.sub-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/svg-script-src.tentative.sub-expected.txt
index 723d0f0..01ce843 100644
--- a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/svg-script-src.tentative.sub-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/generated/document-write/svg-script-src.tentative.sub-expected.txt
@@ -1,4 +1,4 @@
 This is a testharness.js-based test.
-FAIL Speculative parsing, document.write(): svg-script-src Unhandled rejection: assert_equals: speculative case incorrectly fetched expected "" but got "param-encodingcheck: %C4%9E\r\n"
+FAIL Speculative parsing, document.write(): svg-script-src Unhandled rejection: assert_equals: speculative case incorrectly fetched expected "" but got "param-encodingcheck: %C4%9E\r\nAccept: */*\r\nReferer: http://web-platform.test:8001/html/syntax/speculative-parsing/generated/document-write/svg-script-src.tentative.sub.html"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/resources/stash.py b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/resources/stash.py
index 91e3e48..5ad04e15 100644
--- a/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/resources/stash.py
+++ b/third_party/blink/web_tests/external/wpt/html/syntax/speculative-parsing/resources/stash.py
@@ -1,6 +1,12 @@
 def main(request, response):
     if request.GET[b"action"] == b"put":
         encodingcheck = u"param-encodingcheck: " + request.url_parts.query.split(u"&encodingcheck=")[1] + u"\r\n"
-        request.server.stash.put(request.GET[b"uuid"], encodingcheck)
+        headers = []
+        for line in str(request.raw_headers).split(u'\n'):
+          header = line.split(':')[0]
+          # TODO(zcorpan): also test Cookie
+          if header in [u'Origin', u'Accept', u'Referer']:
+            headers.append(line)
+        request.server.stash.put(request.GET[b"uuid"], encodingcheck + u"\r\n".join(headers))
         return u''
     return request.server.stash.take(request.GET[b"uuid"])
diff --git a/third_party/blink/web_tests/external/wpt/url/resources/urltestdata.json b/third_party/blink/web_tests/external/wpt/url/resources/urltestdata.json
index e5e5e76..b39c6e7 100644
--- a/third_party/blink/web_tests/external/wpt/url/resources/urltestdata.json
+++ b/third_party/blink/web_tests/external/wpt/url/resources/urltestdata.json
@@ -3695,6 +3695,27 @@
     "base": "about:blank",
     "failure": true
   },
+  "IDNA labels should be matched case-insensitively",
+  {
+    "input": "http://a.b.c.XN--pokxncvks",
+    "base": "about:blank",
+    "failure": true
+  },
+  {
+    "input": "http://a.b.c.Xn--pokxncvks",
+    "base": "about:blank",
+    "failure": true
+  },
+  {
+    "input": "http://10.0.0.XN--pokxncvks",
+    "base": "about:blank",
+    "failure": true
+  },
+  {
+    "input": "http://10.0.0.xN--pokxncvks",
+    "base": "about:blank",
+    "failure": true
+  },
   "Test name prepping, fullwidth input should be converted to ASCII and NOT IDN-ized. This is 'Go' in fullwidth UTF-8/UTF-16.",
   {
     "input": "http://Go.com",
diff --git a/third_party/blink/web_tests/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png b/third_party/blink/web_tests/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png
deleted file mode 100644
index 8d8a66e..0000000
--- a/third_party/blink/web_tests/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png b/third_party/blink/web_tests/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png
deleted file mode 100644
index 83f4872..0000000
--- a/third_party/blink/web_tests/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png b/third_party/blink/web_tests/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png
deleted file mode 100644
index ed760f9..0000000
--- a/third_party/blink/web_tests/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/focus-rect/textfield-focus-ring-expected.png b/third_party/blink/web_tests/fast/forms/focus-rect/textfield-focus-ring-expected.png
deleted file mode 100644
index 1104839..0000000
--- a/third_party/blink/web_tests/fast/forms/focus-rect/textfield-focus-ring-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/text/input-appearance-autocomplete-very-long-value-expected.html b/third_party/blink/web_tests/fast/forms/text/input-appearance-autocomplete-very-long-value-expected.html
index 9d52433..7bf2fefd 100644
--- a/third_party/blink/web_tests/fast/forms/text/input-appearance-autocomplete-very-long-value-expected.html
+++ b/third_party/blink/web_tests/fast/forms/text/input-appearance-autocomplete-very-long-value-expected.html
@@ -4,5 +4,6 @@
 <script>
 input.focus();
 input.setSelectionRange(0,0);
+input.style.outline = 'none';
 internals.setAutofilled(input, true);
 </script>
diff --git a/third_party/blink/web_tests/fast/forms/text/input-appearance-autocomplete-very-long-value.html b/third_party/blink/web_tests/fast/forms/text/input-appearance-autocomplete-very-long-value.html
index f0eff95..d1905fe 100644
--- a/third_party/blink/web_tests/fast/forms/text/input-appearance-autocomplete-very-long-value.html
+++ b/third_party/blink/web_tests/fast/forms/text/input-appearance-autocomplete-very-long-value.html
@@ -6,4 +6,6 @@
 internals.setSuggestedValue(input, 'Guy WithAVeryLongLastNameThatCouldWrap');
 internals.setAutofilled(input, true);
 input.style.width = '99px';
+// To avoid invisible focus ring pixel differences.
+input.style.outline = 'none';
 </script>
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/editing/caret/caret-color-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/editing/caret/caret-color-expected.png
deleted file mode 100644
index cb4e7e5..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/editing/caret/caret-color-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/editing/input/caret-read-only-after-editable-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/editing/input/caret-read-only-after-editable-expected.png
deleted file mode 100644
index 2e815414..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/editing/input/caret-read-only-after-editable-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/focus-rect/focus-ring-zoom-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/focus-rect/focus-ring-zoom-expected.png
deleted file mode 100644
index 859582e..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/focus-rect/focus-ring-zoom-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/number/number-appearance-datalist-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/number/number-appearance-datalist-expected.png
index d98ca09..9d743070 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/number/number-appearance-datalist-expected.png
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/number/number-appearance-datalist-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/text/input-text-option-delete-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/text/input-text-option-delete-expected.png
deleted file mode 100644
index f5fc8d18..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/text/input-text-option-delete-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/text/text-appearance-datalist-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/text/text-appearance-datalist-expected.png
index 232db0c..bbc72eb1 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/text/text-appearance-datalist-expected.png
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/text/text-appearance-datalist-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/validation-bubble-device-emulation-change-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/validation-bubble-device-emulation-change-expected.png
deleted file mode 100644
index 6cde97f5..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/validation-bubble-device-emulation-change-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/validation-bubble-device-emulation-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/validation-bubble-device-emulation-expected.png
deleted file mode 100644
index 2dd87fd..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/forms/validation-bubble-device-emulation-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/inline/25277-2-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/inline/25277-2-expected.png
deleted file mode 100644
index 9029bf0..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/inline/25277-2-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/inline/25277-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/inline/25277-expected.png
deleted file mode 100644
index 9029bf0..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/fast/inline/25277-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/delete-into-nested-block-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/delete-into-nested-block-expected.txt
index 16931c67..0a6904ad 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/delete-into-nested-block-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/delete-into-nested-block-expected.txt
@@ -6,9 +6,25 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [8, 167, 31, 20],
-        [8, 127, 31, 20],
-        [8, 147, 24, 20]
+        [6, 125, 788, 104]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [8, 127],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutBlockFlow DIV",
+      "position": [6, 125],
+      "bounds": [788, 104],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 788, 104]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/outline/inline-outline-repaint-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/outline/inline-outline-repaint-expected.txt
new file mode 100644
index 0000000..c078e2c8
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/outline/inline-outline-repaint-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [5, 173, 95, 45]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [45, 196],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutBlockFlow HTML",
+      "position": [5, 173],
+      "bounds": [94, 45],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 94, 45]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
index 7d15f580..6d0ec9ac 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
@@ -4,10 +4,27 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [15, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
       "invalidations": [
-        [401, 11, 1, 16],
-        [398, 11, 1, 16]
+        [0, 0, 1, 16]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [383, 11, 0, 1]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
index e7cfd12..7e362ef 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
@@ -27,6 +27,25 @@
         [0, 999, 205, 23]
       ],
       "transform": 2
+    },
+    {
+      "name": "Caret",
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 3
+    },
+    {
+      "name": "LayoutTextControl INPUT id='text'",
+      "position": [0, 999],
+      "bounds": [205, 23],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 205, 23]
+      ],
+      "transform": 2
     }
   ],
   "transforms": [
@@ -48,6 +67,16 @@
         [0, 0, 1, 0],
         [0, -922, 0, 1]
       ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [3, 1003, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/selection/selection-after-delete-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/selection/selection-after-delete-expected.txt
index d859ecf..d451bfdb 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/selection/selection-after-delete-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/selection/selection-after-delete-expected.txt
@@ -12,6 +12,15 @@
         [187, 98, 8, 20],
         [187, 79, 8, 19]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [39, 79],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/transform/caret-with-transformation-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/transform/caret-with-transformation-expected.txt
new file mode 100644
index 0000000..8ceeda4
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/transform/caret-with-transformation-expected.txt
@@ -0,0 +1,50 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [340, 0],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "LayoutBlockFlow DIV",
+      "position": [-2, -2],
+      "bounds": [788, 24],
+      "contentsOpaqueForText": true,
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [0.866025403784439, 0.5, 0, 0],
+        [-0.5, 0.866025403784439, 0, 0],
+        [0, 0, 1, 0],
+        [-13.3974596215561, 223.205080756888, 0, 1]
+      ],
+      "origin": [392, 10]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/text/text-layout-crash-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/text/text-layout-crash-expected.png
index 02add77a..7cd0d9b35 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/text/text-layout-crash-expected.png
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/text/text-layout-crash-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/outline/inline-outline-repaint-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/outline/inline-outline-repaint-expected.txt
new file mode 100644
index 0000000..c078e2c8
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/outline/inline-outline-repaint-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [5, 173, 95, 45]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [45, 196],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutBlockFlow HTML",
+      "position": [5, 173],
+      "bounds": [94, 45],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 94, 45]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
new file mode 100644
index 0000000..6d0ec9ac
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [15, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [383, 11, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
new file mode 100644
index 0000000..7e362ef
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
@@ -0,0 +1,83 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow DIV id='scroller'",
+      "bounds": [100, 100],
+      "contentsOpaqueForText": true,
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow DIV id='scroller'",
+      "bounds": [100, 100],
+      "drawsContent": false,
+      "transform": 1
+    },
+    {
+      "name": "LayoutBlockFlow DIV id='scroller'",
+      "bounds": [205, 1022],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 999, 205, 23]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "Caret",
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 3
+    },
+    {
+      "name": "LayoutTextControl INPUT id='text'",
+      "position": [0, 999],
+      "bounds": [205, 23],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 205, 23]
+      ],
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 48, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -922, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [3, 1003, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/selection/selection-after-delete-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/selection/selection-after-delete-expected.txt
new file mode 100644
index 0000000..d451bfdb
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/selection/selection-after-delete-expected.txt
@@ -0,0 +1,27 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [38, 78, 152, 102],
+        [187, 138, 8, 20],
+        [187, 118, 8, 20],
+        [187, 98, 8, 20],
+        [187, 79, 8, 19]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [39, 79],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/transform/caret-with-transformation-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/transform/caret-with-transformation-expected.txt
new file mode 100644
index 0000000..8ceeda4
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/backface-visibility-interop/paint/invalidation/transform/caret-with-transformation-expected.txt
@@ -0,0 +1,50 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [340, 0],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "LayoutBlockFlow DIV",
+      "position": [-2, -2],
+      "bounds": [788, 24],
+      "contentsOpaqueForText": true,
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [0.866025403784439, 0.5, 0, 0],
+        [-0.5, 0.866025403784439, 0, 0],
+        [0, 0, 1, 0],
+        [-13.3974596215561, 223.205080756888, 0, 1]
+      ],
+      "origin": [392, 10]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/http/tests/xmlhttprequest/cross-origin-unsupported-url-expected.txt b/third_party/blink/web_tests/http/tests/xmlhttprequest/cross-origin-unsupported-url-expected.txt
index a8031b4..688f10d9 100644
--- a/third_party/blink/web_tests/http/tests/xmlhttprequest/cross-origin-unsupported-url-expected.txt
+++ b/third_party/blink/web_tests/http/tests/xmlhttprequest/cross-origin-unsupported-url-expected.txt
@@ -1,9 +1,9 @@
-CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
-CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
-CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
-CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
-CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
-CONSOLE ERROR: line 13: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
+CONSOLE ERROR: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
+CONSOLE ERROR: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
+CONSOLE ERROR: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
+CONSOLE ERROR: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
+CONSOLE ERROR: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
+CONSOLE ERROR: Access to XMLHttpRequest at 'tel:1234' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
 CONSOLE ERROR: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
 CONSOLE ERROR: Access to XMLHttpRequest at 'mailto:foo@bar.com' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
 CONSOLE ERROR: Access to XMLHttpRequest at 'localhost:8080/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-untrusted, https.
diff --git a/third_party/blink/web_tests/paint/invalidation/clip/caret-ancestor-clip-change-expected.txt b/third_party/blink/web_tests/paint/invalidation/clip/caret-ancestor-clip-change-expected.txt
index fd7cfcf2..fec04beb 100644
--- a/third_party/blink/web_tests/paint/invalidation/clip/caret-ancestor-clip-change-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/clip/caret-ancestor-clip-change-expected.txt
@@ -8,6 +8,30 @@
       "invalidations": [
         [8, 58, 109, 62]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [0, 2],
+      "bounds": [1, 112],
+      "contentsOpaque": true,
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGTextControlSingleLine INPUT id='target'",
+      "position": [8, 8],
+      "bounds": [109, 112],
+      "contentsOpaqueForText": true
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [12, 1, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt b/third_party/blink/web_tests/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt
new file mode 100644
index 0000000..55a1dc5
--- /dev/null
+++ b/third_party/blink/web_tests/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [8, 8, 10, 18]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [8, 8],
+      "bounds": [1, 18],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 18]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [6, 6],
+      "bounds": [788, 22],
+      "contentsOpaqueForText": true
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/paint/invalidation/paint-caret-in-div-with-negative-indent-expected.txt b/third_party/blink/web_tests/paint/invalidation/paint-caret-in-div-with-negative-indent-expected.txt
index 8b0dd1d..0ab9739 100644
--- a/third_party/blink/web_tests/paint/invalidation/paint-caret-in-div-with-negative-indent-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/paint-caret-in-div-with-negative-indent-expected.txt
@@ -4,9 +4,15 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [358, 200],
+      "bounds": [1, 20],
+      "contentsOpaque": true,
       "invalidations": [
-        [358, 200, 1, 20]
+        [0, 0, 1, 20]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/fuchsia/fast/inline/inline-focus-ring-expected.png b/third_party/blink/web_tests/platform/fuchsia/fast/inline/inline-focus-ring-expected.png
index ec92183d..8815cc08 100644
--- a/third_party/blink/web_tests/platform/fuchsia/fast/inline/inline-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/fuchsia/fast/inline/inline-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-001-expected.png b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-001-expected.png
index 7c5a5c6..4e4c1b4 100644
--- a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-001-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-002-expected.png b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-002-expected.png
index 7c5a5c6..4e4c1b4 100644
--- a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-002-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-003-expected.png b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-003-expected.png
index 14313b8..5e4b359 100644
--- a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-003-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-003-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-004-expected.png b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-004-expected.png
index 7a24c72..c0a2751 100644
--- a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-004-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-004-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-005-expected.png b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-005-expected.png
index ceeab4b..edc5c7b 100644
--- a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-005-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-005-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-007-expected.png b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-007-expected.png
index 547a7938..8ecd1b079 100644
--- a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-007-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-007-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-010-expected.png b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-010-expected.png
index cbef3f4..657d9ba 100644
--- a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-010-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-010-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-011-expected.png b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-011-expected.png
index edb2289..6b07194 100644
--- a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-011-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-011-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-012-expected.png b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-012-expected.png
index 7c5a5c6..4e4c1b4 100644
--- a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-012-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-012-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-expected.png b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-expected.png
index cb4e7e5..cab992f 100644
--- a/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/caret/caret-color-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/input/caret-read-only-after-editable-expected.png b/third_party/blink/web_tests/platform/linux/editing/input/caret-read-only-after-editable-expected.png
index 2e815414..4028cef3 100644
--- a/third_party/blink/web_tests/platform/linux/editing/input/caret-read-only-after-editable-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/input/caret-read-only-after-editable-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/input/reveal-caret-of-multiline-contenteditable-expected.png b/third_party/blink/web_tests/platform/linux/editing/input/reveal-caret-of-multiline-contenteditable-expected.png
index 079b87a..411fff9 100644
--- a/third_party/blink/web_tests/platform/linux/editing/input/reveal-caret-of-multiline-contenteditable-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/input/reveal-caret-of-multiline-contenteditable-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/input/reveal-caret-of-multiline-input-expected.png b/third_party/blink/web_tests/platform/linux/editing/input/reveal-caret-of-multiline-input-expected.png
index 98971d2..11b0afa6 100644
--- a/third_party/blink/web_tests/platform/linux/editing/input/reveal-caret-of-multiline-input-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/input/reveal-caret-of-multiline-input-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/inserting/4278698-expected.png b/third_party/blink/web_tests/platform/linux/editing/inserting/4278698-expected.png
index b7e0dee..206152a3 100644
--- a/third_party/blink/web_tests/platform/linux/editing/inserting/4278698-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/inserting/4278698-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/inserting/4840662-expected.png b/third_party/blink/web_tests/platform/linux/editing/inserting/4840662-expected.png
index a254abb9..ee7d3ce 100644
--- a/third_party/blink/web_tests/platform/linux/editing/inserting/4840662-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/inserting/4840662-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/inserting/5002441-expected.png b/third_party/blink/web_tests/platform/linux/editing/inserting/5002441-expected.png
index 399c98d..8d456be 100644
--- a/third_party/blink/web_tests/platform/linux/editing/inserting/5002441-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/inserting/5002441-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/inserting/5058163-1-expected.png b/third_party/blink/web_tests/platform/linux/editing/inserting/5058163-1-expected.png
index afd6350b..5776d1f5 100644
--- a/third_party/blink/web_tests/platform/linux/editing/inserting/5058163-1-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/inserting/5058163-1-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/inserting/5058163-2-expected.png b/third_party/blink/web_tests/platform/linux/editing/inserting/5058163-2-expected.png
index 6ca324d..77dc13db 100644
--- a/third_party/blink/web_tests/platform/linux/editing/inserting/5058163-2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/inserting/5058163-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/inserting/5549929-3-expected.png b/third_party/blink/web_tests/platform/linux/editing/inserting/5549929-3-expected.png
index 1a0fda1..df22f3c 100644
--- a/third_party/blink/web_tests/platform/linux/editing/inserting/5549929-3-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/inserting/5549929-3-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/inserting/insert-space-in-empty-doc-expected.png b/third_party/blink/web_tests/platform/linux/editing/inserting/insert-space-in-empty-doc-expected.png
index d1a114b..81be328f 100644
--- a/third_party/blink/web_tests/platform/linux/editing/inserting/insert-space-in-empty-doc-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/inserting/insert-space-in-empty-doc-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/pasteboard/5071074-2-expected.png b/third_party/blink/web_tests/platform/linux/editing/pasteboard/5071074-2-expected.png
index b9c0e3b..0aad4d6 100644
--- a/third_party/blink/web_tests/platform/linux/editing/pasteboard/5071074-2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/pasteboard/5071074-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/pasteboard/copy-standalone-image-expected.png b/third_party/blink/web_tests/platform/linux/editing/pasteboard/copy-standalone-image-expected.png
index 30b711d..7c06b87d 100644
--- a/third_party/blink/web_tests/platform/linux/editing/pasteboard/copy-standalone-image-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/pasteboard/copy-standalone-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/pasteboard/emacs-cntl-y-001-expected.png b/third_party/blink/web_tests/platform/linux/editing/pasteboard/emacs-cntl-y-001-expected.png
deleted file mode 100644
index b2e2cae..0000000
--- a/third_party/blink/web_tests/platform/linux/editing/pasteboard/emacs-cntl-y-001-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/pasteboard/emacs-ctrl-k-y-001-expected.png b/third_party/blink/web_tests/platform/linux/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
deleted file mode 100644
index 73f9f59c1..0000000
--- a/third_party/blink/web_tests/platform/linux/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/pasteboard/paste-blockquote-after-blockquote-expected.png b/third_party/blink/web_tests/platform/linux/editing/pasteboard/paste-blockquote-after-blockquote-expected.png
index 0af3046a..3354937 100644
--- a/third_party/blink/web_tests/platform/linux/editing/pasteboard/paste-blockquote-after-blockquote-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/pasteboard/paste-blockquote-after-blockquote-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png b/third_party/blink/web_tests/platform/linux/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png
index 65145cdb..84dcdcf 100644
--- a/third_party/blink/web_tests/platform/linux/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/contenteditable-click-inside-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/contenteditable-click-inside-expected.png
index 7208dc5c..33f24b4 100644
--- a/third_party/blink/web_tests/platform/linux/editing/selection/contenteditable-click-inside-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/selection/contenteditable-click-inside-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/move-by-sentence-001-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/move-by-sentence-001-expected.png
index 81e48e4..63b7b3be 100644
--- a/third_party/blink/web_tests/platform/linux/editing/selection/move-by-sentence-001-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/selection/move-by-sentence-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/wrapped-line-caret-1-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/wrapped-line-caret-1-expected.png
index e833f153..01d9acf8 100644
--- a/third_party/blink/web_tests/platform/linux/editing/selection/wrapped-line-caret-1-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/selection/wrapped-line-caret-1-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/editing/selection/wrapped-line-caret-2-expected.png b/third_party/blink/web_tests/platform/linux/editing/selection/wrapped-line-caret-2-expected.png
index 92ed0f1..bbcd5ca 100644
--- a/third_party/blink/web_tests/platform/linux/editing/selection/wrapped-line-caret-2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/editing/selection/wrapped-line-caret-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/dom/focus-contenteditable-expected.png b/third_party/blink/web_tests/platform/linux/fast/dom/focus-contenteditable-expected.png
index 2300b2f..7e60871 100644
--- a/third_party/blink/web_tests/platform/linux/fast/dom/focus-contenteditable-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/dom/focus-contenteditable-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/color/color-picker-appearance-imperfect-match-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
index 2011cdb..b2618ba 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/color/color-picker-appearance-manual-color-change-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
index 7007d7f..fa126c41a 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-border-radius-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-border-radius-expected.png
index 89b6b10..aa8e1a5 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-border-radius-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-border-radius-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png
index 92d9249..3ac03b7 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png
index 9a9c04b..b5ae19da 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-zoom-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-zoom-expected.png
index 859582e..3aa083d38 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/focus-ring-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png
index 9ad2e32..c41622c 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/textfield-focus-ring-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/textfield-focus-ring-expected.png
index dbd87a3..f898b892 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/textfield-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/focus-rect/textfield-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/number/number-appearance-datalist-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/number/number-appearance-datalist-expected.png
index 6ffd923..f221a250 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/number/number-appearance-datalist-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/number/number-appearance-datalist-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/plaintext-mode-2-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/plaintext-mode-2-expected.png
index d8eb21ad..c99f366 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/plaintext-mode-2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/plaintext-mode-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/text/input-appearance-focus-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/text/input-appearance-focus-expected.png
index f3b38d9..69f393d 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/text/input-appearance-focus-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/text/input-appearance-focus-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-click-inside-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-click-inside-expected.png
index 4880995..15cbba8f5 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-click-inside-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-click-inside-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-click-outside-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-click-outside-expected.png
index a29664c..810db97 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-click-outside-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-click-outside-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-option-delete-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-option-delete-expected.png
index f5fc8d18..f530c69 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-option-delete-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-option-delete-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-self-emptying-click-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-self-emptying-click-expected.png
index 4d29113..116de94 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-self-emptying-click-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/text/input-text-self-emptying-click-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/text/text-appearance-datalist-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/text/text-appearance-datalist-expected.png
index f9d1125..0f5fe6bf 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/text/text-appearance-datalist-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/text/text-appearance-datalist-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png
index d3576118c..fb857b1 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/textarea/textarea-scrolled-type-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/textarea/textarea-scrolled-type-expected.png
index ac329a2a..d66fd39 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/textarea/textarea-scrolled-type-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/textarea/textarea-scrolled-type-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/validation-bubble-device-emulation-change-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/validation-bubble-device-emulation-change-expected.png
index 6cde97f5..4497de00 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/validation-bubble-device-emulation-change-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/validation-bubble-device-emulation-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/validation-bubble-device-emulation-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/validation-bubble-device-emulation-expected.png
index 2dd87fd..011ce5f8 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/validation-bubble-device-emulation-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/validation-bubble-device-emulation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/inline/25277-2-expected.png b/third_party/blink/web_tests/platform/linux/fast/inline/25277-2-expected.png
index 9029bf0..d1615c4d 100644
--- a/third_party/blink/web_tests/platform/linux/fast/inline/25277-2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/inline/25277-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/inline/25277-expected.png b/third_party/blink/web_tests/platform/linux/fast/inline/25277-expected.png
index 9029bf0..d1615c4d 100644
--- a/third_party/blink/web_tests/platform/linux/fast/inline/25277-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/inline/25277-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/inline/inline-focus-ring-expected.png b/third_party/blink/web_tests/platform/linux/fast/inline/inline-focus-ring-expected.png
index 742fa46..63f07ce9 100644
--- a/third_party/blink/web_tests/platform/linux/fast/inline/inline-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/inline/inline-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/overflow/overflow-focus-ring-expected.png b/third_party/blink/web_tests/platform/linux/fast/overflow/overflow-focus-ring-expected.png
index 732f573..938862e 100644
--- a/third_party/blink/web_tests/platform/linux/fast/overflow/overflow-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/overflow/overflow-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/4776765-expected.png b/third_party/blink/web_tests/platform/linux/paint/invalidation/4776765-expected.png
index 3883762..9e256e32 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/4776765-expected.png
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/4776765-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/delete-into-nested-block-expected.png b/third_party/blink/web_tests/platform/linux/paint/invalidation/delete-into-nested-block-expected.png
index b803737b..4516137 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/delete-into-nested-block-expected.png
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/delete-into-nested-block-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/delete-into-nested-block-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/delete-into-nested-block-expected.txt
deleted file mode 100644
index cb09ef3c..0000000
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/delete-into-nested-block-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "Scrolling background of LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "invalidations": [
-        [8, 167, 31, 19],
-        [8, 127, 31, 19],
-        [8, 127, 27, 20],
-        [8, 147, 24, 20]
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/forms/textarea-caret-expected.png b/third_party/blink/web_tests/platform/linux/paint/invalidation/forms/textarea-caret-expected.png
index c1171acc..5fb59fe 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/forms/textarea-caret-expected.png
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/forms/textarea-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/outline/inline-outline-repaint-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/outline/inline-outline-repaint-expected.txt
index d7009b2..f2a70955 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/outline/inline-outline-repaint-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/outline/inline-outline-repaint-expected.txt
@@ -8,6 +8,24 @@
       "invalidations": [
         [5, 173, 95, 45]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [45, 196],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [5, 173],
+      "bounds": [90, 45],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 90, 45]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
index aaa48d1..bd3500f8 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
@@ -4,10 +4,27 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [16, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
       "invalidations": [
-        [402, 11, 1, 16],
-        [399, 11, 1, 16]
+        [0, 0, 1, 16]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [383, 11, 0, 1]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
index 5dfe8a8..8abac62 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -16,6 +16,16 @@
         [0, 0, 67, 24]
       ],
       "transform": 1
+    },
+    {
+      "name": "Caret",
+      "position": [56, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 2
     }
   ],
   "transforms": [
@@ -27,6 +37,16 @@
         [0, 0, 1, 0],
         [8, 8, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [4, 3, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
index 5dfe8a8..8abac62 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -16,6 +16,16 @@
         [0, 0, 67, 24]
       ],
       "transform": 1
+    },
+    {
+      "name": "Caret",
+      "position": [56, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 2
     }
   ],
   "transforms": [
@@ -27,6 +37,16 @@
         [0, 0, 1, 0],
         [8, 8, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [4, 3, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-after-delete-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-after-delete-expected.txt
index 9d879df..179f6c3 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-after-delete-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-after-delete-expected.txt
@@ -9,6 +9,15 @@
         [38, 78, 152, 102],
         [39, 79, 152, 100]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [39, 79],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/table/caret-contenteditable-content-after-expected.png b/third_party/blink/web_tests/platform/linux/paint/invalidation/table/caret-contenteditable-content-after-expected.png
index 70b65bd..0162c68 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/table/caret-contenteditable-content-after-expected.png
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/table/caret-contenteditable-content-after-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/table/caret-contenteditable-content-after-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/table/caret-contenteditable-content-after-expected.txt
new file mode 100644
index 0000000..f22661b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/table/caret-contenteditable-content-after-expected.txt
@@ -0,0 +1,34 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [8, 48, 22, 19],
+        [30, 48, 8, 19],
+        [23, 48, 8, 19]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [30, 48],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [6, 46],
+      "bounds": [788, 24],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 788, 24]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.png b/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.png
index 9d704ff..0c4d60f77 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.png
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.txt
index 77296d4..2629106 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/transform/caret-with-transformation-expected.txt
@@ -4,11 +4,46 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [340, 0],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
       "invalidations": [
-        [337, 206, 11, 18],
-        [42, 36, 11, 18]
+        [0, 0, 1, 19]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow DIV",
+      "position": [-2, -2],
+      "bounds": [788, 24],
+      "contentsOpaqueForText": true,
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [0.866025403784439, 0.5, 0, 0],
+        [-0.5, 0.866025403784439, 0, 0],
+        [0, 0, 1, 0],
+        [-13.3974596215561, 223.205080756888, 0, 1]
+      ],
+      "origin": [392, 10]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/linux/transforms/transformed-caret-expected.png b/third_party/blink/web_tests/platform/linux/transforms/transformed-caret-expected.png
index 9471b58b..dbdb1b3 100644
--- a/third_party/blink/web_tests/platform/linux/transforms/transformed-caret-expected.png
+++ b/third_party/blink/web_tests/platform/linux/transforms/transformed-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/transforms/transformed-focused-text-input-expected.png b/third_party/blink/web_tests/platform/linux/transforms/transformed-focused-text-input-expected.png
index 2e496b2..f2fa9bba 100644
--- a/third_party/blink/web_tests/platform/linux/transforms/transformed-focused-text-input-expected.png
+++ b/third_party/blink/web_tests/platform/linux/transforms/transformed-focused-text-input-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/4776765-expected.png b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/4776765-expected.png
new file mode 100644
index 0000000..9e256e32
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/4776765-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/delete-into-nested-block-expected.png b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/delete-into-nested-block-expected.png
index b803737b..4516137 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/delete-into-nested-block-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/delete-into-nested-block-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/forms/textarea-caret-expected.png b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/forms/textarea-caret-expected.png
new file mode 100644
index 0000000..5fb59fe
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/forms/textarea-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/outline/inline-outline-repaint-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/outline/inline-outline-repaint-expected.txt
new file mode 100644
index 0000000..f2a70955
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/outline/inline-outline-repaint-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [5, 173, 95, 45]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [45, 196],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [5, 173],
+      "bounds": [90, 45],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 90, 45]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
new file mode 100644
index 0000000..bd3500f8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
@@ -0,0 +1,32 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [16, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [383, 11, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
new file mode 100644
index 0000000..8abac62
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -0,0 +1,53 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGTextControlSingleLine INPUT id='root'",
+      "position": [-1, -1],
+      "bounds": [67, 24],
+      "contentsOpaqueForText": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [0, 0, 67, 24]
+      ],
+      "transform": 1
+    },
+    {
+      "name": "Caret",
+      "position": [56, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [4, 3, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
new file mode 100644
index 0000000..8abac62
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -0,0 +1,53 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutNGTextControlSingleLine INPUT id='root'",
+      "position": [-1, -1],
+      "bounds": [67, 24],
+      "contentsOpaqueForText": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [0, 0, 67, 24]
+      ],
+      "transform": 1
+    },
+    {
+      "name": "Caret",
+      "position": [56, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [4, 3, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/selection/selection-after-delete-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/selection/selection-after-delete-expected.txt
new file mode 100644
index 0000000..179f6c3
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/selection/selection-after-delete-expected.txt
@@ -0,0 +1,24 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [38, 78, 152, 102],
+        [39, 79, 152, 100]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [39, 79],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/table/caret-contenteditable-content-after-expected.png b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/table/caret-contenteditable-content-after-expected.png
new file mode 100644
index 0000000..0162c68
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/table/caret-contenteditable-content-after-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/table/caret-contenteditable-content-after-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/table/caret-contenteditable-content-after-expected.txt
new file mode 100644
index 0000000..f22661b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/table/caret-contenteditable-content-after-expected.txt
@@ -0,0 +1,34 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [8, 48, 22, 19],
+        [30, 48, 8, 19],
+        [23, 48, 8, 19]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [30, 48],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [6, 46],
+      "bounds": [788, 24],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 788, 24]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/transform/caret-with-transformation-expected.png b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/transform/caret-with-transformation-expected.png
new file mode 100644
index 0000000..0c4d60f77
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/transform/caret-with-transformation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/transform/caret-with-transformation-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/transform/caret-with-transformation-expected.txt
new file mode 100644
index 0000000..2629106
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/transform/caret-with-transformation-expected.txt
@@ -0,0 +1,50 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [340, 0],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow DIV",
+      "position": [-2, -2],
+      "bounds": [788, 24],
+      "contentsOpaqueForText": true,
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [0.866025403784439, 0.5, 0, 0],
+        [-0.5, 0.866025403784439, 0, 0],
+        [0, 0, 1, 0],
+        [-13.3974596215561, 223.205080756888, 0, 1]
+      ],
+      "origin": [392, 10]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/transformed-caret-expected.png b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/transformed-caret-expected.png
index 9471b58b..dbdb1b3 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/transformed-caret-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/transformed-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/transformed-focused-text-input-expected.png b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/transformed-focused-text-input-expected.png
new file mode 100644
index 0000000..f2fa9bba
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/transforms/transformed-focused-text-input-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png
index ef1971c..1828b1d 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
index 4618fc2..5ca1dd9 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/shared_array_buffer_on_desktop/fast/dom/focus-contenteditable-expected.png b/third_party/blink/web_tests/platform/linux/virtual/shared_array_buffer_on_desktop/fast/dom/focus-contenteditable-expected.png
new file mode 100644
index 0000000..7e60871
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/shared_array_buffer_on_desktop/fast/dom/focus-contenteditable-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/delete-hard-break-character-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/delete-hard-break-character-expected.png
index 9f3ca21..d099b043 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/delete-hard-break-character-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/delete-hard-break-character-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/rtl-caret-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/rtl-caret-expected.png
index df8d298..fbd9407 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/rtl-caret-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/text-antialias/selection/rtl-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/v8-off-thread-finalization/fast/dom/focus-contenteditable-expected.png b/third_party/blink/web_tests/platform/linux/virtual/v8-off-thread-finalization/fast/dom/focus-contenteditable-expected.png
new file mode 100644
index 0000000..7e60871
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/v8-off-thread-finalization/fast/dom/focus-contenteditable-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/editing/caret/caret-color-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/editing/caret/caret-color-expected.png
index 2e66c99..9f8bce6 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/editing/caret/caret-color-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/editing/caret/caret-color-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/editing/input/emacs-ctrl-o-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/editing/input/emacs-ctrl-o-expected.png
index bd6e49f..a3103e3 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/editing/input/emacs-ctrl-o-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/editing/input/emacs-ctrl-o-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/editing/inserting/insert-space-in-empty-doc-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/editing/inserting/insert-space-in-empty-doc-expected.png
index 260e5ee2..bd67ace 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/editing/inserting/insert-space-in-empty-doc-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/editing/inserting/insert-space-in-empty-doc-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/editing/pasteboard/emacs-cntl-y-001-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/editing/pasteboard/emacs-cntl-y-001-expected.png
index 10f8919..932ff45 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/editing/pasteboard/emacs-cntl-y-001-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/editing/pasteboard/emacs-cntl-y-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/editing/pasteboard/emacs-ctrl-k-y-001-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
index 0519af2..675891eb 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/editing/selection/move-by-sentence-001-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/editing/selection/move-by-sentence-001-expected.png
index 232d4df8..c29a8e57 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/editing/selection/move-by-sentence-001-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/editing/selection/move-by-sentence-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/forms/focus-rect/focus-ring-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/forms/focus-rect/focus-ring-zoom-expected.png
new file mode 100644
index 0000000..1325c31
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/forms/focus-rect/focus-ring-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/delete-hard-break-character-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/delete-hard-break-character-expected.png
index 267e2ef..3839d433 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/delete-hard-break-character-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/delete-hard-break-character-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/rtl-caret-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/rtl-caret-expected.png
index bc50277a..9be398f 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/rtl-caret-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/text-antialias/selection/rtl-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/editing/input/reveal-caret-of-multiline-contenteditable-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/editing/input/reveal-caret-of-multiline-contenteditable-expected.png
deleted file mode 100644
index f9ccb4f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/editing/input/reveal-caret-of-multiline-contenteditable-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/editing/input/reveal-caret-of-multiline-input-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/editing/input/reveal-caret-of-multiline-input-expected.png
deleted file mode 100644
index 5c2f4de7..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/editing/input/reveal-caret-of-multiline-input-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png
deleted file mode 100644
index 680c7d42..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png
deleted file mode 100644
index bd6278c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/textarea/textarea-scrolled-type-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/textarea/textarea-scrolled-type-expected.png
deleted file mode 100644
index 952d468..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/textarea/textarea-scrolled-type-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/paint/invalidation/forms/textarea-caret-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/paint/invalidation/forms/textarea-caret-expected.txt
index 255ed44..2a8905a 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/paint/invalidation/forms/textarea-caret-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/paint/invalidation/forms/textarea-caret-expected.txt
@@ -8,6 +8,47 @@
       "invalidations": [
         [7, 7, 183, 38]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [470, 3],
+      "bounds": [1, 15],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 15]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGTextControlMultiLine TEXTAREA id='editor'",
+      "position": [-1, -1],
+      "bounds": [183, 38],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 183, 38]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-291, 0, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/backface-visibility-interop/paint/invalidation/forms/textarea-caret-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/backface-visibility-interop/paint/invalidation/forms/textarea-caret-expected.txt
index 255ed44..2a8905a 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/backface-visibility-interop/paint/invalidation/forms/textarea-caret-expected.txt
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/backface-visibility-interop/paint/invalidation/forms/textarea-caret-expected.txt
@@ -8,6 +8,47 @@
       "invalidations": [
         [7, 7, 183, 38]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [470, 3],
+      "bounds": [1, 15],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 15]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGTextControlMultiLine TEXTAREA id='editor'",
+      "position": [-1, -1],
+      "bounds": [183, 38],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 183, 38]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-291, 0, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/delete-hard-break-character-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/delete-hard-break-character-expected.png
index 29522cf..2e6ac4e7 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/delete-hard-break-character-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/delete-hard-break-character-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/rtl-caret-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/rtl-caret-expected.png
index ce6a944c..fb45cf3b 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/rtl-caret-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/text-antialias/selection/rtl-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/service-workers/service-worker/resource-timing-fetch-variants.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/service-workers/service-worker/resource-timing-fetch-variants.https-expected.txt
deleted file mode 100644
index ed296ab9..0000000
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/external/wpt/service-workers/service-worker/resource-timing-fetch-variants.https-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-PASS Redirects done from within a service-worker should not be exposed to client ResourceTiming
-PASS Connection info from within a service-worker should not be exposed to client ResourceTiming
-PASS requestStart should never be before fetchStart
-PASS Delay from within service-worker (after internal fetching) should be accessible through `responseStart`
-PASS Delay from within service-worker (before internal fetching) should be measured before responseStart in the client ResourceTiming entry
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/color/color-picker-appearance-imperfect-match-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
index ab9415b..a94d329 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/color/color-picker-appearance-manual-color-change-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
index ff7e67de..f8e8a8a 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
index 1c399f46..aa909a8 100644
--- a/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/caret/caret-color-010-expected.png b/third_party/blink/web_tests/platform/mac/editing/caret/caret-color-010-expected.png
index 5bf9c2b..8146e81 100644
--- a/third_party/blink/web_tests/platform/mac/editing/caret/caret-color-010-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/caret/caret-color-010-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/caret/caret-color-expected.png b/third_party/blink/web_tests/platform/mac/editing/caret/caret-color-expected.png
index 5d4c86de..01d8122 100644
--- a/third_party/blink/web_tests/platform/mac/editing/caret/caret-color-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/caret/caret-color-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/input/caret-read-only-after-editable-expected.png b/third_party/blink/web_tests/platform/mac/editing/input/caret-read-only-after-editable-expected.png
index 9cd992d..e593d91 100644
--- a/third_party/blink/web_tests/platform/mac/editing/input/caret-read-only-after-editable-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/input/caret-read-only-after-editable-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/input/emacs-ctrl-o-expected.png b/third_party/blink/web_tests/platform/mac/editing/input/emacs-ctrl-o-expected.png
index 10ce272..e9cca92 100644
--- a/third_party/blink/web_tests/platform/mac/editing/input/emacs-ctrl-o-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/input/emacs-ctrl-o-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/inserting/4278698-expected.png b/third_party/blink/web_tests/platform/mac/editing/inserting/4278698-expected.png
index 51bfdb5d..74f5df35 100644
--- a/third_party/blink/web_tests/platform/mac/editing/inserting/4278698-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/inserting/4278698-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/inserting/4840662-expected.png b/third_party/blink/web_tests/platform/mac/editing/inserting/4840662-expected.png
index d05ee1a..a24296e 100644
--- a/third_party/blink/web_tests/platform/mac/editing/inserting/4840662-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/inserting/4840662-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/inserting/5002441-expected.png b/third_party/blink/web_tests/platform/mac/editing/inserting/5002441-expected.png
index 1a6fd6b..c3ed237 100644
--- a/third_party/blink/web_tests/platform/mac/editing/inserting/5002441-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/inserting/5002441-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/inserting/5058163-1-expected.png b/third_party/blink/web_tests/platform/mac/editing/inserting/5058163-1-expected.png
index f8ada4d..6f7a68e 100644
--- a/third_party/blink/web_tests/platform/mac/editing/inserting/5058163-1-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/inserting/5058163-1-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/inserting/5058163-2-expected.png b/third_party/blink/web_tests/platform/mac/editing/inserting/5058163-2-expected.png
index ea26bc5..66f3c2b 100644
--- a/third_party/blink/web_tests/platform/mac/editing/inserting/5058163-2-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/inserting/5058163-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/inserting/5549929-3-expected.png b/third_party/blink/web_tests/platform/mac/editing/inserting/5549929-3-expected.png
index 5442621d..e153509a 100644
--- a/third_party/blink/web_tests/platform/mac/editing/inserting/5549929-3-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/inserting/5549929-3-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/inserting/insert-space-in-empty-doc-expected.png b/third_party/blink/web_tests/platform/mac/editing/inserting/insert-space-in-empty-doc-expected.png
index e57878b4..784c6ea 100644
--- a/third_party/blink/web_tests/platform/mac/editing/inserting/insert-space-in-empty-doc-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/inserting/insert-space-in-empty-doc-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/pasteboard/5071074-2-expected.png b/third_party/blink/web_tests/platform/mac/editing/pasteboard/5071074-2-expected.png
index 635d398e..8e90c97 100644
--- a/third_party/blink/web_tests/platform/mac/editing/pasteboard/5071074-2-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/pasteboard/5071074-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/pasteboard/copy-standalone-image-expected.png b/third_party/blink/web_tests/platform/mac/editing/pasteboard/copy-standalone-image-expected.png
index d4c56a98..2c5f3b5 100644
--- a/third_party/blink/web_tests/platform/mac/editing/pasteboard/copy-standalone-image-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/pasteboard/copy-standalone-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/pasteboard/emacs-cntl-y-001-expected.png b/third_party/blink/web_tests/platform/mac/editing/pasteboard/emacs-cntl-y-001-expected.png
index fb8a9491..7683278 100644
--- a/third_party/blink/web_tests/platform/mac/editing/pasteboard/emacs-cntl-y-001-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/pasteboard/emacs-cntl-y-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/pasteboard/emacs-ctrl-k-y-001-expected.png b/third_party/blink/web_tests/platform/mac/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
index 1ee88e8..8ea913e 100644
--- a/third_party/blink/web_tests/platform/mac/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/pasteboard/paste-blockquote-after-blockquote-expected.png b/third_party/blink/web_tests/platform/mac/editing/pasteboard/paste-blockquote-after-blockquote-expected.png
index f8dae20..b124b8ef 100644
--- a/third_party/blink/web_tests/platform/mac/editing/pasteboard/paste-blockquote-after-blockquote-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/pasteboard/paste-blockquote-after-blockquote-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png b/third_party/blink/web_tests/platform/mac/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png
index f4d0c035..2337059 100644
--- a/third_party/blink/web_tests/platform/mac/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/editing/selection/move-by-sentence-001-expected.png b/third_party/blink/web_tests/platform/mac/editing/selection/move-by-sentence-001-expected.png
index 0c8c3c3f..f7d92f2 100644
--- a/third_party/blink/web_tests/platform/mac/editing/selection/move-by-sentence-001-expected.png
+++ b/third_party/blink/web_tests/platform/mac/editing/selection/move-by-sentence-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-expected.txt
index 9af8e6a..f4e57be3 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 665 tests; 383 PASS, 282 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 669 tests; 383 PASS, 286 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -277,6 +277,10 @@
 PASS Parsing: <https://x/�?�#�> against <about:blank>
 FAIL Parsing: <http://a.b.c.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 FAIL Parsing: <http://10.0.0.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://a.b.c.XN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://a.b.c.Xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://10.0.0.XN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://10.0.0.xN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 PASS Parsing: <http://Go.com> against <http://other.com/>
 FAIL Parsing: <http://%41.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
 FAIL Parsing: <http://%ef%bc%85%ef%bc%94%ef%bc%91.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt
index 9af8e6a..f4e57be3 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 665 tests; 383 PASS, 282 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 669 tests; 383 PASS, 286 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -277,6 +277,10 @@
 PASS Parsing: <https://x/�?�#�> against <about:blank>
 FAIL Parsing: <http://a.b.c.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 FAIL Parsing: <http://10.0.0.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://a.b.c.XN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://a.b.c.Xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://10.0.0.XN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://10.0.0.xN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 PASS Parsing: <http://Go.com> against <http://other.com/>
 FAIL Parsing: <http://%41.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
 FAIL Parsing: <http://%ef%bc%85%ef%bc%94%ef%bc%91.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/failure-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/failure-expected.txt
index 03b5393bc..834654f 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/failure-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/failure-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 635 tests; 390 PASS, 245 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 659 tests; 390 PASS, 269 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS URL's constructor's base argument: file://example:1/ should throw
 PASS URL's href: file://example:1/ should throw
@@ -103,6 +103,30 @@
 FAIL sendBeacon(): http://10.0.0.xn--pokxncvks should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
 FAIL Location's href: http://10.0.0.xn--pokxncvks should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
 FAIL window.open(): http://10.0.0.xn--pokxncvks should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://a.b.c.XN--pokxncvks should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://a.b.c.XN--pokxncvks should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://a.b.c.XN--pokxncvks should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://a.b.c.XN--pokxncvks should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://a.b.c.XN--pokxncvks should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://a.b.c.XN--pokxncvks should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://a.b.c.Xn--pokxncvks should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://a.b.c.Xn--pokxncvks should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://a.b.c.Xn--pokxncvks should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://a.b.c.Xn--pokxncvks should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://a.b.c.Xn--pokxncvks should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://a.b.c.Xn--pokxncvks should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://10.0.0.XN--pokxncvks should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://10.0.0.XN--pokxncvks should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://10.0.0.XN--pokxncvks should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://10.0.0.XN--pokxncvks should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://10.0.0.XN--pokxncvks should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://10.0.0.XN--pokxncvks should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://10.0.0.xN--pokxncvks should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://10.0.0.xN--pokxncvks should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://10.0.0.xN--pokxncvks should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://10.0.0.xN--pokxncvks should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://10.0.0.xN--pokxncvks should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://10.0.0.xN--pokxncvks should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
 FAIL URL's constructor's base argument: https://x x:12 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
 FAIL URL's href: https://x x:12 should throw assert_throws_js: function "() => url.href = test.input" did not throw
 FAIL XHR: https://x x:12 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any-expected.txt
index 04e543b1..46a0c816 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 667 tests; 485 PASS, 182 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 671 tests; 485 PASS, 186 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -289,6 +289,18 @@
 FAIL Parsing: <http://10.0.0.xn--pokxncvks> against <about:blank> assert_throws_js: function "function() {
           bURL(expected.input, expected.base)
         }" did not throw
+FAIL Parsing: <http://a.b.c.XN--pokxncvks> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://a.b.c.Xn--pokxncvks> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://10.0.0.XN--pokxncvks> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://10.0.0.xN--pokxncvks> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
 PASS Parsing: <http://Go.com> against <http://other.com/>
 FAIL Parsing: <http://%41.com> against <http://other.com/> assert_throws_js: function "function() {
           bURL(expected.input, expected.base)
diff --git a/third_party/blink/web_tests/platform/mac/fast/dom/focus-contenteditable-expected.png b/third_party/blink/web_tests/platform/mac/fast/dom/focus-contenteditable-expected.png
index 3e25dad..3d33245 100644
--- a/third_party/blink/web_tests/platform/mac/fast/dom/focus-contenteditable-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/dom/focus-contenteditable-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/color/color-picker-appearance-imperfect-match-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
index 030eb4f8..af6db9e6 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/color/color-picker-appearance-manual-color-change-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
index 44205ee0..e830560b 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-border-radius-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-border-radius-expected.png
index 6b72f082..c70f3756f 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-border-radius-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-border-radius-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png
index d54141e9a..c8dea64 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png
index 1c21836..898cb23 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-zoom-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-zoom-expected.png
index 5d98179..a524604 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/focus-ring-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/textfield-focus-ring-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/textfield-focus-ring-expected.png
index 8dd9ff9b..57502d78 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/textfield-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/focus-rect/textfield-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/number/number-appearance-datalist-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/number/number-appearance-datalist-expected.png
index ef8c5815..dcb54d8 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/number/number-appearance-datalist-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/number/number-appearance-datalist-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/text/text-appearance-datalist-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/text/text-appearance-datalist-expected.png
index 9179d0b..77262ac 100644
--- a/third_party/blink/web_tests/platform/mac/fast/forms/text/text-appearance-datalist-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/text/text-appearance-datalist-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/inline/inline-focus-ring-expected.png b/third_party/blink/web_tests/platform/mac/fast/inline/inline-focus-ring-expected.png
index 11ab7d0..2940702 100644
--- a/third_party/blink/web_tests/platform/mac/fast/inline/inline-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/inline/inline-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/4776765-expected.png b/third_party/blink/web_tests/platform/mac/paint/invalidation/4776765-expected.png
index 44a524a..9c954ce 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/4776765-expected.png
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/4776765-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/4776765-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/4776765-expected.txt
index ea1e7dd..bfb4b414 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/4776765-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/4776765-expected.txt
@@ -4,9 +4,24 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [8, 78],
+      "bounds": [1, 18],
+      "contentsOpaque": true,
       "invalidations": [
-        [6, 40, 788, 58]
+        [0, 0, 1, 18]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [6, 40],
+      "bounds": [788, 58],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 788, 58]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/caret-outside-block-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/caret-outside-block-expected.txt
index 3c182b4..0cb3bf98 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/caret-outside-block-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/caret-outside-block-expected.txt
@@ -4,9 +4,15 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [791, 8],
+      "bounds": [1, 18],
+      "contentsOpaque": true,
       "invalidations": [
-        [791, 8, 1, 18]
+        [0, 0, 1, 18]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/caret-subpixel-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/caret-subpixel-expected.txt
index 9afb0b7..a1852e59 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/caret-subpixel-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/caret-subpixel-expected.txt
@@ -8,6 +8,27 @@
       "invalidations": [
         [8, 8, 226, 21]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [200, 0],
+      "bounds": [1, 15],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 15]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [12, 11, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/delete-into-nested-block-expected.png b/third_party/blink/web_tests/platform/mac/paint/invalidation/delete-into-nested-block-expected.png
index ab6fec1..645065a 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/delete-into-nested-block-expected.png
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/delete-into-nested-block-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/delete-into-nested-block-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/delete-into-nested-block-expected.txt
index b39434e2..06d3cad 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/delete-into-nested-block-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/delete-into-nested-block-expected.txt
@@ -6,9 +6,25 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [8, 155, 32, 18],
-        [8, 119, 32, 18],
-        [8, 137, 24, 18]
+        [6, 117, 788, 104]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [8, 119],
+      "bounds": [1, 18],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 18]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow DIV",
+      "position": [6, 117],
+      "bounds": [788, 104],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 788, 104]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/forms/textarea-caret-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/forms/textarea-caret-expected.txt
index e44d1cb..95dd9cfc 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/forms/textarea-caret-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/forms/textarea-caret-expected.txt
@@ -43,6 +43,16 @@
       "transform": 1
     },
     {
+      "name": "Caret",
+      "position": [470, 3],
+      "bounds": [1, 15],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 15]
+      ],
+      "transform": 2
+    },
+    {
       "name": "LayoutNGTextControlMultiLine TEXTAREA id='editor'",
       "position": [-1, -1],
       "bounds": [183, 38],
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/invalidate-caret-before-text-node-update-expected.png b/third_party/blink/web_tests/platform/mac/paint/invalidation/invalidate-caret-before-text-node-update-expected.png
index 22a2208..b160519 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/invalidate-caret-before-text-node-update-expected.png
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/invalidate-caret-before-text-node-update-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt
deleted file mode 100644
index 271e8b5..0000000
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "Scrolling background of LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "invalidations": [
-        [8, 8, 10, 18]
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/outline/inline-outline-repaint-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/outline/inline-outline-repaint-expected.txt
index 5be50cc..760b7fd 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/outline/inline-outline-repaint-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/outline/inline-outline-repaint-expected.txt
@@ -8,6 +8,24 @@
       "invalidations": [
         [5, 163, 98, 42]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [45, 184],
+      "bounds": [1, 18],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 18]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [5, 163],
+      "bounds": [92, 42],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 92, 42]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
index bb926ba..6da51e38 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
@@ -4,10 +4,27 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [16, 0],
+      "bounds": [1, 15],
+      "contentsOpaque": true,
       "invalidations": [
-        [410, 11, 1, 15],
-        [407, 11, 1, 15]
+        [0, 0, 1, 15]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [391, 11, 0, 1]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
index c31a84ee..d18bb84 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
@@ -27,6 +27,25 @@
         [0, 999, 205, 22]
       ],
       "transform": 2
+    },
+    {
+      "name": "Caret",
+      "bounds": [1, 15],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 15]
+      ],
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGTextControlSingleLine INPUT id='text'",
+      "position": [0, 999],
+      "bounds": [205, 22],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 205, 22]
+      ],
+      "transform": 2
     }
   ],
   "transforms": [
@@ -48,6 +67,16 @@
         [0, 0, 1, 0],
         [0, -921, 0, 1]
       ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [3, 1003, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
index 5acde492..b3d0a23 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -16,6 +16,16 @@
         [0, 0, 50, 23]
       ],
       "transform": 1
+    },
+    {
+      "name": "Caret",
+      "position": [38, 0],
+      "bounds": [1, 15],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 15]
+      ],
+      "transform": 2
     }
   ],
   "transforms": [
@@ -27,6 +37,16 @@
         [0, 0, 1, 0],
         [8, 8, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [4, 3, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
index 5acde492..b3d0a23 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -16,6 +16,16 @@
         [0, 0, 50, 23]
       ],
       "transform": 1
+    },
+    {
+      "name": "Caret",
+      "position": [38, 0],
+      "bounds": [1, 15],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 15]
+      ],
+      "transform": 2
     }
   ],
   "transforms": [
@@ -27,6 +37,16 @@
         [0, 0, 1, 0],
         [8, 8, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [4, 3, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-after-delete-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-after-delete-expected.txt
index 1c945cc6..ec22637 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-after-delete-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-after-delete-expected.txt
@@ -8,6 +8,15 @@
       "invalidations": [
         [38, 74, 152, 110]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [39, 75],
+      "bounds": [1, 18],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 18]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/table/caret-contenteditable-content-after-expected.png b/third_party/blink/web_tests/platform/mac/paint/invalidation/table/caret-contenteditable-content-after-expected.png
index 24afe31..d1fb6df 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/table/caret-contenteditable-content-after-expected.png
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/table/caret-contenteditable-content-after-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/table/caret-contenteditable-content-after-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/table/caret-contenteditable-content-after-expected.txt
index 4fc2cf0..512cb12 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/table/caret-contenteditable-content-after-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/table/caret-contenteditable-content-after-expected.txt
@@ -6,7 +6,27 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [6, 42, 788, 22]
+        [8, 44, 23, 18],
+        [30, 44, 9, 18],
+        [23, 44, 9, 18]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [30, 44],
+      "bounds": [1, 18],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 18]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [6, 42],
+      "bounds": [788, 22],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 788, 22]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/transform/caret-with-transformation-expected.png b/third_party/blink/web_tests/platform/mac/paint/invalidation/transform/caret-with-transformation-expected.png
index 978d504..382bdf0 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/transform/caret-with-transformation-expected.png
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/transform/caret-with-transformation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/transform/caret-with-transformation-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/transform/caret-with-transformation-expected.txt
index 8c0ff2c..0f1bbc5 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/transform/caret-with-transformation-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/transform/caret-with-transformation-expected.txt
@@ -4,11 +4,46 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [350, 0],
+      "bounds": [1, 18],
+      "contentsOpaque": true,
       "invalidations": [
-        [345, 211, 11, 17],
-        [42, 36, 11, 17]
+        [0, 0, 1, 18]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow DIV",
+      "position": [-2, -2],
+      "bounds": [788, 22],
+      "contentsOpaqueForText": true,
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [0.866025403784439, 0.5, 0, 0],
+        [-0.5, 0.866025403784439, 0, 0],
+        [0, 0, 1, 0],
+        [-13.3974596215561, 223.205080756888, 0, 1]
+      ],
+      "origin": [392, 9]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/mac/transforms/transformed-caret-expected.png b/third_party/blink/web_tests/platform/mac/transforms/transformed-caret-expected.png
index 6220085..d9469c04 100644
--- a/third_party/blink/web_tests/platform/mac/transforms/transformed-caret-expected.png
+++ b/third_party/blink/web_tests/platform/mac/transforms/transformed-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/transforms/transformed-focused-text-input-expected.png b/third_party/blink/web_tests/platform/mac/transforms/transformed-focused-text-input-expected.png
index 66b7ab2..b5c04d0d 100644
--- a/third_party/blink/web_tests/platform/mac/transforms/transformed-focused-text-input-expected.png
+++ b/third_party/blink/web_tests/platform/mac/transforms/transformed-focused-text-input-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/backface-visibility-interop/transforms/transformed-focused-text-input-expected.png b/third_party/blink/web_tests/platform/mac/virtual/backface-visibility-interop/transforms/transformed-focused-text-input-expected.png
new file mode 100644
index 0000000..b5c04d0d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/backface-visibility-interop/transforms/transformed-focused-text-input-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png
index e7d2df9712..68aa504 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
index f1cef51..39d0f85b 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/delete-hard-break-character-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/delete-hard-break-character-expected.png
index 1ae9548..9e77b62 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/delete-hard-break-character-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/delete-hard-break-character-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/rtl-caret-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/rtl-caret-expected.png
index ce0864f4..fd3eb37 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/rtl-caret-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/selection/rtl-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-001-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-001-expected.png
index 7b90e09..4774705 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-001-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-002-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-002-expected.png
index 7b90e09..4774705 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-002-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-002-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-003-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-003-expected.png
index d2d038f..717500f 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-003-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-003-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-004-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-004-expected.png
index ef7d84c..6881980 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-004-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-004-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-005-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-005-expected.png
index 1e5aaf6..57f6c00f 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-005-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-005-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-007-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-007-expected.png
index fdc22d85..70b300f 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-007-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-007-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-010-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-010-expected.png
index 14fc2532..6017aef 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-010-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-010-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-011-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-011-expected.png
index bf816f6..f418e32 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-011-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-011-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-012-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-012-expected.png
index 7b90e09..4774705 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-012-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-012-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-expected.png
index 19de141..dbf4b730 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-color-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-color-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/caret/caret-position-expected.png b/third_party/blink/web_tests/platform/win/editing/caret/caret-position-expected.png
index e77841c..7f3f7b0 100644
--- a/third_party/blink/web_tests/platform/win/editing/caret/caret-position-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/caret/caret-position-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/input/caret-read-only-after-editable-expected.png b/third_party/blink/web_tests/platform/win/editing/input/caret-read-only-after-editable-expected.png
index 8d78464..c5f8d50b 100644
--- a/third_party/blink/web_tests/platform/win/editing/input/caret-read-only-after-editable-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/input/caret-read-only-after-editable-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/input/reveal-caret-of-multiline-contenteditable-expected.png b/third_party/blink/web_tests/platform/win/editing/input/reveal-caret-of-multiline-contenteditable-expected.png
index d480448..2843232 100644
--- a/third_party/blink/web_tests/platform/win/editing/input/reveal-caret-of-multiline-contenteditable-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/input/reveal-caret-of-multiline-contenteditable-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/input/reveal-caret-of-multiline-input-expected.png b/third_party/blink/web_tests/platform/win/editing/input/reveal-caret-of-multiline-input-expected.png
index f9a69b87..b2eb955 100644
--- a/third_party/blink/web_tests/platform/win/editing/input/reveal-caret-of-multiline-input-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/input/reveal-caret-of-multiline-input-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/inserting/4278698-expected.png b/third_party/blink/web_tests/platform/win/editing/inserting/4278698-expected.png
index 4a654412..a2a985e 100644
--- a/third_party/blink/web_tests/platform/win/editing/inserting/4278698-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/inserting/4278698-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/inserting/4840662-expected.png b/third_party/blink/web_tests/platform/win/editing/inserting/4840662-expected.png
index a4c4e0e..1eefb6c4 100644
--- a/third_party/blink/web_tests/platform/win/editing/inserting/4840662-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/inserting/4840662-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/inserting/5002441-expected.png b/third_party/blink/web_tests/platform/win/editing/inserting/5002441-expected.png
index a126e06..c8d6383 100644
--- a/third_party/blink/web_tests/platform/win/editing/inserting/5002441-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/inserting/5002441-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/inserting/5058163-1-expected.png b/third_party/blink/web_tests/platform/win/editing/inserting/5058163-1-expected.png
index fb031eab..7a94ac21 100644
--- a/third_party/blink/web_tests/platform/win/editing/inserting/5058163-1-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/inserting/5058163-1-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/inserting/5058163-2-expected.png b/third_party/blink/web_tests/platform/win/editing/inserting/5058163-2-expected.png
index 1866747..24fe0c5 100644
--- a/third_party/blink/web_tests/platform/win/editing/inserting/5058163-2-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/inserting/5058163-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/inserting/5549929-3-expected.png b/third_party/blink/web_tests/platform/win/editing/inserting/5549929-3-expected.png
index 0d13102..7742ec4 100644
--- a/third_party/blink/web_tests/platform/win/editing/inserting/5549929-3-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/inserting/5549929-3-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/inserting/insert-space-in-empty-doc-expected.png b/third_party/blink/web_tests/platform/win/editing/inserting/insert-space-in-empty-doc-expected.png
index fafa674e..b456830 100644
--- a/third_party/blink/web_tests/platform/win/editing/inserting/insert-space-in-empty-doc-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/inserting/insert-space-in-empty-doc-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/pasteboard/5071074-2-expected.png b/third_party/blink/web_tests/platform/win/editing/pasteboard/5071074-2-expected.png
index b9df7a0..230cea1 100644
--- a/third_party/blink/web_tests/platform/win/editing/pasteboard/5071074-2-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/pasteboard/5071074-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/pasteboard/copy-standalone-image-expected.png b/third_party/blink/web_tests/platform/win/editing/pasteboard/copy-standalone-image-expected.png
index 34bc9a4..e3746dd3 100644
--- a/third_party/blink/web_tests/platform/win/editing/pasteboard/copy-standalone-image-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/pasteboard/copy-standalone-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/pasteboard/emacs-cntl-y-001-expected.png b/third_party/blink/web_tests/platform/win/editing/pasteboard/emacs-cntl-y-001-expected.png
index cff8af5..932ff45 100644
--- a/third_party/blink/web_tests/platform/win/editing/pasteboard/emacs-cntl-y-001-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/pasteboard/emacs-cntl-y-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/pasteboard/emacs-ctrl-k-y-001-expected.png b/third_party/blink/web_tests/platform/win/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
deleted file mode 100644
index 7dc2ac793..0000000
--- a/third_party/blink/web_tests/platform/win/editing/pasteboard/emacs-ctrl-k-y-001-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/pasteboard/paste-blockquote-after-blockquote-expected.png b/third_party/blink/web_tests/platform/win/editing/pasteboard/paste-blockquote-after-blockquote-expected.png
index 276fca7..4fe53b6 100644
--- a/third_party/blink/web_tests/platform/win/editing/pasteboard/paste-blockquote-after-blockquote-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/pasteboard/paste-blockquote-after-blockquote-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png b/third_party/blink/web_tests/platform/win/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png
index a0049e98..b115e6f0 100644
--- a/third_party/blink/web_tests/platform/win/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/pasteboard/paste-blockquote-into-blockquote-4-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/selection/move-by-sentence-001-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/move-by-sentence-001-expected.png
index ff6a050..efe8340 100644
--- a/third_party/blink/web_tests/platform/win/editing/selection/move-by-sentence-001-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/selection/move-by-sentence-001-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/selection/wrapped-line-caret-1-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/wrapped-line-caret-1-expected.png
index b09c5cc..437d86a 100644
--- a/third_party/blink/web_tests/platform/win/editing/selection/wrapped-line-caret-1-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/selection/wrapped-line-caret-1-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/editing/selection/wrapped-line-caret-2-expected.png b/third_party/blink/web_tests/platform/win/editing/selection/wrapped-line-caret-2-expected.png
index f0b5028e..8e57736 100644
--- a/third_party/blink/web_tests/platform/win/editing/selection/wrapped-line-caret-2-expected.png
+++ b/third_party/blink/web_tests/platform/win/editing/selection/wrapped-line-caret-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt
index 013b99e..f483c8f 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 665 tests; 370 PASS, 295 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 669 tests; 370 PASS, 299 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -277,6 +277,10 @@
 PASS Parsing: <https://x/�?�#�> against <about:blank>
 FAIL Parsing: <http://a.b.c.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 FAIL Parsing: <http://10.0.0.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://a.b.c.XN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://a.b.c.Xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://10.0.0.XN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://10.0.0.xN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 PASS Parsing: <http://Go.com> against <http://other.com/>
 FAIL Parsing: <http://%41.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
 FAIL Parsing: <http://%ef%bc%85%ef%bc%94%ef%bc%91.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
index 013b99e..f483c8f 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 665 tests; 370 PASS, 295 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 669 tests; 370 PASS, 299 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -277,6 +277,10 @@
 PASS Parsing: <https://x/�?�#�> against <about:blank>
 FAIL Parsing: <http://a.b.c.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 FAIL Parsing: <http://10.0.0.xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://a.b.c.XN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://a.b.c.Xn--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://10.0.0.XN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
+FAIL Parsing: <http://10.0.0.xN--pokxncvks> against <about:blank> assert_unreached: Expected URL to fail parsing Reached unreachable code
 PASS Parsing: <http://Go.com> against <http://other.com/>
 FAIL Parsing: <http://%41.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
 FAIL Parsing: <http://%ef%bc%85%ef%bc%94%ef%bc%91.com> against <http://other.com/> assert_unreached: Expected URL to fail parsing Reached unreachable code
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/failure-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/failure-expected.txt
index 8cd3efd..b9949a1 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/failure-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/failure-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 635 tests; 386 PASS, 249 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 659 tests; 386 PASS, 273 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS URL's constructor's base argument: file://example:1/ should throw
 PASS URL's href: file://example:1/ should throw
@@ -103,6 +103,30 @@
 FAIL sendBeacon(): http://10.0.0.xn--pokxncvks should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
 FAIL Location's href: http://10.0.0.xn--pokxncvks should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
 FAIL window.open(): http://10.0.0.xn--pokxncvks should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://a.b.c.XN--pokxncvks should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://a.b.c.XN--pokxncvks should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://a.b.c.XN--pokxncvks should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://a.b.c.XN--pokxncvks should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://a.b.c.XN--pokxncvks should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://a.b.c.XN--pokxncvks should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://a.b.c.Xn--pokxncvks should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://a.b.c.Xn--pokxncvks should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://a.b.c.Xn--pokxncvks should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://a.b.c.Xn--pokxncvks should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://a.b.c.Xn--pokxncvks should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://a.b.c.Xn--pokxncvks should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://10.0.0.XN--pokxncvks should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://10.0.0.XN--pokxncvks should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://10.0.0.XN--pokxncvks should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://10.0.0.XN--pokxncvks should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://10.0.0.XN--pokxncvks should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://10.0.0.XN--pokxncvks should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
+FAIL URL's constructor's base argument: http://10.0.0.xN--pokxncvks should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
+FAIL URL's href: http://10.0.0.xN--pokxncvks should throw assert_throws_js: function "() => url.href = test.input" did not throw
+FAIL XHR: http://10.0.0.xN--pokxncvks should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
+FAIL sendBeacon(): http://10.0.0.xN--pokxncvks should throw assert_throws_js: function "() => self.navigator.sendBeacon(test.input)" did not throw
+FAIL Location's href: http://10.0.0.xN--pokxncvks should throw assert_throws_js: function "() => self[0].location = test.input" did not throw
+FAIL window.open(): http://10.0.0.xN--pokxncvks should throw assert_throws_dom: function "() => self.open(test.input).close()" did not throw
 FAIL URL's constructor's base argument: https://x x:12 should throw assert_throws_js: function "() => new URL("about:blank", test.input)" did not throw
 FAIL URL's href: https://x x:12 should throw assert_throws_js: function "() => url.href = test.input" did not throw
 FAIL XHR: https://x x:12 should throw assert_throws_dom: function "() => client.open("GET", test.input)" did not throw
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any-expected.txt
index 4fd903cd5..a09acb9 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 667 tests; 468 PASS, 199 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 671 tests; 468 PASS, 203 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -289,6 +289,18 @@
 FAIL Parsing: <http://10.0.0.xn--pokxncvks> against <about:blank> assert_throws_js: function "function() {
           bURL(expected.input, expected.base)
         }" did not throw
+FAIL Parsing: <http://a.b.c.XN--pokxncvks> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://a.b.c.Xn--pokxncvks> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://10.0.0.XN--pokxncvks> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
+FAIL Parsing: <http://10.0.0.xN--pokxncvks> against <about:blank> assert_throws_js: function "function() {
+          bURL(expected.input, expected.base)
+        }" did not throw
 PASS Parsing: <http://Go.com> against <http://other.com/>
 FAIL Parsing: <http://%41.com> against <http://other.com/> assert_throws_js: function "function() {
           bURL(expected.input, expected.base)
diff --git a/third_party/blink/web_tests/platform/win/fast/dom/focus-contenteditable-expected.png b/third_party/blink/web_tests/platform/win/fast/dom/focus-contenteditable-expected.png
index 56f1cd7..b3725c2 100644
--- a/third_party/blink/web_tests/platform/win/fast/dom/focus-contenteditable-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/dom/focus-contenteditable-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/color/color-picker-appearance-imperfect-match-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
index 1bced7a..fb20543 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/color/color-picker-appearance-imperfect-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/color/color-picker-appearance-manual-color-change-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
index 898a8ede..289760b1 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/color/color-picker-appearance-manual-color-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-border-radius-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-border-radius-expected.png
index c8b4c72..77ea98b 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-border-radius-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-border-radius-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png
new file mode 100644
index 0000000..54169b0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-border-width-changed-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png
new file mode 100644
index 0000000..529c4046
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-multiline-writingmode-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-zoom-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-zoom-expected.png
index 775e32a..07d6adf 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-zoom-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/focus-ring-zoom-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png
new file mode 100644
index 0000000..a539493
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/textarea-scrolled-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/textfield-focus-ring-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/textfield-focus-ring-expected.png
new file mode 100644
index 0000000..d751935
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/fast/forms/focus-rect/textfield-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/number/number-appearance-datalist-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/number/number-appearance-datalist-expected.png
index 20a03765..488c45a 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/number/number-appearance-datalist-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/number/number-appearance-datalist-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/text/text-appearance-datalist-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/text/text-appearance-datalist-expected.png
index 3ffb52a..ccd7a6c 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/text/text-appearance-datalist-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/text/text-appearance-datalist-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png
index cbd0e22..2777369 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/textarea/textarea-scrolled-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/textarea/textarea-scrolled-type-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/textarea/textarea-scrolled-type-expected.png
index 805c5e4a..11bc4cc 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/textarea/textarea-scrolled-type-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/textarea/textarea-scrolled-type-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/validation-bubble-device-emulation-change-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/validation-bubble-device-emulation-change-expected.png
index 775bfa3..4a0bf2d 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/validation-bubble-device-emulation-change-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/validation-bubble-device-emulation-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/forms/validation-bubble-device-emulation-expected.png b/third_party/blink/web_tests/platform/win/fast/forms/validation-bubble-device-emulation-expected.png
index be4a448..6f9dedd 100644
--- a/third_party/blink/web_tests/platform/win/fast/forms/validation-bubble-device-emulation-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/forms/validation-bubble-device-emulation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/inline/25277-2-expected.png b/third_party/blink/web_tests/platform/win/fast/inline/25277-2-expected.png
index e54c774..6c039d4 100644
--- a/third_party/blink/web_tests/platform/win/fast/inline/25277-2-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/inline/25277-2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/inline/25277-expected.png b/third_party/blink/web_tests/platform/win/fast/inline/25277-expected.png
index e54c774..6c039d4 100644
--- a/third_party/blink/web_tests/platform/win/fast/inline/25277-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/inline/25277-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/inline/inline-focus-ring-expected.png b/third_party/blink/web_tests/platform/win/fast/inline/inline-focus-ring-expected.png
index 5278091..6c5e6234 100644
--- a/third_party/blink/web_tests/platform/win/fast/inline/inline-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/inline/inline-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/overflow/overflow-focus-ring-expected.png b/third_party/blink/web_tests/platform/win/fast/overflow/overflow-focus-ring-expected.png
index 333ce0c6..be885cb 100644
--- a/third_party/blink/web_tests/platform/win/fast/overflow/overflow-focus-ring-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/overflow/overflow-focus-ring-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/4776765-expected.png b/third_party/blink/web_tests/platform/win/paint/invalidation/4776765-expected.png
index 4125c2c..463d59b 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/4776765-expected.png
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/4776765-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/4776765-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/4776765-expected.txt
index 86ec310..34ab081a 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/4776765-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/4776765-expected.txt
@@ -4,9 +4,24 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [8, 84],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
       "invalidations": [
-        [6, 42, 788, 64]
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [6, 42],
+      "bounds": [788, 64],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 788, 64]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/caret-outside-block-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/caret-outside-block-expected.txt
index d96f017c2..d6e0e52 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/caret-outside-block-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/caret-outside-block-expected.txt
@@ -4,9 +4,15 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [791, 8],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
       "invalidations": [
-        [791, 8, 1, 19]
+        [0, 0, 1, 19]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/caret-subpixel-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/caret-subpixel-expected.txt
index 5bfc2809..7fdd4e05 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/caret-subpixel-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/caret-subpixel-expected.txt
@@ -8,6 +8,27 @@
       "invalidations": [
         [8, 8, 226, 22]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [200, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [12, 11, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/delete-into-nested-block-expected.png b/third_party/blink/web_tests/platform/win/paint/invalidation/delete-into-nested-block-expected.png
index 6e1003f..a398b7d4 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/delete-into-nested-block-expected.png
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/delete-into-nested-block-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/delete-into-nested-block-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/delete-into-nested-block-expected.txt
index fe7f23e..0dcb227 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/delete-into-nested-block-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/delete-into-nested-block-expected.txt
@@ -6,10 +6,25 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [8, 167, 30, 19],
-        [8, 127, 30, 19],
-        [8, 127, 26, 20],
-        [8, 147, 23, 20]
+        [6, 125, 788, 104]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [8, 127],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow DIV",
+      "position": [6, 125],
+      "bounds": [788, 104],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 788, 104]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/forms/textarea-caret-expected.png b/third_party/blink/web_tests/platform/win/paint/invalidation/forms/textarea-caret-expected.png
index 5907532..2a490cf 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/forms/textarea-caret-expected.png
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/forms/textarea-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/forms/textarea-caret-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/forms/textarea-caret-expected.txt
index 72eef48..8581d77 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/forms/textarea-caret-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/forms/textarea-caret-expected.txt
@@ -8,6 +8,47 @@
       "invalidations": [
         [7, 7, 183, 40]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [482, 3],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGTextControlMultiLine TEXTAREA id='editor'",
+      "position": [-1, -1],
+      "bounds": [183, 40],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 183, 40]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [-303, 0, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/invalidate-caret-before-text-node-update-expected.png b/third_party/blink/web_tests/platform/win/paint/invalidation/invalidate-caret-before-text-node-update-expected.png
index fdc8bf8..ed40015 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/invalidate-caret-before-text-node-update-expected.png
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/invalidate-caret-before-text-node-update-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt
deleted file mode 100644
index 953a923..0000000
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "Scrolling background of LayoutView #document",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "invalidations": [
-        [8, 8, 10, 18],
-        [18, 8, 1, 18]
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/outline/inline-outline-repaint-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/outline/inline-outline-repaint-expected.txt
index 9fefb554..14ded99 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/outline/inline-outline-repaint-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/outline/inline-outline-repaint-expected.txt
@@ -8,6 +8,24 @@
       "invalidations": [
         [5, 153, 92, 45]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [44, 176],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [5, 153],
+      "bounds": [85, 45],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 85, 45]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
index 5dade5d7..afb14ab 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
@@ -4,10 +4,27 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [16, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
       "invalidations": [
-        [384, 11, 1, 16],
-        [381, 11, 1, 16]
+        [0, 0, 1, 16]
+      ],
+      "transform": 1
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [365, 11, 0, 1]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
index 8b24c05..53deb89 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/caret-with-composited-scroll-expected.txt
@@ -27,6 +27,25 @@
         [0, 999, 205, 23]
       ],
       "transform": 2
+    },
+    {
+      "name": "Caret",
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 3
+    },
+    {
+      "name": "LayoutNGTextControlSingleLine INPUT id='text'",
+      "position": [0, 999],
+      "bounds": [205, 23],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 205, 23]
+      ],
+      "transform": 2
     }
   ],
   "transforms": [
@@ -48,6 +67,16 @@
         [0, 0, 1, 0],
         [0, -922, 0, 1]
       ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [3, 1003, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
index 54736d3..584bf20 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -16,6 +16,16 @@
         [0, 0, 74, 24]
       ],
       "transform": 1
+    },
+    {
+      "name": "Caret",
+      "position": [63, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 2
     }
   ],
   "transforms": [
@@ -27,6 +37,16 @@
         [0, 0, 1, 0],
         [8, 8, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [4, 3, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
index 54736d3..584bf20 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-non-composited-scrolling-container-expected.txt
@@ -16,6 +16,16 @@
         [0, 0, 74, 24]
       ],
       "transform": 1
+    },
+    {
+      "name": "Caret",
+      "position": [63, 0],
+      "bounds": [1, 16],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 16]
+      ],
+      "transform": 2
     }
   ],
   "transforms": [
@@ -27,6 +37,16 @@
         [0, 0, 1, 0],
         [8, 8, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [4, 3, 0, 1]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-after-delete-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-after-delete-expected.txt
index 48e92a4..862e875 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-after-delete-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-after-delete-expected.txt
@@ -8,6 +8,15 @@
       "invalidations": [
         [38, 78, 152, 102]
       ]
+    },
+    {
+      "name": "Caret",
+      "position": [39, 79],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/table/caret-contenteditable-content-after-expected.png b/third_party/blink/web_tests/platform/win/paint/invalidation/table/caret-contenteditable-content-after-expected.png
index 859136d..d453ba7 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/table/caret-contenteditable-content-after-expected.png
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/table/caret-contenteditable-content-after-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/table/caret-contenteditable-content-after-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/table/caret-contenteditable-content-after-expected.txt
index 4e0fe839..d9d3992 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/table/caret-contenteditable-content-after-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/table/caret-contenteditable-content-after-expected.txt
@@ -6,7 +6,26 @@
       "contentsOpaque": true,
       "backgroundColor": "#FFFFFF",
       "invalidations": [
-        [6, 46, 788, 24]
+        [8, 48, 22, 19],
+        [30, 48, 7, 19]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [30, 48],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 19]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [6, 46],
+      "bounds": [788, 24],
+      "contentsOpaqueForText": true,
+      "invalidations": [
+        [0, 0, 788, 24]
       ]
     }
   ]
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/transform/caret-with-transformation-expected.png b/third_party/blink/web_tests/platform/win/paint/invalidation/transform/caret-with-transformation-expected.png
index b3ad373..7d46cf7 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/transform/caret-with-transformation-expected.png
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/transform/caret-with-transformation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/transform/caret-with-transformation-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/transform/caret-with-transformation-expected.txt
index f47ae11a..e7895fcf 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/transform/caret-with-transformation-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/transform/caret-with-transformation-expected.txt
@@ -4,11 +4,46 @@
       "name": "Scrolling background of LayoutView #document",
       "bounds": [800, 600],
       "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Caret",
+      "position": [325, 0],
+      "bounds": [1, 19],
+      "contentsOpaque": true,
       "invalidations": [
-        [42, 36, 11, 18],
-        [324, 199, 11, 17]
+        [0, 0, 1, 19]
+      ],
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow DIV",
+      "position": [-2, -2],
+      "bounds": [788, 24],
+      "contentsOpaqueForText": true,
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
       ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [0.866025403784439, 0.5, 0, 0],
+        [-0.5, 0.866025403784439, 0, 0],
+        [0, 0, 1, 0],
+        [-13.3974596215561, 223.205080756888, 0, 1]
+      ],
+      "origin": [392, 10]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/platform/win/transforms/transformed-caret-expected.png b/third_party/blink/web_tests/platform/win/transforms/transformed-caret-expected.png
index d825e8c..4327865 100644
--- a/third_party/blink/web_tests/platform/win/transforms/transformed-caret-expected.png
+++ b/third_party/blink/web_tests/platform/win/transforms/transformed-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/transforms/transformed-focused-text-input-expected.png b/third_party/blink/web_tests/platform/win/transforms/transformed-focused-text-input-expected.png
index 922ab5c..303193a 100644
--- a/third_party/blink/web_tests/platform/win/transforms/transformed-focused-text-input-expected.png
+++ b/third_party/blink/web_tests/platform/win/transforms/transformed-focused-text-input-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png
index 7a583a08..7c55481a 100644
--- a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/password/password-with-reveal-button-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
index a9bfd55e..cb18451 100644
--- a/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-color-scheme/fast/forms/color-scheme/search/search-cancel-button-clicked-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/delete-hard-break-character-expected.png b/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/delete-hard-break-character-expected.png
index 8c7dad8..4d9e838 100644
--- a/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/delete-hard-break-character-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/delete-hard-break-character-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/rtl-caret-expected.png b/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/rtl-caret-expected.png
index 89c9da77..7cfa60d 100644
--- a/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/rtl-caret-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/text-antialias/selection/rtl-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/text-antialias/selection/delete-hard-break-character-expected.png b/third_party/blink/web_tests/platform/win7/virtual/text-antialias/selection/delete-hard-break-character-expected.png
index 0db275d..db2cbcb68cf 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/text-antialias/selection/delete-hard-break-character-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/text-antialias/selection/delete-hard-break-character-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/text-antialias/selection/rtl-caret-expected.png b/third_party/blink/web_tests/platform/win7/virtual/text-antialias/selection/rtl-caret-expected.png
index 5c0f627..d8685140 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/text-antialias/selection/rtl-caret-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/text-antialias/selection/rtl-caret-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/backface-visibility-interop/paint/invalidation/clip/caret-ancestor-clip-change-expected.txt b/third_party/blink/web_tests/virtual/backface-visibility-interop/paint/invalidation/clip/caret-ancestor-clip-change-expected.txt
new file mode 100644
index 0000000..fec04beb
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/backface-visibility-interop/paint/invalidation/clip/caret-ancestor-clip-change-expected.txt
@@ -0,0 +1,38 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [8, 58, 109, 62]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [0, 2],
+      "bounds": [1, 112],
+      "contentsOpaque": true,
+      "transform": 1
+    },
+    {
+      "name": "LayoutNGTextControlSingleLine INPUT id='target'",
+      "position": [8, 8],
+      "bounds": [109, 112],
+      "contentsOpaqueForText": true
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [12, 1, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/backface-visibility-interop/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt b/third_party/blink/web_tests/virtual/backface-visibility-interop/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt
new file mode 100644
index 0000000..55a1dc5
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/backface-visibility-interop/paint/invalidation/invalidate-caret-before-text-node-update-expected.txt
@@ -0,0 +1,29 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "invalidations": [
+        [8, 8, 10, 18]
+      ]
+    },
+    {
+      "name": "Caret",
+      "position": [8, 8],
+      "bounds": [1, 18],
+      "contentsOpaque": true,
+      "invalidations": [
+        [0, 0, 1, 18]
+      ]
+    },
+    {
+      "name": "LayoutNGBlockFlow HTML",
+      "position": [6, 6],
+      "bounds": [788, 22],
+      "contentsOpaqueForText": true
+    }
+  ]
+}
+
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f662ab4..0f15e06 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3775,6 +3775,14 @@
   <int value="3" label="GmsProxy api"/>
 </enum>
 
+<enum name="ArcAuthCodeStatus">
+  <summary>Defines the status of auth code retrieval.</summary>
+  <int value="16" label="Server does not return authorization"/>
+  <int value="17" label="ARC was disabled for Active Directory user"/>
+  <int value="18" label="Success"/>
+  <int value="21" label="Account is not present in Chrome OS Account Manager"/>
+</enum>
+
 <enum name="ArcAuthMainAccountResolutionStatus">
   <int value="0" label="No hash code no account"/>
   <int value="1" label="No hash code for single account"/>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index 8557dc9..f4ce104 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -471,6 +471,30 @@
   </token>
 </histogram>
 
+<histogram name="Arc.Auth.RequestAccountInfoResult.Primary"
+    enum="ArcAuthCodeStatus" expires_after="2022-06-01">
+  <owner>anastasiian@google.com</owner>
+  <owner>mhasank@google.com</owner>
+  <owner>arc-core@google.com</owner>
+  <summary>
+    Contains the status of the account info and auth code fetch for primary
+    account. Recorded when ARC requests account information (e.g. when account
+    is added, when token is updated).
+  </summary>
+</histogram>
+
+<histogram name="Arc.Auth.RequestAccountInfoResult.Secondary"
+    enum="ArcAuthCodeStatus" expires_after="2022-06-01">
+  <owner>anastasiian@google.com</owner>
+  <owner>mhasank@google.com</owner>
+  <owner>arc-core@google.com</owner>
+  <summary>
+    Contains the status of the account info and auth code fetch for secondary
+    account. Recorded when ARC requests account information (e.g. when account
+    is added, when token is updated).
+  </summary>
+</histogram>
+
 <histogram name="Arc.Auth.SignIn.TimeDelta{ArcUserTypes}" units="ms"
     expires_after="2022-07-04">
   <owner>mhasank@google.com</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index de3ba85..2d2ccb7 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -12,6 +12,10 @@
             "hash": "802fed751d62ca3db41bc4ae27e43b546d000ce8",
             "remote_path": "perfetto_binaries/trace_processor_shell/mac/78be2dc30c38c08ca3eeddb83b0738efc39e4528/trace_processor_shell"
         },
+        "mac_arm64": {
+            "hash": "c0397e87456ad6c6a7aa0133e5b81c97adbab4ab",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac_arm64/cefb3e0ec3a0580c996f801e854fe02963c03d5c/trace_processor_shell"
+        },
         "linux_arm64": {
             "hash": "5074025a2898ec41a872e70a5719e417acb0a380",
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
@@ -25,4 +29,4 @@
         "hash": "5bc5eae952c72d04403409a56838f66da13394d6",
         "remote_path": "perfetto_data/power_profile.sql/20200713T171147/power_profile.sql"
     }
-}
\ No newline at end of file
+}
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps_manager.py b/tools/perf/core/perfetto_binary_roller/binary_deps_manager.py
index 3ed1ac5e..41ef38d7 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps_manager.py
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps_manager.py
@@ -37,7 +37,7 @@
   return uname_arch
 
 
-def _GetBinaryArch(binary_name):
+def _GetLinuxBinaryArch(binary_name):
   file_output = subprocess.check_output(['file', binary_name])
   # TODO(b/206008069): Remove the check when fixed.
   if hasattr(six, 'ensure_str'):
@@ -52,6 +52,15 @@
   return file_arch
 
 
+def _GetMacBinaryArch(binary_name):
+  file_output = subprocess.check_output(['file', binary_name])
+  # TODO(b/206008069): Remove the check when fixed.
+  if hasattr(six, 'ensure_str'):
+    file_output = six.ensure_str(file_output)
+  file_arch = file_output.split()[-1].strip()
+  return file_arch
+
+
 def _GetHostPlatform():
   os_name = py_utils.GetHostOsName()
   # If we're running directly on a Chrome OS device, fetch the binaries for
@@ -61,19 +70,31 @@
     if arch == 'x86_64':
       return 'linux'
     return 'linux_' + arch
+  if os_name == 'mac':
+    return 'mac_arm64' if _GetHostArch() == 'arm64' else 'mac'
   return os_name
 
 
 def _GetBinaryPlatform(binary_name):
   host_platform = _GetHostPlatform()
-  # Binaries built on mac/windows are for mac/windows respectively. Binaries
-  # built on linux may be for linux or chromeos on different architectures.
-  if not host_platform.startswith('linux'):
-    return host_platform
-  arch = _GetBinaryArch(binary_name)
-  if arch == 'x86_64':
-    return 'linux'
-  return 'linux' + '_' + arch
+
+  # Binaries built on linux may be for linux or chromeos on different
+  # architectures.
+  if host_platform.startswith('linux'):
+    arch = _GetLinuxBinaryArch(binary_name)
+    if arch == 'x86_64':
+      return 'linux'
+    return 'linux' + '_' + arch
+
+  # Binaries built on mac may be either for arm64 or intel.
+  if host_platform.startswith('mac'):
+    arch = _GetMacBinaryArch(binary_name)
+    if arch == 'x86_64':
+      return 'mac'
+    return 'mac' + '_' + arch
+
+  # Binaries built on windows are for windows intel always.
+  return host_platform
 
 
 def _CalculateHash(remote_path):
diff --git a/tools/perf/core/perfetto_binary_roller/roll_trace_processor b/tools/perf/core/perfetto_binary_roller/roll_trace_processor
index 2f4580f..2dbdc5ef7 100755
--- a/tools/perf/core/perfetto_binary_roller/roll_trace_processor
+++ b/tools/perf/core/perfetto_binary_roller/roll_trace_processor
@@ -3,6 +3,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from __future__ import print_function
+
 import argparse
 import os
 import sys
@@ -21,7 +23,8 @@
 if __name__ == '__main__':
   parser = argparse.ArgumentParser()
   parser.add_argument(
-      '--platform', help='linux/linux_arm/linux_arm64/mac/win', required=True)
+      '--platform', help='linux/linux_arm/linux_arm64/mac/mac_arm64/win',
+      required=True)
   parser.add_argument(
       '--path', metavar='PATH', help='Switch to using a trace processor version'
       ' stored by this cloud path.')
@@ -39,11 +42,11 @@
                        '--print-latest or --print-current.')
 
   if args.print_latest:
-    print binary_deps_manager.GetLatestPath(
-        trace_processor.TP_BINARY_NAME, args.platform)
+    print(binary_deps_manager.GetLatestPath(
+        trace_processor.TP_BINARY_NAME, args.platform))
   elif args.print_current:
-    print binary_deps_manager.GetCurrentPath(
-        trace_processor.TP_BINARY_NAME, args.platform)
+    print(binary_deps_manager.GetCurrentPath(
+        trace_processor.TP_BINARY_NAME, args.platform))
   else:
     binary_deps_manager.SwitchBinaryToNewPath(
         trace_processor.TP_BINARY_NAME, args.platform, args.path)
diff --git a/ui/views/controls/menu/menu_types.h b/ui/views/controls/menu/menu_types.h
index 5cfc74ff..9dcd603 100644
--- a/ui/views/controls/menu/menu_types.h
+++ b/ui/views/controls/menu/menu_types.h
@@ -8,7 +8,7 @@
 namespace views {
 
 // Where a popup menu should be anchored to for non-RTL languages. The opposite
-// position will be used if base::i18n:IsRTL() is true. The Bubble flags are
+// position will be used if base::i18n::IsRTL() is true. The Bubble flags are
 // used when the menu should get enclosed by a bubble.
 enum class MenuAnchorPosition {
   kTopLeft,