diff --git a/AUTHORS b/AUTHORS
index 57b4da73..b7f610c 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1077,7 +1077,7 @@
 LG Electronics, Inc. <*@lge.com>
 Loongson Technology Corporation Limited. <*@loongson.cn>
 Macadamian <*@macadamian.com>
-Mail.ru Group <*.corp.mail.ru>
+Mail.ru Group <*@corp.mail.ru>
 Mediatek <*@mediatek.com>
 Microsoft <*@microsoft.com>
 MIPS Technologies, Inc. <*@mips.com>
diff --git a/BUILD.gn b/BUILD.gn
index b80ac2e..50fdcb21 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -144,7 +144,6 @@
       "//third_party/dawn:dawn_end2end_tests_temp_group",
       "//third_party/dawn:dawn_unittests_temp_group",
       "//third_party/pdfium/samples:pdfium_test",
-      "//third_party/webrtc/rtc_tools:frame_analyzer",
       "//tools/perf/clear_system_cache",
       "//tools/polymer:polymer_tools_python_unittests",
       "//ui/accessibility:accessibility_perftests",
diff --git a/DEPS b/DEPS
index 9bec2070..5385587 100644
--- a/DEPS
+++ b/DEPS
@@ -172,11 +172,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '12cea8d6c429d06425be97af31db7c31655e5290',
+  'skia_revision': '81a8d282d356667aab7bc8c0513da81bb1c5f3c4',
   # 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': '500af51db17fa412e5913c8f81c0d729ad5668e4',
+  'v8_revision': '29e8c96fb28537121f964b7b261c7dc286dfbf2c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -188,7 +188,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '27a3d31d7a9d65bb49b83da404cc4830eb3ce06e',
+  'swiftshader_revision': 'd9ed1c2732ba9b1bd36461367d348e3030043a25',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -243,7 +243,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': 'b47c6e25f94ebbc276390b621980c0e956490861',
+  'devtools_frontend_revision': '5cb14a4f300bbee3447fb241b57799429492c694',
   # 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.
@@ -428,7 +428,7 @@
     'packages': [
       {
         'package': 'chromium/llvm-build-tools/dsymutil',
-        'version': 'OWlhXkmj18li3yhJk59Kmjbc5KdgLh56TwCd1qBdzlIC',
+        'version': 'M56jPzDv1620Rnm__jTMYS62Zi8rxHVq7yw0qeBFEgkC',
       }
     ],
     'condition': 'checkout_mac',
@@ -871,7 +871,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '48dc2da07f5bb8fd2353d5790f172fa5b4c4f9fe',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a942a9f9a2358e11484e67c9169de0e698be2723',
       'condition': 'checkout_linux',
   },
 
@@ -896,7 +896,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9ab047e78be34f3345b00c3d71cbfcbbe110ab3b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '72a245e4c678f478bf7e1961cae8b2ba5bfa93bf',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1311,7 +1311,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '50d2ff30828b5155f3e1f19d98f66def2dd97b09',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f0d1100a7149dce7efa44e42f9c1f0952e6c9ab0',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1512,7 +1512,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'dd55f3ca8f2ea716ca917a4aaf36f0729fe902b1',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e9f663c8cb8c08501472e2fef8c84dfa98f52b16',
+    Var('webrtc_git') + '/src.git' + '@' + '7c1fb4156d24a217a497eed6a94ac8cb58e162db',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1579,7 +1579,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@12e6d18c4860140a01518d5f269976319010b9f1',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@275467d3f177738333504b9d2ba68e6245494ae4',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/apk/BUILD.gn b/android_webview/apk/BUILD.gn
index 2ffcbb4f..5fbd614 100644
--- a/android_webview/apk/BUILD.gn
+++ b/android_webview/apk/BUILD.gn
@@ -52,6 +52,7 @@
     "//android_webview:common_crash_java",
     "//base:base_java",
     "//components/minidump_uploader:minidump_uploader_java",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
     "//ui/android:ui_java",
   ]
   android_manifest_for_lint = system_webview_android_manifest
diff --git a/android_webview/apk/java/AndroidManifest.xml b/android_webview/apk/java/AndroidManifest.xml
index 6ad7012..6a25f8f 100644
--- a/android_webview/apk/java/AndroidManifest.xml
+++ b/android_webview/apk/java/AndroidManifest.xml
@@ -33,7 +33,7 @@
             <!--suppress HardcodedText -->
             <activity android:name="org.chromium.android_webview.devui.MainActivity"
                       android:label="WebView DevTools"
-                      android:theme="@android:style/Theme.DeviceDefault"
+                      android:theme="@style/Theme.DevUi.DayNight"
                       android:launchMode="singleTask"
                       android:process=":webview_apk">  {# Explicit process required for monochrome compatibility. #}
                 <intent-filter>
@@ -43,7 +43,7 @@
             </activity>
             <activity android:name="org.chromium.android_webview.devui.CrashesListActivity"
                       android:label="WebView Crashes"
-                      android:theme="@android:style/Theme.DeviceDefault"
+                      android:theme="@style/Theme.DevUi.DayNight"
                       android:launchMode="singleTop"
                       android:process=":webview_apk">  {# Explicit process required for monochrome compatibility. #}
             </activity>
diff --git a/android_webview/apk/java/res_devui/layout/activity_crashes_list.xml b/android_webview/apk/java/res_devui/layout/activity_crashes_list.xml
index 67850b9..c07aac7 100644
--- a/android_webview/apk/java/res_devui/layout/activity_crashes_list.xml
+++ b/android_webview/apk/java/res_devui/layout/activity_crashes_list.xml
@@ -7,7 +7,6 @@
 
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/activity_crashes_list"
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
@@ -16,16 +15,23 @@
         android:id="@+id/crashes_summary_textview"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="#A9A9A9"
-        android:textColor="#FFFFFF"
-        android:textSize="20sp"
-        android:paddingBottom="5sp"
-        android:paddingTop="5sp"/>
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:paddingStart="10dp"
+        android:paddingEnd="10dp"
+        android:textAppearance="?android:attr/textAppearanceLarge"/>
 
-    <ListView
+    <!-- horizontal divider -->
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?android:attr/listDivider"/>
+
+    <!-- child divider is transparent (hidden) -->
+    <ExpandableListView
         android:id="@+id/crashes_list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginTop="25dp"/>
+        android:childDivider="#00000000"/>
 
 </LinearLayout>
diff --git a/android_webview/apk/java/res_devui/layout/activity_main.xml b/android_webview/apk/java/res_devui/layout/activity_main.xml
index e8ce823..12de7ef 100644
--- a/android_webview/apk/java/res_devui/layout/activity_main.xml
+++ b/android_webview/apk/java/res_devui/layout/activity_main.xml
@@ -7,7 +7,6 @@
 
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/activity_main"
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
diff --git a/android_webview/apk/java/res_devui/layout/crashes_list_item_body.xml b/android_webview/apk/java/res_devui/layout/crashes_list_item_body.xml
new file mode 100644
index 0000000..4c8830d
--- /dev/null
+++ b/android_webview/apk/java/res_devui/layout/crashes_list_item_body.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 The Chromium Authors. All rights reserved.  Use of this
+  source code is governed by a BSD-style license that can be found in the
+  LICENSE file.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="2dp"
+    android:paddingBottom="2dp"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="?android:attr/expandableListPreferredItemPaddingLeft"
+        android:paddingEnd="100dp"
+        android:orientation="vertical">
+
+        <include layout="@layout/two_line_sublist_item"
+            android:id="@+id/variations"/>
+
+        <include layout="@layout/two_line_sublist_item"
+            android:id="@+id/upload_status"/>
+
+        <!--suppress HardcodedText -->
+        <Button
+            android:id="@+id/crash_report_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:paddingBottom="8dp"
+            android:text="File bug report"
+            android:textAllCaps="false" />
+
+    </LinearLayout>
+
+    <!-- horizontal divider -->
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_marginTop="16dp"
+        android:background="?android:attr/listDivider"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android_webview/apk/java/res_devui/layout/crashes_list_item_header.xml b/android_webview/apk/java/res_devui/layout/crashes_list_item_header.xml
new file mode 100644
index 0000000..23290580a
--- /dev/null
+++ b/android_webview/apk/java/res_devui/layout/crashes_list_item_header.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 The Chromium Authors. All rights reserved.  Use of this
+  source code is governed by a BSD-style license that can be found in the
+  LICENSE file.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="?android:attr/expandableListPreferredItemPaddingLeft"
+    android:paddingTop="16dp"
+    android:paddingBottom="16dp"
+    android:gravity="center"
+    android:orientation="horizontal">
+
+    <!--suppress HardcodedText -->
+    <ImageView
+        android:layout_height="35dp"
+        android:layout_width="35dp"
+        android:id="@+id/crash_package_icon"
+        android:layout_marginEnd="2dp"
+        android:contentDescription="package icon"/>
+
+    <include layout="@layout/two_line_list_item"
+        android:id="@+id/crash_header"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android_webview/apk/java/res_devui/layout/crashes_list_row.xml b/android_webview/apk/java/res_devui/layout/crashes_list_row.xml
deleted file mode 100644
index 00feca5..0000000
--- a/android_webview/apk/java/res_devui/layout/crashes_list_row.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2019 The Chromium Authors. All rights reserved.  Use of this
-  source code is governed by a BSD-style license that can be found in the
-  LICENSE file.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/crash_list_row"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginTop="15dp"
-    android:orientation="vertical">
-
-    <TextView
-        android:id="@+id/crash_info_label"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textIsSelectable="true"
-        android:textStyle="bold"
-        android:textSize="16sp"
-        android:paddingBottom="2dp"
-        android:paddingTop="2dp"
-        android:background="#d3d3d3"/>
-
-    <TextView
-        android:id="@+id/crash_info_textview"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="5dp"
-        android:textIsSelectable="true"/>
-
-    <!--suppress HardcodedText -->
-    <Button
-        android:id="@+id/crash_report_button"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="Provide more info"/>
-
-</LinearLayout>
diff --git a/android_webview/apk/java/res_devui/layout/two_line_list_item.xml b/android_webview/apk/java/res_devui/layout/two_line_list_item.xml
new file mode 100644
index 0000000..bdfb55a
--- /dev/null
+++ b/android_webview/apk/java/res_devui/layout/two_line_list_item.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 The Chromium Authors. All rights reserved.  Use of this
+  source code is governed by a BSD-style license that can be found in the
+  LICENSE file.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="2dp"
+    android:paddingBottom="2dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:mode="twoLine"
+    android:orientation="vertical">
+
+    <TextView android:id="@android:id/text1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textStyle="bold"
+        android:textAlignment="viewStart"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"/>
+
+    <TextView android:id="@android:id/text2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textAlignment="viewStart"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android_webview/apk/java/res_devui/layout/two_line_sublist_item.xml b/android_webview/apk/java/res_devui/layout/two_line_sublist_item.xml
new file mode 100644
index 0000000..60131f9
--- /dev/null
+++ b/android_webview/apk/java/res_devui/layout/two_line_sublist_item.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 The Chromium Authors. All rights reserved.  Use of this
+  source code is governed by a BSD-style license that can be found in the
+  LICENSE file.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="2dp"
+    android:paddingEnd="2dp"
+    android:mode="twoLine"
+    android:orientation="vertical">
+
+    <TextView android:id="@android:id/text1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceListItem"
+        android:textStyle="bold"
+        android:textAlignment="viewStart"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"/>
+
+    <TextView android:id="@android:id/text2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textAlignment="viewStart"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android_webview/apk/java/res_devui/menu/crashes_options_menu.xml b/android_webview/apk/java/res_devui/menu/crashes_options_menu.xml
index bd91e392..3690c1b7 100644
--- a/android_webview/apk/java/res_devui/menu/crashes_options_menu.xml
+++ b/android_webview/apk/java/res_devui/menu/crashes_options_menu.xml
@@ -5,8 +5,7 @@
   LICENSE file.
 -->
 
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
     <group android:id="@+id/crashes_menu">
         <!--suppress HardcodedText -->
         <item
diff --git a/android_webview/apk/java/res_devui/values-night/styles.xml b/android_webview/apk/java/res_devui/values-night/styles.xml
new file mode 100644
index 0000000..fde482f2
--- /dev/null
+++ b/android_webview/apk/java/res_devui/values-night/styles.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="Theme.DevUi.DayNight" parent="@android:style/Theme.Material" />
+</resources>
\ No newline at end of file
diff --git a/android_webview/apk/java/res_devui/values/styles.xml b/android_webview/apk/java/res_devui/values/styles.xml
new file mode 100644
index 0000000..1140bf2
--- /dev/null
+++ b/android_webview/apk/java/res_devui/values/styles.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="Theme.DevUi.DayNight" parent="@android:style/Theme.Material.Light.DarkActionBar" />
+</resources>
\ No newline at end of file
diff --git a/android_webview/apk/java/src/org/chromium/android_webview/devui/CrashesListActivity.java b/android_webview/apk/java/src/org/chromium/android_webview/devui/CrashesListActivity.java
index a1a018a..6604fd8 100644
--- a/android_webview/apk/java/src/org/chromium/android_webview/devui/CrashesListActivity.java
+++ b/android_webview/apk/java/src/org/chromium/android_webview/devui/CrashesListActivity.java
@@ -6,19 +6,23 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
-import android.text.SpannableStringBuilder;
-import android.text.style.StyleSpan;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
+import android.widget.BaseExpandableListAdapter;
 import android.widget.Button;
-import android.widget.ListView;
+import android.widget.ExpandableListView;
+import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import org.chromium.android_webview.common.crash.CrashInfo;
 import org.chromium.android_webview.common.crash.CrashInfo.UploadState;
 import org.chromium.android_webview.devui.util.NavigationMenuHelper;
@@ -35,10 +39,12 @@
     // Max number of crashes to show in the crashes list.
     private static final int MAX_CRASHES_NUMBER = 20;
 
-    private ListView mCrashListView;
     private TextView mCrashesSummaryView;
+    private BaseExpandableListAdapter mCrashListViewAdapter;
     private WebViewCrashInfoCollector mCrashCollector;
 
+    private List<CrashInfo> mCrashInfoList;
+
     private static final String CRASH_REPORT_TEMPLATE = ""
             + "IMPORTANT: Your crash has already been automatically reported to our crash system. "
             + "You only need to fill this out if you can share more information like steps to "
@@ -76,40 +82,87 @@
 
         setContentView(R.layout.activity_crashes_list);
 
-        mCrashListView = findViewById(R.id.crashes_list);
         mCrashesSummaryView = findViewById(R.id.crashes_summary_textview);
         mCrashCollector = new WebViewCrashInfoCollector();
+        mCrashListViewAdapter = new CrashListExpandableAdapter();
 
+        // initialize the crash list before setting the list adapter.
         updateCrashesList();
+
+        ExpandableListView crashListView = findViewById(R.id.crashes_list);
+        crashListView.setAdapter(mCrashListViewAdapter);
     }
 
     /**
-     * Adapter to create crashes list rows from a list of CrashInfo.
+     * Adapter to create crashes list items from a list of CrashInfo.
      */
-    private class CrashListAdapter extends ArrayAdapter<CrashInfo> {
-        private final List<CrashInfo> mCrashInfoList;
-
-        public CrashListAdapter(List<CrashInfo> crashInfoList) {
-            super(CrashesListActivity.this, R.layout.crashes_list_row, crashInfoList);
-            mCrashInfoList = crashInfoList;
-        }
-
+    private class CrashListExpandableAdapter extends BaseExpandableListAdapter {
+        // Group View which is used as header for a crash in crashes list.
+        // We show:
+        //   - Icon of the app where the crash happened.
+        //   - Package name of the app where the crash happened.
+        //   - Time when the crash happened.
         @Override
-        public View getView(int position, View view, ViewGroup parent) {
+        public View getGroupView(
+                int groupPosition, boolean isExpanded, View view, ViewGroup parent) {
             // If the the old view is already created then reuse it, else create a new one by layout
             // inflation.
             if (view == null) {
-                view = getLayoutInflater().inflate(R.layout.crashes_list_row, null, true);
+                view = getLayoutInflater().inflate(R.layout.crashes_list_item_header, null);
             }
 
-            TextView label = view.findViewById(R.id.crash_info_label);
-            TextView infoTextView = view.findViewById(R.id.crash_info_textview);
+            CrashInfo crashInfo = (CrashInfo) getGroup(groupPosition);
+
+            ImageView packageIcon = view.findViewById(R.id.crash_package_icon);
+            String packageName;
+            if (crashInfo.packageName == null) {
+                // This can happen if crash log file where we keep crash info is cleared but other
+                // log files like upload logs still exist.
+                packageName = "unknown app";
+                packageIcon.setImageResource(android.R.drawable.sym_def_app_icon);
+            } else {
+                packageName = crashInfo.packageName;
+                try {
+                    Drawable icon = getPackageManager().getApplicationIcon(packageName);
+                    packageIcon.setImageDrawable(icon);
+                } catch (PackageManager.NameNotFoundException e) {
+                    // This can happen if the app was uninstalled after the crash was recorded.
+                    packageIcon.setImageResource(android.R.drawable.sym_def_app_icon);
+                }
+            }
+            setTwoLineListItemText(view.findViewById(R.id.crash_header), packageName,
+                    new Date(crashInfo.captureTime).toString());
+
+            return view;
+        }
+
+        // Child View where more info about the crash is shown:
+        //    - Variation keys for the crash.
+        //    - Crash report upload status.
+        @Override
+        public View getChildView(int groupPosition, final int childPosition, boolean isLastChild,
+                View view, ViewGroup parent) {
+            // If the the old view is already created then reuse it, else create a new one by layout
+            // inflation.
+            if (view == null) {
+                view = getLayoutInflater().inflate(R.layout.crashes_list_item_body, null);
+            }
+
+            CrashInfo crashInfo = (CrashInfo) getChild(groupPosition, childPosition);
+            // Variations keys
+            setTwoLineListItemText(view.findViewById(R.id.variations), "Variations",
+                    crashInfo.variations == null ? "Not available"
+                                                 : crashInfo.variations.toString());
+            // Upload info
+            String uploadState = uploadStateString(crashInfo.uploadState);
+            String uploadInfo = null;
+            if (crashInfo.uploadState == UploadState.UPLOADED) {
+                uploadInfo = new Date(crashInfo.uploadTime).toString();
+                uploadInfo += "\nID: " + crashInfo.uploadId;
+            }
+            setTwoLineListItemText(view.findViewById(R.id.upload_status), uploadState, uploadInfo);
+
             Button button = view.findViewById(R.id.crash_report_button);
-
-            CrashInfo crashInfo = mCrashInfoList.get(position);
-            label.setText(getCrashInfoLabelText(crashInfo));
-            infoTextView.setText(crashInfoToString(crashInfo));
-
             // Report button is only clickable if the crash report is uploaded.
             if (crashInfo.uploadState == UploadState.UPLOADED) {
                 button.setEnabled(true);
@@ -123,70 +176,110 @@
             return view;
         }
 
-        private String getCrashInfoLabelText(CrashInfo crashInfo) {
-            String status =
-                    crashInfo.uploadState == null ? "UNKOWN" : crashInfo.uploadState.toString();
-            return String.format(Locale.US, "Crash (%s) - [%s]", crashInfo.localId, status);
+        @Override
+        public boolean isChildSelectable(int groupPosition, int childPosition) {
+            return true;
         }
 
-        /**
-         * Convert CrashInfo object to a string to show it in a TexView.
-         * Field name is in BOLD while the value is not.
-         */
-        private SpannableStringBuilder crashInfoToString(CrashInfo crashInfo) {
-            SpannableStringBuilder builder = new SpannableStringBuilder();
-            if (crashInfo.uploadId != null) {
-                builder.append("upload ID: ", new StyleSpan(android.graphics.Typeface.BOLD), 0)
-                        .append(crashInfo.uploadId)
-                        .append("\n");
-            }
-            if (crashInfo.uploadTime >= 0) {
-                builder.append("upload time: ", new StyleSpan(android.graphics.Typeface.BOLD), 0)
-                        .append(new Date(crashInfo.uploadTime).toString())
-                        .append("\n");
-            }
-            if (crashInfo.captureTime >= 0) {
-                builder.append("capture time: ", new StyleSpan(android.graphics.Typeface.BOLD), 0)
-                        .append(new Date(crashInfo.captureTime).toString())
-                        .append("\n");
-            }
-            if (crashInfo.packageName != null) {
-                builder.append("app package name: ", new StyleSpan(android.graphics.Typeface.BOLD),
-                               0)
-                        .append(crashInfo.packageName)
-                        .append("\n");
-            }
-            if (crashInfo.variations != null) {
-                builder.append("variations: ", new StyleSpan(android.graphics.Typeface.BOLD), 0)
-                        .append(crashInfo.variations.toString())
-                        .append("\n");
-            }
-            return builder;
+        @Override
+        public Object getGroup(int groupPosition) {
+            return mCrashInfoList.get(groupPosition);
         }
 
-        // Build a report uri to open an issue on https://bugs.chromium.org/p/chromium/issues/entry.
-        // It uses WebView Bugs Template and adds "User-Submitted" Label.
-        // It adds the upload id at the end of the template and populates the Application package
-        // name field.
-        // TODO(https://crbug.com/991594) populate more fields in the template.
-        private Uri getReportUri(CrashInfo crashInfo) {
-            return new Uri.Builder()
-                    .scheme("https")
-                    .authority("bugs.chromium.org")
-                    .path("/p/chromium/issues/entry")
-                    .appendQueryParameter("template", "Webview+Bugs")
-                    .appendQueryParameter("comment",
-                            String.format(Locale.US, CRASH_REPORT_TEMPLATE, crashInfo.uploadId))
-                    .appendQueryParameter("labels", "User-Submitted")
-                    .build();
+        @Override
+        public Object getChild(int groupPosition, int childPosition) {
+            return mCrashInfoList.get(groupPosition);
+        }
+
+        @Override
+        public long getGroupId(int groupPosition) {
+            // Hash code of local id is unique per crash info object.
+            return ((CrashInfo) getGroup(groupPosition)).localId.hashCode();
+        }
+
+        @Override
+        public long getChildId(int groupPosition, int childPosition) {
+            // Child ID refers to a piece of info in a particular crash report. It is stable
+            // since we don't change the order we show this info in runtime and currently we show
+            // all information in only one child item.
+            return childPosition;
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            // Stable IDs mean both getGroupId and getChildId return stable IDs for each group and
+            // child i.e: an ID always refers to the same object. See getGroupId and getChildId for
+            // why the IDs are stable.
+            return true;
+        }
+
+        @Override
+        public int getChildrenCount(int groupPosition) {
+            // Crash info is shown in one child item.
+            return 1;
+        }
+
+        @Override
+        public int getGroupCount() {
+            return mCrashInfoList.size();
+        }
+    }
+
+    // Build a report uri to open an issue on https://bugs.chromium.org/p/chromium/issues/entry.
+    // It uses WebView Bugs Template and adds "User-Submitted" Label.
+    // It adds the upload id at the end of the template and populates the Application package
+    // name field.
+    // TODO(https://crbug.com/991594) populate more fields in the template.
+    private static Uri getReportUri(CrashInfo crashInfo) {
+        return new Uri.Builder()
+                .scheme("https")
+                .authority("bugs.chromium.org")
+                .path("/p/chromium/issues/entry")
+                .appendQueryParameter("template", "Webview+Bugs")
+                .appendQueryParameter("comment",
+                        String.format(Locale.US, CRASH_REPORT_TEMPLATE, crashInfo.uploadId))
+                .appendQueryParameter("labels", "User-Submitted")
+                .build();
+    }
+
+    private static String uploadStateString(UploadState uploadState) {
+        switch (uploadState) {
+            case UPLOADED:
+                return "Uploaded";
+            case PENDING:
+            case PENDING_USER_REQUESTED:
+                return "Pending upload";
+            case SKIPPED:
+                return "Skipped upload";
+        }
+        return null;
+    }
+
+    // Helper method to find and set text for two line list item. If a null String is passed, the
+    // relevant TextView will be hidden.
+    private static void setTwoLineListItemText(
+            @NonNull View view, @Nullable String title, @Nullable String subtitle) {
+        TextView titleView = view.findViewById(android.R.id.text1);
+        TextView subtitleView = view.findViewById(android.R.id.text2);
+        if (titleView != null) {
+            titleView.setVisibility(View.VISIBLE);
+            titleView.setText(title);
+        } else {
+            titleView.setVisibility(View.GONE);
+        }
+        if (subtitle != null) {
+            subtitleView.setVisibility(View.VISIBLE);
+            subtitleView.setText(subtitle);
+        } else {
+            subtitleView.setVisibility(View.GONE);
         }
     }
 
     private void updateCrashesList() {
-        List<CrashInfo> crashesList = mCrashCollector.loadCrashesInfo(MAX_CRASHES_NUMBER);
-        mCrashListView.setAdapter(new CrashListAdapter(crashesList));
-
-        mCrashesSummaryView.setText(String.format(Locale.US, "Crashes (%d)", crashesList.size()));
+        mCrashInfoList = mCrashCollector.loadCrashesInfo(MAX_CRASHES_NUMBER);
+        mCrashListViewAdapter.notifyDataSetChanged();
+        mCrashesSummaryView.setText(
+                String.format(Locale.US, "Crashes (%d)", mCrashInfoList.size()));
     }
 
     @Override
diff --git a/android_webview/apk/java/src/org/chromium/android_webview/devui/MainActivity.java b/android_webview/apk/java/src/org/chromium/android_webview/devui/MainActivity.java
index 2ff87b08..6f1e6fc 100644
--- a/android_webview/apk/java/src/org/chromium/android_webview/devui/MainActivity.java
+++ b/android_webview/apk/java/src/org/chromium/android_webview/devui/MainActivity.java
@@ -94,7 +94,7 @@
         private final InfoItem[] mItems;
 
         public InfoListAdapter(InfoItem[] items) {
-            super(MainActivity.this, android.R.layout.simple_list_item_2, items);
+            super(MainActivity.this, R.layout.two_line_list_item, items);
             mItems = items;
         }
 
@@ -103,7 +103,7 @@
             // If the the old view is already created then reuse it, else create a new one by layout
             // inflation.
             if (view == null) {
-                view = getLayoutInflater().inflate(android.R.layout.simple_list_item_2, null, true);
+                view = getLayoutInflater().inflate(R.layout.two_line_list_item, null, true);
             }
 
             InfoItem item = mItems[position];
diff --git a/android_webview/java/src/org/chromium/android_webview/FindAddress.java b/android_webview/java/src/org/chromium/android_webview/FindAddress.java
index 781f1ec..fcfc512 100644
--- a/android_webview/java/src/org/chromium/android_webview/FindAddress.java
+++ b/android_webview/java/src/org/chromium/android_webview/FindAddress.java
@@ -250,8 +250,9 @@
     private static boolean checkHouseNumber(String houseNumber) {
         // Make sure that there are at most 5 digits.
         int digitCount = 0;
-        for (int i = 0; i < houseNumber.length(); ++i)
+        for (int i = 0; i < houseNumber.length(); ++i) {
             if (Character.isDigit(houseNumber.charAt(i))) ++digitCount;
+        }
         if (digitCount > 5) return false;
 
         // Make sure that any ordinals are valid.
@@ -324,8 +325,9 @@
         if (stateMatch == null) return false;
         // Work out the index of the state, based on which group matched.
         int stateIndex = stateMatch.groupCount();
-        while (stateIndex > 0)
+        while (stateIndex > 0) {
             if (stateMatch.group(stateIndex--) != null) break;
+        }
         return sZipCodeRe.matcher(zipCode).matches()
                 && sStateZipCodeRanges[stateIndex].matches(zipCode);
     }
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index 3b7512b1..8175c905 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -184,8 +184,8 @@
     // WebView isn't compatible with OOP-D.
     features.DisableIfNotSet(::features::kVizDisplayCompositor);
 
-    // WebView does not support AndroidOverlay yet for video overlays.
-    features.DisableIfNotSet(media::kUseAndroidOverlay);
+    // WebView does not support overlay fullscreen yet for video overlays.
+    features.DisableIfNotSet(media::kOverlayFullscreenVideo);
 
     // WebView doesn't support embedding CompositorFrameSinks which is needed
     // for UseSurfaceLayerForVideo feature. https://crbug.com/853832
diff --git a/ash/accelerators/accelerator_confirmation_dialog.cc b/ash/accelerators/accelerator_confirmation_dialog.cc
index cd0d2cf..1899c456 100644
--- a/ash/accelerators/accelerator_confirmation_dialog.cc
+++ b/ash/accelerators/accelerator_confirmation_dialog.cc
@@ -40,16 +40,14 @@
   AddChildView(std::make_unique<views::Label>(
       l10n_util::GetStringUTF16(dialog_text_id)));
 
-  // Parent the dialog widget to the LockSystemModalContainer, or
-  // OverlayContainer to ensure that it will get displayed on respective
-  // lock/signin or OOBE screen.
+  // Parent the dialog widget to the LockSystemModalContainer to ensure that it
+  // will get displayed on respective lock/signin or OOBE screen.
   SessionControllerImpl* session_controller =
       Shell::Get()->session_controller();
   int container_id = kShellWindowId_SystemModalContainer;
-  if (session_controller->GetSessionState() ==
-      session_manager::SessionState::OOBE) {
-    container_id = kShellWindowId_OverlayContainer;
-  } else if (session_controller->IsUserSessionBlocked()) {
+  if (session_controller->IsUserSessionBlocked() ||
+      session_controller->GetSessionState() ==
+          session_manager::SessionState::OOBE) {
     container_id = kShellWindowId_LockSystemModalContainer;
   }
 
diff --git a/ash/app_list/app_list_controller_impl_unittest.cc b/ash/app_list/app_list_controller_impl_unittest.cc
index 01ab350..8a064458 100644
--- a/ash/app_list/app_list_controller_impl_unittest.cc
+++ b/ash/app_list/app_list_controller_impl_unittest.cc
@@ -28,6 +28,8 @@
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_view.h"
+#include "ash/shelf/shelf_view_test_api.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/system/unified/unified_system_tray_test_api.h"
@@ -473,6 +475,52 @@
 // Tests with both hotseat disabled and enabled.
 INSTANTIATE_TEST_SUITE_P(, HotseatAppListControllerImplTest, testing::Bool());
 
+// Verifies that the pinned app should still show after canceling the drag from
+// AppsGridView to Shelf (https://crbug.com/1021768).
+TEST_P(HotseatAppListControllerImplTest, DragItemFromAppsGridView) {
+  // Turn on the tablet mode.
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
+  EXPECT_TRUE(IsTabletMode());
+
+  Shelf* const shelf = GetPrimaryShelf();
+
+  // Add icons with the same app id to Shelf and AppsGridView respectively.
+  ShelfViewTestAPI shelf_view_test_api(shelf->GetShelfViewForTesting());
+  std::string app_id = shelf_view_test_api.AddItem(TYPE_PINNED_APP).app_id;
+  Shell::Get()->app_list_controller()->GetModel()->AddItem(
+      std::make_unique<AppListItem>(app_id));
+
+  AppsGridView* apps_grid_view = GetAppListView()
+                                     ->app_list_main_view()
+                                     ->contents_view()
+                                     ->GetAppsContainerView()
+                                     ->apps_grid_view();
+  AppListItemView* app_list_item_view =
+      test::AppsGridViewTestApi(apps_grid_view).GetViewAtIndex(GridIndex(0, 0));
+  views::View* shelf_icon_view =
+      shelf->GetShelfViewForTesting()->view_model()->view_at(0);
+
+  // Drag the app icon from AppsGridView to Shelf. Then move the icon back to
+  // AppsGridView before drag ends.
+  GetEventGenerator()->MoveMouseTo(
+      app_list_item_view->GetBoundsInScreen().CenterPoint());
+  GetEventGenerator()->PressLeftButton();
+  app_list_item_view->FireMouseDragTimerForTest();
+  GetEventGenerator()->MoveMouseTo(
+      shelf_icon_view->GetBoundsInScreen().CenterPoint());
+  GetEventGenerator()->MoveMouseTo(
+      apps_grid_view->GetBoundsInScreen().CenterPoint());
+  GetEventGenerator()->ReleaseLeftButton();
+
+  // The icon's opacity updates at the end of animation.
+  shelf_view_test_api.RunMessageLoopUntilAnimationsDone();
+
+  // The icon is pinned before drag starts. So the shelf icon should show in
+  // spite that drag is canceled.
+  EXPECT_TRUE(shelf_icon_view->GetVisible());
+  EXPECT_EQ(1.0f, shelf_icon_view->layer()->opacity());
+}
+
 // Tests for HomeScreenDelegate::GetInitialAppListItemScreenBoundsForWindow
 // implemtenation.
 TEST_P(HotseatAppListControllerImplTest, GetItemBoundsForWindow) {
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index 36af280..7eb8558 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -803,6 +803,10 @@
   layer()->SetFillsBoundsOpaquely(false);
 }
 
+void AppListItemView::FireMouseDragTimerForTest() {
+  mouse_drag_timer_.FireNow();
+}
+
 void AppListItemView::AnimationProgressed(const gfx::Animation* animation) {
   DCHECK(!is_folder_);
 
diff --git a/ash/app_list/views/app_list_item_view.h b/ash/app_list/views/app_list_item_view.h
index 49c865d4..5d38c4f6 100644
--- a/ash/app_list/views/app_list_item_view.h
+++ b/ash/app_list/views/app_list_item_view.h
@@ -136,6 +136,8 @@
   // Ensures this item view has its own layer.
   void EnsureLayer();
 
+  void FireMouseDragTimerForTest();
+
   bool is_folder() const { return is_folder_; }
 
  private:
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index f6ec4953..d88cc4a 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -4612,4 +4612,24 @@
   }
 }
 
+// Test that exiting mirror mode in tablet mode with a fullscreen window
+// does not cause a crash (crbug.com/1021662).
+TEST_F(DisplayManagerTest, ExitMirrorModeInTabletMode) {
+  // Simulate a tablet with and external display connected.
+  display::test::DisplayManagerTestApi(display_manager())
+      .SetFirstDisplayAsInternalDisplay();
+  UpdateDisplay("800x600,800x600");
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
+
+  // Create a window to force in-app shelf.
+  std::unique_ptr<aura::Window> window = CreateTestWindow();
+
+  // Exit mirror mode.
+  display_manager()->SetMirrorMode(display::MirrorMode::kOff, base::nullopt);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(display_manager()->IsInSoftwareMirrorMode());
+}
+
 }  // namespace ash
diff --git a/ash/home_screen/drag_window_from_shelf_controller_unittest.cc b/ash/home_screen/drag_window_from_shelf_controller_unittest.cc
index 6d1802a..57cf031 100644
--- a/ash/home_screen/drag_window_from_shelf_controller_unittest.cc
+++ b/ash/home_screen/drag_window_from_shelf_controller_unittest.cc
@@ -518,7 +518,7 @@
 
 // Test that if drag is cancelled, overview should be dismissed and other
 // hidden windows should restore to its previous visibility state.
-// Flaky on ChromeOS. https://crbug.com/1022319
+// TODO(crbug.com/1022319): flaky.
 TEST_F(DragWindowFromShelfControllerTest, DISABLED_CancelDragDismissOverview) {
   auto window3 = CreateTestWindow();
   auto window2 = CreateTestWindow();
diff --git a/ash/home_screen/home_screen_controller.cc b/ash/home_screen/home_screen_controller.cc
index 0147545..ca56b44 100644
--- a/ash/home_screen/home_screen_controller.cc
+++ b/ash/home_screen/home_screen_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/home_screen/home_screen_controller.h"
 
+#include <memory>
 #include <vector>
 
 #include "ash/home_screen/home_launcher_gesture_handler.h"
@@ -12,6 +13,7 @@
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
+#include "ash/scoped_animation_disabler.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
@@ -157,9 +159,6 @@
     return true;
   }
 
-  delegate_->OnHomeLauncherTargetPositionChanged(true /* showing */,
-                                                 display_id);
-
   // First minimize all inactive windows.
   const bool window_minimized =
       MinimizeAllWindows(windows, active_windows /*windows_to_ignore*/);
@@ -167,8 +166,26 @@
   // Animate currently active windows into the home screen - they will be
   // minimized by WindowTransformToHomeScreenAnimation when the transition
   // finishes.
-
   if (!active_windows.empty()) {
+    {
+      // Disable window animations before updating home launcher target
+      // position. Calling OnHomeLauncherTargetPositionChanged() can cause
+      // display work area update, and resulting cross-fade window bounds change
+      // animation can interfere with WindowTransformToHomeScreenAnimation
+      // visuals.
+      //
+      // TODO(https://crbug.com/1019531): This can be removed once transitions
+      // between in-app state and home do not cause work area updates.
+      std::vector<std::unique_ptr<ScopedAnimationDisabler>> animation_disablers;
+      for (auto* window : active_windows) {
+        animation_disablers.push_back(
+            std::make_unique<ScopedAnimationDisabler>(window));
+      }
+
+      delegate_->OnHomeLauncherTargetPositionChanged(true /* showing */,
+                                                     display_id);
+    }
+
     base::RepeatingClosure window_transforms_callback = base::BarrierClosure(
         active_windows.size(),
         base::BindOnce(&HomeScreenController::NotifyHomeLauncherTransitionEnded,
@@ -193,8 +210,6 @@
                                     : base::NullCallback());
       }
     }
-  } else {
-    delegate_->OnHomeLauncherAnimationComplete(true /*shown*/, display_id);
   }
 
   return window_minimized || !active_windows.empty();
diff --git a/ash/shelf/back_button.cc b/ash/shelf/back_button.cc
index 2f0bb5a..4871082 100644
--- a/ash/shelf/back_button.cc
+++ b/ash/shelf/back_button.cc
@@ -8,6 +8,8 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_focus_cycler.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -57,6 +59,8 @@
 void BackButton::ButtonPressed(views::Button* sender,
                                const ui::Event& event,
                                views::InkDrop* ink_drop) {
+  base::RecordAction(base::UserMetricsAction("AppList_BackButtonPressed"));
+
   // Send up event as well as down event as ARC++ clients expect this sequence.
   // TODO: Investigate if we should be using the current modifiers.
   aura::Window* root_window = GetWidget()->GetNativeWindow()->GetRootWindow();
diff --git a/ash/shelf/home_button.cc b/ash/shelf/home_button.cc
index 814f53c..775bc3f 100644
--- a/ash/shelf/home_button.cc
+++ b/ash/shelf/home_button.cc
@@ -14,6 +14,8 @@
 #include "ash/shell.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/logging.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -85,8 +87,14 @@
 void HomeButton::ButtonPressed(views::Button* sender,
                                const ui::Event& event,
                                views::InkDrop* ink_drop) {
-  Shell::Get()->metrics()->RecordUserMetricsAction(
-      UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON);
+  if (Shell::Get()->tablet_mode_controller()->InTabletMode()) {
+    base::RecordAction(
+        base::UserMetricsAction("AppList_HomeButtonPressedTablet"));
+  } else {
+    base::RecordAction(
+        base::UserMetricsAction("AppList_HomeButtonPressedClamshell"));
+  }
+
   const AppListShowSource show_source =
       event.IsShiftDown() ? kShelfButtonFullscreen : kShelfButton;
   OnPressed(show_source, event.time_stamp());
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 7a2e41e7..8815fd6 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -1351,11 +1351,23 @@
   if (drag_and_drop_item_pinned_ && cancel) {
     model_->UnpinAppWithID(drag_and_drop_shelf_id_.app_id);
   } else if (drag_and_drop_view) {
+    std::unique_ptr<gfx::AnimationDelegate> animation_delegate;
+
+    if (chromeos::switches::ShouldShowScrollableShelf()) {
+      // Resets the dragged view's opacity at the end of drag. Otherwise, if
+      // the app is already pinned on shelf before drag starts, the dragged view
+      // will be invisible when drag ends.
+      animation_delegate = std::make_unique<StartFadeAnimationDelegate>(
+          this, drag_and_drop_view);
+    }
+
     if (cancel) {
       // When a hosted drag gets canceled, the item can remain in the same slot
       // and it might have moved within the bounds. In that case the item need
       // to animate back to its correct location.
       AnimateToIdealBounds();
+      bounds_animator_->SetAnimationDelegate(drag_and_drop_view,
+                                             std::move(animation_delegate));
     } else {
       drag_and_drop_view->SetSize(pre_drag_and_drop_size_);
     }
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 48145fa..e916228 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -1440,6 +1440,45 @@
   EXPECT_FALSE(tooltip_manager->IsVisible());
 }
 
+// Verifies that the time of button press is recorded correctly in clamshell.
+TEST_F(ShelfViewTest, HomeButtonMetricsInClamshell) {
+  const HomeButton* home_button = shelf_view_->shelf_widget()->GetHomeButton();
+
+  // Make sure we're not showing the app list.
+  EXPECT_FALSE(home_button->IsShowingAppList());
+
+  base::UserActionTester user_action_tester;
+  ASSERT_EQ(0, user_action_tester.GetActionCount(
+                   "AppList_HomeButtonPressedClamshell"));
+
+  GetEventGenerator()->GestureTapAt(
+      home_button->GetBoundsInScreen().CenterPoint());
+  ASSERT_EQ(1, user_action_tester.GetActionCount(
+                   "AppList_HomeButtonPressedClamshell"));
+  EXPECT_TRUE(home_button->IsShowingAppList());
+}
+
+// Verifies that the time of button press is recorded correctly in tablet.
+TEST_F(ShelfViewTest, HomeButtonMetricsInTablet) {
+  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
+  const HomeButton* home_button = shelf_view_->shelf_widget()->GetHomeButton();
+
+  // Make sure we're not showing the app list.
+  std::unique_ptr<aura::Window> window = CreateTestWindow();
+  wm::ActivateWindow(window.get());
+  EXPECT_FALSE(home_button->IsShowingAppList());
+
+  base::UserActionTester user_action_tester;
+  ASSERT_EQ(
+      0, user_action_tester.GetActionCount("AppList_HomeButtonPressedTablet"));
+
+  GetEventGenerator()->GestureTapAt(
+      home_button->GetBoundsInScreen().CenterPoint());
+  ASSERT_EQ(
+      1, user_action_tester.GetActionCount("AppList_HomeButtonPressedTablet"));
+  EXPECT_TRUE(home_button->IsShowingAppList());
+}
+
 class HotseatShelfViewTest : public ShelfViewTest,
                              public testing::WithParamInterface<bool> {
  public:
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 6069039b..b56e16e 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -108,6 +108,8 @@
 
   bool CanActivate() const override;
   void ReorderChildLayers(ui::Layer* parent_layer) override;
+  void OnWidgetInitialized() override;
+
   void UpdateBackgroundBlur();
   void UpdateOpaqueBackground();
   void UpdateDragHandle();
@@ -169,8 +171,6 @@
   drag_handle_->layer()->SetRoundedCornerRadius(
       {radius, radius, radius, radius});
   drag_handle_->SetSize(kDragHandleSize);
-
-  UpdateOpaqueBackground();
 }
 
 ShelfWidget::DelegateView::~DelegateView() = default;
@@ -211,6 +211,10 @@
   parent_layer->StackAtBottom(&opaque_background_);
 }
 
+void ShelfWidget::DelegateView::OnWidgetInitialized() {
+  UpdateOpaqueBackground();
+}
+
 void ShelfWidget::DelegateView::UpdateBackgroundBlur() {
   // Blur only if the background is visible.
   const bool should_blur_background =
diff --git a/base/android/junit/src/org/chromium/base/task/AsyncTaskThreadTest.java b/base/android/junit/src/org/chromium/base/task/AsyncTaskThreadTest.java
index 55dc94bd..5072c573 100644
--- a/base/android/junit/src/org/chromium/base/task/AsyncTaskThreadTest.java
+++ b/base/android/junit/src/org/chromium/base/task/AsyncTaskThreadTest.java
@@ -4,11 +4,14 @@
 
 package org.chromium.base.task;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.support.test.filters.SmallTest;
 
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -40,6 +43,9 @@
     private static class BlockAndGetFeedDataTask extends AsyncTask<Boolean> {
         private LinkedBlockingQueue<Boolean> mIncomingQueue = new LinkedBlockingQueue<Boolean>();
         private LinkedBlockingQueue<Boolean> mOutgoingQueue = new LinkedBlockingQueue<Boolean>();
+        private LinkedBlockingQueue<Boolean> mInterruptedExceptionQueue =
+                new LinkedBlockingQueue<Boolean>();
+        private Boolean mPostExecuteResult;
 
         @Override
         protected Boolean doInBackground() {
@@ -51,6 +57,7 @@
         @Override
         protected void onPostExecute(Boolean result) {
             if (DEBUG) Log.i(TAG, "onPostExecute: " + result);
+            mPostExecuteResult = result;
         }
 
         public void feedData(Boolean data) {
@@ -61,6 +68,7 @@
             try {
                 return mIncomingQueue.poll(3, TimeUnit.SECONDS);
             } catch (InterruptedException e) {
+                mInterruptedExceptionQueue.add(true);
                 return false;
             }
         }
@@ -68,8 +76,17 @@
         public void blockUntilDoInBackgroundStarts() throws Exception {
             mOutgoingQueue.poll(3, TimeUnit.SECONDS);
         }
+
+        public Boolean getPostExecuteResult() {
+            return mPostExecuteResult;
+        }
+
+        public LinkedBlockingQueue<Boolean> getInterruptedExceptionQueue() {
+            return mInterruptedExceptionQueue;
+        }
     }
 
+    private final BlockAndGetFeedDataTask mTask = new BlockAndGetFeedDataTask();
     private final RoboExecutorService mRoboExecutorService = new RoboExecutorService();
     private final Scheduler mBackgroundScheduler = Robolectric.getBackgroundThreadScheduler();
 
@@ -85,42 +102,96 @@
         mBackgroundScheduler.pause();
     }
 
+    @After
+    public void tearDown() {
+        // No unexpected interrupted exception.
+        assertNull(mTask.getInterruptedExceptionQueue().poll());
+        Assert.assertTrue(mRoboExecutorService.shutdownNow().isEmpty());
+    }
+
     @Test
     @SmallTest
     public void testCancel_ReturnsFalseOnceTaskFinishes() throws Exception {
-        BlockAndGetFeedDataTask task = new BlockAndGetFeedDataTask();
         // This test requires robo executor service such that we can run
         // one background task.
-        task.executeOnExecutor(mRoboExecutorService);
+        mTask.executeOnExecutor(mRoboExecutorService);
 
-        // We feed the background thread, then cancel.
-        task.feedData(true);
+        // Ensure that the background thread is not blocked.
+        mTask.feedData(true);
+
         mBackgroundScheduler.runOneTask();
 
         // Cannot cancel. The task is already run.
-        assertFalse(task.cancel(false));
-        assertTrue(task.get());
+        assertFalse(mTask.cancel(false /* mayInterruptIfRunning */));
+        assertTrue(mTask.get());
+        assertEquals(Boolean.valueOf(true), mTask.getPostExecuteResult());
     }
 
     @Test
     @SmallTest
     public void testCancel_CanReturnTrueEvenAfterTaskStarts() throws Exception {
-        BlockAndGetFeedDataTask task = new BlockAndGetFeedDataTask();
-        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 
-        // The task has started.
-        task.blockUntilDoInBackgroundStarts();
+        // Wait until the task is started. Note that data is not yet fed.
+        mTask.blockUntilDoInBackgroundStarts();
         // This reflects FutureTask#cancel() behavior. Note that the task is
         // started but cancel can still return true.
-        assertTrue(task.cancel(false));
-        task.feedData(true);
+        assertTrue(mTask.cancel(false /* mayInterruptIfRunning */));
 
-        // get() will raise an exception although the task is run.
+        // Continue the task.
+        mTask.feedData(true);
+
+        // get() will raise an exception although the task is started.
         try {
-            task.get();
+            mTask.get();
             Assert.fail();
         } catch (CancellationException e) {
             // expected
         }
+        assertNull(mTask.getPostExecuteResult()); // onPostExecute did not run.
+    }
+
+    @Test
+    @SmallTest
+    public void testCancel_MayInterrupt_ReturnsFalseOnceTaskFinishes() throws Exception {
+        // This test requires robo executor service such that we can run
+        // one background task.
+        mTask.executeOnExecutor(mRoboExecutorService);
+
+        // Ensure that the background thread is not blocked.
+        mTask.feedData(true);
+
+        mBackgroundScheduler.runOneTask();
+
+        // Cannot cancel. The task is already run.
+        assertFalse(mTask.cancel(true /* mayInterruptIfRunning */));
+        assertTrue(mTask.get());
+        assertEquals(Boolean.valueOf(true), mTask.getPostExecuteResult());
+    }
+
+    @Test
+    @SmallTest
+    public void testCancel_MayInterrupt_TaskIsInterrupted() throws Exception {
+        mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+
+        // Wait until the task is started. Note that data is not yet fed.
+        mTask.blockUntilDoInBackgroundStarts();
+
+        // Cancel and interrupt the current task.
+        assertTrue(mTask.cancel(true /* mayInterruptIfRunning */));
+
+        // Ensure that the background thread is not blocked.
+        mTask.feedData(true);
+
+        // get() will raise an exception although the task is started.
+        try {
+            mTask.get();
+            Assert.fail();
+        } catch (CancellationException e) {
+            // expected
+        }
+        assertNull(mTask.getPostExecuteResult()); // onPostExecute did not run.
+        // Task was interrupted.
+        assertEquals(Boolean.valueOf(true), mTask.getInterruptedExceptionQueue().poll());
     }
 }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/AnnotationProcessingUtils.java b/base/test/android/javatests/src/org/chromium/base/test/util/AnnotationProcessingUtils.java
index 546ff24..d5daa44 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/AnnotationProcessingUtils.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/AnnotationProcessingUtils.java
@@ -192,7 +192,7 @@
      * Abstraction to hide differences between Class, Method and Description with regards to their
      * annotations and what should be analyzed next.
      */
-    private static abstract class AnnotatedNode {
+    private abstract static class AnnotatedNode {
         @Nullable
         abstract AnnotatedNode getParent();
 
diff --git a/build/android/docs/java_optimization.md b/build/android/docs/java_optimization.md
index b8ee80b..e0f0ec7 100644
--- a/build/android/docs/java_optimization.md
+++ b/build/android/docs/java_optimization.md
@@ -89,7 +89,7 @@
 Helpful links for deobfuscation:
 
 * [Internal bits about how mapping files are archived][proguard-site]
-* [More detaled deobfuscation instructions][proguard-doc]
+* [More detailed deobfuscation instructions][proguard-doc]
 * [Script for deobfuscating official builds][deob-official]
 
 [proguard-site]: http://goto.google.com/chrome-android-proguard
diff --git a/build/android/docs/java_toolchain.md b/build/android/docs/java_toolchain.md
index 6a432d3..b8edb46 100644
--- a/build/android/docs/java_toolchain.md
+++ b/build/android/docs/java_toolchain.md
@@ -17,7 +17,7 @@
 * `android_library()`
 * `android_java_prebuilt()`
 
-All targets names must end with "_java" so that the build system can distinguish
+All target names must end with "_java" so that the build system can distinguish
 them from non-java targets (or [other variations](https://cs.chromium.org/chromium/src/build/config/android/internal_rules.gni?rcl=ec2c17d7b4e424e060c3c7972842af87343526a1&l=20)).
 
 ## From Source to Final Dex
@@ -117,7 +117,7 @@
 
 This step happens only when targets have `supports_android = true`.
 
-* [d8] converts `.jar` files contain `.class` files into `.dex.jar` files
+* [d8] converts `.jar` files containing `.class` files into `.dex.jar` files
   containing `.dex` files.
 * Dexing is incremental - it will reuse dex'ed classes from a previous build if
   the corresponding `.class` file is unchanged.
@@ -287,7 +287,7 @@
 * Can be toggle on/off with code comments.
   ```java
   // clang-format off
-  ... non-formated code here ...
+  ... non-formatted code here ...
   // clang-format on
   ```
 * Does not work great for multiple annotations or on some lambda expressions,
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index cf7ba58..16110e5 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8897466458026657776
\ No newline at end of file
+8897419927696300224
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index e7f9e6f..ee69debb 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8897472768194582624
\ No newline at end of file
+8897425289159983392
\ No newline at end of file
diff --git a/cc/metrics/frame_sequence_tracker.cc b/cc/metrics/frame_sequence_tracker.cc
index 0bb4eec0..3b360a2 100644
--- a/cc/metrics/frame_sequence_tracker.cc
+++ b/cc/metrics/frame_sequence_tracker.cc
@@ -17,11 +17,13 @@
 
 // This macro is used with DCHECK to provide addition debug info.
 #if DCHECK_IS_ON()
-#define TRACKER_DCHECK_MSG                                \
-  " in " << kFrameSequenceTrackerTypeNames[this->type_]   \
-         << " tracker: " << frame_sequence_trace_ << " (" \
-         << frame_sequence_trace_.size() << ")";
+#define TRACKER_TRACE_STREAM frame_sequence_trace_
+#define TRACKER_DCHECK_MSG                                      \
+  " in " << kFrameSequenceTrackerTypeNames[this->type_]         \
+         << " tracker: " << frame_sequence_trace_.str() << " (" \
+         << frame_sequence_trace_.str().size() << ")";
 #else
+#define TRACKER_TRACE_STREAM EAT_STREAM_PARAMETERS
 #define TRACKER_DCHECK_MSG ""
 #endif
 
@@ -293,7 +295,7 @@
   if (ShouldIgnoreBeginFrameSource(args.source_id))
     return;
 
-  LogFrameSequenceTrace('b');
+  TRACKER_TRACE_STREAM << 'b';
   UpdateTrackedFrameData(&begin_impl_frame_data_, args.source_id,
                          args.sequence_number);
   impl_throughput_.frames_expected +=
@@ -311,7 +313,9 @@
   if (ShouldIgnoreBeginFrameSource(args.source_id))
     return;
 
-  LogFrameSequenceTrace('B');
+  TRACKER_TRACE_STREAM << 'B';
+  TRACKER_TRACE_STREAM << "(" << begin_main_frame_data_.previous_sequence << ","
+                       << args.sequence_number << ")";
   UpdateTrackedFrameData(&begin_main_frame_data_, args.source_id,
                          args.sequence_number);
   if (first_received_main_sequence_ == 0)
@@ -345,7 +349,7 @@
       origin_args.sequence_number >= first_received_main_sequence_) {
     if (last_submitted_main_sequence_ == 0 ||
         origin_args.sequence_number > last_submitted_main_sequence_) {
-      LogFrameSequenceTrace('S');
+      TRACKER_TRACE_STREAM << 'S';
 
       last_submitted_main_sequence_ = origin_args.sequence_number;
       main_frames_.push_back(frame_token);
@@ -381,7 +385,7 @@
     return;
   }
 
-  LogFrameSequenceTrace('P');
+  TRACKER_TRACE_STREAM << 'P';
 
   TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(
       "cc,benchmark", "FrameSequenceTracker", this, "FramePresented",
@@ -457,7 +461,7 @@
       ack.sequence_number < begin_impl_frame_data_.previous_sequence) {
     return;
   }
-  LogFrameSequenceTrace('n');
+  TRACKER_TRACE_STREAM << 'n';
   DCHECK_GT(impl_throughput_.frames_expected, 0u) << TRACKER_DCHECK_MSG;
   DCHECK_GT(impl_throughput_.frames_expected, impl_throughput_.frames_produced)
       << TRACKER_DCHECK_MSG;
@@ -482,7 +486,9 @@
     return;
   }
 
-  LogFrameSequenceTrace('N');
+  TRACKER_TRACE_STREAM << 'N';
+  TRACKER_TRACE_STREAM << "(" << begin_main_frame_data_.previous_sequence << ","
+                       << args.sequence_number << ")";
   DCHECK_GT(main_throughput_.frames_expected, 0u) << TRACKER_DCHECK_MSG;
   DCHECK_GT(main_throughput_.frames_expected, main_throughput_.frames_produced)
       << TRACKER_DCHECK_MSG;
diff --git a/cc/metrics/frame_sequence_tracker.h b/cc/metrics/frame_sequence_tracker.h
index 449dcc1..e3de825 100644
--- a/cc/metrics/frame_sequence_tracker.h
+++ b/cc/metrics/frame_sequence_tracker.h
@@ -182,12 +182,6 @@
     termination_status_ = TerminationStatus::kScheduledForTermination;
   }
 
-  inline void LogFrameSequenceTrace(unsigned char letter) {
-#if DCHECK_IS_ON()
-    frame_sequence_trace_.push_back(letter);
-#endif
-  }
-
   struct TrackedFrameData {
     // Represents the |BeginFrameArgs::source_id| and
     // |BeginFrameArgs::sequence_number| fields of the last processed
@@ -292,15 +286,15 @@
   const base::TimeDelta time_delta_to_report_ = base::TimeDelta::FromSeconds(5);
 
 #if DCHECK_IS_ON()
-  // This string represents a sequence of frame reporting activities on the
-  // current tracker. Each letter can be one of the following:
+  // This stringstream represents a sequence of frame reporting activities on
+  // the current tracker. Each letter can be one of the following:
   // {'B', 'N', 'b', 'n', 'S', 'P'}, where
   // 'B' = ReportBeginMainFrame(), 'N' = ReportMainFrameCausedNoDamage(),
   // 'b' = ReportBeginImplFrame(), 'n' = ReportMainFrameCausedNoDamage(),
   // 'S' = ReportSubmitFrame() and 'P' = ReportFramePresented().
   // Note that |frame_sequence_trace_| is only defined and populated
   // when DCHECK is on.
-  std::string frame_sequence_trace_;
+  std::stringstream frame_sequence_trace_;
 #endif
 };
 
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 3285707..69ca95b 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -125,6 +125,7 @@
   "javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java",
   "javatests/src/org/chromium/chrome/browser/customtabs/RequestThrottlerTest.java",
   "javatests/src/org/chromium/chrome/browser/customtabs/TrustedCdnPublisherUrlTest.java",
+  "javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java",
   "javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleLoaderTest.java",
   "javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModuleNavigationTest.java",
   "javatests/src/org/chromium/chrome/browser/customtabs/dynamicmodule/CustomTabsDynamicModulePostMessageTest.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
index b2a70219..9c7bb9f 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
@@ -74,8 +74,8 @@
  */
 class ManualFillingMediator extends EmptyTabObserver
         implements KeyboardAccessoryCoordinator.VisibilityDelegate, View.OnLayoutChangeListener {
-    static private final int MINIMAL_AVAILABLE_VERTICAL_SPACE = 128; // in DP.
-    static private final int MINIMAL_AVAILABLE_HORIZONTAL_SPACE = 180; // in DP.
+    private static final int MINIMAL_AVAILABLE_VERTICAL_SPACE = 128; // in DP.
+    private static final int MINIMAL_AVAILABLE_HORIZONTAL_SPACE = 180; // in DP.
 
     private PropertyModel mModel = ManualFillingProperties.createFillingModel();
     private WindowAndroid mWindowAndroid;
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingState.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingState.java
index 2594479..96c5323e 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingState.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingState.java
@@ -25,7 +25,7 @@
  * and its sheet for the {@link WebContents} it is attached to.
  */
 class ManualFillingState {
-    private final static int[] TAB_ORDER = {
+    private static final int[] TAB_ORDER = {
             AccessoryTabType.PASSWORDS,
             AccessoryTabType.CREDIT_CARDS,
             AccessoryTabType.ADDRESSES,
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabCoordinator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabCoordinator.java
index 694be144..6004fb3 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabCoordinator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AccessorySheetTabCoordinator.java
@@ -33,7 +33,7 @@
      * Provides the icon used for a sheet. Simplifies mocking in controller tests.
      */
     @VisibleForTesting
-    static public class IconProvider {
+    public static class IconProvider {
         private static Drawable sTestIcon;
 
         /**
diff --git a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
index 41969be..fc9c4544 100644
--- a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
+++ b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
@@ -1012,14 +1012,14 @@
         android:launchMode="singleTop"
         android:name="org.chromium.android_webview.devui.CrashesListActivity"
         android:process=":webview_apk"
-        android:theme="@android:style/Theme.DeviceDefault"/>
+        android:theme="@style/Theme.DevUi.DayNight"/>
     <activity
         android:label="WebView
         DevTools"
         android:launchMode="singleTask"
         android:name="org.chromium.android_webview.devui.MainActivity"
         android:process=":webview_apk"
-        android:theme="@android:style/Theme.DeviceDefault">
+        android:theme="@style/Theme.DevUi.DayNight">
       <intent-filter>
         <action android:name="com.android.webview.SHOW_DEV_UI"/>
         <category android:name="android.intent.category.DEFAULT"/>
diff --git a/chrome/android/java/res/drawable-hdpi/overlay_side_shadow.9.png b/chrome/android/java/res/drawable-hdpi/overlay_side_shadow.9.png
new file mode 100644
index 0000000..6d0b940
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/overlay_side_shadow.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/overlay_side_shadow.9.png b/chrome/android/java/res/drawable-mdpi/overlay_side_shadow.9.png
new file mode 100644
index 0000000..538d4e5c
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/overlay_side_shadow.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/overlay_side_shadow.9.png b/chrome/android/java/res/drawable-xhdpi/overlay_side_shadow.9.png
new file mode 100644
index 0000000..104c1fa
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/overlay_side_shadow.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/overlay_side_shadow.9.png b/chrome/android/java/res/drawable-xxhdpi/overlay_side_shadow.9.png
new file mode 100644
index 0000000..f28c3454
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/overlay_side_shadow.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/overlay_side_shadow.9.png b/chrome/android/java/res/drawable-xxxhdpi/overlay_side_shadow.9.png
new file mode 100644
index 0000000..a227ed9
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/overlay_side_shadow.9.png
Binary files differ
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java
index 07886697..be66ac09 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityBrowserControlsVisibilityManager.java
@@ -32,7 +32,7 @@
     private final TabObserver mTabObserver = new EmptyTabObserver() {
         @Override
         public void onSSLStateUpdated(Tab tab) {
-            updateBrowserControlsState(tab);
+            updateBrowserControlsState();
         }
     };
 
@@ -40,7 +40,7 @@
             new CustomTabActivityTabProvider.Observer() {
                 @Override
                 public void onTabSwapped(@NonNull Tab tab) {
-                    updateBrowserControlsState(tab);
+                    updateBrowserControlsState();
                 }
             };
 
@@ -60,20 +60,20 @@
         if (mInTwaMode == inTwaMode) return;
 
         mInTwaMode = inTwaMode;
-        updateBrowserControlsState(mTabProvider.getTab());
+        updateBrowserControlsState();
 
         if (mInTwaMode) {
-            mTabObserverRegistrar.registerTabObserver(mTabObserver);
+            mTabObserverRegistrar.registerActivityTabObserver(mTabObserver);
             mTabProvider.addObserver(mActivityTabObserver);
         } else {
-            mTabObserverRegistrar.unregisterTabObserver(mTabObserver);
+            mTabObserverRegistrar.unregisterActivityTabObserver(mTabObserver);
             mTabProvider.removeObserver(mActivityTabObserver);
         }
     }
 
-    private void updateBrowserControlsState(Tab tab) {
+    private void updateBrowserControlsState() {
         @BrowserControlsState
-        int newBrowserControlsState = computeBrowserControlsState(tab);
+        int newBrowserControlsState = computeBrowserControlsState(mTabProvider.getTab());
         if (mBrowserControlsState == newBrowserControlsState) return;
 
         mBrowserControlsState = newBrowserControlsState;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
index a1312b6..740e73a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
@@ -17,6 +17,7 @@
 import org.chromium.base.ApplicationStatus.ActivityStateListener;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.compositor.LayerTitleCache;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager.PanelPriority;
 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
@@ -364,6 +365,12 @@
         return mPanelShown;
     }
 
+    /** @return Whether we're using the new Overlay layout feature. */
+    public static boolean isNewLayout() {
+        return ChromeFeatureList.isInitialized()
+                && ChromeFeatureList.isEnabled(ChromeFeatureList.OVERLAY_NEW_LAYOUT);
+    }
+
     // ============================================================================================
     // ActivityStateListener
     // ============================================================================================
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
index 3cfd637..21ee2d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelBase.java
@@ -16,7 +16,6 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.PanelState;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
 import org.chromium.chrome.browser.util.MathUtils;
@@ -151,8 +150,8 @@
         mProgressBarHeight = PROGRESS_BAR_HEIGHT_DP;
         mBarBorderHeight = BAR_BORDER_HEIGHT_DP;
 
-        int bar_height_dimen = isNewLayout() ? R.dimen.overlay_panel_bar_height
-                                             : R.dimen.overlay_panel_bar_height_legacy;
+        int bar_height_dimen = OverlayPanel.isNewLayout() ? R.dimen.overlay_panel_bar_height
+                                                          : R.dimen.overlay_panel_bar_height_legacy;
         mBarHeight = mContext.getResources().getDimension(bar_height_dimen) * mPxToDp;
 
         final Resources resources = mContext.getResources();
@@ -164,7 +163,7 @@
         mButtonPaddingDps =
                 (int) (mPxToDp * resources.getDimension(R.dimen.overlay_panel_button_padding));
         mBarShadowVisible = true;
-        mPanelShadowVisible = !isNewLayout();
+        mPanelShadowVisible = true;
     }
 
     // ============================================================================================
@@ -355,12 +354,6 @@
         return Math.round(mMaximumHeight / mPxToDp);
     }
 
-    /** @return Whether we're using the new Overlay layout feature. */
-    private boolean isNewLayout() {
-        return ChromeFeatureList.isInitialized()
-                && ChromeFeatureList.isEnabled(ChromeFeatureList.OVERLAY_NEW_LAYOUT);
-    }
-
     /** @return The offset for the page content in DPs. */
     protected float getLayoutOffsetYDps() {
         return mLayoutYOffset * mPxToDp;
@@ -513,7 +506,7 @@
      * @return The opacity of the arrow icon.
      */
     public float getArrowIconOpacity() {
-        return isNewLayout() ? ARROW_ICON_OPACITY_TRANSPARENT : mArrowIconOpacity;
+        return OverlayPanel.isNewLayout() ? ARROW_ICON_OPACITY_TRANSPARENT : mArrowIconOpacity;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabBarControl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabBarControl.java
index 97fab76..b86f0aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabBarControl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabBarControl.java
@@ -8,6 +8,7 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
 
 /**
@@ -36,7 +37,7 @@
     public EphemeralTabBarControl(EphemeralTabPanel panel, Context context, ViewGroup container,
             DynamicResourceLoader loader) {
         mTitle = new EphemeralTabTitleControl(panel, context, container, loader);
-        mCaption = EphemeralTabPanel.isNewLayout() || panel.canPromoteToNewTab()
+        mCaption = OverlayPanel.isNewLayout() || panel.canPromoteToNewTab()
                 ? new EphemeralTabCaptionControl(panel, context, container, loader)
                 : null;
         mTextLayerMinHeight =
@@ -65,7 +66,7 @@
      * @param percentage The percentage to the more opened state.
      */
     public void updateForCloseOrPeek(float percentage) {
-        if (EphemeralTabPanel.isNewLayout()) {
+        if (OverlayPanel.isNewLayout()) {
             updateForMaximize(SOLID_OPAQUE);
         } else {
             if (percentage == SOLID_OPAQUE) updateForMaximize(SOLID_TRANSPARENT);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCaptionControl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCaptionControl.java
index bee7048..0d210a1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCaptionControl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCaptionControl.java
@@ -14,6 +14,7 @@
 
 import org.chromium.base.Supplier;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelTextViewInflater;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
@@ -56,10 +57,10 @@
             DynamicResourceLoader resourceLoader) {
         super(panel, R.layout.ephemeral_tab_caption_view, R.id.ephemeral_tab_caption_view, context,
                 container, resourceLoader,
-                (EphemeralTabPanel.isNewLayout() ? R.dimen.overlay_panel_end_buttons_width
-                                                 : R.dimen.overlay_panel_padded_button_width),
-                (EphemeralTabPanel.isNewLayout() ? R.dimen.overlay_panel_end_buttons_width
-                                                 : R.dimen.overlay_panel_padded_button_width));
+                (OverlayPanel.isNewLayout() ? R.dimen.overlay_panel_end_buttons_width
+                                            : R.dimen.overlay_panel_padded_button_width),
+                (OverlayPanel.isNewLayout() ? R.dimen.overlay_panel_end_buttons_width
+                                            : R.dimen.overlay_panel_padded_button_width));
         mUrl = panel::getUrl;
         mIconMargin = context.getResources().getDimensionPixelSize(
                 R.dimen.preview_tab_security_icon_size);
@@ -77,7 +78,7 @@
             if (mCaption == null) {
                 // |mCaption| gets initialized synchronously in |onFinishInflate|.
                 inflate();
-                if (EphemeralTabPanel.isNewLayout()) {
+                if (OverlayPanel.isNewLayout()) {
                     mCaption.setText(
                             UrlFormatter.formatUrlForSecurityDisplayOmitScheme(mUrl.get()));
                 } else {
@@ -139,7 +140,7 @@
      * @return The current percentage ranging from 0.0 to 1.0.
      */
     public float getAnimationPercentage() {
-        return EphemeralTabPanel.isNewLayout() ? 1.f : mAnimationPercentage;
+        return OverlayPanel.isNewLayout() ? 1.f : mAnimationPercentage;
     }
 
     /**
@@ -167,7 +168,7 @@
 
         View view = getView();
         mCaption = (TextView) view.findViewById(R.id.ephemeral_tab_caption);
-        if (EphemeralTabPanel.isNewLayout()) {
+        if (OverlayPanel.isNewLayout()) {
             ((MarginLayoutParams) mCaption.getLayoutParams()).leftMargin = mIconMargin;
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabPanel.java
index 2bf5ba7..a1a97db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabPanel.java
@@ -97,10 +97,6 @@
                 && !SysUtils.isLowEndDevice();
     }
 
-    static boolean isNewLayout() {
-        return ChromeFeatureList.isEnabled(ChromeFeatureList.OVERLAY_NEW_LAYOUT);
-    }
-
     /**
      * @param context The current Android {@link Context}.
      * @param updateHost The {@link LayoutUpdateHost} used to request updates in the Layout.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
index 9efc7ac6..5a1ac80 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
@@ -8,7 +8,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchBarBannerControl;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchBarControl;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchImageControl;
@@ -65,13 +65,11 @@
         int searchTermViewId = searchBarControl.getSearchTermViewId();
         int searchCaptionViewId = searchBarControl.getCaptionViewId();
 
-        int openNewTabIconId = (ChromeFeatureList.isEnabled(ChromeFeatureList.OVERLAY_NEW_LAYOUT)
-                                       && panel.canPromoteToNewTab())
+        int openNewTabIconId = OverlayPanel.isNewLayout() && panel.canPromoteToNewTab()
                 ? R.drawable.open_in_new_tab
                 : INVALID_RESOURCE_ID;
-        int dragHandlebarId = ChromeFeatureList.isEnabled(ChromeFeatureList.OVERLAY_NEW_LAYOUT)
-                ? R.drawable.drag_handlebar
-                : INVALID_RESOURCE_ID;
+        int dragHandlebarId =
+                OverlayPanel.isNewLayout() ? R.drawable.drag_handlebar : INVALID_RESOURCE_ID;
 
         int searchPromoViewId = promoControl.getViewId();
         boolean searchPromoVisible = promoControl.isVisible();
@@ -143,14 +141,16 @@
         WebContents panelWebContents = panel.getWebContents();
 
         int roundedBarTopResourceId =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.OVERLAY_NEW_LAYOUT)
-                ? R.drawable.top_round
-                : INVALID_RESOURCE_ID;
+                OverlayPanel.isNewLayout() ? R.drawable.top_round : INVALID_RESOURCE_ID;
         int separatorLineColor = panel.getSeparatorLineColor();
+        // The panel shadow goes all the way around in the old layout, but in the new layout
+        // the top_round resource also includes the shadow so we only need a side shadow.
+        // In either case there's just one shadow-only resource needed.
         int panelShadowResourceId = panel.getPanelShadowVisible()
-                ? R.drawable.contextual_search_bar_background
+                ? (OverlayPanel.isNewLayout() ? R.drawable.overlay_side_shadow
+                                              : R.drawable.contextual_search_bar_background)
                 : INVALID_RESOURCE_ID;
-        int closeIconResourceId = ChromeFeatureList.isEnabled(ChromeFeatureList.OVERLAY_NEW_LAYOUT)
+        int closeIconResourceId = OverlayPanel.isNewLayout()
                 ? INVALID_RESOURCE_ID
                 : ContextualSearchPanel.CLOSE_ICON_DRAWABLE_ID;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/EphemeralTabSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/EphemeralTabSceneLayer.java
index 1c31ef8..780714b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/EphemeralTabSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/EphemeralTabSceneLayer.java
@@ -10,7 +10,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
 import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTabBarControl;
 import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTabCaptionControl;
 import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTabPanel;
@@ -70,18 +70,19 @@
             EphemeralTabSceneLayerJni.get().createEphemeralTabLayer(mNativePtr,
                     EphemeralTabSceneLayer.this, resourceManager,
                     () -> panel.startFaviconAnimation(true));
-            int openInTabIconId = (ChromeFeatureList.isEnabled(ChromeFeatureList.OVERLAY_NEW_LAYOUT)
-                                          && panel.canPromoteToNewTab())
+            int openInTabIconId = (OverlayPanel.isNewLayout() && panel.canPromoteToNewTab())
                     ? R.drawable.open_in_new_tab
                     : INVALID_RESOURCE_ID;
-            int dragHandlebarId = ChromeFeatureList.isEnabled(ChromeFeatureList.OVERLAY_NEW_LAYOUT)
-                    ? R.drawable.drag_handlebar
-                    : INVALID_RESOURCE_ID;
-            int roundedBarTopId = ChromeFeatureList.isEnabled(ChromeFeatureList.OVERLAY_NEW_LAYOUT)
-                    ? R.drawable.top_round
-                    : INVALID_RESOURCE_ID;
+            int dragHandlebarId =
+                    OverlayPanel.isNewLayout() ? R.drawable.drag_handlebar : INVALID_RESOURCE_ID;
+            int roundedBarTopId =
+                    OverlayPanel.isNewLayout() ? R.drawable.top_round : INVALID_RESOURCE_ID;
+            // The panel shadow goes all the way around in the old layout, but in the new layout
+            // the top_round resource also includes the shadow so we only need a side shadow.
+            // In either case there's just one shadow-only resource needed.
             int panelShadowResourceId = panel.getPanelShadowVisible()
-                    ? R.drawable.contextual_search_bar_background
+                    ? (OverlayPanel.isNewLayout() ? R.drawable.overlay_side_shadow
+                                                  : R.drawable.contextual_search_bar_background)
                     : INVALID_RESOURCE_ID;
             EphemeralTabSceneLayerJni.get().setResourceIds(mNativePtr, EphemeralTabSceneLayer.this,
                     title.getViewId(), panelShadowResourceId, roundedBarTopId,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionPersisterImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionPersisterImpl.java
index e0ea35b..1f14f19 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionPersisterImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionPersisterImpl.java
@@ -7,7 +7,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchInteractionRecorder.Feature;
-import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 
 import java.util.Map;
@@ -71,40 +71,38 @@
     /** @param eventId An event ID to write to local storage. */
     private void writeEventIDToPersistantStorage(long eventId) {
         SharedPreferencesManager.getInstance().writeLong(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_EVENT_ID, eventId);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_EVENT_ID, eventId);
     }
 
     /** @return The event ID from local storage. */
     private long readEventIdFromPersistantStorage() {
         return SharedPreferencesManager.getInstance().readLong(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_EVENT_ID,
-                NO_EVENT_ID);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_EVENT_ID, NO_EVENT_ID);
     }
 
     /** @param bitEncodedValue An encoded outcome to write to local storage. */
     private void writeOutcomesToPersistantStorage(int bitEncodedValue) {
         SharedPreferencesManager.getInstance().writeInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_ENCODED_OUTCOMES,
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_ENCODED_OUTCOMES,
                 bitEncodedValue);
     }
 
     /** @return The encoded outcome from local storage. */
     private int readOutcomesFromPersistantStorage() {
         return SharedPreferencesManager.getInstance().readInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_ENCODED_OUTCOMES);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_ENCODED_OUTCOMES);
     }
 
     /** Writes the current time stamp to local storage. */
     private void writeTimestampToPersistantStorage(long timestamp) {
         SharedPreferencesManager.getInstance().writeLong(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_TIMESTAMP,
-                timestamp);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_TIMESTAMP, timestamp);
     }
 
     /** @return The time stamp when we wrote the outcome to local storage. */
     private long readTimestampFromPersistantStorage() {
         return SharedPreferencesManager.getInstance().readLong(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_TIMESTAMP, 0);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_TIMESTAMP, 0);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
index a72e321a..c1ec9b5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
@@ -20,7 +20,7 @@
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial.ContextualSearchSetting;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial.ContextualSearchSwitch;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchSelectionController.SelectionType;
-import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.util.UrlConstants;
@@ -197,14 +197,13 @@
             if (promoTapCounter.isEnabled()) promoTapCounter.increment();
         }
         int tapsSinceOpen = mPreferencesManager.incrementInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_COUNT);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_COUNT);
         if (isUserUndecided()) {
             ContextualSearchUma.logTapsSinceOpenForUndecided(tapsSinceOpen);
         } else {
             ContextualSearchUma.logTapsSinceOpenForDecided(tapsSinceOpen);
         }
-        mPreferencesManager.incrementInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_ALL_TIME_TAP_COUNT);
+        mPreferencesManager.incrementInt(ChromePreferenceKeys.CONTEXTUAL_SEARCH_ALL_TIME_TAP_COUNT);
     }
 
     /**
@@ -213,9 +212,9 @@
     void updateCountersForOpen() {
         // Always completely reset the tap counters that accumulate only since the last open.
         mPreferencesManager.writeInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_COUNT, 0);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_COUNT, 0);
         mPreferencesManager.writeInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_QUICK_ANSWER_COUNT, 0);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_QUICK_ANSWER_COUNT, 0);
 
         // Disable the "promo tap" counter, but only if we're using the Opt-out onboarding.
         // For Opt-in, we never disable the promo tap counter.
@@ -224,11 +223,11 @@
 
             // Bump the total-promo-opens counter.
             int count = mPreferencesManager.incrementInt(
-                    ChromePreferenceManager.CONTEXTUAL_SEARCH_PROMO_OPEN_COUNT);
+                    ChromePreferenceKeys.CONTEXTUAL_SEARCH_PROMO_OPEN_COUNT);
             ContextualSearchUma.logPromoOpenCount(count);
         }
         mPreferencesManager.incrementInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_ALL_TIME_OPEN_COUNT);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_ALL_TIME_OPEN_COUNT);
     }
 
     /**
@@ -240,9 +239,9 @@
     void updateCountersForQuickAnswer(boolean wasActivatedByTap, boolean doesAnswer) {
         if (wasActivatedByTap && doesAnswer) {
             mPreferencesManager.incrementInt(
-                    ChromePreferenceManager.CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_QUICK_ANSWER_COUNT);
+                    ChromePreferenceKeys.CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_QUICK_ANSWER_COUNT);
             mPreferencesManager.incrementInt(
-                    ChromePreferenceManager.CONTEXTUAL_SEARCH_ALL_TIME_TAP_QUICK_ANSWER_COUNT);
+                    ChromePreferenceKeys.CONTEXTUAL_SEARCH_ALL_TIME_TAP_QUICK_ANSWER_COUNT);
         }
     }
 
@@ -343,10 +342,10 @@
         if (selectionType == SelectionType.TAP) {
             long currentTimeMillis = System.currentTimeMillis();
             long lastAnimatedTimeMillis = mPreferencesManager.readLong(
-                    ChromePreferenceManager.CONTEXTUAL_SEARCH_LAST_ANIMATION_TIME);
+                    ChromePreferenceKeys.CONTEXTUAL_SEARCH_LAST_ANIMATION_TIME);
             if (Math.abs(currentTimeMillis - lastAnimatedTimeMillis) > DateUtils.DAY_IN_MILLIS) {
                 mPreferencesManager.writeLong(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_LAST_ANIMATION_TIME,
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_LAST_ANIMATION_TIME,
                         currentTimeMillis);
                 return true;
             } else {
@@ -391,8 +390,7 @@
      */
     @VisibleForTesting
     int getPromoOpenCount() {
-        return mPreferencesManager.readInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_PROMO_OPEN_COUNT);
+        return mPreferencesManager.readInt(ChromePreferenceKeys.CONTEXTUAL_SEARCH_PROMO_OPEN_COUNT);
     }
 
     /**
@@ -401,7 +399,7 @@
     @VisibleForTesting
     int getTapCount() {
         return mPreferencesManager.readInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_COUNT);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_COUNT);
     }
 
     // --------------------------------------------------------------------------------------------
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
index f5c857d2..c64b2e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
@@ -62,9 +62,10 @@
     public ContextualSearchRankerLoggerImpl(
             ContextualSearchInteractionPersister interactionPersister) {
         mInteractionPersister = interactionPersister;
-        if (isEnabled())
+        if (isEnabled()) {
             mNativePointer = ContextualSearchRankerLoggerImplJni.get().init(
                     ContextualSearchRankerLoggerImpl.this);
+        }
     }
 
     /**
@@ -74,7 +75,7 @@
      *         not.
      */
     @VisibleForTesting
-    protected final static String outcomeName(@Feature int feature) {
+    protected static final String outcomeName(@Feature int feature) {
         switch (feature) {
             case Feature.OUTCOME_WAS_PANEL_OPENED:
                 return "OutcomeWasPanelOpened";
@@ -97,7 +98,7 @@
      *         expected to be logged.
      */
     @VisibleForTesting
-    protected final static String featureName(@Feature int feature) {
+    protected static final String featureName(@Feature int feature) {
         // NOTE: this list needs to be kept in sync with the white list in
         // predictor_config_definitions.cc and with ukm.xml!
         switch (feature) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/CtrSuppression.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/CtrSuppression.java
index b866b4f..447e094 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/CtrSuppression.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/CtrSuppression.java
@@ -6,7 +6,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 
 /**
@@ -149,14 +149,12 @@
      *         or we have never checked.
      */
     private boolean didWeekChange(int currentWeekNumber) {
-        if (mPreferenceManager.readInt(
-                    ChromePreferenceManager.CONTEXTUAL_SEARCH_CURRENT_WEEK_NUMBER)
+        if (mPreferenceManager.readInt(ChromePreferenceKeys.CONTEXTUAL_SEARCH_CURRENT_WEEK_NUMBER)
                 == currentWeekNumber) {
             return false;
         } else {
             mPreferenceManager.writeInt(
-                    ChromePreferenceManager.CONTEXTUAL_SEARCH_CURRENT_WEEK_NUMBER,
-                    currentWeekNumber);
+                    ChromePreferenceKeys.CONTEXTUAL_SEARCH_CURRENT_WEEK_NUMBER, currentWeekNumber);
             return true;
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/DisableablePromoTapCounter.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/DisableablePromoTapCounter.java
index bcb7c45..c9a5876 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/DisableablePromoTapCounter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/DisableablePromoTapCounter.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.contextualsearch;
 
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 
 /**
@@ -52,7 +52,7 @@
     private DisableablePromoTapCounter(SharedPreferencesManager prefsManager) {
         mPrefsManager = prefsManager;
         setRawCounter(prefsManager.readInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_TAP_TRIGGERED_PROMO_COUNT));
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_TAP_TRIGGERED_PROMO_COUNT));
     }
 
     /**
@@ -108,7 +108,7 @@
      */
     private void writeRawCounter() {
         mPrefsManager.writeInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_TAP_TRIGGERED_PROMO_COUNT, mCounter);
+                ChromePreferenceKeys.CONTEXTUAL_SEARCH_TAP_TRIGGERED_PROMO_COUNT, mCounter);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/EngagementSuppression.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/EngagementSuppression.java
index 297c2e62..f4e33231 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/EngagementSuppression.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/EngagementSuppression.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.contextualsearch;
 
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial.ContextualSearchSwitch;
-import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 
 /**
@@ -27,23 +27,22 @@
         // OR had a Quick Action presented but none taken and at least one ignored.
         boolean hadEntityImpression =
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_ENTITY_IMPRESSIONS_COUNT)
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_ENTITY_IMPRESSIONS_COUNT)
                 > 0;
-        boolean hadEntityOpen =
-                mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_ENTITY_OPENS_COUNT)
+        boolean hadEntityOpen = mPreferenceManager.readInt(
+                                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_ENTITY_OPENS_COUNT)
                 > 0;
         boolean hadQuickActionImpression =
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_QUICK_ACTION_IMPRESSIONS_COUNT)
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_QUICK_ACTION_IMPRESSIONS_COUNT)
                 > 0;
         boolean hadQuickActionTaken =
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_QUICK_ACTIONS_TAKEN_COUNT)
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_QUICK_ACTIONS_TAKEN_COUNT)
                 > 0;
         boolean hadQuickActionIgnored =
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_QUICK_ACTIONS_IGNORED_COUNT)
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_QUICK_ACTIONS_IGNORED_COUNT)
                 > 0;
         mIsConditionSatisfied = (hadEntityImpression && !hadEntityOpen)
                 || (hadQuickActionImpression && !hadQuickActionTaken && hadQuickActionIgnored);
@@ -58,13 +57,11 @@
     public static void registerQuickActionImpression(
             boolean wasPanelOpened, boolean wasActionClicked) {
         SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
-        prefs.incrementInt(
-                ChromePreferenceManager.CONTEXTUAL_SEARCH_QUICK_ACTION_IMPRESSIONS_COUNT);
+        prefs.incrementInt(ChromePreferenceKeys.CONTEXTUAL_SEARCH_QUICK_ACTION_IMPRESSIONS_COUNT);
         if (wasActionClicked) {
-            prefs.incrementInt(ChromePreferenceManager.CONTEXTUAL_SEARCH_QUICK_ACTIONS_TAKEN_COUNT);
+            prefs.incrementInt(ChromePreferenceKeys.CONTEXTUAL_SEARCH_QUICK_ACTIONS_TAKEN_COUNT);
         } else if (wasPanelOpened) {
-            prefs.incrementInt(
-                    ChromePreferenceManager.CONTEXTUAL_SEARCH_QUICK_ACTIONS_IGNORED_COUNT);
+            prefs.incrementInt(ChromePreferenceKeys.CONTEXTUAL_SEARCH_QUICK_ACTIONS_IGNORED_COUNT);
         }
     }
 
@@ -75,9 +72,9 @@
      */
     public static void registerContextualCardsImpression(boolean wasPanelOpened) {
         SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
-        prefs.incrementInt(ChromePreferenceManager.CONTEXTUAL_SEARCH_ENTITY_IMPRESSIONS_COUNT);
+        prefs.incrementInt(ChromePreferenceKeys.CONTEXTUAL_SEARCH_ENTITY_IMPRESSIONS_COUNT);
         if (wasPanelOpened) {
-            prefs.incrementInt(ChromePreferenceManager.CONTEXTUAL_SEARCH_ENTITY_OPENS_COUNT);
+            prefs.incrementInt(ChromePreferenceKeys.CONTEXTUAL_SEARCH_ENTITY_OPENS_COUNT);
         }
     }
 
@@ -95,29 +92,29 @@
         // These counters are updated in ContextualSearchPolcy when taps and opens are registered.
         logger.logFeature(ContextualSearchInteractionRecorder.Feature.TAP_COUNT,
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_ALL_TIME_TAP_COUNT));
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_ALL_TIME_TAP_COUNT));
         logger.logFeature(ContextualSearchInteractionRecorder.Feature.OPEN_COUNT,
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_ALL_TIME_OPEN_COUNT));
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_ALL_TIME_OPEN_COUNT));
         logger.logFeature(ContextualSearchInteractionRecorder.Feature.QUICK_ANSWER_COUNT,
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_ALL_TIME_TAP_QUICK_ANSWER_COUNT));
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_ALL_TIME_TAP_QUICK_ANSWER_COUNT));
         // These counters are updated in the #registerX static methods of this class.
         logger.logFeature(ContextualSearchInteractionRecorder.Feature.ENTITY_IMPRESSIONS_COUNT,
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_ENTITY_IMPRESSIONS_COUNT));
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_ENTITY_IMPRESSIONS_COUNT));
         logger.logFeature(ContextualSearchInteractionRecorder.Feature.ENTITY_OPENS_COUNT,
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_ENTITY_OPENS_COUNT));
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_ENTITY_OPENS_COUNT));
         logger.logFeature(
                 ContextualSearchInteractionRecorder.Feature.QUICK_ACTION_IMPRESSIONS_COUNT,
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_QUICK_ACTION_IMPRESSIONS_COUNT));
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_QUICK_ACTION_IMPRESSIONS_COUNT));
         logger.logFeature(ContextualSearchInteractionRecorder.Feature.QUICK_ACTIONS_TAKEN_COUNT,
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_QUICK_ACTIONS_TAKEN_COUNT));
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_QUICK_ACTIONS_TAKEN_COUNT));
         logger.logFeature(ContextualSearchInteractionRecorder.Feature.QUICK_ACTIONS_IGNORED_COUNT,
                 mPreferenceManager.readInt(
-                        ChromePreferenceManager.CONTEXTUAL_SEARCH_QUICK_ACTIONS_IGNORED_COUNT));
+                        ChromePreferenceKeys.CONTEXTUAL_SEARCH_QUICK_ACTIONS_IGNORED_COUNT));
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java
index 05029b2..6bc6b18 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.customtabs.content;
 
+import androidx.annotation.NonNull;
+
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
@@ -25,9 +27,37 @@
  */
 @ActivityScope
 public class TabObserverRegistrar extends EmptyTabModelObserver implements Destroyable {
+    private CustomTabActivityTabProvider mTabProvider;
     private final Set<PageLoadMetrics.Observer> mPageLoadMetricsObservers = new HashSet<>();
     private final Set<TabObserver> mTabObservers = new HashSet<>();
 
+    /** Observers for active tab. */
+    private final Set<TabObserver> mActivityTabObservers = new HashSet<>();
+
+    /**
+     * Caches the {@link CustomTabActivityTabProvider}'s active tab so that TabObservers can be
+     * removed from the previous active tab when the active tab changes.
+     */
+    private Tab mTabProviderTab;
+
+    private final CustomTabActivityTabProvider.Observer mActivityTabProviderObserver =
+            new CustomTabActivityTabProvider.Observer() {
+                @Override
+                public void onInitialTabCreated(@NonNull Tab tab, @TabCreationMode int mode) {
+                    onTabProviderTabUpdated();
+                }
+
+                @Override
+                public void onTabSwapped(@NonNull Tab tab) {
+                    onTabProviderTabUpdated();
+                }
+
+                @Override
+                public void onAllTabsClosed() {
+                    onTabProviderTabUpdated();
+                }
+            };
+
     /**
      * Registers a {@link PageLoadMetrics.Observer} to be managed by this Registrar.
      */
@@ -49,8 +79,33 @@
         mTabObservers.remove(observer);
     }
 
+    /**
+     * Registers a TabObserver for the CustomTabActivity's active tab. Changes the Tab that is
+     * being observed when the CustomTabActivity's active tab changes.
+     * Differs from {@link #registerTabObserver()} which observes all newly created tabs.
+     */
+    public void registerActivityTabObserver(TabObserver observer) {
+        mActivityTabObservers.add(observer);
+        Tab activeTab = mTabProvider.getTab();
+        if (activeTab != null) {
+            activeTab.addObserver(observer);
+        }
+    }
+
+    public void unregisterActivityTabObserver(TabObserver observer) {
+        mActivityTabObservers.remove(observer);
+        Tab activeTab = mTabProvider.getTab();
+        if (activeTab != null) {
+            activeTab.removeObserver(observer);
+        }
+    }
+
     @Inject
-    public TabObserverRegistrar(ActivityLifecycleDispatcher lifecycleDispatcher) {
+    public TabObserverRegistrar(ActivityLifecycleDispatcher lifecycleDispatcher,
+            CustomTabActivityTabProvider tabProvider) {
+        mTabProvider = tabProvider;
+        mTabProvider.addObserver(mActivityTabProviderObserver);
+
         lifecycleDispatcher.register(this);
     }
 
@@ -69,7 +124,7 @@
     @Override
     public void tabRemoved(Tab tab) {
         removePageLoadMetricsObservers();
-        removeTabObservers(tab);
+        removeTabObservers(tab, mTabObservers);
     }
 
     /**
@@ -78,7 +133,7 @@
      */
     public void addObserversForTab(Tab tab) {
         addPageLoadMetricsObservers();
-        addTabObservers(tab);
+        addTabObservers(tab, mTabObservers);
     }
 
     private void addPageLoadMetricsObservers() {
@@ -93,14 +148,27 @@
         }
     }
 
-    private void addTabObservers(Tab tab) {
-        for (TabObserver observer : mTabObservers) {
+    /**
+     * Called when the {@link CustomTabActivityTabProvider}'s active tab has changed.
+     */
+    private void onTabProviderTabUpdated() {
+        if (mTabProviderTab != null) {
+            removeTabObservers(mTabProviderTab, mActivityTabObservers);
+        }
+        mTabProviderTab = mTabProvider.getTab();
+        if (mTabProviderTab != null) {
+            addTabObservers(mTabProviderTab, mActivityTabObservers);
+        }
+    }
+
+    private void addTabObservers(Tab tab, Set<TabObserver> tabObservers) {
+        for (TabObserver observer : tabObservers) {
             tab.addObserver(observer);
         }
     }
 
-    private void removeTabObservers(Tab tab) {
-        for (TabObserver observer : mTabObservers) {
+    private void removeTabObservers(Tab tab, Set<TabObserver> tabObservers) {
+        for (TabObserver observer : tabObservers) {
             tab.removeObserver(observer);
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java
index cdfe253..36ff57e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java
@@ -20,6 +20,7 @@
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabFactory;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
 import org.chromium.chrome.browser.customtabs.content.CustomTabIntentHandler;
+import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar;
 import org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleCoordinator;
 import org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleToolbarController;
 import org.chromium.chrome.browser.customtabs.features.ImmersiveModeController;
@@ -56,6 +57,7 @@
     CustomTabCompositorContentInitializer resolveCompositorContentInitializer();
     CustomTabSessionHandler resolveSessionHandler();
     CustomTabActivityClientConnectionKeeper resolveConnectionKeeper();
+    TabObserverRegistrar resolveTabObserverRegistrar();
     TwaFinishHandler resolveTwaFinishHandler();
     ImmersiveModeController resolveImmersiveModeController();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java
index ff8c798..7165391a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java
@@ -4,8 +4,11 @@
 
 package org.chromium.chrome.browser.customtabs.features.toolbar;
 
+import androidx.annotation.NonNull;
+
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
+import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
 import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
@@ -28,16 +31,33 @@
     private final BrowserServicesIntentDataProvider mIntentDataProvider;
     private final ChromeActivity mActivity;
     private final TabObserverRegistrar mTabObserverRegistrar;
+    private final CustomTabActivityTabProvider mTabProvider;
+
+    /** Keeps track of the original color before the preview was shown. */
+    private int mOriginalColor;
+
+    /** True if a change to the toolbar color was made because of a preview. */
+    private boolean mTriggeredPreviewChange;
+
+    private final CustomTabActivityTabProvider.Observer mActivityTabObserver =
+            new CustomTabActivityTabProvider.Observer() {
+                @Override
+                public void onTabSwapped(@NonNull Tab tab) {
+                    updateColor(tab);
+                }
+            };
 
     @Inject
     public CustomTabToolbarColorController(ActivityLifecycleDispatcher lifecycleDispatcher,
             Lazy<ToolbarManager> toolbarManager,
             BrowserServicesIntentDataProvider intentDataProvider,
             ChromeActivity activity,
+            CustomTabActivityTabProvider tabProvider,
             TabObserverRegistrar tabObserverRegistrar) {
         mToolbarManager = toolbarManager;
         mIntentDataProvider = intentDataProvider;
         mActivity = activity;
+        mTabProvider = tabProvider;
         mTabObserverRegistrar = tabObserverRegistrar;
         lifecycleDispatcher.register(this);
     }
@@ -56,16 +76,11 @@
             manager.setShouldUpdateToolbarPrimaryColor(false);
         }
         observeTabToUpdateColor();
+        mTabProvider.addObserver(mActivityTabObserver);
     }
 
     private void observeTabToUpdateColor() {
-        mTabObserverRegistrar.registerTabObserver(new EmptyTabObserver() {
-            /** Keeps track of the original color before the preview was shown. */
-            private int mOriginalColor;
-
-            /** True if a change to the toolbar color was made because of a preview. */
-            private boolean mTriggeredPreviewChange;
-
+        mTabObserverRegistrar.registerActivityTabObserver(new EmptyTabObserver() {
             @Override
             public void onPageLoadFinished(Tab tab, String url) {
                 // Update the color when the page load finishes.
@@ -77,37 +92,37 @@
                 // Update the color on every new URL.
                 updateColor(tab);
             }
-
-            /**
-             * Updates the color of the Activity's CCT Toolbar. When a preview is shown, it should
-             * be reset to the default color. If the user later navigates away from that preview to
-             * a non-preview page, reset the color back to the original. This does not interfere
-             * with site-specific theme colors which are disabled when a preview is being shown.
-             */
-            private void updateColor(Tab tab) {
-                ToolbarManager manager = mToolbarManager.get();
-
-                // Record the original toolbar color in case we need to revert back to it later
-                // after a preview has been shown then the user navigates to another non-preview
-                // page.
-                if (mOriginalColor == 0) mOriginalColor = manager.getPrimaryColor();
-
-                final boolean shouldUpdateOriginal = manager.getShouldUpdateToolbarPrimaryColor();
-                manager.setShouldUpdateToolbarPrimaryColor(true);
-
-                if (tab.isPreview()) {
-                    final int defaultColor =
-                            ChromeColors.getDefaultThemeColor(mActivity.getResources(), false);
-                    manager.onThemeColorChanged(defaultColor, false);
-                    mTriggeredPreviewChange = true;
-                } else if (mOriginalColor != manager.getPrimaryColor() && mTriggeredPreviewChange) {
-                    manager.onThemeColorChanged(mOriginalColor, false);
-                    mTriggeredPreviewChange = false;
-                    mOriginalColor = 0;
-                }
-
-                manager.setShouldUpdateToolbarPrimaryColor(shouldUpdateOriginal);
-            }
         });
     }
+
+    /**
+     * Updates the color of the Activity's CCT Toolbar. When a preview is shown, it should
+     * be reset to the default color. If the user later navigates away from that preview to
+     * a non-preview page, reset the color back to the original. This does not interfere
+     * with site-specific theme colors which are disabled when a preview is being shown.
+     */
+    private void updateColor(Tab tab) {
+        ToolbarManager manager = mToolbarManager.get();
+
+        // Record the original toolbar color in case we need to revert back to it later
+        // after a preview has been shown then the user navigates to another non-preview
+        // page.
+        if (mOriginalColor == 0) mOriginalColor = manager.getPrimaryColor();
+
+        final boolean shouldUpdateOriginal = manager.getShouldUpdateToolbarPrimaryColor();
+        manager.setShouldUpdateToolbarPrimaryColor(true);
+
+        if (tab.isPreview()) {
+            final int defaultColor =
+                    ChromeColors.getDefaultThemeColor(mActivity.getResources(), false);
+            manager.onThemeColorChanged(defaultColor, false);
+            mTriggeredPreviewChange = true;
+        } else if (mOriginalColor != manager.getPrimaryColor() && mTriggeredPreviewChange) {
+            manager.onThemeColorChanged(mOriginalColor, false);
+            mTriggeredPreviewChange = false;
+            mOriginalColor = 0;
+        }
+
+        manager.setShouldUpdateToolbarPrimaryColor(shouldUpdateOriginal);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameUtils.java
index fd234d1..2ce252b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/rename/RenameUtils.java
@@ -11,7 +11,7 @@
  * A class containing some utility static methods for rename.
  */
 public class RenameUtils {
-    static private boolean sIsDisabledNativeForTesting;
+    private static boolean sIsDisabledNativeForTesting;
 
     /**
      * Determine the extension of a downloaded item.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
index 1dffa8c..bff5d25a4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
@@ -68,7 +68,7 @@
      * Returns whether prefetched suggestions metrics should be reported for a given category.
      * @param category given category to check.
      */
-    static public boolean shouldReportPrefetchedSuggestionsMetrics(@CategoryInt int category) {
+    public static boolean shouldReportPrefetchedSuggestionsMetrics(@CategoryInt int category) {
         return category == KnownCategories.ARTICLES && !NetworkChangeNotifier.isOnline();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
index a3380d6..6b51d56 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
@@ -78,10 +78,10 @@
             // children this way doesn't affect the views' bounds (including hit rect). But these
             // hit rects are preserved for the views that matter (the icon and the url actions
             // container).
-            int lateral_padding = getResources().getDimensionPixelOffset(
+            int lateralPadding = getResources().getDimensionPixelOffset(
                     R.dimen.sei_location_bar_lateral_padding);
-            setPaddingRelative(
-                    lateral_padding, getPaddingTop(), lateral_padding, getPaddingBottom());
+            setPaddingRelative(lateralPadding, getPaddingTop(), lateralPadding, getPaddingBottom());
+            updateUrlBarPaddingForSearchEngineIcon();
         }
 
         // This branch will be hit if the search engine logo experiment is enabled and we should
@@ -107,10 +107,12 @@
     private void updateUrlBarPaddingForSearchEngineIcon() {
         if (mUrlBar == null || mStatusView == null) return;
 
+        // TODO(crbug.com/1019019): Come up with a better solution for M80 or M81.
         int endPadding = 0;
-        if (SearchEngineLogoUtils.shouldShowSearchEngineLogo(mToolbarDataProvider.isIncognito())) {
+        if (SearchEngineLogoUtils.shouldShowSearchEngineLogo(mToolbarDataProvider.isIncognito())
+                && hasFocus()) {
             // This padding prevents the UrlBar's content from extending past the available space
-            // and into the next view.
+            // and into the next view while focused.
             endPadding = mStatusView.getEndPaddingPixelSizeForFocusState(true)
                     - mStatusView.getEndPaddingPixelSizeForFocusState(false);
         }
@@ -239,6 +241,7 @@
             setFocusable(false);
             setFocusableInTouchMode(false);
         }
+        updateUrlBarPaddingForSearchEngineIcon();
         setUrlFocusChangeInProgress(true);
         updateShouldAnimateIconChanges();
         super.onUrlFocusChange(hasFocus);
@@ -292,6 +295,7 @@
             setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN, false);
             getWindowAndroid().getKeyboardDelegate().showKeyboard(mUrlBar);
         }
+        updateUrlBarPaddingForSearchEngineIcon();
         mStatusViewCoordinator.onUrlAnimationFinished(hasFocus);
         setUrlFocusChangeInProgress(false);
         updateShouldAnimateIconChanges();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
index af82827..4e9a8e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
@@ -170,7 +170,12 @@
                         model.get(SuggestionCommonProperties.USE_DARK_COLORS)
                                 ? R.color.suggestion_url_dark_modern
                                 : R.color.suggestion_url_light_modern);
-                textLine2Direction = View.TEXT_DIRECTION_LTR;
+
+                if (suggestionType == OmniboxSuggestionType.CLIPBOARD_TEXT) {
+                    textLine2Direction = View.TEXT_DIRECTION_INHERIT;
+                } else {
+                    textLine2Direction = View.TEXT_DIRECTION_LTR;
+                }
             } else {
                 textLine2 = null;
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
index 5b0f8f8..cf42341b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceHost.java
@@ -332,10 +332,11 @@
             @DecodeVideoTask.DecodingResult int decodingResult) {
         switch (decodingResult) {
             case DecodeVideoTask.DecodingResult.SUCCESS:
-                if (bitmaps == null || bitmaps.size() == 0)
+                if (bitmaps == null || bitmaps.size() == 0) {
                     mFailedVideoDecodesUnknown++;
-                else
+                } else {
                     mSuccessfulVideoDecodes++;
+                }
                 break;
             case DecodeVideoTask.DecodingResult.FILE_ERROR:
                 mFailedVideoDecodesFile++;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
index 26ac805d..ca5cd42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -12,6 +12,8 @@
 
 /**
  * ChromePreferenceManager stores and retrieves various values in Android shared preferences.
+ *
+ * TODO(crbug.com/1022107): Finish moving constants to ChromePreferenceKeys.
  */
 public class ChromePreferenceManager {
     // For new int values with a default of 0, just document the key and its usage, and call
@@ -19,82 +21,6 @@
     // For new boolean values, document the key and its usage, call #readBoolean and #writeBoolean
     // directly. While calling #readBoolean, default value is required.
 
-    /** An all-time counter of taps that triggered the Contextual Search peeking panel. */
-    public static final String CONTEXTUAL_SEARCH_ALL_TIME_TAP_COUNT =
-            "contextual_search_all_time_tap_count";
-    /** An all-time counter of Contextual Search panel opens triggered by any gesture.*/
-    public static final String CONTEXTUAL_SEARCH_ALL_TIME_OPEN_COUNT =
-            "contextual_search_all_time_open_count";
-    /**
-     * The number of times a tap gesture caused a Contextual Search Quick Answer to be shown.
-     * Cumulative, starting at M-69.
-     */
-    public static final String CONTEXTUAL_SEARCH_ALL_TIME_TAP_QUICK_ANSWER_COUNT =
-            "contextual_search_all_time_tap_quick_answer_count";
-    /**
-     * The number of times that a tap triggered the Contextual Search panel to peek since the last
-     * time the panel was opened.  Note legacy string value without "open".
-     */
-    public static final String CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_COUNT =
-            "contextual_search_tap_count";
-    /**
-     * The number of times a tap gesture caused a Contextual Search Quick Answer to be shown since
-     * the last time the panel was opened.  Note legacy string value without "open".
-     */
-    public static final String CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_QUICK_ANSWER_COUNT =
-            "contextual_search_tap_quick_answer_count";
-    /**
-     * The number of times the Contextual Search panel was opened with the opt-in promo visible.
-     */
-    public static final String CONTEXTUAL_SEARCH_PROMO_OPEN_COUNT =
-            "contextual_search_promo_open_count";
-    /**
-     * The entity-data impressions count for Contextual Search, i.e. thumbnails shown in the Bar.
-     * Cumulative, starting at M-69.
-     */
-    public static final String CONTEXTUAL_SEARCH_ENTITY_IMPRESSIONS_COUNT =
-            "contextual_search_entity_impressions_count";
-    /**
-     * The entity-data opens count for Contextual Search, e.g. Panel opens following thumbnails
-     * shown in the Bar. Cumulative, starting at M-69.
-     */
-    public static final String CONTEXTUAL_SEARCH_ENTITY_OPENS_COUNT =
-            "contextual_search_entity_opens_count";
-    /**
-     * The Quick Action impressions count for Contextual Search, i.e. actions presented in the Bar.
-     * Cumulative, starting at M-69.
-     */
-    public static final String CONTEXTUAL_SEARCH_QUICK_ACTION_IMPRESSIONS_COUNT =
-            "contextual_search_quick_action_impressions_count";
-    /**
-     * The Quick Actions taken count for Contextual Search, i.e. phone numbers dialed and similar
-     * actions. Cumulative, starting at M-69.
-     */
-    public static final String CONTEXTUAL_SEARCH_QUICK_ACTIONS_TAKEN_COUNT =
-            "contextual_search_quick_actions_taken_count";
-    /**
-     * The Quick Actions ignored count, i.e. phone numbers available but not dialed.
-     * Cumulative, starting at M-69.
-     */
-    public static final String CONTEXTUAL_SEARCH_QUICK_ACTIONS_IGNORED_COUNT =
-            "contextual_search_quick_actions_ignored_count";
-    /**
-     * A user interaction event ID for interaction with Contextual Search, stored as a long.
-     */
-    public static final String CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_EVENT_ID =
-            "contextual_search_previous_interaction_event_id";
-    /**
-     * An encoded set of outcomes of user interaction with Contextual Search, stored as an int.
-     */
-    public static final String CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_ENCODED_OUTCOMES =
-            "contextual_search_previous_interaction_encoded_outcomes";
-    /**
-     * A timestamp indicating when we updated the user interaction with Contextual Search, stored
-     * as a long, with resolution in days.
-     */
-    public static final String CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_TIMESTAMP =
-            "contextual_search_previous_interaction_timestamp";
-
     /**
      * Whether the promotion for data reduction has been skipped on first invocation.
      * Default value is false.
@@ -117,13 +43,6 @@
     @Deprecated
     private static final String PREF_WEBSITE_SETTINGS_FILTER = "website_settings_filter";
 
-    public static final String CONTEXTUAL_SEARCH_TAP_TRIGGERED_PROMO_COUNT =
-            "contextual_search_tap_triggered_promo_count";
-    public static final String CONTEXTUAL_SEARCH_LAST_ANIMATION_TIME =
-            "contextual_search_last_animation_time";
-    public static final String CONTEXTUAL_SEARCH_CURRENT_WEEK_NUMBER =
-            "contextual_search_current_week_number";
-
     /**
      * Whether Chrome is set as the default browser.
      * Default value is false.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarManager.java
index 283650f..f03908c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/SnackbarManager.java
@@ -258,8 +258,9 @@
 
         if (AccessibilityUtil.isAccessibilityEnabled()) {
             durationMs *= 2;
-            if (durationMs < sAccessibilitySnackbarDurationMs)
+            if (durationMs < sAccessibilitySnackbarDurationMs) {
                 durationMs = sAccessibilitySnackbarDurationMs;
+            }
         }
 
         return durationMs;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java
index 885b52f..561f32a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/TracingNotificationManager.java
@@ -109,8 +109,9 @@
         // selecting the stop button, so choose a different message.
         AccessibilityManager am =
                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (am.isEnabled() && am.isTouchExplorationEnabled())
+        if (am.isEnabled() && am.isTouchExplorationEnabled()) {
             message = MSG_ACTIVE_NOTIFICATION_ACCESSIBILITY_MESSAGE;
+        }
 
         sTracingActiveNotificationBuilder =
                 createNotificationBuilder()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProvider.java
index 1fb2fa06..bda27e2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkIntentDataProvider.java
@@ -227,6 +227,16 @@
         int primaryMaskableIconId =
                 IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.MASKABLE_ICON_ID, 0);
 
+        // There are a few WebAPKs with bad shells (between v105 and v114) that would previously
+        // cause chrome to crash. The check below fixes it. See crbug.com/1019318#c8 for details.
+        if (shellApkVersion >= 105 && shellApkVersion <= 114) {
+            try {
+                ApiCompatibilityUtils.getDrawable(res, primaryMaskableIconId);
+            } catch (Resources.NotFoundException e) {
+                primaryMaskableIconId = 0;
+            }
+        }
+
         boolean isPrimaryIconMaskable =
                 primaryMaskableIconId != 0 && ShortcutHelper.doesAndroidSupportMaskableIcons();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java
new file mode 100644
index 0000000..46fbb1b
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrarTest.java
@@ -0,0 +1,114 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.customtabs.content;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.customtabs.CustomTabActivity;
+import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
+import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
+import org.chromium.chrome.browser.tab.EmptyTabObserver;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
+import org.chromium.chrome.browser.tabmodel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.test.util.DOMUtils;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.net.test.EmbeddedTestServer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link TabObserverRegistrar}.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class TabObserverRegistrarTest {
+    private static class LoadUrlTabObserver extends EmptyTabObserver {
+        private List<String> mUrlLoadRequests = new ArrayList<>();
+
+        List<String> getLoadUrlRequests() {
+            return mUrlLoadRequests;
+        }
+
+        @Override
+        public void onLoadUrl(Tab tab, LoadUrlParams params, int loadType) {
+            mUrlLoadRequests.add(params.getUrl());
+        }
+    }
+
+    @Rule
+    public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();
+
+    /**
+     * Tests that the TabObserver registered by
+     * {@link TabObserverRegistrar#registerActivityTabObserver()} switches when the active tab is
+     * switched.
+     */
+    @Test
+    @MediumTest
+    public void testObserveActiveTab() throws Throwable {
+        EmbeddedTestServer testServer = mCustomTabActivityTestRule.getTestServer();
+        final String windowOpenUrl =
+                testServer.getURL("/chrome/test/data/android/customtabs/test_window_open.html");
+        final String url1 = testServer.getURL("/chrome/test/data/android/about.html");
+        final String url2 = testServer.getURL("/chrome/test/data/android/simple.html");
+
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(
+                CustomTabsTestUtils.createMinimalCustomTabIntent(
+                        InstrumentationRegistry.getTargetContext(), windowOpenUrl));
+
+        // Register TabObserver via TabObserverRegistrar#registerActiveTabObserver()
+        CustomTabActivity customTabActivity = mCustomTabActivityTestRule.getActivity();
+        TabObserverRegistrar tabObserverRegistrar =
+                customTabActivity.getComponent().resolveTabObserverRegistrar();
+        LoadUrlTabObserver loadUrlTabObserver = new LoadUrlTabObserver();
+        tabObserverRegistrar.registerActivityTabObserver(loadUrlTabObserver);
+
+        final TabModelSelector tabSelector = customTabActivity.getTabModelSelector();
+        final Tab initialActiveTab = tabSelector.getCurrentTab();
+
+        // Open and wait for popup.
+        final CallbackHelper openTabHelper = new CallbackHelper();
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            tabSelector.getModel(false).addObserver(new EmptyTabModelObserver() {
+                @Override
+                public void didAddTab(Tab tab, @TabLaunchType int type) {
+                    openTabHelper.notifyCalled();
+                }
+            });
+        });
+        DOMUtils.clickNode(mCustomTabActivityTestRule.getWebContents(), "new_window");
+        openTabHelper.waitForCallback(0, 1);
+
+        assertEquals(2, tabSelector.getModel(false).getCount());
+        final Tab activeTab = tabSelector.getCurrentTab();
+        assertNotEquals(activeTab, initialActiveTab);
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            android.util.Log.e("ABCD", "loadUrl0");
+            initialActiveTab.loadUrl(new LoadUrlParams(url1));
+            activeTab.loadUrl(new LoadUrlParams(url2));
+        });
+
+        List<String> urlRequests = loadUrlTabObserver.getLoadUrlRequests();
+        assertEquals(1, urlRequests.size());
+        assertEquals(url2, urlRequests.get(0));
+    }
+}
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index 4aa999c..48daff6 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -85,6 +85,7 @@
     "photo_camera.icon",
     "picture_in_picture_control_background.icon",
     "picture_in_picture_alt.icon",
+    "protected_content.icon",
     "qrcode_generator.icon",
     "reader_mode.icon",
     "reload_touch.icon",
diff --git a/chrome/app/vector_icons/protected_content.icon b/chrome/app/vector_icons/protected_content.icon
new file mode 100644
index 0000000..eae3d0b
--- /dev/null
+++ b/chrome/app/vector_icons/protected_content.icon
@@ -0,0 +1,33 @@
+// 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.
+
+CANVAS_DIMENSIONS, 24,
+MOVE_TO, 21, 17,
+H_LINE_TO, 3,
+V_LINE_TO, 5,
+R_H_LINE_TO, 18,
+R_V_LINE_TO, 12,
+CLOSE,
+R_MOVE_TO, 0, -14,
+H_LINE_TO, 3,
+R_CUBIC_TO, -1.11f, 0, -2, 0.89f, -2, 2,
+R_V_LINE_TO, 12,
+R_ARC_TO, 2, 2, 0, 0, 0, 2, 2,
+R_H_LINE_TO, 5,
+R_V_LINE_TO, 2,
+R_H_LINE_TO, 8,
+R_V_LINE_TO, -2,
+R_H_LINE_TO, 5,
+R_CUBIC_TO, 1.1f, 0, 1.99f, -0.9f, 1.99f, -2,
+LINE_TO, 23, 5,
+R_ARC_TO, 2, 2, 0, 0, 0, -2, -2,
+CLOSE,
+MOVE_TO, 10, 15,
+R_LINE_TO, -4, -3.82f,
+R_LINE_TO, 1.41f, -1.35f,
+LINE_TO, 10, 12.3f,
+LINE_TO, 16.59f, 6,
+LINE_TO, 18, 7.36f,
+LINE_TO, 10, 15,
+CLOSE
diff --git a/chrome/browser/android/compositor/layer/overlay_panel_layer.cc b/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
index 5435137..96e2e5f 100644
--- a/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
+++ b/chrome/browser/android/compositor/layer/overlay_panel_layer.cc
@@ -83,10 +83,48 @@
   // Round values to avoid pixel gap between layers.
   bar_height = floor(bar_height);
 
+  // ---------------------------------------------------------------------------
+  // Content setup, to center in space below drag handle (when present).
+  // ---------------------------------------------------------------------------
   float bar_top_y = bar_offset_y;
   float bar_bottom = bar_top_y + bar_height;
 
   bool is_rtl = l10n_util::IsLayoutRtl();
+  bool is_new_layout = rounded_bar_top_resource_id_ != kInvalidResourceID;
+
+  int content_top_y = bar_top_y;
+  int content_height = bar_height;
+  int rounded_top_adjust = 0;
+  int rounded_shadow_top = 0;
+  gfx::Size rounded_bar_top_size;
+  gfx::PointF rounded_bar_top_position;
+
+  ui::NinePatchResource* rounded_bar_top_resource = nullptr;
+  if (is_new_layout) {
+    content_top_y += bar_margin_top;
+    content_height -= bar_margin_top;
+
+    rounded_bar_top_resource =
+        ui::NinePatchResource::From(resource_manager_->GetResource(
+            ui::ANDROID_RESOURCE_TYPE_STATIC, rounded_bar_top_resource_id_));
+
+    rounded_bar_top_size =
+        gfx::Size(rounded_bar_top_resource->size().width() -
+                      rounded_bar_top_resource->padding().width(),
+                  rounded_bar_top_resource->size().height() -
+                      rounded_bar_top_resource->padding().height());
+
+    // TODO(donnd): fix correctly.
+    const int vertical_fudge_factor = 2;  // Create an overlap to avoid a seam.
+    rounded_top_adjust = rounded_bar_top_size.height() - vertical_fudge_factor;
+    // This is the position of the side-shadows vertically.
+    // TODO(donnd): fix this so it's pixel perfect.
+    rounded_shadow_top = rounded_top_adjust;
+
+    rounded_bar_top_position =
+        gfx::PointF(-rounded_bar_top_resource->padding().x(),
+                    bar_top_y - rounded_top_adjust);
+  }
 
   // ---------------------------------------------------------------------------
   // Panel Shadow
@@ -94,6 +132,8 @@
   if (panel_shadow_resource_id_ != kInvalidResourceID) {
     if (panel_shadow_->parent() != layer_) {
       layer_->AddChild(panel_shadow_);
+      if (is_new_layout)
+        layer_->AddChild(panel_shadow_right_);
     }
     ui::NinePatchResource* panel_shadow_resource =
         ui::NinePatchResource::From(resource_manager_->GetResource(
@@ -102,56 +142,61 @@
 
     gfx::Size shadow_res_size = panel_shadow_resource->size();
     gfx::Rect shadow_res_padding = panel_shadow_resource->padding();
-    gfx::Size shadow_bounds(panel_width + shadow_res_size.width() -
-                                shadow_res_padding.size().width(),
-                            panel_height + shadow_res_size.height() -
-                                shadow_res_padding.size().height());
     panel_shadow_->SetUIResourceId(panel_shadow_resource->ui_resource()->id());
-    panel_shadow_->SetBorder(panel_shadow_resource->Border(shadow_bounds));
     panel_shadow_->SetAperture(panel_shadow_resource->aperture());
-    panel_shadow_->SetBounds(shadow_bounds);
-    gfx::PointF shadow_position(-shadow_res_padding.origin().x(),
-                                -shadow_res_padding.origin().y());
-    panel_shadow_->SetPosition(shadow_position);
-  }
+    if (is_new_layout) {
+      DCHECK(rounded_bar_top_resource);
 
-  // ---------------------------------------------------------------------------
-  // Content setup, to center in space below drag handle (when present).
-  // ---------------------------------------------------------------------------
-  int content_top_y = bar_top_y;
-  int content_height = bar_height;
-  int rounded_top_adjust = 0;
-  if (rounded_bar_top_resource_id_ != kInvalidResourceID) {
-    content_top_y += bar_margin_top;
-    content_height -= bar_margin_top;
+      gfx::Size shadow_bounds(shadow_res_size.width(),
+                              panel_height + shadow_res_size.height());
+      panel_shadow_->SetBounds(shadow_bounds);
+      panel_shadow_->SetBorder(panel_shadow_resource->Border(shadow_bounds));
+      // Position the top of the side shadow to the match the top of the
+      // rounded_bar_top shadow (which is indicated by its top padding).
+      // TODO(donnd): revisit side-shadow asset and positioning as discussed
+      // in https://crbug.com/1005975.
+      gfx::PointF shadow_position(-shadow_res_padding.size().width(),
+                                  bar_top_y - rounded_shadow_top);
+      panel_shadow_->SetPosition(shadow_position);
+
+      // Do the right hand side as a mirror of the left shadow.
+      panel_shadow_right_->SetUIResourceId(
+          panel_shadow_resource->ui_resource()->id());
+      panel_shadow_right_->SetAperture(panel_shadow_resource->aperture());
+      panel_shadow_right_->SetBounds(shadow_bounds);
+      panel_shadow_right_->SetBorder(
+          panel_shadow_resource->Border(shadow_bounds));
+      gfx::PointF right_shadow_position(
+          panel_width + shadow_res_padding.size().width(),
+          bar_top_y - rounded_shadow_top);
+      panel_shadow_right_->SetPosition(right_shadow_position);
+
+      // Flip it from the left side to the right side.
+      gfx::Transform flip_right_transform;
+      flip_right_transform.RotateAboutYAxis(180.0);
+      panel_shadow_right_->SetTransform(flip_right_transform);
+    } else {
+      gfx::Size shadow_bounds(panel_width + shadow_res_size.width() -
+                                  shadow_res_padding.size().width(),
+                              panel_height + shadow_res_size.height() -
+                                  shadow_res_padding.size().height());
+      panel_shadow_->SetBounds(shadow_bounds);
+      panel_shadow_->SetBorder(panel_shadow_resource->Border(shadow_bounds));
+      gfx::PointF shadow_position(-shadow_res_padding.origin().x(),
+                                  -shadow_res_padding.origin().y());
+      panel_shadow_->SetPosition(shadow_position);
+    }
   }
 
   // ---------------------------------------------------------------------------
   // Rounded Bar Top
   // ---------------------------------------------------------------------------
-  if (rounded_bar_top_resource_id_ != kInvalidResourceID) {
+  if (is_new_layout) {
+    DCHECK(rounded_bar_top_resource_id_ != kInvalidResourceID);
     rounded_bar_top_->SetIsDrawable(true);
 
-    ui::NinePatchResource* rounded_bar_top_resource =
-        ui::NinePatchResource::From(resource_manager_->GetResource(
-            ui::ANDROID_RESOURCE_TYPE_STATIC, rounded_bar_top_resource_id_));
-
     DCHECK(rounded_bar_top_resource);
 
-    const gfx::Size rounded_bar_top_size(
-        rounded_bar_top_resource->size().width() -
-            rounded_bar_top_resource->padding().width(),
-        rounded_bar_top_resource->size().height() -
-            rounded_bar_top_resource->padding().height());
-
-    // TODO(donnd): fix correctly.
-    const int vertical_fudge_factor = 2;  // Create an overlap to avoid a seam.
-    rounded_top_adjust = rounded_bar_top_size.height() - vertical_fudge_factor;
-
-    gfx::PointF rounded_bar_top_position(
-        -rounded_bar_top_resource->padding().x(),
-        bar_top_y - rounded_top_adjust);
-
     gfx::Size bounds(panel_width - rounded_bar_top_size.width(),
                      rounded_bar_top_resource->size().height());
 
@@ -426,6 +471,7 @@
     : resource_manager_(resource_manager),
       layer_(cc::Layer::Create()),
       panel_shadow_(cc::NinePatchLayer::Create()),
+      panel_shadow_right_(cc::NinePatchLayer::Create()),
       rounded_bar_top_(cc::NinePatchLayer::Create()),
       bar_background_(cc::SolidColorLayer::Create()),
       bar_text_(cc::UIResourceLayer::Create()),
@@ -444,10 +490,15 @@
   layer_->SetMasksToBounds(false);
   layer_->SetIsDrawable(true);
 
-  // Panel Shadow
+  // Panel Shadow -- shadow on the left side of the panel, or the whole panel
+  // when not using the new layout.
   panel_shadow_->SetIsDrawable(true);
   panel_shadow_->SetFillCenter(false);
 
+  // Panel Shadow Right -- shadow on the right side of the panel.
+  panel_shadow_right_->SetIsDrawable(true);
+  panel_shadow_right_->SetFillCenter(false);
+
   // Rounded Bar Top
   // Puts the layer near the bottom -- we'll decide if it's actually drawable
   // later.
diff --git a/chrome/browser/android/compositor/layer/overlay_panel_layer.h b/chrome/browser/android/compositor/layer/overlay_panel_layer.h
index 9305387..bfae8c41 100644
--- a/chrome/browser/android/compositor/layer/overlay_panel_layer.h
+++ b/chrome/browser/android/compositor/layer/overlay_panel_layer.h
@@ -82,6 +82,7 @@
   scoped_refptr<cc::Layer> layer_;
 
   scoped_refptr<cc::NinePatchLayer> panel_shadow_;
+  scoped_refptr<cc::NinePatchLayer> panel_shadow_right_;
   scoped_refptr<cc::NinePatchLayer> rounded_bar_top_;
   scoped_refptr<cc::SolidColorLayer> bar_background_;
   scoped_refptr<cc::UIResourceLayer> bar_text_;
diff --git a/chrome/browser/android/provider/chrome_browser_provider.cc b/chrome/browser/android/provider/chrome_browser_provider.cc
index 2e475ae9..1e9212a 100644
--- a/chrome/browser/android/provider/chrome_browser_provider.cc
+++ b/chrome/browser/android/provider/chrome_browser_provider.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/bookmarks/managed_bookmark_service_factory.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/history/android/sqlite_cursor.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/top_sites_factory.h"
@@ -37,7 +36,6 @@
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/managed/managed_bookmark_service.h"
-#include "components/favicon/core/favicon_service.h"
 #include "components/history/core/browser/android/android_history_types.h"
 #include "components/history/core/browser/top_sites.h"
 #include "components/search_engines/template_url.h"
@@ -45,7 +43,6 @@
 #include "content/public/browser/browser_thread.h"
 #include "ui/base/layout.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/favicon_size.h"
 
 using base::android::AttachCurrentThread;
 using base::android::CheckException;
@@ -315,7 +312,7 @@
 // ------------- Aynchronous requests classes ------------- //
 
 // Base class for asynchronous blocking requests to Chromium services.
-// Service: type of the service to use (e.g. HistoryService, FaviconService).
+// Service: type of the service to use (e.g. HistoryService).
 template <typename Service>
 class AsyncServiceRequest : protected BlockingUIThreadAsyncRequest {
  public:
@@ -797,8 +794,6 @@
   profile_ = g_browser_process->profile_manager()->GetLastUsedProfile();
   bookmark_model_ = BookmarkModelFactory::GetForBrowserContext(profile_);
   top_sites_ = TopSitesFactory::GetForProfile(profile_);
-  favicon_service_ = FaviconServiceFactory::GetForProfile(
-      profile_, ServiceAccessType::EXPLICIT_ACCESS),
   service_.reset(new AndroidHistoryProviderService(profile_));
 
   // Register as observer for service we are interested.
diff --git a/chrome/browser/android/provider/chrome_browser_provider.h b/chrome/browser/android/provider/chrome_browser_provider.h
index 87c31538..70527116 100644
--- a/chrome/browser/android/provider/chrome_browser_provider.h
+++ b/chrome/browser/android/provider/chrome_browser_provider.h
@@ -21,10 +21,6 @@
 class AndroidHistoryProviderService;
 class Profile;
 
-namespace favicon {
-class FaviconService;
-}
-
 namespace history {
 class TopSites;
 }
@@ -139,50 +135,6 @@
       const base::android::JavaParamRef<jstring>& selections,
       const base::android::JavaParamRef<jobjectArray>& selection_args);
 
-  // Custom provider API methods. ---------------------------------------------
-  jlong CreateBookmarksFolderOnce(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& title,
-      jlong parent_id);
-
-  void RemoveAllUserBookmarks(JNIEnv* env,
-                              const base::android::JavaParamRef<jobject>& obj);
-
-  base::android::ScopedJavaLocalRef<jobject> GetEditableBookmarkFolders(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj);
-
-  base::android::ScopedJavaLocalRef<jobject> GetBookmarkNode(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      jlong id,
-      jboolean get_parent,
-      jboolean get_children);
-
-  base::android::ScopedJavaLocalRef<jobject> GetMobileBookmarksFolder(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj);
-
-  jboolean IsBookmarkInMobileBookmarksBranch(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      jlong id);
-
-  jboolean BookmarkNodeExists(JNIEnv* env,
-                              const base::android::JavaParamRef<jobject>& obj,
-                              jlong id);
-
-  base::android::ScopedJavaLocalRef<jbyteArray> GetFaviconOrTouchIcon(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& url);
-
-  base::android::ScopedJavaLocalRef<jbyteArray> GetThumbnail(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jstring>& url);
-
  private:
   ~ChromeBrowserProvider() override;
 
@@ -223,7 +175,6 @@
   Profile* profile_;
   bookmarks::BookmarkModel* bookmark_model_;
   scoped_refptr<history::TopSites> top_sites_;
-  favicon::FaviconService* favicon_service_;
 
   std::unique_ptr<AndroidHistoryProviderService> service_;
 
diff --git a/chrome/browser/android/vr/BUILD.gn b/chrome/browser/android/vr/BUILD.gn
index aab2e058..3d62eaf 100644
--- a/chrome/browser/android/vr/BUILD.gn
+++ b/chrome/browser/android/vr/BUILD.gn
@@ -27,6 +27,7 @@
     "gl_browser_interface.h",
     "gvr_consent_helper_impl.cc",
     "gvr_consent_helper_impl.h",
+    "gvr_gamepad_data.h",
     "gvr_graphics_delegate.cc",
     "gvr_graphics_delegate.h",
     "gvr_input_delegate.cc",
diff --git a/chrome/browser/android/vr/gl_browser_interface.h b/chrome/browser/android/vr/gl_browser_interface.h
index ad0ffc4..620e43a 100644
--- a/chrome/browser/android/vr/gl_browser_interface.h
+++ b/chrome/browser/android/vr/gl_browser_interface.h
@@ -8,9 +8,9 @@
 #include <memory>
 
 #include "base/android/jni_weak_ref.h"
+#include "chrome/browser/android/vr/gvr_gamepad_data.h"
 #include "chrome/browser/vr/assets_load_status.h"
 #include "chrome/browser/vr/ui_test_input.h"
-#include "device/vr/android/gvr/gvr_gamepad_data_provider.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
 #include "ui/gfx/transform.h"
@@ -36,7 +36,7 @@
   virtual void SendRequestPresentReply(device::mojom::XRSessionPtr) = 0;
   virtual void DialogSurfaceCreated(jobject surface,
                                     gl::SurfaceTexture* texture) = 0;
-  virtual void UpdateGamepadData(device::GvrGamepadData) = 0;
+  virtual void UpdateGamepadData(GvrGamepadData) = 0;
   virtual void ToggleCardboardGamepad(bool enabled) = 0;
 };
 
diff --git a/chrome/browser/android/vr/gvr_gamepad_data.h b/chrome/browser/android/vr/gvr_gamepad_data.h
new file mode 100644
index 0000000..2961709
--- /dev/null
+++ b/chrome/browser/android/vr/gvr_gamepad_data.h
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_VR_GVR_GAMEPAD_DATA_H_
+#define CHROME_BROWSER_ANDROID_VR_GVR_GAMEPAD_DATA_H_
+
+#include "ui/gfx/geometry/quaternion.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+#include "ui/gfx/geometry/vector3d_f.h"
+
+namespace vr {
+
+// Subset of GVR controller data needed for the gamepad API. Filled in
+// by vr_shell's VrController.
+struct GvrGamepadData {
+  GvrGamepadData()
+      : timestamp(0),
+        is_touching(false),
+        controller_button_pressed(false),
+        right_handed(true),
+        connected(false) {}
+  int64_t timestamp;
+  gfx::Vector2dF touch_pos;
+  gfx::Quaternion orientation;
+  gfx::Vector3dF accel;
+  gfx::Vector3dF gyro;
+  bool is_touching;
+  bool controller_button_pressed;
+  bool right_handed;
+  bool connected;
+};
+
+}  // namespace vr
+#endif  // CHROME_BROWSER_ANDROID_VR_GVR_GAMEPAD_DATA_H_
diff --git a/chrome/browser/android/vr/gvr_input_delegate.cc b/chrome/browser/android/vr/gvr_input_delegate.cc
index 847b0779..a20748b 100644
--- a/chrome/browser/android/vr/gvr_input_delegate.cc
+++ b/chrome/browser/android/vr/gvr_input_delegate.cc
@@ -19,7 +19,7 @@
 namespace {
 constexpr gfx::Vector3dF kForwardVector = {0.0f, 0.0f, -1.0f};
 
-device::Gamepad CreateGamepad(const device::GvrGamepadData& data) {
+device::Gamepad CreateGamepad(const vr::GvrGamepadData& data) {
   device::Gamepad gamepad;
 
   // Unless the controller state is updated on a different thread,
@@ -81,7 +81,7 @@
                                         bool is_webxr_frame) {
   controller_->UpdateState(head_pose);
 
-  device::GvrGamepadData controller_data = controller_->GetGamepadData();
+  GvrGamepadData controller_data = controller_->GetGamepadData();
   if (!is_webxr_frame)
     controller_data.connected = false;
   browser_->UpdateGamepadData(controller_data);
diff --git a/chrome/browser/android/vr/vr_controller.cc b/chrome/browser/android/vr/vr_controller.cc
index 5900c57..89cd50c3 100644
--- a/chrome/browser/android/vr/vr_controller.cc
+++ b/chrome/browser/android/vr/vr_controller.cc
@@ -93,8 +93,8 @@
     controller_api_->Pause();
 }
 
-device::GvrGamepadData VrController::GetGamepadData() {
-  device::GvrGamepadData pad = {};
+GvrGamepadData VrController::GetGamepadData() {
+  GvrGamepadData pad = {};
   pad.connected = IsConnected();
   pad.timestamp = controller_state_->GetLastOrientationTimestamp();
 
diff --git a/chrome/browser/android/vr/vr_controller.h b/chrome/browser/android/vr/vr_controller.h
index e58b023..bfde641 100644
--- a/chrome/browser/android/vr/vr_controller.h
+++ b/chrome/browser/android/vr/vr_controller.h
@@ -10,10 +10,10 @@
 
 #include "base/macros.h"
 #include "base/time/time.h"
+#include "chrome/browser/android/vr/gvr_gamepad_data.h"
 #include "chrome/browser/android/vr/gvr_util.h"
 #include "chrome/browser/vr/gesture_detector.h"
 #include "chrome/browser/vr/platform_controller.h"
-#include "device/vr/android/gvr/gvr_gamepad_data_provider.h"
 #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/quaternion.h"
@@ -47,7 +47,7 @@
   // Must be called when the Activity gets OnPause().
   void OnPause();
 
-  device::GvrGamepadData GetGamepadData();
+  GvrGamepadData GetGamepadData();
 
   // Called once per frame to update controller state.
   void UpdateState(const gfx::Transform& head_pose);
diff --git a/chrome/browser/android/vr/vr_gl_thread.cc b/chrome/browser/android/vr/vr_gl_thread.cc
index 7169942a..0753c3d 100644
--- a/chrome/browser/android/vr/vr_gl_thread.cc
+++ b/chrome/browser/android/vr/vr_gl_thread.cc
@@ -118,7 +118,7 @@
                                 weak_vr_shell_, std::move(session)));
 }
 
-void VrGLThread::UpdateGamepadData(device::GvrGamepadData pad) {
+void VrGLThread::UpdateGamepadData(GvrGamepadData pad) {
   DCHECK(OnGlThread());
   main_thread_task_runner_->PostTask(
       FROM_HERE,
diff --git a/chrome/browser/android/vr/vr_gl_thread.h b/chrome/browser/android/vr/vr_gl_thread.h
index ffcf816..ca4d3bd 100644
--- a/chrome/browser/android/vr/vr_gl_thread.h
+++ b/chrome/browser/android/vr/vr_gl_thread.h
@@ -71,7 +71,7 @@
   void SendRequestPresentReply(device::mojom::XRSessionPtr) override;
   void DialogSurfaceCreated(jobject surface,
                             gl::SurfaceTexture* texture) override;
-  void UpdateGamepadData(device::GvrGamepadData) override;
+  void UpdateGamepadData(GvrGamepadData) override;
   void ToggleCardboardGamepad(bool enabled) override;
 
   // BrowserRendererBrowserInterface implementation (BrowserRenderer calling to
diff --git a/chrome/browser/android/vr/vr_shell.cc b/chrome/browser/android/vr/vr_shell.cc
index 2e3382a..c3e9c4d 100644
--- a/chrome/browser/android/vr/vr_shell.cc
+++ b/chrome/browser/android/vr/vr_shell.cc
@@ -1073,7 +1073,7 @@
   dialog_gesture_target_->DispatchInputEvent(std::move(event));
 }
 
-void VrShell::UpdateGamepadData(device::GvrGamepadData pad) {
+void VrShell::UpdateGamepadData(GvrGamepadData pad) {
   if (gvr_gamepad_source_active_ != pad.connected)
     ToggleGvrGamepad(pad.connected);
 }
diff --git a/chrome/browser/android/vr/vr_shell.h b/chrome/browser/android/vr/vr_shell.h
index ea465f02..842c56ee 100644
--- a/chrome/browser/android/vr/vr_shell.h
+++ b/chrome/browser/android/vr/vr_shell.h
@@ -16,6 +16,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string16.h"
 #include "base/timer/timer.h"
+#include "chrome/browser/android/vr/gvr_gamepad_data.h"
 #include "chrome/browser/ui/page_info/page_info_ui.h"
 #include "chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.h"
 #include "chrome/browser/vr/assets_load_status.h"
@@ -28,7 +29,6 @@
 #include "chrome/browser/vr/ui_initial_state.h"
 #include "chrome/browser/vr/ui_unsupported_mode.h"
 #include "content/public/browser/web_contents_observer.h"
-#include "device/vr/android/gvr/gvr_gamepad_data_provider.h"
 #include "device/vr/public/cpp/session_mode.h"
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/vr_device.h"
@@ -65,8 +65,7 @@
 
 // The native instance of the Java VrShell. This class is not threadsafe and
 // must only be used on the UI thread.
-class VrShell : device::GvrGamepadDataProvider,
-                VoiceResultDelegate,
+class VrShell : VoiceResultDelegate,
                 public ChromeLocationBarModelDelegate,
                 public PageInfoUI {
  public:
@@ -255,8 +254,7 @@
       device::mojom::VRDisplayInfoPtr display_info,
       device::mojom::XRRuntimeSessionOptionsPtr options);
 
-  // device::GvrGamepadDataProvider implementation.
-  void UpdateGamepadData(device::GvrGamepadData) override;
+  void UpdateGamepadData(GvrGamepadData);
 
   // ChromeLocationBarModelDelegate implementation.
   content::WebContents* GetActiveWebContents() const override;
diff --git a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
index fafa707..3276010 100644
--- a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
+++ b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
@@ -314,13 +314,15 @@
   DISALLOW_COPY_AND_ASSIGN(TestHost);
 };
 
-class ExtensionAppShimHandlerTest : public testing::Test {
+class ExtensionAppShimHandlerTestBase : public testing::Test {
  protected:
-  ExtensionAppShimHandlerTest()
-      : delegate_(new MockDelegate),
-        handler_(new TestingExtensionAppShimHandler(delegate_)),
-        profile_path_a_("Profile A"),
-        profile_path_b_("Profile B") {
+  ExtensionAppShimHandlerTestBase() {}
+
+  void SetUp() override {
+    delegate_ = new MockDelegate;
+    handler_.reset(new TestingExtensionAppShimHandler(delegate_));
+    profile_path_a_ = base::FilePath("Profile A");
+    profile_path_b_ = base::FilePath("Profile B");
     AppShimHostBootstrap::SetClient(handler_.get());
     bootstrap_aa_ = (new TestingAppShimHostBootstrap(
                          profile_path_a_, kTestAppIdA,
@@ -421,7 +423,7 @@
         .WillRepeatedly(Return());
   }
 
-  ~ExtensionAppShimHandlerTest() override {
+  ~ExtensionAppShimHandlerTestBase() override {
     host_aa_unique_.reset();
     host_ab_unique_.reset();
     host_bb_unique_.reset();
@@ -529,7 +531,34 @@
   scoped_refptr<const Extension> extension_b_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(ExtensionAppShimHandlerTest);
+  DISALLOW_COPY_AND_ASSIGN(ExtensionAppShimHandlerTestBase);
+};
+
+class ExtensionAppShimHandlerTest : public ExtensionAppShimHandlerTestBase {
+ public:
+  void SetUp() override {
+    scoped_features_.InitWithFeatures(
+        /*enabled_features=*/{},
+        /*disabled_features=*/{features::kAppShimMultiProfile});
+    ExtensionAppShimHandlerTestBase::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_features_;
+};
+
+class ExtensionAppShimHandlerTestMultiProfile
+    : public ExtensionAppShimHandlerTestBase {
+ public:
+  void SetUp() override {
+    scoped_features_.InitWithFeatures(
+        /*enabled_features=*/{features::kAppShimMultiProfile},
+        /*disabled_features=*/{});
+    ExtensionAppShimHandlerTestBase::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_features_;
 };
 
 TEST_F(ExtensionAppShimHandlerTest, LaunchProfileNotFound) {
@@ -866,12 +895,7 @@
   EXPECT_EQ(host_aa_.get(), handler_->FindHost(&profile_a_, kTestAppIdA));
 }
 
-TEST_F(ExtensionAppShimHandlerTest, MultiProfile) {
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitWithFeatures(
-      /*enabled_features=*/{features::kAppShimMultiProfile},
-      /*disabled_features=*/{});
-
+TEST_F(ExtensionAppShimHandlerTestMultiProfile, MultiProfile) {
   // Test with a bookmark app (host is shared).
   {
     // Create a host for profile A.
@@ -907,12 +931,7 @@
   }
 }
 
-TEST_F(ExtensionAppShimHandlerTest, MultiProfileShimLaunch) {
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitWithFeatures(
-      /*enabled_features=*/{features::kAppShimMultiProfile},
-      /*disabled_features=*/{});
-
+TEST_F(ExtensionAppShimHandlerTestMultiProfile, MultiProfileShimLaunch) {
   delegate_->SetHostForCreate(std::move(host_aa_unique_));
   ShimLaunchedCallback launched_callback;
   delegate_->SetCaptureShimLaunchedCallback(&launched_callback);
@@ -939,12 +958,7 @@
   EXPECT_TRUE(terminated_callback);
 }
 
-TEST_F(ExtensionAppShimHandlerTest, MultiProfileSelectMenu) {
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitWithFeatures(
-      /*enabled_features=*/{features::kAppShimMultiProfile},
-      /*disabled_features=*/{});
-
+TEST_F(ExtensionAppShimHandlerTestMultiProfile, MultiProfileSelectMenu) {
   delegate_->SetHostForCreate(std::move(host_aa_unique_));
   ShimLaunchedCallback launched_callback;
   delegate_->SetCaptureShimLaunchedCallback(&launched_callback);
@@ -980,12 +994,7 @@
   host_aa_->ProfileSelectedFromMenu(profile_path_b_);
 }
 
-TEST_F(ExtensionAppShimHandlerTest, ProfileMenuOneProfile) {
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitWithFeatures(
-      /*enabled_features=*/{features::kAppShimMultiProfile},
-      /*disabled_features=*/{});
-
+TEST_F(ExtensionAppShimHandlerTestMultiProfile, ProfileMenuOneProfile) {
   // Set this app to be installed for profile A.
   {
     auto item_a = chrome::mojom::ProfileMenuItem::New();
@@ -1048,12 +1057,7 @@
   EXPECT_FALSE(delegate_->RunGetProfilesForAppCallback());
 }
 
-TEST_F(ExtensionAppShimHandlerTest, FindProfileFromBadProfile) {
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitWithFeatures(
-      /*enabled_features=*/{features::kAppShimMultiProfile},
-      /*disabled_features=*/{});
-
+TEST_F(ExtensionAppShimHandlerTestMultiProfile, FindProfileFromBadProfile) {
   // Set this app to be installed for profile A.
   {
     auto item_a = chrome::mojom::ProfileMenuItem::New();
@@ -1076,12 +1080,7 @@
   EXPECT_EQ(host_aa_.get(), handler_->FindHost(&profile_a_, kTestAppIdA));
 }
 
-TEST_F(ExtensionAppShimHandlerTest, FindProfileFromNoProfile) {
-  base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitWithFeatures(
-      /*enabled_features=*/{features::kAppShimMultiProfile},
-      /*disabled_features=*/{});
-
+TEST_F(ExtensionAppShimHandlerTestMultiProfile, FindProfileFromNoProfile) {
   // Set this app to be installed for profile A.
   {
     auto item_a = chrome::mojom::ProfileMenuItem::New();
diff --git a/chrome/browser/chromeos/certificate_provider/certificate_provider_service.cc b/chrome/browser/chromeos/certificate_provider/certificate_provider_service.cc
index 0765b1e..66918fd 100644
--- a/chrome/browser/chromeos/certificate_provider/certificate_provider_service.cc
+++ b/chrome/browser/chromeos/certificate_provider/certificate_provider_service.cc
@@ -19,7 +19,7 @@
 #include "base/strings/string_piece.h"
 #include "base/task_runner.h"
 #include "base/task_runner_util.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/chromeos/certificate_provider/certificate_provider.h"
 #include "net/base/net_errors.h"
 #include "third_party/boringssl/src/include/openssl/digest.h"
@@ -32,14 +32,14 @@
 void PostSignResult(net::SSLPrivateKey::SignCallback callback,
                     net::Error error,
                     const std::vector<uint8_t>& signature) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), error, signature));
 }
 
 void PostIdentities(
     base::OnceCallback<void(net::ClientCertIdentityList)> callback,
     net::ClientCertIdentityList certs) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), std::move(certs)));
 }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index befc973..5f3d485 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -72,7 +72,6 @@
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "google_apis/drive/auth_service.h"
-#include "net/base/hex_utils.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "storage/common/file_system/file_system_types.h"
 #include "storage/common/file_system/file_system_util.h"
@@ -1093,7 +1092,10 @@
   const std::unique_ptr<Params> params(Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  std::string input = net::HexDecode(params->bytes);
+  std::string input;
+  if (!base::HexStringToString(params->bytes, &input))
+    input.clear();
+
   std::string encoding;
   bool success = base::DetectEncoding(input, &encoding);
   return RespondNow(OneArgument(std::make_unique<base::Value>(
diff --git a/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc
index 89655fe..341455c 100644
--- a/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc
@@ -557,8 +557,7 @@
   EXPECT_EQ(base::Value(base::Value::Type::LIST), *fast_reinstall_packages);
 }
 
-// Disabled due to flakiness: https://crbug.com/982161
-IN_PROC_BROWSER_TEST_F(RecommendAppsScreenTest, DISABLED_NoRecommendedApps) {
+IN_PROC_BROWSER_TEST_F(RecommendAppsScreenTest, NoRecommendedApps) {
   recommend_apps_screen_->Show();
 
   OobeScreenWaiter screen_waiter(RecommendAppsScreenView::kScreenId);
@@ -587,8 +586,7 @@
 
   test::OobeJS().CreateDisplayedWaiter(true, skip_button)->Wait();
   test::OobeJS().ExpectEnabledPath(skip_button);
-  test::OobeJS().ExpectPathDisplayed(false, install_button);
-  test::OobeJS().ExpectPathDisplayed(false, retry_button);
+  test::OobeJS().ExpectDisabledPath(install_button);
   test::OobeJS().ExpectPathDisplayed(false, retry_button);
 
   test::OobeJS().TapOnPath(skip_button);
diff --git a/chrome/browser/extensions/external_provider_impl_unittest.cc b/chrome/browser/extensions/external_provider_impl_unittest.cc
index e044d3a3..bbd2f29 100644
--- a/chrome/browser/extensions/external_provider_impl_unittest.cc
+++ b/chrome/browser/extensions/external_provider_impl_unittest.cc
@@ -48,6 +48,11 @@
 #include "components/user_manager/scoped_user_manager.h"
 #endif
 
+#if defined(OS_WIN)
+#include "base/test/test_reg_util_win.h"
+#include "base/win/registry.h"
+#endif
+
 namespace extensions {
 
 namespace {
@@ -59,6 +64,13 @@
 const char kExternalAppId[] = "kekdneafjmhmndejhmbcadfiiofngffo";
 #endif
 
+#if defined(OS_WIN)
+const char kExternalAppCrxPath[] =
+    "external\\kekdneafjmhmndejhmbcadfiiofngffo.crx";
+const wchar_t kExternalAppRegistryKey[] =
+    L"Software\\Google\\Chrome\\Extensions\\kekdneafjmhmndejhmbcadfiiofngffo";
+#endif
+
 class ExternalProviderImplTest : public ExtensionServiceTestBase {
  public:
   ExternalProviderImplTest() {}
@@ -92,8 +104,25 @@
   }
 
   void OverrideExternalExtensionsPath() {
+    // Windows doesn't use the provider that installs the |kExternalAppId|
+    // extension implicitly, so to test that the blocking policy works on
+    // Windows it is installed through a Windows-specific registry provider.
+#if defined(OS_WIN)
+    EXPECT_NO_FATAL_FAILURE(
+        registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
+    EXPECT_EQ(ERROR_SUCCESS,
+              external_extension_key_.Create(
+                  HKEY_CURRENT_USER, kExternalAppRegistryKey, KEY_ALL_ACCESS));
+    EXPECT_EQ(ERROR_SUCCESS,
+              external_extension_key_.WriteValue(
+                  L"path",
+                  data_dir().AppendASCII(kExternalAppCrxPath).value().c_str()));
+    EXPECT_EQ(ERROR_SUCCESS,
+              external_extension_key_.WriteValue(L"version", L"1"));
+#else
     external_externsions_overrides_.reset(new base::ScopedPathOverride(
         chrome::DIR_EXTERNAL_EXTENSIONS, data_dir().AppendASCII("external")));
+#endif
   }
 
   void SetExternalExtensionsBlockedByPolicy(const bool block_external) {
@@ -174,6 +203,12 @@
   chromeos::system::ScopedFakeStatisticsProvider fake_statistics_provider_;
 #endif
 
+#if defined(OS_WIN)
+  // Registry key pointing to the external extension for Windows.
+  base::win::RegKey external_extension_key_;
+  registry_util::RegistryOverrideManager registry_override_manager_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(ExternalProviderImplTest);
 };
 
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index 3208b37..4d688be 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -61,8 +61,6 @@
     "org.chromium.externalclearkey.fileiotest";
 const char kExternalClearKeyInitializeFailKeySystem[] =
     "org.chromium.externalclearkey.initializefail";
-const char kExternalClearKeyOutputProtectionTestKeySystem[] =
-    "org.chromium.externalclearkey.outputprotectiontest";
 const char kExternalClearKeyPlatformVerificationTestKeySystem[] =
     "org.chromium.externalclearkey.platformverificationtest";
 const char kExternalClearKeyCrashKeySystem[] =
@@ -350,6 +348,17 @@
                           PlayCount::ONCE, expected_title);
   }
 
+  void TestOutputProtection(bool create_recorder_before_media_keys) {
+    // Make sure the Clear Key CDM is properly registered in CdmRegistry.
+    EXPECT_TRUE(IsLibraryCdmRegistered(media::kClearKeyCdmGuid));
+
+    base::StringPairs query_params;
+    if (create_recorder_before_media_keys)
+      query_params.emplace_back("createMediaRecorderBeforeMediaKeys", "1");
+    RunMediaTestPage("eme_and_get_display_media.html", query_params,
+                     kUnitTestSuccess, true);
+  }
+
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     EncryptedMediaTestBase::SetUpCommandLine(command_line);
@@ -358,6 +367,10 @@
     command_line->AppendSwitchASCII(
         switches::kOverrideEnabledCdmInterfaceVersion,
         base::NumberToString(GetCdmInterfaceVersion()));
+    // The output protection tests create a MediaRecorder on a MediaStream,
+    // so this allows for a fake stream to be created.
+    command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
+    command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
   }
 };
 
@@ -384,7 +397,6 @@
     command_line->AppendSwitch(switches::kIncognito);
   }
 };
-
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
 // Tests encrypted media playback with a combination of parameters:
@@ -795,11 +807,13 @@
   TestNonPlaybackCases(kExternalClearKeyFileIOTestKeySystem, kUnitTestSuccess);
 }
 
-// TODO(xhwang): Investigate how to fake capturing activities to test the
-// network link detection logic in OutputProtectionProxy.
-IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, OutputProtectionTest) {
-  TestNonPlaybackCases(kExternalClearKeyOutputProtectionTestKeySystem,
-                       kUnitTestSuccess);
+// Output protection tests.
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, OutputProtectionBeforeMediaKeys) {
+  TestOutputProtection(/*create_recorder_before_media_keys=*/true);
+}
+
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, OutputProtectionAfterMediaKeys) {
+  TestOutputProtection(/*create_recorder_before_media_keys=*/false);
 }
 
 IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, PlatformVerificationTest) {
@@ -963,5 +977,4 @@
                             kExternalClearKeyKeySystem, query_params,
                             media::kEnded);
 }
-
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index 6d54a5b4..541df95b 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -175,7 +175,8 @@
       hint_cache_(std::make_unique<optimization_guide::HintCache>(
           std::make_unique<optimization_guide::OptimizationGuideStore>(
               database_provider,
-              profile_path,
+              profile_path.AddExtensionASCII(
+                  optimization_guide::kOptimizationGuideHintStore),
               background_task_runner_))),
       top_host_provider_(top_host_provider),
       url_loader_factory_(url_loader_factory),
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
index 52382c4..4f44f8a 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -37,6 +37,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
 #include "net/base/net_errors.h"
+#include "services/metrics/public/cpp/metrics_utils.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
@@ -605,7 +606,9 @@
                             10)
       .SetAdVideoBytes(aggregate_frame_data_->GetAdNetworkBytesForMime(
                            FrameData::ResourceMimeType::kVideo) >>
-                       10);
+                       10)
+      .SetMainframeAdBytes(ukm::GetExponentialBucketMinForBytes(
+          main_frame_data_->ad_network_bytes()));
 
   // Record cpu metrics for the page.
   builder.SetAdCpuTime(
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
index 744fd2d..6e34e3ad 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
@@ -1229,6 +1229,10 @@
             0);
   EXPECT_GT(
       *ukm_recorder.GetEntryMetric(
+          entries.front(), ukm::builders::AdPageLoad::kMainframeAdBytesName),
+      0);
+  EXPECT_GT(
+      *ukm_recorder.GetEntryMetric(
           entries.front(), ukm::builders::AdPageLoad::kAdBytesPerSecondName),
       0);
 
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index c973711..d49f308 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -1112,6 +1112,15 @@
   // Verify page total for network bytes.
   histogram_tester().ExpectUniqueSample(
       SuffixedHistogram("Resources.Bytes.Ads2"), 20, 1);
+
+  // Verify main frame ad bytes recorded in UKM.
+  auto entries = test_ukm_recorder().GetEntriesByName(
+      ukm::builders::AdPageLoad::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+  EXPECT_EQ(
+      *test_ukm_recorder().GetEntryMetric(
+          entries.front(), ukm::builders::AdPageLoad::kMainframeAdBytesName),
+      ukm::GetExponentialBucketMinForBytes(10 * 1024));
 }
 
 // Tests that memory cache ad bytes are recorded correctly.
@@ -1184,6 +1193,10 @@
           entries.front(),
           ukm::builders::AdPageLoad::kAdBytesPerSecondAfterInteractiveName),
       0);
+  EXPECT_EQ(
+      *test_ukm_recorder().GetEntryMetric(
+          entries.front(), ukm::builders::AdPageLoad::kMainframeAdBytesName),
+      ukm::GetExponentialBucketMinForBytes(20 * 1024));
   EXPECT_EQ(*ukm_recorder.GetEntryMetric(
                 entries.front(), ukm::builders::AdPageLoad::kAdCpuTimeName),
             500);
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index d062a277..71d06fc 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -3310,8 +3310,16 @@
                                   TabStripModel::ADD_ACTIVE);
 }
 
+// Flaky on Linux and Windows.  http://crbug.com/1022531
+#if defined(OS_LINUX) || defined(OS_WIN)
+#define MAYBE_FillWhenFormWithHiddenUsername \
+  DISABLED_FillWhenFormWithHiddenUsername
+#else
+#define MAYBE_FillWhenFormWithHiddenUsername \
+  FillWhenFormWithHiddenUsername
+#endif
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
-                       FillWhenFormWithHiddenUsername) {
+                       MAYBE_FillWhenFormWithHiddenUsername) {
   // At first let us save a credential to the password store.
   scoped_refptr<password_manager::TestPasswordStore> password_store =
       static_cast<password_manager::TestPasswordStore*>(
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index f974c9b..56079c34 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -69,6 +69,8 @@
 #include "content/public/common/context_menu_params.h"
 #include "content/public/common/mime_handler_view_mode.h"
 #include "content/public/common/url_constants.h"
+#include "content/public/test/accessibility_notification_waiter.h"
+#include "content/public/test/browser_accessibility.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/dump_accessibility_test_helper.h"
 #include "content/public/test/hit_test_region_observer.h"
@@ -81,6 +83,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "pdf/pdf_features.h"
 #include "services/network/public/cpp/features.h"
+#include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_enum_util.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node.h"
@@ -2515,12 +2518,13 @@
     // Find the embedded PDF and dump the accessibility tree.
     content::FindAccessibilityNodeCriteria find_criteria;
     find_criteria.role = ax::mojom::Role::kEmbeddedObject;
-    content::BrowserAccessibility* pdf_root =
+    content::TestBrowserAccessibility* pdf_root =
         content::FindAccessibilityNode(guest_contents, find_criteria);
     CHECK(pdf_root);
 
     base::string16 actual_contents_utf16;
-    formatter->FormatAccessibilityTree(pdf_root, &actual_contents_utf16);
+    content::TestBrowserAccessibility::FormatAccessibilityTree(
+        formatter.get(), pdf_root, &actual_contents_utf16);
     std::string actual_contents = base::UTF16ToUTF8(actual_contents_utf16);
 
     std::vector<std::string> actual_lines =
@@ -2626,3 +2630,38 @@
 IN_PROC_BROWSER_TEST_P(PDFExtensionAccessibilityTreeDumpTest, TextStyle) {
   RunPDFTest(FILE_PATH_LITERAL("text-style.pdf"));
 }
+
+// This test suite validates the navigation done using the accessibility client.
+using PDFExtensionAccessibilityNavigationTest = PDFExtensionTest;
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionAccessibilityNavigationTest,
+                       LinkNavigation) {
+  // Enable accessibility and load the test file.
+  content::BrowserAccessibilityState::GetInstance()->EnableAccessibility();
+  GURL url(embedded_test_server()->GetURL("/pdf/accessibility/weblinks.pdf"));
+  WebContents* guest_contents = LoadPdfGetGuestContents(url);
+  ASSERT_TRUE(guest_contents);
+  WaitForAccessibilityTreeToContainNodeWithName(guest_contents, "Page 1");
+
+  // Find the specific link node.
+  content::FindAccessibilityNodeCriteria find_criteria;
+  find_criteria.role = ax::mojom::Role::kLink;
+  find_criteria.name = "http://bing.com";
+  content::TestBrowserAccessibility* link_node =
+      content::FindAccessibilityNode(guest_contents, find_criteria);
+  ASSERT_TRUE(link_node);
+
+  // Invoke action on a link and wait for navigation to complete.
+  content::AccessibilityNotificationWaiter event_waiter(
+      GetActiveWebContents(), ui::kAXModeComplete,
+      ax::mojom::Event::kLoadComplete);
+  ui::AXActionData action_data;
+  action_data.action = ax::mojom::Action::kDoDefault;
+  action_data.target_node_id = link_node->GetData().id;
+  link_node->AccessibilityPerformAction(action_data);
+  event_waiter.WaitForNotification();
+
+  // Test that navigation occurred correctly.
+  const GURL& expected_url = GetActiveWebContents()->GetURL();
+  EXPECT_EQ("https://bing.com/", expected_url.spec());
+}
diff --git a/chrome/browser/plugins/plugin_info_host_impl.cc b/chrome/browser/plugins/plugin_info_host_impl.cc
index 865ca928..d49a1df 100644
--- a/chrome/browser/plugins/plugin_info_host_impl.cc
+++ b/chrome/browser/plugins/plugin_info_host_impl.cc
@@ -40,7 +40,6 @@
 #include "components/nacl/common/buildflags.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
-#include "components/rappor/rappor_service_impl.h"
 #include "components/ukm/content/source_url_recorder.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/plugin_service.h"
@@ -412,7 +411,7 @@
   context_.MaybeGrantAccess(output->status, output->plugin.path);
 
   if (output->status != chrome::mojom::PluginStatus::kNotFound) {
-    ReportMetrics(params.render_frame_id, output->actual_mime_type, params.url,
+    ReportMetrics(params.render_frame_id, output->actual_mime_type,
                   params.main_frame_origin);
   }
   std::move(callback).Run(std::move(output));
@@ -420,7 +419,6 @@
 
 void PluginInfoHostImpl::ReportMetrics(int render_frame_id,
                                        const base::StringPiece& mime_type,
-                                       const GURL& url,
                                        const url::Origin& main_frame_origin) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
@@ -435,10 +433,6 @@
   if (web_contents->GetBrowserContext()->IsOffTheRecord())
     return;
 
-  rappor::RapporServiceImpl* rappor_service =
-      g_browser_process->rappor_service();
-  if (!rappor_service)
-    return;
   if (main_frame_origin.opaque())
     return;
 
@@ -447,16 +441,6 @@
     return;
   }
 
-  rappor_service->RecordSampleString(
-      "Plugins.FlashOriginUrl", rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
-      net::registry_controlled_domains::GetDomainAndRegistry(
-          main_frame_origin.GetURL(),
-          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
-  rappor_service->RecordSampleString(
-      "Plugins.FlashUrl", rappor::ETLD_PLUS_ONE_RAPPOR_TYPE,
-      net::registry_controlled_domains::GetDomainAndRegistry(
-          url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
-
   ukm::builders::Plugins_FlashInstance(
       ukm::GetSourceIdForWebContentsDocument(web_contents))
       .Record(ukm::UkmRecorder::Get());
diff --git a/chrome/browser/plugins/plugin_info_host_impl.h b/chrome/browser/plugins/plugin_info_host_impl.h
index 33c692ea..cd0de67 100644
--- a/chrome/browser/plugins/plugin_info_host_impl.h
+++ b/chrome/browser/plugins/plugin_info_host_impl.h
@@ -130,10 +130,9 @@
                            GetPluginInfoCallback callback,
                            std::unique_ptr<PluginMetadata> plugin_metadata);
 
-  // Reports usage metrics to RAPPOR and UKM.
+  // Reports usage metrics to UKM.
   void ReportMetrics(int render_frame_id,
                      const base::StringPiece& mime_type,
-                     const GURL& url,
                      const url::Origin& main_frame_origin);
 
   Context context_;
diff --git a/chrome/browser/preferences/BUILD.gn b/chrome/browser/preferences/BUILD.gn
index 9de8dd6d..5b3a39c8 100644
--- a/chrome/browser/preferences/BUILD.gn
+++ b/chrome/browser/preferences/BUILD.gn
@@ -5,7 +5,10 @@
 import("//build/config/android/rules.gni")
 
 android_library("java") {
-  java_files = [ "android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManager.java" ]
+  java_files = [
+    "android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java",
+    "android/java/src/org/chromium/chrome/browser/preferences/SharedPreferencesManager.java",
+  ]
   deps = [
     "//base:base_java",
   ]
diff --git a/chrome/browser/preferences/OWNERS b/chrome/browser/preferences/OWNERS
index 6885464..1fa5c33 100644
--- a/chrome/browser/preferences/OWNERS
+++ b/chrome/browser/preferences/OWNERS
@@ -1,2 +1,6 @@
 chouinard@chromium.org
 hnakashima@chromium.org
+
+per-file *ChromePreferenceKeys.java=*
+
+# COMPONENT: Internals>Preferences
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
new file mode 100644
index 0000000..8e7580be
--- /dev/null
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -0,0 +1,102 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences;
+
+/**
+ * Contains String constants with the SharedPreferences keys used by Chrome.
+ *
+ * All Chrome layer SharedPreferences keys should be added here.
+ *
+ * Do not remove constants from here. Keys that were used in past versions but are not used anymore
+ * cannot be reused. Mark them as @Deprecated.
+ *
+ * TODO(crbug.com/1022107): Finish moving constants from ChromePreferenceManager.
+ * TODO(crbug.com/1013781): Implement key deprecation. For now, just mark them as @Deprecated.
+ */
+public final class ChromePreferenceKeys {
+    /** An all-time counter of taps that triggered the Contextual Search peeking panel. */
+    public static final String CONTEXTUAL_SEARCH_ALL_TIME_TAP_COUNT =
+            "contextual_search_all_time_tap_count";
+    /** An all-time counter of Contextual Search panel opens triggered by any gesture.*/
+    public static final String CONTEXTUAL_SEARCH_ALL_TIME_OPEN_COUNT =
+            "contextual_search_all_time_open_count";
+    /**
+     * The number of times a tap gesture caused a Contextual Search Quick Answer to be shown.
+     * Cumulative, starting at M-69.
+     */
+    public static final String CONTEXTUAL_SEARCH_ALL_TIME_TAP_QUICK_ANSWER_COUNT =
+            "contextual_search_all_time_tap_quick_answer_count";
+    /**
+     * The number of times that a tap triggered the Contextual Search panel to peek since the last
+     * time the panel was opened.  Note legacy string value without "open".
+     */
+    public static final String CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_COUNT =
+            "contextual_search_tap_count";
+    /**
+     * The number of times a tap gesture caused a Contextual Search Quick Answer to be shown since
+     * the last time the panel was opened.  Note legacy string value without "open".
+     */
+    public static final String CONTEXTUAL_SEARCH_TAP_SINCE_OPEN_QUICK_ANSWER_COUNT =
+            "contextual_search_tap_quick_answer_count";
+    /**
+     * The number of times the Contextual Search panel was opened with the opt-in promo visible.
+     */
+    public static final String CONTEXTUAL_SEARCH_PROMO_OPEN_COUNT =
+            "contextual_search_promo_open_count";
+    /**
+     * The entity-data impressions count for Contextual Search, i.e. thumbnails shown in the Bar.
+     * Cumulative, starting at M-69.
+     */
+    public static final String CONTEXTUAL_SEARCH_ENTITY_IMPRESSIONS_COUNT =
+            "contextual_search_entity_impressions_count";
+    /**
+     * The entity-data opens count for Contextual Search, e.g. Panel opens following thumbnails
+     * shown in the Bar. Cumulative, starting at M-69.
+     */
+    public static final String CONTEXTUAL_SEARCH_ENTITY_OPENS_COUNT =
+            "contextual_search_entity_opens_count";
+    /**
+     * The Quick Action impressions count for Contextual Search, i.e. actions presented in the Bar.
+     * Cumulative, starting at M-69.
+     */
+    public static final String CONTEXTUAL_SEARCH_QUICK_ACTION_IMPRESSIONS_COUNT =
+            "contextual_search_quick_action_impressions_count";
+    /**
+     * The Quick Actions taken count for Contextual Search, i.e. phone numbers dialed and similar
+     * actions. Cumulative, starting at M-69.
+     */
+    public static final String CONTEXTUAL_SEARCH_QUICK_ACTIONS_TAKEN_COUNT =
+            "contextual_search_quick_actions_taken_count";
+    /**
+     * The Quick Actions ignored count, i.e. phone numbers available but not dialed.
+     * Cumulative, starting at M-69.
+     */
+    public static final String CONTEXTUAL_SEARCH_QUICK_ACTIONS_IGNORED_COUNT =
+            "contextual_search_quick_actions_ignored_count";
+    /**
+     * A user interaction event ID for interaction with Contextual Search, stored as a long.
+     */
+    public static final String CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_EVENT_ID =
+            "contextual_search_previous_interaction_event_id";
+    /**
+     * An encoded set of outcomes of user interaction with Contextual Search, stored as an int.
+     */
+    public static final String CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_ENCODED_OUTCOMES =
+            "contextual_search_previous_interaction_encoded_outcomes";
+    /**
+     * A timestamp indicating when we updated the user interaction with Contextual Search, stored
+     * as a long, with resolution in days.
+     */
+    public static final String CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_TIMESTAMP =
+            "contextual_search_previous_interaction_timestamp";
+    public static final String CONTEXTUAL_SEARCH_TAP_TRIGGERED_PROMO_COUNT =
+            "contextual_search_tap_triggered_promo_count";
+    public static final String CONTEXTUAL_SEARCH_LAST_ANIMATION_TIME =
+            "contextual_search_last_animation_time";
+    public static final String CONTEXTUAL_SEARCH_CURRENT_WEEK_NUMBER =
+            "contextual_search_current_week_number";
+
+    private ChromePreferenceKeys() {}
+}
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index 91f6610..eec91acb 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -24,6 +24,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/frame/window_frame_util.h"
 #include "chrome/common/extensions/manifest_handlers/theme_handler.h"
 #include "chrome/common/themes/autogenerated_theme_util.h"
@@ -31,7 +32,10 @@
 #include "components/crx_file/id_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/resource/data_pack.h"
+#include "ui/color/color_mixer.h"
+#include "ui/color/color_provider.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/color_analysis.h"
@@ -1011,6 +1015,29 @@
   return false;
 }
 
+void BrowserThemePack::AddCustomThemeColorMixers(
+    ui::ColorProvider* provider) const {
+  // A map from theme property IDs to color IDs for use in color mixers.
+  constexpr struct {
+    int property_id;
+    int color_id;
+  } kThemePropertiesMap[] = {
+      {TP::COLOR_TOOLBAR, kColorToolbar},
+      {TP::COLOR_OMNIBOX_TEXT, kColorOmniboxText},
+      {TP::COLOR_OMNIBOX_BACKGROUND, kColorOmniboxBackground},
+  };
+
+  ui::ColorSet::ColorMap theme_colors;
+  SkColor color;
+  for (const auto& entry : kThemePropertiesMap) {
+    if (GetColor(entry.property_id, &color))
+      theme_colors.insert({entry.color_id, color});
+  }
+  if (theme_colors.empty())
+    return;
+  provider->AddMixer()->AddSet({kColorSetCustomTheme, std::move(theme_colors)});
+}
+
 // private:
 
 void BrowserThemePack::AdjustThemePack() {
diff --git a/chrome/browser/themes/browser_theme_pack.h b/chrome/browser/themes/browser_theme_pack.h
index f578388..86fc5a0 100644
--- a/chrome/browser/themes/browser_theme_pack.h
+++ b/chrome/browser/themes/browser_theme_pack.h
@@ -31,6 +31,7 @@
 }
 
 namespace ui {
+class ColorProvider;
 class DataPack;
 }
 
@@ -94,6 +95,10 @@
       const override;
   bool HasCustomImage(int id) const override;
 
+  // Builds the color mixers that represent the state of the current browser
+  // theme instance.
+  void AddCustomThemeColorMixers(ui::ColorProvider* provider) const;
+
  private:
   friend class BrowserThemePackTest;
 
diff --git a/chrome/browser/themes/browser_theme_pack_unittest.cc b/chrome/browser/themes/browser_theme_pack_unittest.cc
index cf5748f..f315f5c 100644
--- a/chrome/browser/themes/browser_theme_pack_unittest.cc
+++ b/chrome/browser/themes/browser_theme_pack_unittest.cc
@@ -13,12 +13,16 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/frame/window_frame_util.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/themes/autogenerated_theme_util.h"
 #include "chrome/grit/theme_resources.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/color/color_mixer.h"
+#include "ui/color/color_provider.h"
+#include "ui/color/color_test_ids.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
@@ -733,6 +737,56 @@
   EXPECT_FALSE(LoadRawBitmapsTo(out_file_paths));
 }
 
+TEST_F(BrowserThemePackTest, TestCreateColorMixersOmniboxNoValues) {
+  // Tests to make sure that existing colors within the color provider are not
+  // overwritten or lost in the absence of any user provided theme values.
+  ui::ColorProvider provider;
+  provider.AddMixer()->AddSet({ui::kColorSetTest0,
+                               {{kColorToolbar, SK_ColorRED},
+                                {kColorOmniboxText, SK_ColorGREEN},
+                                {kColorOmniboxBackground, SK_ColorBLUE}}});
+  theme_pack().AddCustomThemeColorMixers(&provider);
+  EXPECT_EQ(SK_ColorRED, provider.GetColor(kColorToolbar));
+  EXPECT_EQ(SK_ColorGREEN, provider.GetColor(kColorOmniboxText));
+  EXPECT_EQ(SK_ColorBLUE, provider.GetColor(kColorOmniboxBackground));
+}
+
+TEST_F(BrowserThemePackTest, TestCreateColorMixersOmniboxPartialValues) {
+  // Tests to make sure that only provided theme values are replicated into the
+  // color provider.
+  ui::ColorProvider provider;
+  provider.AddMixer()->AddSet({ui::kColorSetTest0,
+                               {{kColorToolbar, SK_ColorRED},
+                                {kColorOmniboxText, SK_ColorGREEN},
+                                {kColorOmniboxBackground, SK_ColorBLUE}}});
+  std::string color_json = R"({ "toolbar": [0, 20, 40],
+                                "omnibox_text": [60, 80, 100] })";
+  LoadColorJSON(color_json);
+  theme_pack().AddCustomThemeColorMixers(&provider);
+  EXPECT_EQ(SkColorSetRGB(0, 20, 40), provider.GetColor(kColorToolbar));
+  EXPECT_EQ(SkColorSetRGB(60, 80, 100), provider.GetColor(kColorOmniboxText));
+  EXPECT_EQ(SK_ColorBLUE, provider.GetColor(kColorOmniboxBackground));
+}
+
+TEST_F(BrowserThemePackTest, TestCreateColorMixersOmniboxAllValues) {
+  // Tests to make sure that all available colors are properly loaded into the
+  // color provider.
+  ui::ColorProvider provider;
+  provider.AddMixer()->AddSet({ui::kColorSetTest0,
+                               {{kColorToolbar, SK_ColorRED},
+                                {kColorOmniboxText, SK_ColorGREEN},
+                                {kColorOmniboxBackground, SK_ColorBLUE}}});
+  std::string color_json = R"({ "toolbar": [0, 20, 40],
+                                "omnibox_text": [60, 80, 100],
+                                "omnibox_background": [120, 140, 160] })";
+  LoadColorJSON(color_json);
+  theme_pack().AddCustomThemeColorMixers(&provider);
+  EXPECT_EQ(SkColorSetRGB(0, 20, 40), provider.GetColor(kColorToolbar));
+  EXPECT_EQ(SkColorSetRGB(60, 80, 100), provider.GetColor(kColorOmniboxText));
+  EXPECT_EQ(SkColorSetRGB(120, 140, 160),
+            provider.GetColor(kColorOmniboxBackground));
+}
+
 // TODO(erg): This test should actually test more of the built resources from
 // the extension data, but for now, exists so valgrind can test some of the
 // tricky memory stuff that BrowserThemePack does.
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.cc
index b92e536f..e5a1e996 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.cc
@@ -4,172 +4,23 @@
 
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h"
 
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/important_file_writer.h"
-#include "base/guid.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/task/post_task.h"
-#include "base/threading/scoped_blocking_call.h"
-#include "base/time/time.h"
-#include "base/unguessable_token.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.h"
-#include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder_state.pb.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder_util.h"
-#include "components/metrics/metrics_log.h"
-#include "crypto/sha2.h"
-#include "third_party/metrics_proto/chrome_os_app_list_launch_event.pb.h"
-#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
 
 namespace app_list {
 namespace {
 
-using ::metrics::ChromeOSAppListLaunchEventProto;
 using ::metrics::ChromeUserMetricsExtension;
-using ::metrics::MetricsLog;
-
-// Length of the user secret string, in bytes.
-constexpr size_t kSecretSize = 32;
-
-// Tries to serialize the given AppListLaunchRecorderStateProto to disk at the
-// given |filepath|.
-void SaveStateToDisk(const base::FilePath& filepath,
-                     const AppListLaunchRecorderStateProto& proto) {
-  std::string proto_str;
-  if (!proto.SerializeToString(&proto_str)) {
-    LOG(DFATAL) << "Error serializing AppListLaunchRecorderStateProto.";
-    LogMetricsProviderError(MetricsProviderError::kStateToProtoError);
-    return;
-  }
-
-  bool write_result = false;
-  {
-    base::ScopedBlockingCall scoped_blocking_call(
-        FROM_HERE, base::BlockingType::MAY_BLOCK);
-    write_result = base::ImportantFileWriter::WriteFileAtomically(
-        filepath, proto_str, "AppListLaunchMetricsProvider");
-  }
-
-  if (!write_result) {
-    LOG(DFATAL) << "Error writing AppListLaunchRecorderStateProto.";
-    LogMetricsProviderError(MetricsProviderError::kStateWriteError);
-  }
-}
-
-// Tries to load an |AppListLaunchRecorderStateProto| from the given filepath.
-// If it fails, returns nullopt.
-base::Optional<AppListLaunchRecorderStateProto> LoadStateFromDisk(
-    const base::FilePath& filepath) {
-  std::string proto_str;
-  {
-    base::ScopedBlockingCall scoped_blocking_call(
-        FROM_HERE, base::BlockingType::MAY_BLOCK);
-    // If the state file doesn't exist, it's not necessarily an error: this may
-    // be the first time the provider is being run.
-    if (!base::PathExists(filepath))
-      return base::nullopt;
-
-    if (!base::ReadFileToString(filepath, &proto_str)) {
-      LOG(DFATAL) << "Error reading AppListLaunchRecorderStateProto.";
-      LogMetricsProviderError(MetricsProviderError::kStateReadError);
-      return base::nullopt;
-    }
-  }
-
-  AppListLaunchRecorderStateProto proto;
-  if (!proto.ParseFromString(proto_str)) {
-    LOG(DFATAL) << "Error parsing AppListLaunchRecorderStateProto.";
-    LogMetricsProviderError(MetricsProviderError::kStateFromProtoError);
-    return base::nullopt;
-  }
-
-  return proto;
-}
-
-// Returns the file path of the primary user's profile, if it exists and is not
-// a guest session or system profile. Otherwise, returns base::nullopt.
-base::Optional<base::FilePath> GetProfileDir() {
-  // We do not handle multiprofile, events from all logged-in profiles are
-  // logged under the primary user.
-  Profile* profile = ProfileManager::GetPrimaryUserProfile();
-  // Only enable if the current profile is a regular profile, not a guest
-  // session or the login screen.
-  if (!profile || profile->IsGuestSession() || profile->IsSystemProfile())
-    return base::nullopt;
-  return profile->GetPath();
-}
-
-// Generates a cryptographically secure random secret of size |kSecretSize|.
-Secret GenerateSecret() {
-  const auto secret = base::UnguessableToken::Create().ToString();
-  DCHECK_EQ(secret.size(), kSecretSize);
-  return {secret};
-}
-
-// Generates a random user ID.
-uint64_t GenerateUserID() {
-  // This is analogous to how the UMA client ID is generated in
-  // metrics::MetricsStateManager.
-  return MetricsLog::Hash(base::GenerateGUID());
-}
-
-// Generates a proto containing a new secret and user ID.
-AppListLaunchRecorderStateProto GenerateStateProto() {
-  AppListLaunchRecorderStateProto proto;
-  proto.set_secret(GenerateSecret().value);
-  proto.set_recurrence_ranker_user_id(GenerateUserID());
-  return proto;
-}
-
-// Returns the user secret stored in |proto|.
-Secret GetSecretFromProto(const AppListLaunchRecorderStateProto& proto) {
-  DCHECK(proto.has_secret() && !proto.secret().empty());
-  return {proto.secret()};
-}
-
-// Returns the first 8 bytes of the SHA256 hash of |secret| concatenated with
-// |value|.
-uint64_t HashWithSecret(const std::string& value, const Secret& secret) {
-  DCHECK(!secret.value.empty());
-  uint64_t hash;
-  crypto::SHA256HashString(secret.value + value, &hash, sizeof(uint64_t));
-  return hash;
-}
 
 }  // namespace
 
 int AppListLaunchMetricsProvider::kMaxEventsPerUpload = 100;
-char AppListLaunchMetricsProvider::kStateProtoFilename[] =
-    "app_list_launch_recorder_state.pb";
 
-AppListLaunchMetricsProvider::AppListLaunchMetricsProvider(
-    base::RepeatingCallback<base::Optional<base::FilePath>()>
-        get_profile_dir_callback)
-    : get_profile_dir_callback_(get_profile_dir_callback),
-      init_state_(InitState::DISABLED),
-      secret_(base::nullopt),
-      user_id_(base::nullopt) {}
-
-AppListLaunchMetricsProvider::AppListLaunchMetricsProvider()
-    : AppListLaunchMetricsProvider(base::BindRepeating(GetProfileDir)) {}
-
+AppListLaunchMetricsProvider::AppListLaunchMetricsProvider() = default;
 AppListLaunchMetricsProvider::~AppListLaunchMetricsProvider() = default;
 
 void AppListLaunchMetricsProvider::OnRecordingEnabled() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // We do not perform actual initialization or set to InitState::ENABLED here.
-  // Initialization must occur after the browser thread has finished
-  // initializing, which may not be the case when OnRecordingEnabled is
-  // called. Instead, initialization happens on the first call to
-  // OnAppListLaunch.
-  init_state_ = InitState::UNINITIALIZED;
-
   subscription_ = AppListLaunchRecorder::GetInstance()->RegisterCallback(
       base::BindRepeating(&AppListLaunchMetricsProvider::OnAppListLaunch,
                           weak_factory_.GetWeakPtr()));
@@ -177,116 +28,20 @@
 
 void AppListLaunchMetricsProvider::OnRecordingDisabled() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  init_state_ = InitState::DISABLED;
-  launch_info_cache_.clear();
-  secret_.reset();
-  user_id_.reset();
   subscription_.reset();
 }
 
 void AppListLaunchMetricsProvider::Initialize() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  const auto& profile_dir = get_profile_dir_callback_.Run();
-  if (!profile_dir) {
-    OnRecordingDisabled();
-    return;
-  }
-  const base::FilePath& proto_filepath = profile_dir.value().AppendASCII(
-      AppListLaunchMetricsProvider::kStateProtoFilename);
-
-  PostTaskAndReplyWithResult(
-      FROM_HERE, {base::ThreadPool(), base::MayBlock()},
-      base::BindOnce(&LoadStateFromDisk, proto_filepath),
-      base::BindOnce(&AppListLaunchMetricsProvider::OnStateLoaded,
-                     weak_factory_.GetWeakPtr(), proto_filepath));
-}
-
-void AppListLaunchMetricsProvider::OnStateLoaded(
-    const base::FilePath& proto_filepath,
-    const base::Optional<AppListLaunchRecorderStateProto>& proto) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (proto) {
-    secret_ = GetSecretFromProto(proto.value());
-    user_id_ = proto.value().recurrence_ranker_user_id();
-  } else {
-    // Either there is no state proto saved, or it was corrupt. Generate a new
-    // secret and ID regardless. We log an 'error' to UMA despite this not
-    // always being an error, because this happening too frequently would
-    // indicate an issue with the system.
-    LogMetricsProviderError(MetricsProviderError::kNoStateProto);
-
-    AppListLaunchRecorderStateProto new_proto = GenerateStateProto();
-    PostTask(FROM_HERE, {base::ThreadPool(), base::MayBlock()},
-             base::BindOnce(&SaveStateToDisk, proto_filepath, new_proto));
-
-    secret_ = GetSecretFromProto(new_proto);
-    user_id_ = new_proto.recurrence_ranker_user_id();
-  }
-
-  if (!user_id_) {
-    LogMetricsProviderError(MetricsProviderError::kInvalidUserId);
-    OnRecordingDisabled();
-  } else if (!secret_ || secret_.value().value.size() != kSecretSize) {
-    LogMetricsProviderError(MetricsProviderError::kInvalidSecret);
-    OnRecordingDisabled();
-  } else {
-    init_state_ = InitState::ENABLED;
-  }
 }
 
 void AppListLaunchMetricsProvider::ProvideCurrentSessionData(
     ChromeUserMetricsExtension* uma_proto) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (init_state_ == InitState::ENABLED) {
-    for (const auto& launch_info : launch_info_cache_) {
-      CreateLaunchEvent(launch_info,
-                        uma_proto->add_chrome_os_app_list_launch_event());
-    }
-    launch_info_cache_.clear();
-  }
 }
 
 void AppListLaunchMetricsProvider::OnAppListLaunch(
     const AppListLaunchRecorder::LaunchInfo& launch_info) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (init_state_ == InitState::UNINITIALIZED) {
-    init_state_ = InitState::INIT_STARTED;
-    Initialize();
-  }
-
-  if (init_state_ == InitState::DISABLED ||
-      launch_info_cache_.size() >= static_cast<size_t>(kMaxEventsPerUpload))
-    return;
-
-  if (launch_info.client == AppListLaunchRecorder::Client::kUnspecified) {
-    LogMetricsProviderError(MetricsProviderError::kLaunchTypeUnspecified);
-    return;
-  }
-
-  launch_info_cache_.push_back(launch_info);
-
-  // We want the metric to reflect the number of uploads affected, not the
-  // number of dropped events, so only log an error when max events is first
-  // exceeded.
-  if (launch_info_cache_.size() == static_cast<size_t>(kMaxEventsPerUpload))
-    LogMetricsProviderError(MetricsProviderError::kMaxEventsPerUploadExceeded);
-}
-
-void AppListLaunchMetricsProvider::CreateLaunchEvent(
-    const AppListLaunchRecorder::LaunchInfo& launch_info,
-    ChromeOSAppListLaunchEventProto* event) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(secret_ && user_id_);
-
-  base::Time::Exploded now;
-  base::Time::Now().LocalExplode(&now);
-
-  // TODO(crbug.com/1016655): set hashed and unhashed data after proto has been
-  // updated.
-
-  // Dummy call so that HashWithSecret compiles.
-  LOG(ERROR) << HashWithSecret("dummy hash", secret_.value());
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h
index cd8e694..8801533 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider.h
@@ -25,14 +25,7 @@
 
 namespace app_list {
 
-// Stores a user's random secret. This struct exists to make clear at the type
-// system level what is a secret and what isn't.
-struct Secret {
-  std::string value;
-};
-
 class AppListLaunchMetricsProviderTest;
-class AppListLaunchRecorderStateProto;
 
 // AppListLaunchMetricsProvider is responsible for filling out the
 // |app_list_launch_event| section of the UMA proto. This class should not be
@@ -51,76 +44,28 @@
  private:
   friend class ::ChromeMetricsServiceClient;
   friend class app_list::AppListLaunchMetricsProviderTest;
-  FRIEND_TEST_ALL_PREFIXES(AppListLaunchMetricsProviderTest, EventsAreCapped);
-
-  // Filename for the the state proto within the user's home directory. This is
-  // just the basename, not the full path.
-  static char kStateProtoFilename[];
 
   // Beyond this number of OnAppListLaunch calls between successive calls to
   // ProvideCurrentSessionData, we stop logging.
   static int kMaxEventsPerUpload;
 
-  enum class InitState { UNINITIALIZED, INIT_STARTED, ENABLED, DISABLED };
-
   // The constructors are private so that this class can only be instantied by
-  // ChromeMetricsServiceClient and tests. The class has two constructors so
-  // that the tests can override the profile dir.
+  // ChromeMetricsServiceClient and tests.
   AppListLaunchMetricsProvider();
 
-  AppListLaunchMetricsProvider(
-      base::RepeatingCallback<base::Optional<base::FilePath>()>
-          get_profile_dir_callback);
-
   // Records the information in |launch_info|, to be converted into a hashed
   // form and logged to UMA. Actual logging occurs periodically, so it is not
   // guaranteed that any call to this method will be logged.
   //
-  // OnAppListLaunch should only be called from the browser thread, after it has
-  // finished initializing.
+  // OnAppListLaunch should only be called from the browser UI sequence.
   void OnAppListLaunch(const AppListLaunchRecorder::LaunchInfo& launch_info);
 
-  // Starts initialization if needed. This involves reading the secret from
-  // disk.
+  // Performs initialization once a user has logged in.
   void Initialize();
 
-  // Populates this object's internal state from the given state |proto|, and
-  // finishes initialisation. This is called by Initialize() and passed the
-  // state proto that has been loaded from disk, if it exists. OnStateLoaded may
-  // write a new proto to disk if, for example, the given |proto| is invalid.
-  void OnStateLoaded(
-      const base::FilePath& proto_filepath,
-      const base::Optional<AppListLaunchRecorderStateProto>& proto);
-
-  // Converts |launch_info| into a hashed |event| proto ready for logging.
-  void CreateLaunchEvent(const AppListLaunchRecorder::LaunchInfo& launch_info,
-                         metrics::ChromeOSAppListLaunchEventProto* event);
-
-  // A function that returns the directory of the current profile. This is a
-  // callback so that it can be faked out for testing.
-  const base::RepeatingCallback<base::Optional<base::FilePath>()>
-      get_profile_dir_callback_;
-
   // Subscription for receiving logging event callbacks from HashedLogger.
   std::unique_ptr<AppListLaunchRecorder::LaunchEventSubscription> subscription_;
 
-  // Cache of input launch data, to be hashed and returned when
-  // ProvideCurrentSessionData() is called.
-  std::vector<AppListLaunchRecorder::LaunchInfo> launch_info_cache_;
-
-  // The initialization state of the AppListLaunchMetricsProvider. Events are
-  // only provided on a call to ProvideCurrentSessionData() once this is
-  // enabled, and nothing is recorded if this is disabled.
-  InitState init_state_;
-
-  // Secret per-user salt concatenated with values before hashing. Serialized
-  // to disk.
-  base::Optional<Secret> secret_;
-
-  // Per-user per-client ID used only for AppListLaunchMetricsProvider.
-  // Serialized to disk.
-  base::Optional<uint64_t> user_id_;
-
   SEQUENCE_CHECKER(sequence_checker_);
 
   base::WeakPtrFactory<AppListLaunchMetricsProvider> weak_factory_{this};
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc
index 33b6e5dd..065f62305 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_metrics_provider_unittest.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder.h"
-#include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder_state.pb.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_list_launch_recorder_util.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
@@ -31,7 +30,6 @@
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/metrics_proto/chrome_os_app_list_launch_event.pb.h"
 #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
 
@@ -40,44 +38,6 @@
 namespace {
 
 using ::chromeos::ProfileHelper;
-using ::metrics::ChromeOSAppListLaunchEventProto;
-
-// 32 bytes long, matching the size of a real secret.
-constexpr char kSecret[] = "abcdefghijklmnopqrstuvwxyzabcdef";
-
-constexpr uint64_t kUserId = 1123u;
-
-constexpr char kValue1[] = "value one";
-constexpr char kValue2[] = "value two";
-constexpr char kValue3[] = "value three";
-constexpr char kValue4[] = "value four";
-
-// These are hex encoded values of the first 8 bytes of sha256(kSecret +
-// kValueN), generated with the sha256sum command. These are hex encoded to make
-// debugging incorrect values easier.
-constexpr char kValue1Hash[] = "E79C24CD2117A2BB";
-constexpr char kValue2Hash[] = "506ECDDC0BA3C341";
-constexpr char kValue3Hash[] = "1E0CDC361557A12F";
-constexpr char kValue4Hash[] = "4875030730DE902A";
-
-enum class TestEvent {
-  kValueA = 0,
-  kValueB = 1,
-  kValueC = 2,
-};
-
-std::string HashToHex(uint64_t hash) {
-  return base::HexEncode(&hash, sizeof(uint64_t));
-}
-
-void ExpectLoggingEventEquals(ChromeOSAppListLaunchEventProto proto,
-                              std::string value_a,
-                              std::string value_b,
-                              int value_c) {
-  // TODO(crbug.com/1016655): reimplement once proto has been refactored.
-  // Dummy call so HashToHex compiles.
-  LOG(ERROR) << HashToHex(0u);
-}
 
 }  // namespace
 
@@ -87,119 +47,6 @@
 
   base::Optional<base::FilePath> GetTempDir() { return temp_dir_.GetPath(); }
 
-  void MakeProvider() {
-    // Using WrapUnique to access a private constructor.
-    provider_ = base::WrapUnique(new AppListLaunchMetricsProvider(
-        base::BindRepeating(&AppListLaunchMetricsProviderTest::GetTempDir,
-                            base::Unretained(this))));
-    provider_->OnRecordingEnabled();
-  }
-
-  void InitProvider() {
-    AddLog("", "", 0);
-    Wait();
-  }
-
-  void ExpectUninitialized() {
-    EXPECT_EQ(AppListLaunchMetricsProvider::InitState::UNINITIALIZED,
-              provider_->init_state_);
-  }
-
-  void ExpectInitStarted() {
-    EXPECT_EQ(AppListLaunchMetricsProvider::InitState::INIT_STARTED,
-              provider_->init_state_);
-  }
-
-  void ExpectEnabled() {
-    EXPECT_EQ(AppListLaunchMetricsProvider::InitState::ENABLED,
-              provider_->init_state_);
-  }
-
-  void ExpectDisabled() {
-    EXPECT_EQ(AppListLaunchMetricsProvider::InitState::DISABLED,
-              provider_->init_state_);
-  }
-
-  base::Optional<Secret> GetSecret() { return provider_->secret_; }
-
-  std::string ReadSecret() {
-    std::string proto_str;
-    {
-      base::ScopedBlockingCall scoped_blocking_call(
-          FROM_HERE, base::BlockingType::MAY_BLOCK);
-
-      base::FilePath path = temp_dir_.GetPath().AppendASCII(
-          AppListLaunchMetricsProvider::kStateProtoFilename);
-      CHECK(base::ReadFileToString(path, &proto_str));
-    }
-
-    auto proto = std::make_unique<AppListLaunchRecorderStateProto>();
-    CHECK(proto->ParseFromString(proto_str));
-    return proto->secret();
-  }
-
-  uint64_t ReadUserId() {
-    std::string proto_str;
-    {
-      base::ScopedBlockingCall scoped_blocking_call(
-          FROM_HERE, base::BlockingType::MAY_BLOCK);
-
-      base::FilePath path = temp_dir_.GetPath().AppendASCII(
-          AppListLaunchMetricsProvider::kStateProtoFilename);
-      CHECK(base::ReadFileToString(path, &proto_str));
-    }
-
-    auto proto = std::make_unique<AppListLaunchRecorderStateProto>();
-    CHECK(proto->ParseFromString(proto_str));
-    return proto->recurrence_ranker_user_id();
-  }
-
-  void WriteStateProto(const std::string& secret) {
-    AppListLaunchRecorderStateProto proto;
-    proto.set_recurrence_ranker_user_id(kUserId);
-    proto.set_secret(secret);
-
-    std::string proto_str;
-    CHECK(proto.SerializeToString(&proto_str));
-    {
-      base::ScopedBlockingCall scoped_blocking_call(
-          FROM_HERE, base::BlockingType::MAY_BLOCK);
-      CHECK(base::ImportantFileWriter::WriteFileAtomically(
-          temp_dir_.GetPath().AppendASCII(
-              AppListLaunchMetricsProvider::kStateProtoFilename),
-          proto_str, "AppListLaunchMetricsProviderTest"));
-    }
-
-    Wait();
-  }
-
-  void DeleteStateProto() {
-    DeleteFile(temp_dir_.GetPath().AppendASCII(
-                   AppListLaunchMetricsProvider::kStateProtoFilename),
-               false);
-  }
-
-  void AddLog(std::string value_a, std::string value_b, int value_c) {
-    AppListLaunchRecorder::LaunchInfo event;
-    event.client = AppListLaunchRecorder::Client::kTesting;
-    event.hashed = {{static_cast<int>(TestEvent::kValueA), value_a},
-                    {static_cast<int>(TestEvent::kValueB), value_b}};
-    event.unhashed = {{static_cast<int>(TestEvent::kValueC), value_c}};
-
-    provider_->OnAppListLaunch(event);
-  }
-
-  google::protobuf::RepeatedPtrField<ChromeOSAppListLaunchEventProto>
-  GetLogs() {
-    metrics::ChromeUserMetricsExtension uma_log;
-    provider_->ProvideCurrentSessionData(&uma_log);
-    return uma_log.chrome_os_app_list_launch_event();
-  }
-
-  void ExpectNoErrors() {
-    histogram_tester_.ExpectTotalCount("Apps.AppListLaunchRecorderError", 0);
-  }
-
   void Wait() { task_environment_.RunUntilIdle(); }
 
   base::HistogramTester histogram_tester_;
@@ -209,166 +56,4 @@
   std::unique_ptr<AppListLaunchMetricsProvider> provider_;
 };
 
-TEST_F(AppListLaunchMetricsProviderTest,
-       DISABLED_ProvidesNothingWhenUninitialized) {
-  MakeProvider();
-
-  ExpectUninitialized();
-  EXPECT_TRUE(GetLogs().empty());
-  ExpectNoErrors();
-}
-
-TEST_F(AppListLaunchMetricsProviderTest, DISABLED_SucceedsGeneratingNewSecret) {
-  MakeProvider();
-  InitProvider();
-
-  ExpectEnabled();
-  // Because the secret is random, we settle for just checking its length.
-  // In the object:
-  EXPECT_EQ(32ul, GetSecret().value().value.size());
-  // On disk:
-  EXPECT_EQ(32ul, ReadSecret().size());
-
-  EXPECT_EQ(GetSecret().value().value, ReadSecret());
-
-  histogram_tester_.ExpectUniqueSample("Apps.AppListLaunchRecorderError",
-                                       MetricsProviderError::kNoStateProto, 1);
-}
-
-TEST_F(AppListLaunchMetricsProviderTest,
-       DISABLED_SucceedsLoadingExistingSecret) {
-  WriteStateProto(kSecret);
-
-  MakeProvider();
-  InitProvider();
-
-  ExpectEnabled();
-  EXPECT_EQ(kSecret, GetSecret().value().value);
-  ExpectNoErrors();
-}
-
-TEST_F(AppListLaunchMetricsProviderTest, DISABLED_DisableOnInvalidSecret) {
-  WriteStateProto("wrong length");
-
-  MakeProvider();
-  InitProvider();
-
-  ExpectDisabled();
-  histogram_tester_.ExpectUniqueSample("Apps.AppListLaunchRecorderError",
-                                       MetricsProviderError::kInvalidSecret, 1);
-}
-
-// Tests that a call to ProvideCurrentSessionData populates protos for each log,
-// and that those protos contain the right values.
-TEST_F(AppListLaunchMetricsProviderTest, DISABLED_CorrectHashedValues) {
-  WriteStateProto(kSecret);
-  MakeProvider();
-  InitProvider();
-
-  AddLog(kValue1, kValue2, 5);
-  AddLog(kValue2, kValue3, 7);
-  AddLog(kValue3, kValue4, 9);
-
-  const auto& events = GetLogs();
-  // events[0] is a dummy log created in |InitProvider|. We don't test for its
-  // contents below.
-  ASSERT_EQ(events.size(), 4);
-
-  ExpectLoggingEventEquals(events[1], kValue1Hash, kValue2Hash, 5);
-  ExpectLoggingEventEquals(events[2], kValue2Hash, kValue3Hash, 7);
-  ExpectLoggingEventEquals(events[3], kValue3Hash, kValue4Hash, 9);
-  ExpectNoErrors();
-}
-
-// Tests that the logs reported in one call to ProvideCurrentSessionData do no
-// appear in the next.
-TEST_F(AppListLaunchMetricsProviderTest, DISABLED_EventsNotDuplicated) {
-  WriteStateProto(kSecret);
-  MakeProvider();
-  InitProvider();
-
-  AddLog(kValue1, kValue2, 44);
-  auto events = GetLogs();
-  ASSERT_EQ(events.size(), 2);
-  ExpectLoggingEventEquals(events[1], kValue1Hash, kValue2Hash, 44);
-
-  AddLog(kValue1, kValue4, 22);
-  events = GetLogs();
-  ASSERT_EQ(events.size(), 1);
-  ExpectLoggingEventEquals(events[0], kValue3Hash, kValue4Hash, 22);
-
-  EXPECT_TRUE(GetLogs().empty());
-  ExpectNoErrors();
-}
-
-// Tests that logging events are dropped after an unreasonably large number of
-// them are made between uploads.
-TEST_F(AppListLaunchMetricsProviderTest, DISABLED_EventsAreCapped) {
-  MakeProvider();
-  InitProvider();
-
-  const int max_events = AppListLaunchMetricsProvider::kMaxEventsPerUpload;
-
-  // Not enough events to hit the cap.
-  for (int i = 0; i < max_events / 2; ++i)
-    AddLog(kValue1, kValue2, 1);
-  // One event from init, kMaxEventsPerUpload/2 from the loop.
-  EXPECT_EQ(1 + max_events / 2, GetLogs().size());
-
-  // Enough events to hit the cap.
-  for (int i = 0; i < 2 * max_events; ++i)
-    AddLog(kValue1, kValue2, 1);
-  EXPECT_EQ(max_events, GetLogs().size());
-
-  histogram_tester_.ExpectBucketCount(
-      "Apps.AppListLaunchRecorderError",
-      MetricsProviderError::kMaxEventsPerUploadExceeded, 1);
-}
-
-// Tests that logging events that occur before the provider is initialized are
-// still correctly logged after initialization.
-TEST_F(AppListLaunchMetricsProviderTest,
-       DISABLED_LaunchEventsBeforeInitializationAreRecorded) {
-  WriteStateProto(kSecret);
-  MakeProvider();
-
-  // To begin with, the provider is uninitialized and has no logs at all.
-  EXPECT_TRUE(GetLogs().empty());
-  // These logs are added before the provider has finished initialising.
-  AddLog(kValue1, kValue2, 1);
-  AddLog(kValue2, kValue3, 2);
-  AddLog(kValue3, kValue4, 3);
-  // Initialisation is finished here.
-  Wait();
-
-  const auto& events = GetLogs();
-  ASSERT_EQ(events.size(), 3);
-  ExpectLoggingEventEquals(events[0], kValue1Hash, kValue2Hash, 1);
-  ExpectLoggingEventEquals(events[1], kValue2Hash, kValue3Hash, 2);
-  ExpectLoggingEventEquals(events[2], kValue3Hash, kValue4Hash, 3);
-
-  EXPECT_TRUE(GetLogs().empty());
-  ExpectNoErrors();
-}
-
-// Without an existing saved state, instantiating a metrics provider should save
-// an almost certainly unique user ID and secret. Test this by creating a few
-// blank-slate metrics providers.
-TEST_F(AppListLaunchMetricsProviderTest, DISABLED_UserIDsAndSecretsAreUnique) {
-  const int num_runs = 10;
-
-  std::set<uint64_t> user_ids;
-  std::set<std::string> secrets;
-  for (int i = 0; i < num_runs; ++i) {
-    DeleteStateProto();
-    MakeProvider();
-    InitProvider();
-    secrets.insert(ReadSecret());
-    user_ids.insert(ReadUserId());
-  }
-
-  EXPECT_EQ(static_cast<size_t>(num_runs), secrets.size());
-  EXPECT_EQ(static_cast<size_t>(num_runs), user_ids.size());
-}
-
 }  // namespace app_list
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h
index b1a2efb..6b67962 100644
--- a/chrome/browser/ui/color/chrome_color_id.h
+++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -36,4 +36,8 @@
   kColorToolbar,  // TODO(pkasting): Add a recipe
 };
 
+enum ChromeColorSetIds : ui::ColorSetId {
+  kColorSetCustomTheme = ui::kUiColorSetsEnd,
+};
+
 #endif  // CHROME_BROWSER_UI_COLOR_CHROME_COLOR_ID_H_
diff --git a/chrome/browser/ui/libgtkui/gtk_event_loop_x11.cc b/chrome/browser/ui/libgtkui/gtk_event_loop_x11.cc
index 5224e946..421e8a61 100644
--- a/chrome/browser/ui/libgtkui/gtk_event_loop_x11.cc
+++ b/chrome/browser/ui/libgtkui/gtk_event_loop_x11.cc
@@ -18,15 +18,18 @@
 
 namespace {
 
-// Xkb Events stores group attribute into XKeyEvent::state bit field while
-// GdkEventKey objects has separate fields for that purpose, they are ::state
-// and ::group respectively. So recompose them when build XKeyEvent from
-// GdkEventKey here. This is similar to XkbBuildCoreState(), but takes takes a
-// uint instead of assuming a 8-bit Xkb state.
+// Xkb Events store group attribute into XKeyEvent::state bit field, along with
+// other state-related info, while GdkEventKey objects have separate fields for
+// that purpose, they are ::state and ::group. This function is responsible for
+// recomposing them into a single bit field value when translating GdkEventKey
+// into XKeyEvent. This is similar to XkbBuildCoreState(), but assumes state is
+// an uint rather than an uchar.
 //
 // More details:
-// https://code.woboq.org/qt5/include/X11/extensions/XKB.h.html#_M/XkbBuildCoreState
+// https://gitlab.freedesktop.org/xorg/proto/xorgproto/blob/master/include/X11/extensions/XKB.h#L372
 int BuildXkbStateFromGdkEvent(const GdkEventKey& keyev) {
+  DCHECK_EQ(0u, XkbGroupForCoreState(keyev.state));
+  DCHECK(XkbIsLegalGroup(keyev.group));
   return keyev.state | ((keyev.group & 0x3) << 13);
 }
 
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index abea74a..08ff0064 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -130,6 +130,9 @@
     ContentSettingsType::BACKGROUND_SYNC,
     ContentSettingsType::SOUND,
     ContentSettingsType::AUTOMATIC_DOWNLOADS,
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
+    ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
+#endif
     ContentSettingsType::AUTOPLAY,
     ContentSettingsType::MIDI_SYSEX,
     ContentSettingsType::CLIPBOARD_READ,
diff --git a/chrome/browser/ui/page_info/page_info_ui.cc b/chrome/browser/ui/page_info/page_info_ui.cc
index 31d033c3..45425958 100644
--- a/chrome/browser/ui/page_info/page_info_ui.cc
+++ b/chrome/browser/ui/page_info/page_info_ui.cc
@@ -164,6 +164,10 @@
      IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL},
     {ContentSettingsType::MIDI_SYSEX, IDS_PAGE_INFO_TYPE_MIDI_SYSEX},
     {ContentSettingsType::BACKGROUND_SYNC, IDS_PAGE_INFO_TYPE_BACKGROUND_SYNC},
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
+    {ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
+     IDS_PAGE_INFO_TYPE_PROTECTED_MEDIA_IDENTIFIER},
+#endif
     {ContentSettingsType::AUTOPLAY, IDS_PAGE_INFO_TYPE_AUTOPLAY},
     {ContentSettingsType::ADS, IDS_PAGE_INFO_TYPE_ADS},
     {ContentSettingsType::SOUND, IDS_PAGE_INFO_TYPE_SOUND},
@@ -583,6 +587,11 @@
     case ContentSettingsType::AUTOMATIC_DOWNLOADS:
       icon = &kFileDownloadIcon;
       break;
+#if defined(OS_CHROMEOS)
+    case ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER:
+      icon = &kProtectedContentIcon;
+      break;
+#endif
     case ContentSettingsType::MIDI_SYSEX:
       icon = &vector_icons::kMidiIcon;
       break;
@@ -604,11 +613,9 @@
     case ContentSettingsType::USB_GUARD:
       icon = &vector_icons::kUsbIcon;
       break;
-#if !defined(OS_ANDROID)
     case ContentSettingsType::SERIAL_GUARD:
       icon = &vector_icons::kSerialPortIcon;
       break;
-#endif
     case ContentSettingsType::BLUETOOTH_SCANNING:
       icon = &vector_icons::kBluetoothScanningIcon;
       break;
diff --git a/chrome/browser/ui/page_info/page_info_ui.h b/chrome/browser/ui/page_info/page_info_ui.h
index 8c55c7f..90619d77 100644
--- a/chrome/browser/ui/page_info/page_info_ui.h
+++ b/chrome/browser/ui/page_info/page_info_ui.h
@@ -204,7 +204,7 @@
 
   // Returns the connection icon ID for the given connection |status|.
   static int GetConnectionIconID(PageInfo::SiteConnectionStatus status);
-#else
+#else  // !defined(OS_ANDROID)
   // Returns icons for the given PermissionInfo |info|. If |info|'s current
   // setting is CONTENT_SETTING_DEFAULT, it will return the icon for |info|'s
   // default setting.
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index ade8263..95d183a7f 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -26,6 +26,8 @@
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/search/instant_test_utils.h"
 #include "chrome/browser/ui/search/local_ntp_browsertest_base.h"
 #include "chrome/browser/ui/search/local_ntp_test_utils.h"
@@ -37,6 +39,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/ntp_tiles/constants.h"
+#include "components/omnibox/browser/omnibox_view.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/interstitial_page.h"
@@ -1190,6 +1193,53 @@
                                            handle_observer.page_transition()));
 }
 
+// This is a regression test for https://crbug.com/1020610 - it verifies that
+// NTP navigations do show up as a pending navigation.
+IN_PROC_BROWSER_TEST_F(LocalNTPTest, PendingNavigations) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // Open an NTP.
+  content::WebContents* ntp_tab = local_ntp_test_utils::OpenNewTab(
+      browser(), GURL(chrome::kChromeUINewTabURL));
+
+  // Inject and click a link to foo.com/hung and wait for the navigation to
+  // start.
+  GURL slow_url(embedded_test_server()->GetURL("/hung"));
+  const char* kNavScriptTemplate = R"(
+      var a = document.createElement('a');
+      a.href = $1;
+      a.innerText = 'Simulated most-visited link';
+      document.body.appendChild(a);
+      a.click();
+  )";
+  content::TestNavigationManager nav_manager(ntp_tab, slow_url);
+  ASSERT_TRUE(content::ExecuteScript(
+      ntp_tab, content::JsReplace(kNavScriptTemplate, slow_url)));
+  ASSERT_TRUE(nav_manager.WaitForRequestStart());
+
+  // Verify that the visible entry points at the |slow_url|.
+  content::NavigationEntry* pending_entry =
+      ntp_tab->GetController().GetPendingEntry();
+  ASSERT_TRUE(pending_entry);
+  content::NavigationEntry* visible_entry =
+      ntp_tab->GetController().GetVisibleEntry();
+  ASSERT_TRUE(visible_entry);
+  content::NavigationEntry* committed_entry =
+      ntp_tab->GetController().GetLastCommittedEntry();
+  ASSERT_TRUE(committed_entry);
+  EXPECT_EQ(visible_entry, pending_entry);
+  EXPECT_EQ(slow_url, visible_entry->GetURL());
+  EXPECT_NE(pending_entry, committed_entry);
+  EXPECT_NE(slow_url, committed_entry->GetURL());
+
+  // Verify that the omnibox displays |slow_url|.
+  OmniboxView* view = browser()->window()->GetLocationBar()->GetOmniboxView();
+  std::string omnibox_text = base::UTF16ToUTF8(view->GetText());
+  EXPECT_THAT(omnibox_text, ::testing::StartsWith(slow_url.host()));
+  EXPECT_THAT(omnibox_text, ::testing::EndsWith(slow_url.path()));
+  EXPECT_THAT(slow_url.spec(), ::testing::EndsWith(omnibox_text));
+}
+
 // Verifies that Chrome won't spawn a separate renderer process for
 // every single NTP tab.  This behavior goes all the way back to
 // the initial commit [1] which achieved that behavior by forcing
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
index ebc9310..6a99049 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
@@ -59,32 +59,17 @@
 // When enabled, closes the container upon any event in the window not
 // destined for the container and cancels the event. If an event is
 // destined for the container, it passes it through.
-class WebUITabStripContainerView::AutoCloser : public ui::EventHandler,
-                                               public views::ViewObserver {
+class WebUITabStripContainerView::AutoCloser : public ui::EventHandler {
  public:
-  AutoCloser(views::View* container,
+  using EventPassthroughPredicate =
+      base::RepeatingCallback<bool(const ui::Event& event)>;
+
+  AutoCloser(EventPassthroughPredicate event_passthrough_predicate,
              base::RepeatingClosure close_container_callback)
-      : container_(container),
-        close_container_callback_(std::move(close_container_callback)),
-        view_observer_(this) {
-    view_observer_.Add(container);
-    WidgetChanged();
+      : event_passthrough_predicate_(std::move(event_passthrough_predicate)),
+        close_container_callback_(std::move(close_container_callback)) {}
 
-    // Our observed Widget's NativeView may be destroyed before us. We
-    // have no reasonable way of un-registering our pre-target handler
-    // from the NativeView while the Widget is destroying. This disables
-    // EventHandler's check that it has been removed from all
-    // EventTargets.
-    DisableCheckTargets();
-  }
-
-  ~AutoCloser() override {
-    // If |container_| was in a Widget and that Widget still has its
-    // NativeView, remove ourselves from it. Otherwise, the NativeView
-    // is already destroying so we don't need to do anything.
-    if (last_widget_ && last_widget_->GetNativeView())
-      last_widget_->GetNativeView()->RemovePreTargetHandler(this);
-  }
+  ~AutoCloser() override {}
 
   // Sets whether to inspect events. If not enabled, all events are
   // ignored and passed through as usual.
@@ -94,54 +79,16 @@
   void OnEvent(ui::Event* event) override {
     if (!enabled_)
       return;
-    if (!event->IsLocatedEvent())
+    if (event_passthrough_predicate_.Run(*event))
       return;
 
-    ui::LocatedEvent* located_event = event->AsLocatedEvent();
-    // Let any events destined for |container_| pass through.
-    const gfx::Rect container_bounds_in_window =
-        container_->ConvertRectToWidget(container_->GetLocalBounds());
-    if (container_bounds_in_window.Contains(located_event->root_location()))
-      return;
-
-    // Upon any user action outside |container_|, cancel the event and close the
-    // container.
-    if (EventTypeCanCloseTabStrip(located_event->type())) {
-      located_event->StopPropagation();
-      close_container_callback_.Run();
-    }
-  }
-
-  // views::ViewObserver:
-  void OnViewAddedToWidget(views::View* observed_view) override {
-    DCHECK_EQ(container_, observed_view);
-    WidgetChanged();
-  }
-
-  void OnViewRemovedFromWidget(views::View* observed_view) override {
-    DCHECK_EQ(container_, observed_view);
-    WidgetChanged();
+    event->StopPropagation();
+    close_container_callback_.Run();
   }
 
  private:
-  // Handle when |container_| is added to a new Widget or removed from
-  // its Widget.
-  void WidgetChanged() {
-    views::Widget* new_widget = container_->GetWidget();
-    if (new_widget == last_widget_)
-      return;
-
-    if (last_widget_)
-      last_widget_->GetNativeView()->RemovePreTargetHandler(this);
-    if (new_widget)
-      new_widget->GetNativeView()->AddPreTargetHandler(this);
-    last_widget_ = new_widget;
-  }
-
-  views::View* const container_;
+  EventPassthroughPredicate event_passthrough_predicate_;
   base::RepeatingClosure close_container_callback_;
-  views::Widget* last_widget_ = nullptr;
-  ScopedObserver<View, ViewObserver> view_observer_;
   bool enabled_ = false;
 };
 
@@ -182,7 +129,8 @@
           AddChildView(std::make_unique<views::WebView>(browser->profile()))),
       tab_contents_container_(tab_contents_container),
       auto_closer_(std::make_unique<AutoCloser>(
-          this,
+          base::Bind(&WebUITabStripContainerView::EventShouldPropagate,
+                     base::Unretained(this)),
           base::Bind(&WebUITabStripContainerView::CloseContainer,
                      base::Unretained(this)))) {
   animation_.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN);
@@ -211,6 +159,13 @@
   TabStripUI* const tab_strip_ui = static_cast<TabStripUI*>(
       web_view_->GetWebContents()->GetWebUI()->GetController());
   tab_strip_ui->Initialize(browser_, this);
+
+  // Our observed Widget's NativeView may be destroyed before us. We
+  // have no reasonable way of un-registering our pre-target handler
+  // from the NativeView while the Widget is destroying. This disables
+  // EventHandler's check that it has been removed from all
+  // EventTargets.
+  auto_closer_->DisableCheckTargets();
 }
 
 WebUITabStripContainerView::~WebUITabStripContainerView() = default;
@@ -221,14 +176,20 @@
 
 std::unique_ptr<ToolbarButton>
 WebUITabStripContainerView::CreateNewTabButton() {
+  DCHECK_EQ(nullptr, new_tab_button_);
   auto new_tab_button = std::make_unique<ToolbarButton>(this);
   new_tab_button->SetID(VIEW_ID_WEBUI_TAB_STRIP_NEW_TAB_BUTTON);
   new_tab_button->SetTooltipText(
       l10n_util::GetStringUTF16(IDS_TOOLTIP_NEW_TAB));
+
+  new_tab_button_ = new_tab_button.get();
+  view_observer_.Add(new_tab_button_);
+
   return new_tab_button;
 }
 
 std::unique_ptr<views::View> WebUITabStripContainerView::CreateTabCounter() {
+  DCHECK_EQ(nullptr, tab_counter_);
   // TODO(999557): Create a custom text style to get the correct size/weight.
   // TODO(999557): Figure out how to get the right font.
   auto tab_counter = std::make_unique<views::LabelButton>(
@@ -259,6 +220,9 @@
   browser_->tab_strip_model()->AddObserver(tab_counter_model_observer_.get());
   tab_counter_model_observer_->UpdateCounter(browser_->tab_strip_model());
 
+  tab_counter_ = tab_counter.get();
+  view_observer_.Add(tab_counter_);
+
   return tab_counter;
 }
 
@@ -279,6 +243,30 @@
   auto_closer_->set_enabled(target_visible);
 }
 
+bool WebUITabStripContainerView::EventShouldPropagate(const ui::Event& event) {
+  if (!event.IsLocatedEvent())
+    return true;
+  const ui::LocatedEvent* located_event = event.AsLocatedEvent();
+
+  if (!EventTypeCanCloseTabStrip(located_event->type()))
+    return true;
+
+  // If the event is in the container or control buttons, let it be handled.
+  for (views::View* view :
+       {static_cast<views::View*>(this), new_tab_button_, tab_counter_}) {
+    if (!view)
+      continue;
+
+    const gfx::Rect bounds_in_window =
+        view->ConvertRectToWidget(view->GetLocalBounds());
+    if (bounds_in_window.Contains(located_event->root_location()))
+      return true;
+  }
+
+  // Otherwise, cancel the event and close the container.
+  return false;
+}
+
 void WebUITabStripContainerView::AnimationEnded(
     const gfx::Animation* animation) {
   DCHECK_EQ(&animation_, animation);
@@ -309,6 +297,16 @@
       tab_contents_container_->size());
 }
 
+void WebUITabStripContainerView::AddedToWidget() {
+  GetWidget()->GetNativeView()->AddPreTargetHandler(auto_closer_.get());
+}
+
+void WebUITabStripContainerView::RemovedFromWidget() {
+  aura::Window* const native_view = GetWidget()->GetNativeView();
+  if (native_view)
+    native_view->RemovePreTargetHandler(auto_closer_.get());
+}
+
 int WebUITabStripContainerView::GetHeightForWidth(int w) const {
   return desired_height_ * animation_.GetCurrentValue();
 }
@@ -325,7 +323,9 @@
 }
 
 void WebUITabStripContainerView::OnViewBoundsChanged(View* observed_view) {
-  DCHECK_EQ(tab_contents_container_, observed_view);
+  if (observed_view != tab_contents_container_)
+    return;
+
   desired_height_ =
       TabStripUILayout::CalculateForWebViewportSize(observed_view->size())
           .CalculateContainerHeight();
@@ -338,3 +338,10 @@
       web_view_->GetWebContents()->GetWebUI()->GetController());
   tab_strip_ui->LayoutChanged();
 }
+
+void WebUITabStripContainerView::OnViewIsDeleting(View* observed_view) {
+  if (observed_view == new_tab_button_)
+    new_tab_button_ = nullptr;
+  else if (observed_view == tab_counter_)
+    tab_counter_ = nullptr;
+}
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h
index 5841c4b..28d262b 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h
@@ -44,7 +44,7 @@
 
   views::NativeViewHost* GetNativeViewHost();
 
-  // Control buttons.
+  // Control buttons. Each must only be called once.
   std::unique_ptr<ToolbarButton> CreateNewTabButton();
   std::unique_ptr<views::View> CreateTabCounter();
 
@@ -53,6 +53,11 @@
 
   void SetContainerTargetVisibility(bool target_visible);
 
+  // When the container is open, it intercepts most tap and click
+  // events. This checks if each event should be intercepted or passed
+  // through to its target.
+  bool EventShouldPropagate(const ui::Event& event);
+
   // TabStripUI::Embedder:
   void CloseContainer() override;
   void ShowContextMenuAtPoint(
@@ -61,6 +66,8 @@
   TabStripUILayout GetLayout() override;
 
   // views::View:
+  void AddedToWidget() override;
+  void RemovedFromWidget() override;
   int GetHeightForWidth(int w) const override;
 
   // gfx::AnimationDelegate:
@@ -72,10 +79,13 @@
 
   // views::ViewObserver:
   void OnViewBoundsChanged(View* observed_view) override;
+  void OnViewIsDeleting(View* observed_view) override;
 
   Browser* const browser_;
   views::WebView* const web_view_;
   views::View* const tab_contents_container_;
+  views::View* new_tab_button_ = nullptr;
+  views::View* tab_counter_ = nullptr;
 
   int desired_height_ = 0;
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index 02e0de155..16e9fea 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -194,11 +194,6 @@
   model_->SetSelectedLineState(OmniboxPopupModel::NORMAL);
 }
 
-void OmniboxPopupContentsView::ProvideButtonFocusHint(size_t line) {
-  OmniboxResultView* result = result_view_at(line);
-  result->ProvideButtonFocusHint();
-}
-
 bool OmniboxPopupContentsView::InExplicitExperimentalKeywordMode() {
   return model_->edit_model()->InExplicitExperimentalKeywordMode();
 }
@@ -300,6 +295,10 @@
   InvalidateLayout();
 }
 
+void OmniboxPopupContentsView::ProvideButtonFocusHint(size_t line) {
+  result_view_at(line)->ProvideButtonFocusHint();
+}
+
 void OmniboxPopupContentsView::OnMatchIconUpdated(size_t match_index) {
   result_view_at(match_index)->OnMatchIconUpdated();
 }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
index cfb7155..7f80af3 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.h
@@ -60,9 +60,6 @@
   // Called by the active result view to inform model (due to mouse event).
   void UnselectButton();
 
-  // Called to inform result view of button focus.
-  void ProvideButtonFocusHint(size_t line);
-
   // Returns whether we're in experimental keyword mode and the input gives
   // sufficient confidence that the user wants keyword mode.
   bool InExplicitExperimentalKeywordMode();
@@ -72,6 +69,7 @@
   void InvalidateLine(size_t line) override;
   void OnLineSelected(size_t line) override;
   void UpdatePopupAppearance() override;
+  void ProvideButtonFocusHint(size_t line) override;
   void OnMatchIconUpdated(size_t match_index) override;
   void OnDragCanceled() override;
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 309d734c..131cd34 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -671,8 +671,6 @@
       model()->popup_model()->SelectedLineHasTabMatch()) {
     model()->popup_model()->SetSelectedLineState(
         OmniboxPopupModel::BUTTON_FOCUSED);
-    popup_view_->ProvideButtonFocusHint(
-        model()->popup_model()->selected_line());
   }
 
   return true;
@@ -715,8 +713,6 @@
           OmniboxPopupModel::NORMAL) {
     model()->popup_model()->SetSelectedLineState(
         OmniboxPopupModel::BUTTON_FOCUSED);
-    popup_view_->ProvideButtonFocusHint(
-        model()->popup_model()->selected_line());
     return true;
   }
   return false;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index 115c695..b0c55e9 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -756,7 +756,7 @@
 
 views::View* ToolbarView::GetDefaultExtensionDialogAnchorView() {
   if (extensions_container_)
-    return extensions_container_;
+    return extensions_container_->extensions_button();
   return GetAppMenuButton();
 }
 
diff --git a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
index c5fc69c..c0e86a1 100644
--- a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/apps/launch_service/launch_service.h"
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
 #include "chrome/common/web_application_info.h"
+#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "third_party/blink/public/common/features.h"
 
@@ -81,6 +82,13 @@
         apps::LaunchService::Get(profile())->OpenApplication(params);
 
     navigation_observer.Wait();
+
+    // Attach the launchParams to the window so we can inspect them easily.
+    auto result = content::EvalJs(web_contents,
+                                  "launchQueue.setConsumer(launchParams => {"
+                                  "  window.launchParams = launchParams;"
+                                  "});");
+
     return web_contents;
   }
 
@@ -93,7 +101,7 @@
 
   const std::string app_id = InstallFileHandlingPWA();
   content::WebContents* web_contents = LaunchWithFiles(app_id, {});
-  EXPECT_EQ(0, content::EvalJs(web_contents, "launchParams.files.length"));
+  EXPECT_EQ(false, content::EvalJs(web_contents, "!!window.launchParams"));
 }
 
 IN_PROC_BROWSER_TEST_P(WebAppFileHandlingBrowserTest,
@@ -105,9 +113,10 @@
   content::WebContents* web_contents =
       LaunchWithFiles(app_id, {test_file_path});
 
-  EXPECT_EQ(1, content::EvalJs(web_contents, "launchParams.files.length"));
+  EXPECT_EQ(1,
+            content::EvalJs(web_contents, "window.launchParams.files.length"));
   EXPECT_EQ(test_file_path.BaseName().value(),
-            content::EvalJs(web_contents, "launchParams.files[0].name"));
+            content::EvalJs(web_contents, "window.launchParams.files[0].name"));
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_mac_unittest.mm b/chrome/browser/web_applications/components/web_app_shortcut_mac_unittest.mm
index 24d289f5..79941fa 100644
--- a/chrome/browser/web_applications/components/web_app_shortcut_mac_unittest.mm
+++ b/chrome/browser/web_applications/components/web_app_shortcut_mac_unittest.mm
@@ -20,6 +20,8 @@
 #include "base/path_service.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #import "chrome/common/mac/app_mode_common.h"
@@ -348,6 +350,11 @@
 }
 
 TEST_F(WebAppShortcutCreatorTest, DeleteAllShortcutsForProfile) {
+  base::test::ScopedFeatureList scoped_features;
+  scoped_features.InitWithFeatures(
+      /*enabled_features=*/{},
+      /*disabled_features=*/{features::kAppShimMultiProfile});
+
   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(app_data_dir_,
                                                        info_.get());
   base::FilePath profile_path = info_->profile_path;
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 6fa7942a..76a556a6 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -37,7 +37,7 @@
 // Enable the new multi-profile-aware app shim mode.
 // TODO(https://crbug.com/982024): Delete this flag when feature is complete.
 const base::Feature kAppShimMultiProfile{"AppShimMultiProfile",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
+                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Can be used to disable RemoteCocoa (hosting NSWindows for apps in the app
 // process). For debugging purposes only.
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 3db2f17..3a8e702 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -500,6 +500,7 @@
 const char* const kChromeHostURLs[] = {
     kChromeUIAboutHost,
     kChromeUIAccessibilityHost,
+    kChromeUIAutofillInternalsHost,
     kChromeUIBluetoothInternalsHost,
     kChromeUIChromeURLsHost,
     kChromeUIComponentsHost,
diff --git a/chrome/installer/mac/sign_chrome.py b/chrome/installer/mac/sign_chrome.py
index 54ecbb05..4f24534 100755
--- a/chrome/installer/mac/sign_chrome.py
+++ b/chrome/installer/mac/sign_chrome.py
@@ -81,9 +81,6 @@
     parser = argparse.ArgumentParser(
         description='Code sign and package Chrome for channel distribution.')
     parser.add_argument(
-        '--keychain',
-        help='The keychain to load the identity from. OBSOLETE AND INGORED.')
-    parser.add_argument(
         '--identity',
         required=True,
         help='The identity to sign everything but PKGs with.')
diff --git a/chrome/renderer/content_settings_agent_impl.cc b/chrome/renderer/content_settings_agent_impl.cc
index a0accbab..e3fd5300 100644
--- a/chrome/renderer/content_settings_agent_impl.cc
+++ b/chrome/renderer/content_settings_agent_impl.cc
@@ -125,9 +125,7 @@
   render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
       base::Bind(&ContentSettingsAgentImpl::OnContentSettingsAgentRequest,
                  base::Unretained(this)));
-
-  render_frame->GetBrowserInterfaceBroker()->GetInterface(
-      content_settings_manager_.BindNewPipeAndPassReceiver());
+  EnsureContentSettingsManagerConnection();
 
   content::RenderFrame* main_frame =
       render_frame->GetRenderView()->GetMainRenderFrame();
@@ -147,6 +145,16 @@
 
 ContentSettingsAgentImpl::~ContentSettingsAgentImpl() {}
 
+void ContentSettingsAgentImpl::EnsureContentSettingsManagerConnection() {
+  if (content_settings_manager_.is_bound() &&
+      content_settings_manager_.is_connected())
+    return;
+
+  content_settings_manager_.reset();
+  render_frame()->GetBrowserInterfaceBroker()->GetInterface(
+      content_settings_manager_.BindNewPipeAndPassReceiver());
+}
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 void ContentSettingsAgentImpl::SetExtensionDispatcher(
     extensions::Dispatcher* extension_dispatcher) {
@@ -204,6 +212,8 @@
   if (frame->Parent())
     return;  // Not a top-level navigation.
 
+  EnsureContentSettingsManagerConnection();
+
   if (!is_same_document_navigation) {
     // Clear "block" flags for the new page. This needs to happen before any of
     // |allowScript()|, |allowScriptFromSource()|, |allowImage()|, or
diff --git a/chrome/renderer/content_settings_agent_impl.h b/chrome/renderer/content_settings_agent_impl.h
index 91dfe7c..b9530532ae 100644
--- a/chrome/renderer/content_settings_agent_impl.h
+++ b/chrome/renderer/content_settings_agent_impl.h
@@ -165,6 +165,9 @@
   bool AllowStorageAccess(
       chrome::mojom::ContentSettingsManager::StorageType storage_type);
 
+  // Ensures that |content_settings_manager_| is connected.
+  void EnsureContentSettingsManagerConnection();
+
   mojo::Remote<chrome::mojom::ContentSettingsManager> content_settings_manager_;
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/renderer/extensions/cast_streaming_native_handler.cc b/chrome/renderer/extensions/cast_streaming_native_handler.cc
index dd9509e4..46b4abd 100644
--- a/chrome/renderer/extensions/cast_streaming_native_handler.cc
+++ b/chrome/renderer/extensions/cast_streaming_native_handler.cc
@@ -64,8 +64,6 @@
 constexpr char kInvalidAesKey[] = "Invalid value for AES key";
 constexpr char kInvalidDestination[] = "Invalid destination";
 constexpr char kInvalidRtpParams[] = "Invalid value for RTP params";
-constexpr char kInvalidLatency[] = "Invalid value for max_latency. (0-1000)";
-constexpr char kInvalidRtpTimebase[] = "Invalid rtp_timebase. (1000-1000000)";
 constexpr char kInvalidStreamArgs[] = "Invalid stream arguments";
 constexpr char kRtpStreamNotFound[] = "The RTP stream cannot be found";
 constexpr char kUdpTransportNotFound[] = "The UDP transport cannot be found";
@@ -800,90 +798,6 @@
   return NULL;
 }
 
-bool CastStreamingNativeHandler::FrameReceiverConfigFromArg(
-    v8::Isolate* isolate,
-    const v8::Local<v8::Value>& arg,
-    media::cast::FrameReceiverConfig* config) const {
-  std::unique_ptr<base::Value> params_value =
-      V8ValueConverter::Create()->FromV8Value(arg, context()->v8_context());
-  if (!params_value) {
-    isolate->ThrowException(v8::Exception::TypeError(
-        v8::String::NewFromUtf8(isolate, kUnableToConvertParams,
-                                v8::NewStringType::kNormal)
-            .ToLocalChecked()));
-    return false;
-  }
-  std::unique_ptr<RtpReceiverParams> params =
-      RtpReceiverParams::FromValue(*params_value);
-  if (!params) {
-    isolate->ThrowException(v8::Exception::TypeError(
-        v8::String::NewFromUtf8(isolate, kInvalidRtpParams,
-                                v8::NewStringType::kNormal)
-            .ToLocalChecked()));
-    return false;
-  }
-
-  config->receiver_ssrc = params->receiver_ssrc;
-  config->sender_ssrc = params->sender_ssrc;
-  config->rtp_max_delay_ms = params->max_latency;
-  if (config->rtp_max_delay_ms < 0 || config->rtp_max_delay_ms > 1000) {
-    isolate->ThrowException(v8::Exception::TypeError(
-        v8::String::NewFromUtf8(isolate, kInvalidLatency,
-                                v8::NewStringType::kNormal)
-            .ToLocalChecked()));
-    return false;
-  }
-  config->channels = 2;
-  if (params->codec_name == "OPUS") {
-    config->codec = media::cast::CODEC_AUDIO_OPUS;
-    config->rtp_timebase = 48000;
-    config->rtp_payload_type = media::cast::RtpPayloadType::AUDIO_OPUS;
-  } else if (params->codec_name == "PCM16") {
-    config->codec = media::cast::CODEC_AUDIO_PCM16;
-    config->rtp_timebase = 48000;
-    config->rtp_payload_type = media::cast::RtpPayloadType::AUDIO_PCM16;
-  } else if (params->codec_name == "AAC") {
-    config->codec = media::cast::CODEC_AUDIO_AAC;
-    config->rtp_timebase = 48000;
-    config->rtp_payload_type = media::cast::RtpPayloadType::AUDIO_AAC;
-  } else if (params->codec_name == "VP8") {
-    config->codec = media::cast::CODEC_VIDEO_VP8;
-    config->rtp_timebase = 90000;
-    config->rtp_payload_type = media::cast::RtpPayloadType::VIDEO_VP8;
-  } else if (params->codec_name == "H264") {
-    config->codec = media::cast::CODEC_VIDEO_H264;
-    config->rtp_timebase = 90000;
-    config->rtp_payload_type = media::cast::RtpPayloadType::VIDEO_H264;
-  }
-  if (params->rtp_timebase) {
-    config->rtp_timebase = *params->rtp_timebase;
-    if (config->rtp_timebase < 1000 || config->rtp_timebase > 1000000) {
-      isolate->ThrowException(v8::Exception::TypeError(
-          v8::String::NewFromUtf8(isolate, kInvalidRtpTimebase,
-                                  v8::NewStringType::kNormal)
-              .ToLocalChecked()));
-      return false;
-    }
-  }
-  if (params->aes_key &&
-      !HexDecode(*params->aes_key, &config->aes_key)) {
-    isolate->ThrowException(
-        v8::Exception::Error(v8::String::NewFromUtf8(isolate, kInvalidAesKey,
-                                                     v8::NewStringType::kNormal)
-                                 .ToLocalChecked()));
-    return false;
-  }
-  if (params->aes_iv_mask &&
-      !HexDecode(*params->aes_iv_mask, &config->aes_iv_mask)) {
-    isolate->ThrowException(
-        v8::Exception::Error(v8::String::NewFromUtf8(isolate, kInvalidAesIvMask,
-                                                     v8::NewStringType::kNormal)
-                                 .ToLocalChecked()));
-    return false;
-  }
-  return true;
-}
-
 bool CastStreamingNativeHandler::IPEndPointFromArg(
     v8::Isolate* isolate,
     const v8::Local<v8::Value>& arg,
diff --git a/chrome/renderer/extensions/cast_streaming_native_handler.h b/chrome/renderer/extensions/cast_streaming_native_handler.h
index 6fdcc7b2..9f135b3 100644
--- a/chrome/renderer/extensions/cast_streaming_native_handler.h
+++ b/chrome/renderer/extensions/cast_streaming_native_handler.h
@@ -25,12 +25,6 @@
 class IPEndPoint;
 }
 
-namespace media {
-namespace cast {
-struct FrameReceiverConfig;
-}
-}
-
 namespace extensions {
 class NativeExtensionBindingsSystem;
 
@@ -103,15 +97,6 @@
   CastRtpStream* GetRtpStreamOrThrow(int stream_id) const;
   CastUdpTransport* GetUdpTransportOrThrow(int transport_id) const;
 
-  // Fills out a media::cast::FrameReceiverConfig from the v8
-  // equivialent. (cast.streaming.receiverSession.RtpReceiverParams)
-  // Returns true if everything was ok, raises a v8 exception and
-  // returns false if anything went wrong.
-  bool FrameReceiverConfigFromArg(
-      v8::Isolate* isolate,
-      const v8::Local<v8::Value>& arg,
-      media::cast::FrameReceiverConfig* config) const;
-
   bool IPEndPointFromArg(v8::Isolate* isolate,
                          const v8::Local<v8::Value>& arg,
                          net::IPEndPoint* ip_endpoint) const;
diff --git a/chromecast/bindings/bindings_manager_cast_browsertest.cc b/chromecast/bindings/bindings_manager_cast_browsertest.cc
index fe99d318..43ddcc5 100644
--- a/chromecast/bindings/bindings_manager_cast_browsertest.cc
+++ b/chromecast/bindings/bindings_manager_cast_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/string_piece.h"
@@ -266,7 +267,9 @@
   DISALLOW_COPY_AND_ASSIGN(MockWebContentsDelegate);
 };
 
-class MockCastWebContentsDelegate : public CastWebContents::Delegate {
+class MockCastWebContentsDelegate
+    : public base::SupportsWeakPtr<MockCastWebContentsDelegate>,
+      public CastWebContents::Delegate {
  public:
   MockCastWebContentsDelegate() {}
   ~MockCastWebContentsDelegate() override = default;
@@ -311,9 +314,10 @@
 
     // CastWebContents::Delegate must be set for receiving PageStateChanged
     // event.
-    CastWebContents::InitParams init_params = {
-        &mock_cast_wc_delegate_ /* delegate */, false /* enabled_for_dev */,
-        false /* use_cma_renderer */, true /* is_root_window */};
+    CastWebContents::InitParams init_params;
+    init_params.delegate = mock_cast_wc_delegate_.AsWeakPtr();
+    init_params.is_root_window = true;
+
     cast_web_contents_ =
         std::make_unique<CastWebContentsImpl>(web_contents_.get(), init_params);
     title_change_observer_.Observe(cast_web_contents_.get());
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 2320297..3371115 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -89,8 +89,8 @@
     "cast_system_memory_pressure_evaluator_adjuster.h",
     "cast_web_contents_impl.cc",
     "cast_web_contents_impl.h",
-    "cast_web_contents_manager.cc",
-    "cast_web_contents_manager.h",
+    "cast_web_service.cc",
+    "cast_web_service.h",
     "cast_web_view_default.cc",
     "cast_web_view_default.h",
     "cast_web_view_factory.cc",
@@ -212,7 +212,7 @@
       "android/cast_content_window_android.cc",
       "android/cast_content_window_android.h",
       "android/cast_metrics_helper_android.cc",
-      "android/cast_web_contents_manager_android.cc",
+      "android/cast_web_service_android.cc",
     ]
     deps += [
       ":jni_headers",
@@ -248,7 +248,7 @@
     sources += [
       "cast_content_window_aura.cc",
       "cast_content_window_aura.h",
-      "cast_web_contents_manager_aura.cc",
+      "cast_web_service_aura.cc",
     ]
 
     deps += [
diff --git a/chromecast/browser/android/cast_content_window_android.cc b/chromecast/browser/android/cast_content_window_android.cc
index 018932f..7a1193d5 100644
--- a/chromecast/browser/android/cast_content_window_android.cc
+++ b/chromecast/browser/android/cast_content_window_android.cc
@@ -39,15 +39,14 @@
 
 CastContentWindowAndroid::CastContentWindowAndroid(
     const CastContentWindow::CreateParams& params)
-    : delegate_(params.delegate),
+    : CastContentWindow(params),
+      activity_id_(delegate_->GetId()),
       java_window_(CreateJavaWindow(reinterpret_cast<jlong>(this),
                                     params.is_headless,
                                     params.enable_touch_input,
                                     params.is_remote_control_mode,
                                     params.turn_on_screen,
-                                    params.session_id)) {
-  DCHECK(delegate_);
-}
+                                    params.session_id)) {}
 
 CastContentWindowAndroid::~CastContentWindowAndroid() {
   JNIEnv* env = base::android::AttachCurrentThread();
@@ -85,7 +84,9 @@
 void CastContentWindowAndroid::OnActivityStopped(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller) {
-  delegate_->OnWindowDestroyed();
+  if (delegate_) {
+    delegate_->OnWindowDestroyed();
+  }
 }
 
 void CastContentWindowAndroid::RequestVisibility(
@@ -102,7 +103,9 @@
 
 void CastContentWindowAndroid::NotifyVisibilityChange(
     VisibilityType visibility_type) {
-  delegate_->OnVisibilityChange(visibility_type);
+  if (delegate_) {
+    delegate_->OnVisibilityChange(visibility_type);
+  }
   for (auto& observer : observer_list_) {
     observer.OnVisibilityChange(visibility_type);
   }
@@ -117,7 +120,10 @@
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
     int gesture_type) {
-  return delegate_->ConsumeGesture(static_cast<GestureType>(gesture_type));
+  if (delegate_) {
+    return delegate_->ConsumeGesture(static_cast<GestureType>(gesture_type));
+  }
+  return false;
 }
 
 void CastContentWindowAndroid::OnVisibilityChange(
@@ -130,7 +136,7 @@
 base::android::ScopedJavaLocalRef<jstring> CastContentWindowAndroid::GetId(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller) {
-  return ConvertUTF8ToJavaString(env, delegate_->GetId());
+  return ConvertUTF8ToJavaString(env, activity_id_);
 }
 
 }  // namespace chromecast
diff --git a/chromecast/browser/android/cast_content_window_android.h b/chromecast/browser/android/cast_content_window_android.h
index 67ce1f8..57e9558 100644
--- a/chromecast/browser/android/cast_content_window_android.h
+++ b/chromecast/browser/android/cast_content_window_android.h
@@ -53,7 +53,7 @@
       const base::android::JavaParamRef<jobject>& jcaller);
 
  private:
-  CastContentWindow::Delegate* const delegate_;
+  const std::string activity_id_;
   base::android::ScopedJavaGlobalRef<jobject> java_window_;
 
   DISALLOW_COPY_AND_ASSIGN(CastContentWindowAndroid);
diff --git a/chromecast/browser/android/cast_web_contents_manager_android.cc b/chromecast/browser/android/cast_web_service_android.cc
similarity index 76%
rename from chromecast/browser/android/cast_web_contents_manager_android.cc
rename to chromecast/browser/android/cast_web_service_android.cc
index e06487c..b6ccdaa 100644
--- a/chromecast/browser/android/cast_web_contents_manager_android.cc
+++ b/chromecast/browser/android/cast_web_service_android.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromecast/browser/cast_web_contents_manager.h"
+#include "chromecast/browser/cast_web_service.h"
 
 #include <memory>
 
@@ -10,7 +10,7 @@
 
 namespace chromecast {
 
-std::unique_ptr<CastContentWindow> CastWebContentsManager::CreateWindow(
+std::unique_ptr<CastContentWindow> CastWebService::CreateWindow(
     const CastContentWindow::CreateParams& params) {
   return std::make_unique<CastContentWindowAndroid>(params);
 }
diff --git a/chromecast/browser/cast_content_gesture_handler.cc b/chromecast/browser/cast_content_gesture_handler.cc
index 141f649..a6390bf 100644
--- a/chromecast/browser/cast_content_gesture_handler.cc
+++ b/chromecast/browser/cast_content_gesture_handler.cc
@@ -13,7 +13,7 @@
 }  // namespace
 
 CastContentGestureHandler::CastContentGestureHandler(
-    CastContentWindow::Delegate* delegate,
+    base::WeakPtr<CastContentWindow::Delegate> delegate,
     bool enable_top_drag_gesture)
     : priority_(Priority::NONE),
       enable_top_drag_gesture_(enable_top_drag_gesture),
@@ -25,11 +25,13 @@
 }
 
 CastContentGestureHandler::CastContentGestureHandler(
-    CastContentWindow::Delegate* delegate)
+    base::WeakPtr<CastContentWindow::Delegate> delegate)
     : CastContentGestureHandler(
           delegate,
           GetSwitchValueBoolean(switches::kEnableTopDragGesture, false)) {}
 
+CastContentGestureHandler::~CastContentGestureHandler() = default;
+
 void CastContentGestureHandler::SetPriority(
     CastGestureHandler::Priority priority) {
   priority_ = priority;
@@ -41,6 +43,8 @@
 
 bool CastContentGestureHandler::CanHandleSwipe(
     CastSideSwipeOrigin swipe_origin) {
+  if (!delegate_)
+    return false;
   return delegate_->CanHandleGesture(GestureForSwipeOrigin(swipe_origin));
 }
 
@@ -66,7 +70,6 @@
   if (!CanHandleSwipe(swipe_origin)) {
     return;
   }
-
   GestureType gesture_type = GestureForSwipeOrigin(swipe_origin);
 
   switch (event) {
@@ -102,7 +105,7 @@
 
 void CastContentGestureHandler::HandleTapDownGesture(
     const gfx::Point& touch_location) {
-  if (!delegate_->CanHandleGesture(GestureType::TAP_DOWN)) {
+  if (!delegate_ || !delegate_->CanHandleGesture(GestureType::TAP_DOWN)) {
     return;
   }
   delegate_->ConsumeGesture(GestureType::TAP_DOWN);
@@ -110,7 +113,7 @@
 
 void CastContentGestureHandler::HandleTapGesture(
     const gfx::Point& touch_location) {
-  if (!delegate_->CanHandleGesture(GestureType::TAP)) {
+  if (!delegate_ || !delegate_->CanHandleGesture(GestureType::TAP)) {
     return;
   }
   delegate_->ConsumeGesture(GestureType::TAP);
diff --git a/chromecast/browser/cast_content_gesture_handler.h b/chromecast/browser/cast_content_gesture_handler.h
index 1291ac04..2071c67d 100644
--- a/chromecast/browser/cast_content_gesture_handler.h
+++ b/chromecast/browser/cast_content_gesture_handler.h
@@ -6,6 +6,7 @@
 #define CHROMECAST_BROWSER_CAST_CONTENT_GESTURE_HANDLER_H_
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/timer/elapsed_timer.h"
 #include "chromecast/browser/cast_content_window.h"
 #include "chromecast/graphics/gestures/cast_gesture_handler.h"
@@ -16,7 +17,9 @@
 // CastContentWindow::Delegate.
 class CastContentGestureHandler : public CastGestureHandler {
  public:
-  explicit CastContentGestureHandler(CastContentWindow::Delegate* delegate);
+  explicit CastContentGestureHandler(
+      base::WeakPtr<CastContentWindow::Delegate> delegate);
+  ~CastContentGestureHandler() override;
 
   // CastGestureHandler implementation:
   Priority GetPriority() override;
@@ -31,7 +34,7 @@
 
  private:
   friend class CastContentGestureHandlerTest;
-  CastContentGestureHandler(CastContentWindow::Delegate* delegate,
+  CastContentGestureHandler(base::WeakPtr<CastContentWindow::Delegate> delegate,
                             bool enable_top_drag_gesture);
   GestureType GestureForSwipeOrigin(CastSideSwipeOrigin swipe_origin);
 
@@ -41,7 +44,7 @@
 
   // Number of pixels past swipe origin to consider as a back gesture.
   const int back_horizontal_threshold_;
-  CastContentWindow::Delegate* const delegate_;
+  base::WeakPtr<CastContentWindow::Delegate> const delegate_;
   base::ElapsedTimer current_swipe_time_;
 };
 
diff --git a/chromecast/browser/cast_content_gesture_handler_test.cc b/chromecast/browser/cast_content_gesture_handler_test.cc
index 03e1917f2..271deaf 100644
--- a/chromecast/browser/cast_content_gesture_handler_test.cc
+++ b/chromecast/browser/cast_content_gesture_handler_test.cc
@@ -4,6 +4,7 @@
 
 #include "chromecast/browser/cast_content_gesture_handler.h"
 
+#include "base/memory/weak_ptr.h"
 #include "chromecast/base/chromecast_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_base.h"
@@ -39,7 +40,9 @@
 
 }  // namespace
 
-class MockCastContentWindowDelegate : public CastContentWindow::Delegate {
+class MockCastContentWindowDelegate
+    : public base::SupportsWeakPtr<MockCastContentWindowDelegate>,
+      public CastContentWindow::Delegate {
  public:
   ~MockCastContentWindowDelegate() override = default;
 
@@ -56,7 +59,7 @@
 
 class CastContentGestureHandlerTest : public testing::Test {
  public:
-  CastContentGestureHandlerTest() : dispatcher_(&delegate_, true) {}
+  CastContentGestureHandlerTest() : dispatcher_(delegate_.AsWeakPtr(), true) {}
 
  protected:
   MockCastContentWindowDelegate delegate_;
diff --git a/chromecast/browser/cast_content_window.cc b/chromecast/browser/cast_content_window.cc
index 90adeab..ebe3ea59 100644
--- a/chromecast/browser/cast_content_window.cc
+++ b/chromecast/browser/cast_content_window.cc
@@ -6,13 +6,15 @@
 
 namespace chromecast {
 
-CastContentWindow::CastContentWindow() = default;
+CastContentWindow::CastContentWindow(const CreateParams& params)
+    : delegate_(params.delegate) {}
 
 CastContentWindow::~CastContentWindow() = default;
 
 CastContentWindow::CreateParams::CreateParams() = default;
 CastContentWindow::CreateParams::CreateParams(const CreateParams& other) =
     default;
+CastContentWindow::CreateParams::~CreateParams() = default;
 
 void CastContentWindow::AddObserver(Observer* observer) {
   observer_list_.AddObserver(observer);
diff --git a/chromecast/browser/cast_content_window.h b/chromecast/browser/cast_content_window.h
index 3973f6e..c62b1b5b 100644
--- a/chromecast/browser/cast_content_window.h
+++ b/chromecast/browser/cast_content_window.h
@@ -128,8 +128,10 @@
 
   // The parameters used to create a CastContentWindow instance.
   struct CreateParams {
-    // The delegate for the CastContentWindow. Must be non-null.
-    Delegate* delegate = nullptr;
+    // The delegate for the CastContentWindow. Must be non-null. If the delegate
+    // is destroyed before CastContentWindow, the WeakPtr will be invalidated on
+    // the main UI thread.
+    base::WeakPtr<Delegate> delegate = nullptr;
 
     // True if this CastContentWindow is for a headless build.
     bool is_headless = false;
@@ -152,6 +154,7 @@
 
     CreateParams();
     CreateParams(const CreateParams& other);
+    ~CreateParams();
   };
 
   class Observer : public base::CheckedObserver {
@@ -163,7 +166,7 @@
     ~Observer() override {}
   };
 
-  CastContentWindow();
+  explicit CastContentWindow(const CreateParams& params);
   virtual ~CastContentWindow();
 
   // Creates a full-screen window for |cast_web_contents| and displays it if
@@ -218,6 +221,7 @@
   void RemoveObserver(Observer* observer);
 
  protected:
+  base::WeakPtr<Delegate> delegate_;
   base::ObserverList<Observer> observer_list_;
 };
 
diff --git a/chromecast/browser/cast_content_window_aura.cc b/chromecast/browser/cast_content_window_aura.cc
index e9b1b76d..068cae6 100644
--- a/chromecast/browser/cast_content_window_aura.cc
+++ b/chromecast/browser/cast_content_window_aura.cc
@@ -72,16 +72,14 @@
 CastContentWindowAura::CastContentWindowAura(
     const CastContentWindow::CreateParams& params,
     CastWindowManager* window_manager)
-    : delegate_(params.delegate),
+    : CastContentWindow(params),
       window_manager_(window_manager),
       gesture_dispatcher_(
           std::make_unique<CastContentGestureHandler>(delegate_)),
       gesture_priority_(params.gesture_priority),
       is_touch_enabled_(params.enable_touch_input),
       window_(nullptr),
-      has_screen_access_(false) {
-  DCHECK(delegate_);
-}
+      has_screen_access_(false) {}
 
 CastContentWindowAura::~CastContentWindowAura() {
   CastWebContents::Observer::Observe(nullptr);
@@ -167,7 +165,9 @@
 
 void CastContentWindowAura::NotifyVisibilityChange(
     VisibilityType visibility_type) {
-  delegate_->OnVisibilityChange(visibility_type);
+  if (delegate_) {
+    delegate_->OnVisibilityChange(visibility_type);
+  }
   for (auto& observer : observer_list_) {
     observer.OnVisibilityChange(visibility_type);
   }
diff --git a/chromecast/browser/cast_content_window_aura.h b/chromecast/browser/cast_content_window_aura.h
index 73e826b..bff975c2 100644
--- a/chromecast/browser/cast_content_window_aura.h
+++ b/chromecast/browser/cast_content_window_aura.h
@@ -50,7 +50,6 @@
   void OnWindowDestroyed(aura::Window* window) override;
 
  private:
-  CastContentWindow::Delegate* const delegate_;
   CastWindowManager* const window_manager_;
 
   // Utility class for detecting and dispatching gestures to delegates.
diff --git a/chromecast/browser/cast_web_contents.cc b/chromecast/browser/cast_web_contents.cc
index ff2d4cc7..855d153 100644
--- a/chromecast/browser/cast_web_contents.cc
+++ b/chromecast/browser/cast_web_contents.cc
@@ -33,4 +33,8 @@
   cast_web_contents_ = nullptr;
 }
 
+CastWebContents::InitParams::InitParams() = default;
+CastWebContents::InitParams::InitParams(const InitParams& other) = default;
+CastWebContents::InitParams::~InitParams() = default;
+
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_web_contents.h b/chromecast/browser/cast_web_contents.h
index cb61f16..6f08898 100644
--- a/chromecast/browser/cast_web_contents.h
+++ b/chromecast/browser/cast_web_contents.h
@@ -56,7 +56,7 @@
 // We consider the CastWebContents to be in a LOADED state when the content of
 // the main frame is fully loaded and running (all resources fetched, JS is
 // running). Iframes might still be loading in this case, but in general we
-// consider the page to be in a presentable state at this stage. It is
+// consider the page to be in a presentable state at this stage, so it is
 // appropriate to display the WebContents to the user.
 //
 // During or after the page is loaded, there are multiple error conditions that
@@ -184,8 +184,10 @@
 
   // Initialization parameters for CastWebContents.
   struct InitParams {
-    // Delegate for CastWebContents. This can be null for an inner WebContents.
-    Delegate* delegate = nullptr;
+    // The delegate for the CastWebContents. Must be non-null. If the delegate
+    // is destroyed before CastWebContents, the WeakPtr will be invalidated on
+    // the main UI thread.
+    base::WeakPtr<Delegate> delegate = nullptr;
     // Enable development mode for this CastWebCastWebContents. Whitelists
     // certain functionality for the WebContents, like remote debugging and
     // debugging interfaces.
@@ -204,6 +206,10 @@
     // Background color for the WebContents view. If not provided, the color
     // will fall back to the platform default.
     BackgroundColor background_color = BackgroundColor::NONE;
+
+    InitParams();
+    InitParams(const InitParams& other);
+    ~InitParams();
   };
 
   // Page state for the main frame.
@@ -234,9 +240,6 @@
   // Initialization and Setup
   // ===========================================================================
 
-  // Set the delegate. SetDelegate(nullptr) can be used to stop notifications.
-  virtual void SetDelegate(Delegate* delegate) = 0;
-
   // Add a set of features for all renderers in the WebContents. Features are
   // configured when `CastWebContents::RenderFrameCreated` is invoked.
   virtual void AddRendererFeatures(std::vector<RendererFeature> features) = 0;
diff --git a/chromecast/browser/cast_web_contents_browsertest.cc b/chromecast/browser/cast_web_contents_browsertest.cc
index 3a3960bd..bd3fdeb7 100644
--- a/chromecast/browser/cast_web_contents_browsertest.cc
+++ b/chromecast/browser/cast_web_contents_browsertest.cc
@@ -101,7 +101,9 @@
 // =============================================================================
 // Mocks
 // =============================================================================
-class MockCastWebContentsDelegate : public CastWebContents::Delegate {
+class MockCastWebContentsDelegate
+    : public base::SupportsWeakPtr<MockCastWebContentsDelegate>,
+      public CastWebContents::Delegate {
  public:
   MockCastWebContentsDelegate() {}
   ~MockCastWebContentsDelegate() override = default;
@@ -273,9 +275,10 @@
     web_contents_ = content::WebContents::Create(create_params);
     web_contents_->SetDelegate(&mock_wc_delegate_);
 
-    CastWebContents::InitParams init_params = {
-        &mock_cast_wc_delegate_, false /* enabled_for_dev */,
-        false /* use_cma_renderer */, true /* is_root_window */};
+    CastWebContents::InitParams init_params;
+    init_params.delegate = mock_cast_wc_delegate_.AsWeakPtr();
+    init_params.is_root_window = true;
+
     cast_web_contents_ =
         std::make_unique<CastWebContentsImpl>(web_contents_.get(), init_params);
     mock_cast_wc_observer_.Observe(cast_web_contents_.get());
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index 5b63dea..20d4550 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -225,11 +225,6 @@
     media_blocker_->EnableBackgroundVideoPlayback(enabled);
 }
 
-void CastWebContentsImpl::SetDelegate(CastWebContents::Delegate* delegate) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  delegate_ = delegate;
-}
-
 void CastWebContentsImpl::AllowWebAndMojoWebUiBindings() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   content::RenderViewHost* rvh = web_contents_->GetRenderViewHost();
@@ -411,6 +406,12 @@
     mojo::ScopedMessagePipeHandle* interface_pipe) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (!delegate_) {
+    // Don't let the page bind any more interfaces at this point, since the
+    // owning client has been torn down. This is a cheap trick so that all of
+    // the interfaces don't have to provide binder callbacks with WeakPtr.
+    return;
+  }
   if (binder_registry_.TryBindInterface(interface_name, interface_pipe)) {
     return;
   }
@@ -712,11 +713,12 @@
     content::WebContents* inner_web_contents) {
   if (!handle_inner_contents_ || !delegate_)
     return;
-  auto result = inner_contents_.insert(std::make_unique<CastWebContentsImpl>(
-      inner_web_contents,
-      InitParams{nullptr, enabled_for_dev_, false /* use_cma_renderer */,
-                 false /* is_root_window */, false /* handle_inner_contents */,
-                 false /* use_media_blocker */, view_background_color_}));
+  InitParams params;
+  params.delegate = delegate_;
+  params.enabled_for_dev = enabled_for_dev_;
+  params.background_color = view_background_color_;
+  auto result = inner_contents_.insert(
+      std::make_unique<CastWebContentsImpl>(inner_web_contents, params));
   delegate_->InnerContentsCreated(result.first->get(), this);
 }
 
diff --git a/chromecast/browser/cast_web_contents_impl.h b/chromecast/browser/cast_web_contents_impl.h
index 9eb6604..c7b671c 100644
--- a/chromecast/browser/cast_web_contents_impl.h
+++ b/chromecast/browser/cast_web_contents_impl.h
@@ -45,7 +45,6 @@
 
   // CastWebContents implementation:
   int tab_id() const override;
-  void SetDelegate(Delegate* delegate) override;
   void AddRendererFeatures(std::vector<RendererFeature> features) override;
   void AllowWebAndMojoWebUiBindings() override;
   void ClearRenderWidgetHostView() override;
@@ -67,8 +66,6 @@
       const std::string& target_origin,
       const std::string& data,
       std::vector<mojo::ScopedMessagePipeHandle> channels) override;
-
-  // Observer interface:
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
 
@@ -141,7 +138,7 @@
   std::vector<chromecast::shell::mojom::FeaturePtr> GetRendererFeatures();
 
   content::WebContents* web_contents_;
-  Delegate* delegate_;
+  base::WeakPtr<Delegate> delegate_;
   PageState page_state_;
   PageState last_state_;
   const bool enabled_for_dev_;
@@ -168,10 +165,10 @@
 
   base::ObserverList<Observer>::Unchecked observer_list_;
 
-  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
   service_manager::BinderRegistry binder_registry_;
 
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
   // Map of InterfaceSet -> InterfaceProvider pointer.
   base::flat_map<InterfaceSet, service_manager::InterfaceProvider*>
       interface_providers_map_;
diff --git a/chromecast/browser/cast_web_contents_manager.cc b/chromecast/browser/cast_web_contents_manager.cc
deleted file mode 100644
index 944c202..0000000
--- a/chromecast/browser/cast_web_contents_manager.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromecast/browser/cast_web_contents_manager.h"
-
-#include <algorithm>
-#include <memory>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/sequenced_task_runner.h"
-#include "base/stl_util.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "base/time/time.h"
-#include "chromecast/browser/cast_web_view_default.h"
-#include "chromecast/browser/cast_web_view_factory.h"
-#include "chromecast/chromecast_buildflags.h"
-#include "content/public/browser/media_session.h"
-#include "content/public/browser/site_instance.h"
-#include "content/public/browser/web_contents.h"
-
-namespace chromecast {
-
-CastWebContentsManager::CastWebContentsManager(
-    content::BrowserContext* browser_context,
-    CastWebViewFactory* web_view_factory,
-    CastWindowManager* window_manager)
-    : browser_context_(browser_context),
-      web_view_factory_(web_view_factory),
-      window_manager_(window_manager),
-      task_runner_(base::SequencedTaskRunnerHandle::Get()),
-      weak_factory_(this) {
-  DCHECK(browser_context_);
-  DCHECK(web_view_factory_);
-  DCHECK(task_runner_);
-}
-
-CastWebContentsManager::~CastWebContentsManager() = default;
-
-std::unique_ptr<CastWebView> CastWebContentsManager::CreateWebView(
-    const CastWebView::CreateParams& params,
-    scoped_refptr<content::SiteInstance> site_instance,
-    const GURL& initial_url) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return web_view_factory_->CreateWebView(
-      params, this, std::move(site_instance), initial_url);
-}
-
-std::unique_ptr<CastWebView> CastWebContentsManager::CreateWebView(
-    const CastWebView::CreateParams& params,
-    const GURL& initial_url) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return web_view_factory_->CreateWebView(
-      params, this,
-      content::SiteInstance::CreateForURL(browser_context_, initial_url),
-      initial_url);
-}
-
-void CastWebContentsManager::DelayWebContentsDeletion(
-    std::unique_ptr<content::WebContents> web_contents,
-    base::TimeDelta time_delta) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(web_contents);
-  if (time_delta <= base::TimeDelta()) {
-    LOG(INFO) << "Deleting WebContents for " << web_contents->GetVisibleURL();
-    web_contents.reset();
-    return;
-  }
-  auto* web_contents_ptr = web_contents.get();
-  // Suspend the MediaSession to free up media resources for the next content
-  // window.
-  content::MediaSession::Get(web_contents_ptr)
-      ->Suspend(content::MediaSession::SuspendType::kSystem);
-  LOG(INFO) << "WebContents for " << web_contents->GetVisibleURL()
-            << " will be deleted in " << time_delta.InMilliseconds()
-            << " milliseconds.";
-  expiring_web_contents_.insert(std::move(web_contents));
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&CastWebContentsManager::DeleteWebContents,
-                     weak_factory_.GetWeakPtr(), web_contents_ptr),
-      time_delta);
-}
-
-void CastWebContentsManager::DeleteWebContents(
-    content::WebContents* web_contents) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(web_contents);
-  LOG(INFO) << "Deleting WebContents for " << web_contents->GetVisibleURL();
-  base::EraseIf(
-      expiring_web_contents_,
-      [web_contents](const std::unique_ptr<content::WebContents>& ptr) {
-        return ptr.get() == web_contents;
-      });
-}
-
-}  // namespace chromecast
diff --git a/chromecast/browser/cast_web_contents_manager.h b/chromecast/browser/cast_web_contents_manager.h
deleted file mode 100644
index 3aaedd4..0000000
--- a/chromecast/browser/cast_web_contents_manager.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMECAST_BROWSER_CAST_WEB_CONTENTS_MANAGER_H_
-#define CHROMECAST_BROWSER_CAST_WEB_CONTENTS_MANAGER_H_
-
-#include <memory>
-
-#include "base/containers/flat_set.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/sequence_checker.h"
-#include "chromecast/browser/cast_web_view.h"
-
-namespace base {
-class SequencedTaskRunner;
-}  // namespace base
-
-namespace content {
-class BrowserContext;
-class WebContents;
-}  // namespace content
-
-namespace chromecast {
-
-class CastWebViewFactory;
-class CastWindowManager;
-
-// This class dispenses CastWebView objects which are used to wrap WebContents
-// in cast_shell. This class can take ownership of a WebContents instance when
-// the page is in the process of tearing down; we cannot simply post a delayed
-// task since WebContents may try to use browser objects that get deleted as a
-// result of browser shutdown.
-class CastWebContentsManager {
- public:
-  CastWebContentsManager(content::BrowserContext* browser_context,
-                         CastWebViewFactory* web_view_factory,
-                         CastWindowManager* window_manager);
-  ~CastWebContentsManager();
-
-  std::unique_ptr<CastWebView> CreateWebView(
-      const CastWebView::CreateParams& params,
-      scoped_refptr<content::SiteInstance> site_instance,
-      const GURL& initial_url);
-
-  std::unique_ptr<CastWebView> CreateWebView(
-      const CastWebView::CreateParams& params,
-      const GURL& initial_url);
-
-  std::unique_ptr<CastContentWindow> CreateWindow(
-      const CastContentWindow::CreateParams& params);
-
-  // Take ownership of |web_contents| and delete after |time_delta|, or sooner
-  // if necessary.
-  void DelayWebContentsDeletion(
-      std::unique_ptr<content::WebContents> web_contents,
-      base::TimeDelta time_delta);
-
- private:
-  void DeleteWebContents(content::WebContents* web_contents);
-
-  content::BrowserContext* const browser_context_;
-  CastWebViewFactory* const web_view_factory_;
-  CastWindowManager* const window_manager_;
-  base::flat_set<std::unique_ptr<content::WebContents>> expiring_web_contents_;
-
-  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-  base::WeakPtrFactory<CastWebContentsManager> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(CastWebContentsManager);
-};
-
-}  // namespace chromecast
-
-#endif  // CHROMECAST_BROWSER_CAST_WEB_CONTENTS_MANAGER_H_
diff --git a/chromecast/browser/cast_web_service.cc b/chromecast/browser/cast_web_service.cc
new file mode 100644
index 0000000..0897edf3
--- /dev/null
+++ b/chromecast/browser/cast_web_service.cc
@@ -0,0 +1,104 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/browser/cast_web_service.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/sequenced_task_runner.h"
+#include "base/stl_util.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "chromecast/browser/cast_web_view_default.h"
+#include "chromecast/browser/cast_web_view_factory.h"
+#include "chromecast/chromecast_buildflags.h"
+#include "content/public/browser/media_session.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+
+namespace chromecast {
+
+CastWebService::CastWebService(content::BrowserContext* browser_context,
+                               CastWebViewFactory* web_view_factory,
+                               CastWindowManager* window_manager)
+    : browser_context_(browser_context),
+      web_view_factory_(web_view_factory),
+      window_manager_(window_manager),
+      task_runner_(base::SequencedTaskRunnerHandle::Get()),
+      weak_factory_(this) {
+  DCHECK(browser_context_);
+  DCHECK(web_view_factory_);
+  DCHECK(task_runner_);
+  weak_ptr_ = weak_factory_.GetWeakPtr();
+}
+
+CastWebService::~CastWebService() = default;
+
+CastWebView::Scoped CastWebService::CreateWebView(
+    const CastWebView::CreateParams& params,
+    scoped_refptr<content::SiteInstance> site_instance,
+    const GURL& initial_url) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto web_view = web_view_factory_->CreateWebView(
+      params, this, std::move(site_instance), initial_url);
+  CastWebView::Scoped scoped(web_view.get(), [this](CastWebView* web_view) {
+    OwnerDestroyed(web_view);
+  });
+  web_views_.insert(std::move(web_view));
+  return scoped;
+}
+
+CastWebView::Scoped CastWebService::CreateWebView(
+    const CastWebView::CreateParams& params,
+    const GURL& initial_url) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto web_view = web_view_factory_->CreateWebView(
+      params, this,
+      content::SiteInstance::CreateForURL(browser_context_, initial_url),
+      initial_url);
+  CastWebView::Scoped scoped(web_view.get(), [this](CastWebView* web_view) {
+    OwnerDestroyed(web_view);
+  });
+  web_views_.insert(std::move(web_view));
+  return scoped;
+}
+
+void CastWebService::OwnerDestroyed(CastWebView* web_view) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  content::WebContents* web_contents = web_view->web_contents();
+  GURL url;
+  if (web_contents) {
+    url = web_contents->GetVisibleURL();
+    // Suspend the MediaSession to free up media resources for the next content
+    // window.
+    content::MediaSession::Get(web_contents)
+        ->Suspend(content::MediaSession::SuspendType::kSystem);
+  }
+  auto delay = web_view->shutdown_delay();
+  if (delay <= base::TimeDelta()) {
+    LOG(INFO) << "Immediately deleting CastWebView for " << url;
+    DeleteWebView(web_view);
+    return;
+  }
+  LOG(INFO) << "Deleting CastWebView for " << url << " in "
+            << delay.InMilliseconds() << " milliseconds.";
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&CastWebService::DeleteWebView, weak_ptr_, web_view),
+      delay);
+}
+
+void CastWebService::DeleteWebView(CastWebView* web_view) {
+  LOG(INFO) << "Deleting CastWebView.";
+  base::EraseIf(web_views_,
+                [web_view](const std::unique_ptr<CastWebView>& ptr) {
+                  return ptr.get() == web_view;
+                });
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_web_service.h b/chromecast/browser/cast_web_service.h
new file mode 100644
index 0000000..c2aa303e
--- /dev/null
+++ b/chromecast/browser/cast_web_service.h
@@ -0,0 +1,73 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BROWSER_CAST_WEB_SERVICE_H_
+#define CHROMECAST_BROWSER_CAST_WEB_SERVICE_H_
+
+#include <memory>
+
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "chromecast/browser/cast_web_view.h"
+
+namespace base {
+class SequencedTaskRunner;
+}  // namespace base
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace chromecast {
+
+class CastWebViewFactory;
+class CastWindowManager;
+
+// This class dispenses CastWebView objects which are used to wrap WebContents
+// in cast_shell. This class temporarily takes ownership of CastWebViews when
+// they go out of scope, allowing us to keep the pages alive for extra time if
+// needed. CastWebService allows us to synchronously destroy all pages when the
+// system is shutting down, preventing use of freed browser resources.
+class CastWebService {
+ public:
+  CastWebService(content::BrowserContext* browser_context,
+                 CastWebViewFactory* web_view_factory,
+                 CastWindowManager* window_manager);
+  ~CastWebService();
+
+  CastWebView::Scoped CreateWebView(
+      const CastWebView::CreateParams& params,
+      scoped_refptr<content::SiteInstance> site_instance,
+      const GURL& initial_url);
+
+  CastWebView::Scoped CreateWebView(const CastWebView::CreateParams& params,
+                                    const GURL& initial_url);
+
+  std::unique_ptr<CastContentWindow> CreateWindow(
+      const CastContentWindow::CreateParams& params);
+
+ private:
+  void OwnerDestroyed(CastWebView* web_view);
+  void DeleteWebView(CastWebView* web_view);
+
+  content::BrowserContext* const browser_context_;
+  CastWebViewFactory* const web_view_factory_;
+  CastWindowManager* const window_manager_;
+  base::flat_set<std::unique_ptr<CastWebView>> web_views_;
+
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  base::WeakPtr<CastWebService> weak_ptr_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<CastWebService> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastWebService);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_CAST_WEB_SERVICE_H_
diff --git a/chromecast/browser/cast_web_contents_manager_aura.cc b/chromecast/browser/cast_web_service_aura.cc
similarity index 76%
rename from chromecast/browser/cast_web_contents_manager_aura.cc
rename to chromecast/browser/cast_web_service_aura.cc
index e4b51da..62b5189 100644
--- a/chromecast/browser/cast_web_contents_manager_aura.cc
+++ b/chromecast/browser/cast_web_service_aura.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromecast/browser/cast_web_contents_manager.h"
+#include "chromecast/browser/cast_web_service.h"
 
 #include <memory>
 
@@ -10,7 +10,7 @@
 
 namespace chromecast {
 
-std::unique_ptr<CastContentWindow> CastWebContentsManager::CreateWindow(
+std::unique_ptr<CastContentWindow> CastWebService::CreateWindow(
     const CastContentWindow::CreateParams& params) {
   return std::make_unique<CastContentWindowAura>(params, window_manager_);
 }
diff --git a/chromecast/browser/cast_web_view.cc b/chromecast/browser/cast_web_view.cc
index e74bea8..5f6e9d4d 100644
--- a/chromecast/browser/cast_web_view.cc
+++ b/chromecast/browser/cast_web_view.cc
@@ -13,7 +13,9 @@
   return nullptr;
 }
 
-CastWebView::CastWebView() {}
+CastWebView::CastWebView(const CreateParams& create_params)
+    : delegate_(create_params.delegate),
+      shutdown_delay_(create_params.shutdown_delay) {}
 
 CastWebView::~CastWebView() {
   for (Observer& observer : observer_list_) {
@@ -21,6 +23,11 @@
   }
 }
 
+void CastWebView::ForceClose() {
+  shutdown_delay_ = base::TimeDelta();
+  ClosePage();
+}
+
 void CastWebView::AddObserver(CastWebView::Observer* observer) {
   observer_list_.AddObserver(observer);
 }
@@ -29,10 +36,8 @@
   observer_list_.RemoveObserver(observer);
 }
 
-CastWebView::CreateParams::CreateParams() {}
-
+CastWebView::CreateParams::CreateParams() = default;
 CastWebView::CreateParams::CreateParams(const CreateParams& other) = default;
-
 CastWebView::CreateParams::~CreateParams() = default;
 
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_web_view.h b/chromecast/browser/cast_web_view.h
index 97ed5697..6ca15a3 100644
--- a/chromecast/browser/cast_web_view.h
+++ b/chromecast/browser/cast_web_view.h
@@ -49,11 +49,19 @@
     virtual ~Observer() {}
   };
 
+  // When the unique_ptr is reset, the CastWebView may not necessarily be
+  // destroyed. In some cases ownership will be passed to the CastWebService,
+  // which eventually handles destruction.
+  using Scoped =
+      std::unique_ptr<CastWebView, std::function<void(CastWebView*)>>;
+
   // The parameters used to create a CastWebView instance. Passed to
-  // CastWebContentsManager::CreateWebView().
+  // CastWebService::CreateWebView().
   struct CreateParams {
-    // The delegate for the CastWebView. Must be non-null.
-    Delegate* delegate = nullptr;
+    // The delegate for the CastWebView. Must be non-null. If the delegate is
+    // destroyed before CastWebView, the WeakPtr will be invalidated on the main
+    // UI thread.
+    base::WeakPtr<Delegate> delegate = nullptr;
 
     // Parameters for initializing CastWebContents. These will be passed as-is
     // to a CastWebContents instance, which should be used by all CastWebView
@@ -82,12 +90,17 @@
     // of console log messages.
     std::string log_prefix = "";
 
+    // Delays CastWebView deletion after CastWebView::Scoped is reset. The
+    // default value is zero, which means the CastWebView will be deleted
+    // immediately and synchronously.
+    base::TimeDelta shutdown_delay = base::TimeDelta();
+
     CreateParams();
     CreateParams(const CreateParams& other);
     ~CreateParams();
   };
 
-  CastWebView();
+  explicit CastWebView(const CreateParams& create_params);
   virtual ~CastWebView();
 
   virtual CastContentWindow* window() const = 0;
@@ -96,6 +109,8 @@
 
   virtual CastWebContents* cast_web_contents() = 0;
 
+  base::TimeDelta shutdown_delay() const { return shutdown_delay_; }
+
   // Navigates to |url|. The loaded page will be preloaded if MakeVisible has
   // not been called on the object.
   virtual void LoadUrl(GURL url) = 0;
@@ -103,8 +118,12 @@
   // Begins the close process for this page (ie. triggering document.onunload).
   // A consumer of the class can be notified when the process has been finished
   // via Delegate::OnPageStopped(). The page will be torn down after
-  // |shutdown_delay| has elapsed, or sooner if required.
-  virtual void ClosePage(const base::TimeDelta& shutdown_delay) = 0;
+  // |CreateParams::shutdown_delay| has elapsed, or immediately if the browser
+  // is shutting down.
+  virtual void ClosePage() = 0;
+
+  // Closes the page immediately, ignoring |CreateParams::shutdown_delay|.
+  void ForceClose();
 
   // Adds the page to the window manager and makes it visible to the user if
   // |is_visible| is true. |z_order| determines how this window is layered in
@@ -124,7 +143,12 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+ protected:
+  base::WeakPtr<Delegate> delegate_;
+
  private:
+  base::TimeDelta shutdown_delay_;
+
   base::ObserverList<Observer>::Unchecked observer_list_;
 
   DISALLOW_COPY_AND_ASSIGN(CastWebView);
diff --git a/chromecast/browser/cast_web_view_default.cc b/chromecast/browser/cast_web_view_default.cc
index 44f1584..fa9b18c 100644
--- a/chromecast/browser/cast_web_view_default.cc
+++ b/chromecast/browser/cast_web_view_default.cc
@@ -15,7 +15,7 @@
 #include "chromecast/base/chromecast_switches.h"
 #include "chromecast/base/metrics/cast_metrics_helper.h"
 #include "chromecast/browser/cast_browser_process.h"
-#include "chromecast/browser/cast_web_contents_manager.h"
+#include "chromecast/browser/cast_web_service.h"
 #include "chromecast/chromecast_buildflags.h"
 #include "content/public/browser/media_capture_devices.h"
 #include "content/public/browser/media_session.h"
@@ -52,14 +52,14 @@
 
 CastWebViewDefault::CastWebViewDefault(
     const CreateParams& params,
-    CastWebContentsManager* web_contents_manager,
+    CastWebService* web_service,
     content::BrowserContext* browser_context,
     scoped_refptr<content::SiteInstance> site_instance,
     std::unique_ptr<CastContentWindow> cast_content_window)
-    : web_contents_manager_(web_contents_manager),
+    : CastWebView(params),
+      web_service_(web_service),
       browser_context_(browser_context),
       site_instance_(std::move(site_instance)),
-      delegate_(params.delegate),
       activity_id_(params.activity_id),
       session_id_(params.window_params.session_id),
       sdk_version_(params.sdk_version),
@@ -69,10 +69,9 @@
       cast_web_contents_(web_contents_.get(), params.web_contents_params),
       window_(cast_content_window
                   ? std::move(cast_content_window)
-                  : web_contents_manager->CreateWindow(params.window_params)),
+                  : web_service->CreateWindow(params.window_params)),
       resize_window_when_navigation_starts_(true) {
-  DCHECK(delegate_);
-  DCHECK(web_contents_manager_);
+  DCHECK(web_service_);
   DCHECK(browser_context_);
   DCHECK(window_);
   content::WebContentsObserver::Observe(web_contents_.get());
@@ -109,8 +108,7 @@
   cast_web_contents_.LoadUrl(url);
 }
 
-void CastWebViewDefault::ClosePage(const base::TimeDelta& shutdown_delay) {
-  shutdown_delay_ = shutdown_delay;
+void CastWebViewDefault::ClosePage() {
   content::WebContentsObserver::Observe(nullptr);
   cast_web_contents_.ClosePage();
 }
@@ -118,13 +116,6 @@
 void CastWebViewDefault::CloseContents(content::WebContents* source) {
   DCHECK_EQ(source, web_contents_.get());
   window_.reset();  // Window destructor requires live web_contents on Android.
-  if (!shutdown_delay_.is_zero()) {
-    // We need to delay the deletion of web_contents_ to give (and guarantee)
-    // the renderer enough time to finish 'onunload' handler (but we don't want
-    // to wait any longer than that to delay the starting of next app).
-    web_contents_manager_->DelayWebContentsDeletion(std::move(web_contents_),
-                                                    shutdown_delay_);
-  }
   // This will signal to the owner that |web_contents_| is no longer in use,
   // permitting the owner to tear down.
   cast_web_contents_.Stop(net::OK);
@@ -269,7 +260,10 @@
 CastWebViewDefault::RunBluetoothChooser(
     content::RenderFrameHost* frame,
     const content::BluetoothChooser::EventHandler& event_handler) {
-  auto chooser = delegate_->RunBluetoothChooser(frame, event_handler);
+  std::unique_ptr<content::BluetoothChooser> chooser;
+  if (delegate_) {
+    chooser = delegate_->RunBluetoothChooser(frame, event_handler);
+  }
   return chooser
              ? std::move(chooser)
              : WebContentsDelegate::RunBluetoothChooser(frame, event_handler);
diff --git a/chromecast/browser/cast_web_view_default.h b/chromecast/browser/cast_web_view_default.h
index 5aaa091..becf4006 100644
--- a/chromecast/browser/cast_web_view_default.h
+++ b/chromecast/browser/cast_web_view_default.h
@@ -25,19 +25,19 @@
 
 namespace chromecast {
 
-class CastWebContentsManager;
+class CastWebService;
 
 // A simplified interface for loading and displaying WebContents in cast_shell.
 class CastWebViewDefault : public CastWebView,
                            content::WebContentsObserver,
                            content::WebContentsDelegate {
  public:
-  // |web_contents_manager| and |browser_context| should outlive this object.
-  // If |cast_content_window| is not provided, an instance will be constructed
-  // from |web_contents_manager|.
+  // |web_service| and |browser_context| should outlive this object. If
+  // |cast_content_window| is not provided, an instance will be constructed from
+  // |web_service|.
   CastWebViewDefault(
       const CreateParams& params,
-      CastWebContentsManager* web_contents_manager,
+      CastWebService* web_service,
       content::BrowserContext* browser_context,
       scoped_refptr<content::SiteInstance> site_instance,
       std::unique_ptr<CastContentWindow> cast_content_window = nullptr);
@@ -48,7 +48,7 @@
   content::WebContents* web_contents() const override;
   CastWebContents* cast_web_contents() override;
   void LoadUrl(GURL url) override;
-  void ClosePage(const base::TimeDelta& shutdown_delay) override;
+  void ClosePage() override;
   void InitializeWindow(mojom::ZOrder z_order,
                         VisibilityPriority initial_priority) override;
   void GrantScreenAccess() override;
@@ -85,11 +85,10 @@
                                          const url::Origin& origin,
                                          const GURL& resource_url) override;
 
-  CastWebContentsManager* const web_contents_manager_;
+  CastWebService* const web_service_;
   content::BrowserContext* const browser_context_;
   const scoped_refptr<content::SiteInstance> site_instance_;
 
-  Delegate* const delegate_;
   const std::string activity_id_;
   const std::string session_id_;
   const std::string sdk_version_;
@@ -100,7 +99,6 @@
   CastWebContentsImpl cast_web_contents_;
   std::unique_ptr<CastContentWindow> window_;
   bool resize_window_when_navigation_starts_;
-  base::TimeDelta shutdown_delay_;
 
   DISALLOW_COPY_AND_ASSIGN(CastWebViewDefault);
 };
diff --git a/chromecast/browser/cast_web_view_factory.cc b/chromecast/browser/cast_web_view_factory.cc
index 566487d..0b9eb9d5c 100644
--- a/chromecast/browser/cast_web_view_factory.cc
+++ b/chromecast/browser/cast_web_view_factory.cc
@@ -22,12 +22,12 @@
 
 std::unique_ptr<CastWebView> CastWebViewFactory::CreateWebView(
     const CastWebView::CreateParams& params,
-    CastWebContentsManager* web_contents_manager,
+    CastWebService* web_service,
     scoped_refptr<content::SiteInstance> site_instance,
     const GURL& initial_url) {
   std::unique_ptr<CastWebView> webview;
   webview = std::make_unique<CastWebViewDefault>(
-      params, web_contents_manager, browser_context_, site_instance);
+      params, web_service, browser_context_, site_instance);
   if (webview) {
     webview->AddObserver(this);
   }
diff --git a/chromecast/browser/cast_web_view_factory.h b/chromecast/browser/cast_web_view_factory.h
index 65cbe07..6fe95e9 100644
--- a/chromecast/browser/cast_web_view_factory.h
+++ b/chromecast/browser/cast_web_view_factory.h
@@ -21,7 +21,7 @@
 
 namespace chromecast {
 
-class CastWebContentsManager;
+class CastWebService;
 
 class CastWebViewFactory : public CastWebView::Observer {
  public:
@@ -30,7 +30,7 @@
 
   virtual std::unique_ptr<CastWebView> CreateWebView(
       const CastWebView::CreateParams& params,
-      CastWebContentsManager* web_contents_manager,
+      CastWebService* web_service,
       scoped_refptr<content::SiteInstance> site_instance,
       const GURL& initial_url);
 
diff --git a/chromecast/browser/service/cast_service_simple.cc b/chromecast/browser/service/cast_service_simple.cc
index 0bbdc21..3f7ea0ab 100644
--- a/chromecast/browser/service/cast_service_simple.cc
+++ b/chromecast/browser/service/cast_service_simple.cc
@@ -11,7 +11,7 @@
 #include "base/time/time.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/cast_content_window.h"
-#include "chromecast/browser/cast_web_contents_manager.h"
+#include "chromecast/browser/cast_web_service.h"
 #include "chromecast/browser/cast_web_view_factory.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
@@ -44,10 +44,9 @@
                                      CastWindowManager* window_manager)
     : CastService(browser_context, pref_service),
       web_view_factory_(std::make_unique<CastWebViewFactory>(browser_context)),
-      web_contents_manager_(
-          std::make_unique<CastWebContentsManager>(browser_context,
-                                                   web_view_factory_.get(),
-                                                   window_manager)) {
+      web_service_(std::make_unique<CastWebService>(browser_context,
+                                                    web_view_factory_.get(),
+                                                    window_manager)) {
   shell::CastBrowserProcess::GetInstance()->SetWebViewFactory(
       web_view_factory_.get());
 }
@@ -68,13 +67,13 @@
   }
 
   CastWebView::CreateParams params;
-  params.delegate = this;
-  params.web_contents_params.delegate = this;
+  params.delegate = weak_factory_.GetWeakPtr();
+  params.web_contents_params.delegate = weak_factory_.GetWeakPtr();
   params.web_contents_params.enabled_for_dev = true;
-  params.window_params.delegate = this;
+  params.window_params.delegate = weak_factory_.GetWeakPtr();
   cast_web_view_ =
-      web_contents_manager_->CreateWebView(params, nullptr, /* site_instance */
-                                           GURL() /* initial_url */);
+      web_service_->CreateWebView(params, nullptr, /* site_instance */
+                                  GURL() /* initial_url */);
   cast_web_view_->LoadUrl(startup_url_);
   cast_web_view_->GrantScreenAccess();
   cast_web_view_->InitializeWindow(
@@ -84,7 +83,7 @@
 
 void CastServiceSimple::StopInternal() {
   if (cast_web_view_) {
-    cast_web_view_->ClosePage(base::TimeDelta());
+    cast_web_view_->ClosePage();
   }
   cast_web_view_.reset();
 }
diff --git a/chromecast/browser/service/cast_service_simple.h b/chromecast/browser/service/cast_service_simple.h
index 71c13fc..461a2707 100644
--- a/chromecast/browser/service/cast_service_simple.h
+++ b/chromecast/browser/service/cast_service_simple.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "chromecast/browser/cast_content_window.h"
 #include "chromecast/browser/cast_web_view.h"
 #include "chromecast/service/cast_service.h"
@@ -15,7 +16,7 @@
 
 namespace chromecast {
 
-class CastWebContentsManager;
+class CastWebService;
 class CastWebViewFactory;
 class CastWindowManager;
 
@@ -44,10 +45,11 @@
 
  private:
   const std::unique_ptr<CastWebViewFactory> web_view_factory_;
-  const std::unique_ptr<CastWebContentsManager> web_contents_manager_;
-  std::unique_ptr<CastWebView> cast_web_view_;
+  const std::unique_ptr<CastWebService> web_service_;
+  CastWebView::Scoped cast_web_view_;
   GURL startup_url_;
 
+  base::WeakPtrFactory<CastServiceSimple> weak_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(CastServiceSimple);
 };
 
diff --git a/chromecast/browser/test/cast_browser_test.cc b/chromecast/browser/test/cast_browser_test.cc
index 4ca3862..5f9300e4 100644
--- a/chromecast/browser/test/cast_browser_test.cc
+++ b/chromecast/browser/test/cast_browser_test.cc
@@ -12,7 +12,7 @@
 #include "chromecast/browser/cast_browser_context.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/cast_content_window.h"
-#include "chromecast/browser/cast_web_contents_manager.h"
+#include "chromecast/browser/cast_web_service.h"
 #include "chromecast/browser/cast_web_view_factory.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
@@ -47,7 +47,7 @@
   metrics::CastMetricsHelper::GetInstance()->SetDummySessionIdForTesting();
   web_view_factory_ = std::make_unique<CastWebViewFactory>(
       CastBrowserProcess::GetInstance()->browser_context());
-  web_contents_manager_ = std::make_unique<CastWebContentsManager>(
+  web_service_ = std::make_unique<CastWebService>(
       CastBrowserProcess::GetInstance()->browser_context(),
       web_view_factory_.get(), nullptr /* window_manager */);
 }
@@ -58,14 +58,14 @@
 
 content::WebContents* CastBrowserTest::CreateWebView() {
   CastWebView::CreateParams params;
-  params.delegate = this;
-  params.web_contents_params.delegate = this;
+  params.delegate = weak_factory_.GetWeakPtr();
+  params.web_contents_params.delegate = weak_factory_.GetWeakPtr();
   params.web_contents_params.use_cma_renderer = true;
   params.web_contents_params.enabled_for_dev = true;
-  params.window_params.delegate = this;
+  params.window_params.delegate = weak_factory_.GetWeakPtr();
   cast_web_view_ =
-      web_contents_manager_->CreateWebView(params, nullptr, /* site_instance */
-                                           GURL() /* initial_url */);
+      web_service_->CreateWebView(params, nullptr, /* site_instance */
+                                  GURL() /* initial_url */);
 
   return cast_web_view_->web_contents();
 }
diff --git a/chromecast/browser/test/cast_browser_test.h b/chromecast/browser/test/cast_browser_test.h
index dfb64ad..1bcad42 100644
--- a/chromecast/browser/test/cast_browser_test.h
+++ b/chromecast/browser/test/cast_browser_test.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "chromecast/browser/cast_web_view.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_base.h"
@@ -18,7 +19,7 @@
 
 namespace chromecast {
 
-class CastWebContentsManager;
+class CastWebService;
 class CastWebViewFactory;
 
 namespace shell {
@@ -28,7 +29,8 @@
 // case, then shuts down the entire shell.
 // Note that this process takes 7-10 seconds per test case on Chromecast, so
 // fewer test cases with more assertions are preferable.
-class CastBrowserTest : public content::BrowserTestBase, CastWebView::Delegate {
+class CastBrowserTest : public content::BrowserTestBase,
+                        public CastWebView::Delegate {
  protected:
   CastBrowserTest();
   ~CastBrowserTest() override;
@@ -51,9 +53,10 @@
   std::string GetId() override;
 
   std::unique_ptr<CastWebViewFactory> web_view_factory_;
-  std::unique_ptr<CastWebContentsManager> web_contents_manager_;
-  std::unique_ptr<CastWebView> cast_web_view_;
+  std::unique_ptr<CastWebService> web_service_;
+  CastWebView::Scoped cast_web_view_;
 
+  base::WeakPtrFactory<CastBrowserTest> weak_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(CastBrowserTest);
 };
 
diff --git a/chromecast/browser/webview/webview_controller.cc b/chromecast/browser/webview/webview_controller.cc
index ae3626eb..8d83a468 100644
--- a/chromecast/browser/webview/webview_controller.cc
+++ b/chromecast/browser/webview/webview_controller.cc
@@ -55,7 +55,7 @@
   CastWebContents::InitParams cast_contents_init;
   cast_contents_init.is_root_window = true;
   cast_contents_init.enabled_for_dev = CAST_IS_DEBUG_BUILD();
-  cast_contents_init.delegate = this;
+  cast_contents_init.delegate = weak_ptr_factory_.GetWeakPtr();
   cast_web_contents_ = std::make_unique<CastWebContentsImpl>(
       contents_.get(), cast_contents_init);
   cast_web_contents_->AddObserver(this);
diff --git a/chromeos/dbus/power/power_manager_client.cc b/chromeos/dbus/power/power_manager_client.cc
index 213e531..7e7420a 100644
--- a/chromeos/dbus/power/power_manager_client.cc
+++ b/chromeos/dbus/power/power_manager_client.cc
@@ -178,6 +178,8 @@
     const std::map<const char*, SignalMethod> kSignalMethods = {
         {power_manager::kScreenBrightnessChangedSignal,
          &PowerManagerClientImpl::ScreenBrightnessChangedReceived},
+        {power_manager::kAmbientColorTemperatureChangedSignal,
+         &PowerManagerClientImpl::AmbientColorTemperatureChangedReceived},
         {power_manager::kKeyboardBrightnessChangedSignal,
          &PowerManagerClientImpl::KeyboardBrightnessChangedReceived},
         {power_manager::kScreenIdleStateChangedSignal,
@@ -617,6 +619,20 @@
       observer.ScreenBrightnessChanged(proto);
   }
 
+  void AmbientColorTemperatureChangedReceived(dbus::Signal* signal) {
+    dbus::MessageReader reader(signal);
+    int32_t color_temperature = 0;
+    if (!reader.PopInt32(&color_temperature)) {
+      POWER_LOG(ERROR) << "Unable to decode read ambient color from "
+                       << power_manager::kAmbientColorTemperatureChangedSignal
+                       << " signal";
+      return;
+    }
+
+    for (auto& observer : observers_)
+      observer.AmbientColorChanged(color_temperature);
+  }
+
   void KeyboardBrightnessChangedReceived(dbus::Signal* signal) {
     dbus::MessageReader reader(signal);
     power_manager::BacklightBrightnessChange proto;
diff --git a/chromeos/dbus/power/power_manager_client.h b/chromeos/dbus/power/power_manager_client.h
index 72a95126..d2c3a3f9 100644
--- a/chromeos/dbus/power/power_manager_client.h
+++ b/chromeos/dbus/power/power_manager_client.h
@@ -82,6 +82,9 @@
     virtual void ScreenBrightnessChanged(
         const power_manager::BacklightBrightnessChange& change) {}
 
+    // Called when the ambient light changed.
+    virtual void AmbientColorChanged(const int32_t color_temperature) {}
+
     // Called when the keyboard brightness is changed.
     virtual void KeyboardBrightnessChanged(
         const power_manager::BacklightBrightnessChange& change) {}
diff --git a/chromeos/dbus/power/power_manager_client_unittest.cc b/chromeos/dbus/power/power_manager_client_unittest.cc
index 7bc2e89..f96d57b6 100644
--- a/chromeos/dbus/power/power_manager_client_unittest.cc
+++ b/chromeos/dbus/power/power_manager_client_unittest.cc
@@ -96,6 +96,9 @@
   const base::UnguessableToken& block_suspend_token() const {
     return block_suspend_token_;
   }
+  int32_t ambient_color_temperature() const {
+    return ambient_color_temperature_;
+  }
 
   void set_should_block_suspend(bool take_callback) {
     should_block_suspend_ = take_callback;
@@ -135,6 +138,9 @@
     if (run_unblock_suspend_immediately_)
       CHECK(UnblockSuspend());
   }
+  void AmbientColorChanged(const int32_t color_temperature) override {
+    ambient_color_temperature_ = color_temperature;
+  }
 
  private:
   PowerManagerClient* client_;  // Not owned.
@@ -157,6 +163,8 @@
   // When non-empty, the token for the outstanding block-suspend registration.
   base::UnguessableToken block_suspend_token_;
 
+  // Ambient color temperature
+  int32_t ambient_color_temperature_ = 0;
   DISALLOW_COPY_AND_ASSIGN(TestObserver);
 };
 
@@ -592,4 +600,17 @@
   EmitSuspendDoneSignal(kSuspendId);
 }
 
+// Tests that observers are notified about changes in ambient color temperature.
+TEST_F(PowerManagerClientTest, ChangeAmbientColorTemperature) {
+  TestObserver observer(client_);
+
+  constexpr int32_t kTemperature = 6500;
+  dbus::Signal signal(kInterface,
+                      power_manager::kAmbientColorTemperatureChangedSignal);
+  dbus::MessageWriter(&signal).AppendInt32(kTemperature);
+  EmitSignal(&signal);
+
+  EXPECT_EQ(kTemperature, observer.ambient_color_temperature());
+}
+
 }  // namespace chromeos
diff --git a/chromeos/profiles/airmont.afdo.newest.txt b/chromeos/profiles/airmont.afdo.newest.txt
index 885dc947..b342292 100644
--- a/chromeos/profiles/airmont.afdo.newest.txt
+++ b/chromeos/profiles/airmont.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-airmont-80-3945.13-1572864139-benchmark-80.0.3960.0-r1-redacted.afdo.xz
\ No newline at end of file
+chromeos-chrome-amd64-airmont-80-3945.13-1572864139-benchmark-80.0.3961.0-r1-redacted.afdo.xz
\ No newline at end of file
diff --git a/chromeos/profiles/broadwell.afdo.newest.txt b/chromeos/profiles/broadwell.afdo.newest.txt
index acade739..5da2d2c 100644
--- a/chromeos/profiles/broadwell.afdo.newest.txt
+++ b/chromeos/profiles/broadwell.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-broadwell-80-3943.1-1572873357-benchmark-80.0.3960.0-r1-redacted.afdo.xz
\ No newline at end of file
+chromeos-chrome-amd64-broadwell-80-3943.1-1572873357-benchmark-80.0.3961.0-r1-redacted.afdo.xz
\ No newline at end of file
diff --git a/chromeos/profiles/silvermont.afdo.newest.txt b/chromeos/profiles/silvermont.afdo.newest.txt
index 2b766db..3e19076 100644
--- a/chromeos/profiles/silvermont.afdo.newest.txt
+++ b/chromeos/profiles/silvermont.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-silvermont-80-3943.1-1572866479-benchmark-80.0.3960.0-r1-redacted.afdo.xz
\ No newline at end of file
+chromeos-chrome-amd64-silvermont-80-3943.1-1572866479-benchmark-80.0.3961.0-r1-redacted.afdo.xz
\ No newline at end of file
diff --git a/components/cdm/browser/cdm_message_filter_android.cc b/components/cdm/browser/cdm_message_filter_android.cc
index 1357cf68..90136e5 100644
--- a/components/cdm/browser/cdm_message_filter_android.cc
+++ b/components/cdm/browser/cdm_message_filter_android.cc
@@ -201,10 +201,10 @@
 
   bool are_overlay_supported =
       content::AndroidOverlayProvider::GetInstance()->AreOverlaysSupported();
-  bool use_android_overlay =
-      base::FeatureList::IsEnabled(media::kUseAndroidOverlay);
+  bool overlay_fullscreen_video =
+      base::FeatureList::IsEnabled(media::kOverlayFullscreenVideo);
   if (force_to_support_secure_codecs_ ||
-      (are_overlay_supported && use_android_overlay)) {
+      (are_overlay_supported && overlay_fullscreen_video)) {
     DVLOG(1) << "Rendering the output of secure codecs is supported!";
     response->secure_codecs = GetSupportedCodecs(request, true);
   }
diff --git a/components/media_message_center/media_notification_item.cc b/components/media_message_center/media_notification_item.cc
index cd56d6b..8f3cee72 100644
--- a/components/media_message_center/media_notification_item.cc
+++ b/components/media_message_center/media_notification_item.cc
@@ -8,10 +8,10 @@
 
 // static
 const char MediaNotificationItem::kUserActionHistogramName[] =
-    "Media.Notification.Source";
+    "Media.Notification.UserAction";
 
 // static
 const char MediaNotificationItem::kSourceHistogramName[] =
-    "Media.Notification.UserAction";
+    "Media.Notification.Source";
 
 }  // namespace media_message_center
diff --git a/components/module_installer/readme.md b/components/module_installer/readme.md
new file mode 100644
index 0000000..f24916f
--- /dev/null
+++ b/components/module_installer/readme.md
@@ -0,0 +1,50 @@
+# Chrome on Android Dynamic Feature Module Installer Backend
+
+This component houses code to install and load Android
+[dynamice feature modules](https://developer.android.com/guide/app-bundle). See
+the [onboarding guide](../../docs/android_dynamic_feature_modules.md) for how to
+create such a feature module in Chrome. Broadly, this component offers two APIs
+- _install engine_ to install modules and _module builder_ to set up modules on
+first access.
+
+## Install Engine
+
+The install engine is a wrapper around Play Core's
+[split install API](https://developer.android.com/guide/app-bundle/playcore)
+that performs extra setup such as
+[SplitCompat](https://developer.android.com/guide/app-bundle/playcore#access_downloaded_modules),
+collects metrics and provides
+[fake install](android/java/src/org/chromium/components/module_installer/engine/FakeEngine.java).
+You can install a module by name with the following code snippet:
+
+```java
+InstallEngine installEngine = new EngineFactory().getEngine();
+installEngine.install("foo", success -> {
+    // Module installed successfully if |success| is true.
+});
+```
+
+You can use the install engine on its own but will have to take care of module
+setup such as loading native code and resources. To simplify that you can use
+the module builder API.
+
+## Module Builder
+
+The module builder simplifies module set up by loading native code and resources
+on first module access and determines whether a module is installed. The module
+builder uses the install engine in the back. It primarily provides the following
+building blocks:
+
+* [`@ModuleInterface`](android/java/src/org/chromium/components/module_installer/builder/ModuleInterface.java)
+  to annotate the entry point of your module. Using this with the
+  [`module_interface_processor`](android/BUILD.gn) will create a module class
+  such as `FooModule` that lets you install and load a module. See
+  [`Module`](android/java/src/org/chromium/components/module_installer/builder/Module.java)
+  for its interface.
+
+* [`Module`](android/java/src/org/chromium/components/module_installer/builder/Module.java)
+  needs to be able to retrieve a
+  [`ModuleDescriptor`](android/java/src/org/chromium/components/module_installer/builder/ModuleDescriptor.java)
+  implementation for each module via reflection. You can create such an
+  implementation with the [`module_desc_java`](android/module_desc_java.gni)
+  template.
diff --git a/components/ntp_snippets/features.cc b/components/ntp_snippets/features.cc
index 72fca90..226d09dc 100644
--- a/components/ntp_snippets/features.cc
+++ b/components/ntp_snippets/features.cc
@@ -87,7 +87,7 @@
 // Provides ability to customize the referrer URL.
 // When specifying a referrer through a field trial, it must contain a path.
 // In case of default value above the path is empty, but it is specified.
-base::FeatureParam<std::string> kArticleSuggestionsReferrerURLParam{
+const base::FeatureParam<std::string> kArticleSuggestionsReferrerURLParam{
     &kArticleSuggestionsFeature, "referrer_url", kDefaultReferrerUrl};
 
 std::string GetContentSuggestionsReferrerURL() {
diff --git a/components/omnibox/browser/omnibox_popup_model.cc b/components/omnibox/browser/omnibox_popup_model.cc
index a5d9d116..b4ee988 100644
--- a/components/omnibox/browser/omnibox_popup_model.cc
+++ b/components/omnibox/browser/omnibox_popup_model.cc
@@ -180,16 +180,9 @@
   DCHECK(!result().empty());
   DCHECK_NE(kNoMatch, selected_line_);
 
-  const AutocompleteResult& result = this->result();
-  if (result.empty())
-    return;
-
-  const AutocompleteMatch& match = result.match_at(selected_line_);
+  const AutocompleteMatch& match = result().match_at(selected_line_);
   GURL current_destination(match.destination_url);
-
-  if (state == KEYWORD) {
-    DCHECK(match.associated_keyword.get());
-  }
+  DCHECK((state != KEYWORD) || match.associated_keyword.get());
 
   if (state == BUTTON_FOCUSED) {
     // TODO(orinj): If in-suggestion Pedals are kept, refactor a bit
@@ -201,10 +194,10 @@
   selected_line_state_ = state;
   view_->InvalidateLine(selected_line_);
 
-  // Ensures update of accessibility data for button text.
   if (state == BUTTON_FOCUSED) {
     edit_model_->view()->SetAccessibilityLabel(edit_model_->view()->GetText(),
                                                match);
+    view_->ProvideButtonFocusHint(selected_line_);
   }
 }
 
diff --git a/components/omnibox/browser/omnibox_popup_model_unittest.cc b/components/omnibox/browser/omnibox_popup_model_unittest.cc
index 5c1ea279..c3e818b 100644
--- a/components/omnibox/browser/omnibox_popup_model_unittest.cc
+++ b/components/omnibox/browser/omnibox_popup_model_unittest.cc
@@ -33,6 +33,7 @@
   void InvalidateLine(size_t line) override {}
   void OnLineSelected(size_t line) override {}
   void UpdatePopupAppearance() override {}
+  void ProvideButtonFocusHint(size_t line) override {}
   void OnMatchIconUpdated(size_t match_index) override {}
   void OnDragCanceled() override {}
 };
diff --git a/components/omnibox/browser/omnibox_popup_view.h b/components/omnibox/browser/omnibox_popup_view.h
index c2cfb4a..e5accd4f 100644
--- a/components/omnibox/browser/omnibox_popup_view.h
+++ b/components/omnibox/browser/omnibox_popup_view.h
@@ -32,6 +32,9 @@
   // mean opening or closing the window.
   virtual void UpdatePopupAppearance() = 0;
 
+  // Called to inform result view of button focus.
+  virtual void ProvideButtonFocusHint(size_t line) = 0;
+
   // Notification that the icon used for the given match has been updated.
   virtual void OnMatchIconUpdated(size_t match_index) = 0;
 
diff --git a/components/optimization_guide/optimization_guide_constants.cc b/components/optimization_guide/optimization_guide_constants.cc
index ee89a1d..3657d2e 100644
--- a/components/optimization_guide/optimization_guide_constants.cc
+++ b/components/optimization_guide/optimization_guide_constants.cc
@@ -20,4 +20,6 @@
 const char kLoadedHintLocalHistogramString[] =
     "OptimizationGuide.LoadedHint.Result";
 
+const char kOptimizationGuideHintStore[] = "previews_hint_cache_store";
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/optimization_guide_constants.h b/components/optimization_guide/optimization_guide_constants.h
index 9c566dd..33b656b 100644
--- a/components/optimization_guide/optimization_guide_constants.h
+++ b/components/optimization_guide/optimization_guide_constants.h
@@ -25,6 +25,9 @@
 // the cache and are ready for use.
 extern const char kLoadedHintLocalHistogramString[];
 
+// The folder where the hint data will be stored on disk.
+extern const char kOptimizationGuideHintStore[];
+
 }  // namespace optimization_guide
 
 #endif  // COMPONENTS_OPTIMIZATION_GUIDE_OPTIMIZATION_GUIDE_CONSTANTS_H_
diff --git a/components/optimization_guide/optimization_guide_store.cc b/components/optimization_guide/optimization_guide_store.cc
index 04343176..bf183c5 100644
--- a/components/optimization_guide/optimization_guide_store.cc
+++ b/components/optimization_guide/optimization_guide_store.cc
@@ -27,9 +27,6 @@
         static_cast<int>(OptimizationGuideStore::StoreEntryType::kMaxValue),
     "mismatched StoreEntryType enums");
 
-// The folder where the data will be stored on disk.
-constexpr char kOptimizationGuideStore[] = "previews_hint_cache_store";
-
 // The amount of data to build up in memory before converting to a sorted on-
 // disk file.
 constexpr size_t kDatabaseWriteBufferSizeBytes = 128 * 1024;
@@ -97,10 +94,8 @@
     leveldb_proto::ProtoDatabaseProvider* database_provider,
     const base::FilePath& database_dir,
     scoped_refptr<base::SequencedTaskRunner> store_task_runner) {
-  base::FilePath hint_store_dir =
-      database_dir.AppendASCII(kOptimizationGuideStore);
   database_ = database_provider->GetDB<proto::StoreEntry>(
-      leveldb_proto::ProtoDbType::HINT_CACHE_STORE, hint_store_dir,
+      leveldb_proto::ProtoDbType::HINT_CACHE_STORE, database_dir,
       store_task_runner);
 
   RecordStatusChange(status_);
@@ -216,7 +211,7 @@
   EntryKeyPrefix filter_prefix = GetComponentHintEntryKeyPrefixWithoutVersion();
 
   // Add the new component data and purge any old component hints from the db.
-  // After processing finishes, OnUpdateHints() is called, which loads
+  // After processing finishes, OnUpdateStore() is called, which loads
   // the updated hint entry keys from the database.
   database_->UpdateEntriesWithRemoveFilter(
       component_data->TakeUpdateEntries(),
@@ -227,7 +222,7 @@
                    key.compare(0, filter_prefix.length(), filter_prefix) == 0;
           },
           retain_prefix, filter_prefix),
-      base::BindOnce(&OptimizationGuideStore::OnUpdateHints,
+      base::BindOnce(&OptimizationGuideStore::OnUpdateStore,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
@@ -256,7 +251,7 @@
       fetched_hints_data->TakeUpdateEntries(),
       base::BindRepeating(&DatabasePrefixFilter,
                           GetMetadataTypeEntryKey(MetadataType::kFetched)),
-      base::BindOnce(&OptimizationGuideStore::OnUpdateHints,
+      base::BindOnce(&OptimizationGuideStore::OnUpdateStore,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
@@ -309,7 +304,7 @@
             return keys_to_remove->find(key) != keys_to_remove->end();
           },
           keys_to_remove.get()),
-      base::BindOnce(&OptimizationGuideStore::OnUpdateHints,
+      base::BindOnce(&OptimizationGuideStore::OnUpdateStore,
                      weak_ptr_factory_.GetWeakPtr(), base::DoNothing::Once()));
 }
 
@@ -531,19 +526,18 @@
   // TODO(mcrouse): Add histogram to record the number of hints being removed.
   entry_keys_.reset();
 
-  // Removes all |kFetchedHint| store entries. OnUpdateHints will handle
-  // updating status and re-filling hint_entry_keys with the hints still in the
+  // Removes all |kFetchedHint| store entries. OnUpdateStore will handle
+  // updating status and re-filling entry_keys with the entries still in the
   // store.
   database_->UpdateEntriesWithRemoveFilter(
       std::move(entries_to_save),  // this should be empty.
       base::BindRepeating(&DatabasePrefixFilter,
                           GetFetchedHintEntryKeyPrefix()),
-      base::BindOnce(&OptimizationGuideStore::OnUpdateHints,
+      base::BindOnce(&OptimizationGuideStore::OnUpdateStore,
                      weak_ptr_factory_.GetWeakPtr(), base::DoNothing::Once()));
 }
 
-void OptimizationGuideStore::MaybeLoadHintEntryKeys(
-    base::OnceClosure callback) {
+void OptimizationGuideStore::MaybeLoadEntryKeys(base::OnceClosure callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // If the database is unavailable or if there's an in-flight component data
@@ -559,25 +553,25 @@
   // loaded by the DB. Ownership of the KeySet is passed into the
   // LoadKeysAndEntriesCallback callback, guaranteeing that the KeySet has a
   // lifespan longer than the filter calls.
-  std::unique_ptr<EntryKeySet> hint_entry_keys(std::make_unique<EntryKeySet>());
-  EntryKeySet* raw_hint_entry_keys_pointer = hint_entry_keys.get();
+  std::unique_ptr<EntryKeySet> entry_keys(std::make_unique<EntryKeySet>());
+  EntryKeySet* raw_entry_keys_pointer = entry_keys.get();
   database_->LoadKeysAndEntriesWithFilter(
       base::BindRepeating(
-          [](EntryKeySet* hint_entry_keys, const std::string& filter_prefix,
+          [](EntryKeySet* entry_keys, const std::string& filter_prefix,
              const std::string& entry_key) {
             if (entry_key.compare(0, filter_prefix.length(), filter_prefix) !=
                 0) {
-              hint_entry_keys->insert(entry_key);
+              entry_keys->insert(entry_key);
             }
             return false;
           },
-          raw_hint_entry_keys_pointer, GetMetadataEntryKeyPrefix()),
-      base::BindOnce(&OptimizationGuideStore::OnLoadHintEntryKeys,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(hint_entry_keys),
+          raw_entry_keys_pointer, GetMetadataEntryKeyPrefix()),
+      base::BindOnce(&OptimizationGuideStore::OnLoadEntryKeys,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(entry_keys),
                      std::move(callback)));
 }
 
-size_t OptimizationGuideStore::GetHintEntryKeyCount() const {
+size_t OptimizationGuideStore::GetEntryKeyCount() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return entry_keys_ ? entry_keys_->size() : 0;
 }
@@ -691,7 +685,7 @@
   }
 
   UpdateStatus(Status::kAvailable);
-  MaybeLoadHintEntryKeys(std::move(callback));
+  MaybeLoadEntryKeys(std::move(callback));
 }
 
 void OptimizationGuideStore::OnPurgeDatabase(base::OnceClosure callback,
@@ -704,7 +698,7 @@
   std::move(callback).Run();
 }
 
-void OptimizationGuideStore::OnUpdateHints(base::OnceClosure callback,
+void OptimizationGuideStore::OnUpdateStore(base::OnceClosure callback,
                                            bool success) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(data_update_in_flight_);
@@ -715,10 +709,10 @@
     std::move(callback).Run();
     return;
   }
-  MaybeLoadHintEntryKeys(std::move(callback));
+  MaybeLoadEntryKeys(std::move(callback));
 }
 
-void OptimizationGuideStore::OnLoadHintEntryKeys(
+void OptimizationGuideStore::OnLoadEntryKeys(
     std::unique_ptr<EntryKeySet> hint_entry_keys,
     base::OnceClosure callback,
     bool success,
@@ -801,4 +795,97 @@
   std::move(callback).Run(entry_key, std::move(loaded_hint));
 }
 
+std::unique_ptr<StoreUpdateData>
+OptimizationGuideStore::CreateUpdateDataForPredictionModels() const {
+  // Create and returns a StoreUpdateData object. This object has prediction
+  // models from the GetModelsResponse moved into and organizes them in a format
+  // usable by the store. The object will be stored with
+  // UpdatePredictionModels().
+  return StoreUpdateData::CreatePredictionModelStoreUpdateData();
+}
+
+void OptimizationGuideStore::UpdatePredictionModels(
+    std::unique_ptr<StoreUpdateData> prediction_models_update_data,
+    base::OnceClosure callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(prediction_models_update_data);
+  DCHECK(!data_update_in_flight_);
+
+  if (!IsAvailable()) {
+    std::move(callback).Run();
+    return;
+  }
+
+  data_update_in_flight_ = true;
+
+  entry_keys_.reset();
+
+  std::unique_ptr<EntryVector> entry_vectors =
+      prediction_models_update_data->TakeUpdateEntries();
+
+  database_->UpdateEntries(
+      std::move(entry_vectors), std::make_unique<leveldb_proto::KeyVector>(),
+      base::BindOnce(&OptimizationGuideStore::OnUpdateStore,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+bool OptimizationGuideStore::FindPredictionModelEntryKey(
+    proto::OptimizationTarget optimization_target,
+    OptimizationGuideStore::EntryKey* out_prediction_model_entry_key) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!entry_keys_)
+    return false;
+  *out_prediction_model_entry_key =
+      GetPredictionModelEntryKeyPrefix() +
+      base::NumberToString(static_cast<int>(optimization_target));
+  if (entry_keys_->find(*out_prediction_model_entry_key) != entry_keys_->end())
+    return true;
+  return false;
+}
+
+void OptimizationGuideStore::LoadPredictionModel(
+    const EntryKey& prediction_model_entry_key,
+    PredictionModelLoadedCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!IsAvailable()) {
+    std::move(callback).Run(nullptr);
+    return;
+  }
+
+  database_->GetEntry(
+      prediction_model_entry_key,
+      base::BindOnce(&OptimizationGuideStore::OnLoadPredictionModel,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void OptimizationGuideStore::OnLoadPredictionModel(
+    PredictionModelLoadedCallback callback,
+    bool success,
+    std::unique_ptr<proto::StoreEntry> entry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // If either the request failed, the store was set to unavailable after the
+  // request was started, or there's an in-flight update, which
+  // means the entry is about to be invalidated, then the loaded model should
+  // not be considered valid. Reset the entry so that nothing is returned to
+  // the requester.
+  UMA_HISTOGRAM_BOOLEAN("OptimizationGuide.PredictionModelStore.OnLoadCollided",
+                        data_update_in_flight_);
+  if (!success || !IsAvailable() || data_update_in_flight_) {
+    entry.reset();
+  }
+
+  if (!entry || !entry->has_prediction_model()) {
+    std::unique_ptr<proto::PredictionModel> loaded_prediction_model(nullptr);
+    std::move(callback).Run(std::move(loaded_prediction_model));
+    return;
+  }
+
+  std::unique_ptr<proto::PredictionModel> loaded_prediction_model(
+      entry->release_prediction_model());
+  std::move(callback).Run(std::move(loaded_prediction_model));
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/optimization_guide_store.h b/components/optimization_guide/optimization_guide_store.h
index b914a8d..92f701d3 100644
--- a/components/optimization_guide/optimization_guide_store.h
+++ b/components/optimization_guide/optimization_guide_store.h
@@ -18,6 +18,7 @@
 #include "base/version.h"
 #include "components/leveldb_proto/public/proto_database.h"
 #include "components/leveldb_proto/public/proto_database_provider.h"
+#include "components/optimization_guide/proto/models.pb.h"
 #include "components/optimization_guide/store_update_data.h"
 
 namespace base {
@@ -39,6 +40,8 @@
   using HintLoadedCallback =
       base::OnceCallback<void(const std::string&,
                               std::unique_ptr<proto::Hint>)>;
+  using PredictionModelLoadedCallback =
+      base::OnceCallback<void(std::unique_ptr<proto::PredictionModel>)>;
   using EntryKey = std::string;
   using StoreEntryProtoDatabase =
       leveldb_proto::ProtoDatabase<proto::StoreEntry>;
@@ -135,7 +138,7 @@
   void UpdateFetchedHints(std::unique_ptr<StoreUpdateData> fetched_hints_data,
                           base::OnceClosure callback);
 
-  // Removes fetched hint store entries from |this|. |hint_entry_keys_| is
+  // Removes fetched hint store entries from |this|. |entry_keys_| is
   // updated after the fetched hint entries are removed.
   void ClearFetchedHintsFromDatabase();
 
@@ -158,10 +161,38 @@
   base::Time FetchedHintsUpdateTime() const;
 
   // Removes all fetched hints that have expired from the store.
-  // |hint_entry_keys| is updated after the expired fetched hints are
+  // |entry_keys_| is updated after the expired fetched hints are
   // removed.
   void PurgeExpiredFetchedHints();
 
+  // Creates and returns a StoreUpdateData object for Prediction Models. This
+  // object is used to collect a batch of prediction models in a format that is
+  // usable to update the store on a background thread. This is always created
+  // when prediction models have been successfully fetched from the remote
+  // Optimization Guide Service so the store can update old prediction models.
+  std::unique_ptr<StoreUpdateData> CreateUpdateDataForPredictionModels() const;
+
+  // Updates the prediciton models contained in the store. The callback is run
+  // asynchronously after the database stores the prediction models.
+  void UpdatePredictionModels(
+      std::unique_ptr<StoreUpdateData> prediction_models_update_data,
+      base::OnceClosure callback);
+
+  // Finds the entry key for the prediction model if it is known to the store.
+  // Returns true if an entry key is found and |out_prediction_model_entry_key|
+  // is populated with the matching key.
+  bool FindPredictionModelEntryKey(
+      proto::OptimizationTarget optimization_target,
+      OptimizationGuideStore::EntryKey* out_prediction_model_entry_key);
+
+  // Loads the prediction model specified by |prediction_model_entry_key|. After
+  // the load finishes, the prediction model data is passed to |callback|. In
+  // the case where the prediction model cannot be loaded, the callback is run
+  // with a nullptr. Depending on the load result, the callback may be
+  // synchronous or asynchronous.
+  void LoadPredictionModel(const EntryKey& prediction_model_entry_key,
+                           PredictionModelLoadedCallback callback);
+
  private:
   friend class OptimizationGuideStoreTest;
   friend class StoreUpdateData;
@@ -237,15 +268,14 @@
   // their default state. Called after the database is destroyed.
   void ClearComponentVersion();
 
-  // Asynchronously loads the hint entry keys from the store, populates
-  // |hint_entry_keys_| with them, and runs the provided callback after they
-  // finish loading. In the case where there is currently an in-flight component
-  // update, this does nothing, as the hint entry keys will be loaded after the
-  // component update completes.
-  void MaybeLoadHintEntryKeys(base::OnceClosure callback);
+  // Asynchronously loads the entry keys from the store, populates |entry_keys_|
+  // with them, and runs the provided callback after they finish loading. In the
+  // case where there is currently an in-flight update, this does nothing, as
+  // the entry keys will be loaded after the update completes.
+  void MaybeLoadEntryKeys(base::OnceClosure callback);
 
-  // Returns the total hint entry keys contained within the store.
-  size_t GetHintEntryKeyCount() const;
+  // Returns the total entry keys contained within the store.
+  size_t GetEntryKeyCount() const;
 
   // Finds the most specific host suffix of the host name that the store has an
   // hint with the provided prefix, |hint_entry_key_prefix|. |out_entry_key| is
@@ -283,20 +313,20 @@
   // Callback that runs after the database is purged during initialization.
   void OnPurgeDatabase(base::OnceClosure callback, bool success);
 
-  // Callback that runs after the hints data within the store is fully
-  // updated. If the update was successful, it attempts to load all of the hint
+  // Callback that runs after the data within the store is fully
+  // updated. If the update was successful, it attempts to load all of the
   // entry keys contained within the database.
-  void OnUpdateHints(base::OnceClosure callback, bool success);
+  void OnUpdateStore(base::OnceClosure callback, bool success);
 
   // Callback that runs after the hint entry keys are fully loaded. If there's
   // currently an in-flight component update, then the hint entry keys will be
   // loaded again after the component update completes, so the results are
-  // tossed; otherwise, |hint_entry_keys| is moved into |hint_entry_keys_|.
+  // tossed; otherwise, |entry_keys| is moved into |entry_keys_|.
   // Regardless of the outcome of loading the keys, the callback always runs.
-  void OnLoadHintEntryKeys(std::unique_ptr<EntryKeySet> hint_entry_keys,
-                           base::OnceClosure callback,
-                           bool success,
-                           std::unique_ptr<EntryMap> unused);
+  void OnLoadEntryKeys(std::unique_ptr<EntryKeySet> entry_keys,
+                       base::OnceClosure callback,
+                       bool success,
+                       std::unique_ptr<EntryMap> unused);
 
   // Callback that runs after a hint entry is loaded from the database. If
   // there's currently an in-flight component update, then the hint is about to
@@ -309,6 +339,17 @@
                   bool success,
                   std::unique_ptr<proto::StoreEntry> entry);
 
+  // Callback that runs after a prediction model entry is loaded from the
+  // database. If there's currently an in-flight update, then the data could be
+  // invalidated, so loaded model is discarded. Otherwise, the prediction model
+  // is released into the callback, allowing the caller to own the prediction
+  // model without copying it. Regardless of the success or failure of
+  // retrieving the key, the callback always runs (it simply runs with a nullptr
+  // on failure).
+  void OnLoadPredictionModel(PredictionModelLoadedCallback callback,
+                             bool success,
+                             std::unique_ptr<proto::StoreEntry> entry);
+
   // Proto database used by the store.
   std::unique_ptr<StoreEntryProtoDatabase> database_;
 
diff --git a/components/optimization_guide/optimization_guide_store_unittest.cc b/components/optimization_guide/optimization_guide_store_unittest.cc
index 72c8ea3..6efd1fa 100644
--- a/components/optimization_guide/optimization_guide_store_unittest.cc
+++ b/components/optimization_guide/optimization_guide_store_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/leveldb_proto/testing/fake_db.h"
 #include "components/optimization_guide/optimization_guide_features.h"
 #include "components/optimization_guide/proto/hint_cache.pb.h"
+#include "components/optimization_guide/proto/models.pb.h"
 #include "components/optimization_guide/store_update_data.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -46,6 +47,22 @@
   kValid,
 };
 
+std::unique_ptr<proto::PredictionModel> CreatePredictionModel() {
+  std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
+      std::make_unique<optimization_guide::proto::PredictionModel>();
+
+  optimization_guide::proto::ModelInfo* model_info =
+      prediction_model->mutable_model_info();
+  model_info->set_version(1);
+  model_info->add_supported_model_features(
+      proto::CLIENT_MODEL_FEATURE_EFFECTIVE_CONNECTION_TYPE);
+  model_info->set_optimization_target(
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+  model_info->add_supported_model_types(
+      proto::ModelType::MODEL_TYPE_DECISION_TREE);
+  return prediction_model;
+}
+
 }  // namespace
 
 class OptimizationGuideStoreTest : public testing::Test {
@@ -137,21 +154,33 @@
     }
   }
 
+  // Moves a prediction model with |optimization_target| into the update data.
+  void SeedPredictionModelUpdateData(
+      StoreUpdateData* update_data,
+      optimization_guide::proto::OptimizationTarget optimization_target) {
+    std::unique_ptr<optimization_guide::proto::PredictionModel>
+        prediction_model = CreatePredictionModel();
+    prediction_model->mutable_model_info()->set_optimization_target(
+        optimization_target);
+    update_data->MovePredictionModelIntoUpdateData(
+        std::move(*prediction_model));
+  }
+
   void CreateDatabase() {
     // Reset everything.
     db_ = nullptr;
-    hint_store_.reset();
+    guide_store_.reset();
 
     // Setup the fake db and the class under test.
     auto db = std::make_unique<FakeDB<StoreEntry>>(&db_store_);
     db_ = db.get();
 
-    hint_store_ = std::make_unique<OptimizationGuideStore>(std::move(db));
+    guide_store_ = std::make_unique<OptimizationGuideStore>(std::move(db));
   }
 
   void InitializeDatabase(bool success, bool purge_existing_data = false) {
     EXPECT_CALL(*this, OnInitialized());
-    hint_store()->Initialize(
+    guide_store()->Initialize(
         purge_existing_data,
         base::BindOnce(&OptimizationGuideStoreTest::OnInitialized,
                        base::Unretained(this)));
@@ -173,7 +202,7 @@
     // OnLoadMetadata callback
     db()->LoadCallback(true);
     if (state == MetadataSchemaState::kValid) {
-      // OnLoadHintEntryKeys callback
+      // OnLoadEntryKeys callback
       db()->LoadCallback(true);
     } else {
       // OnPurgeDatabase callback
@@ -184,15 +213,15 @@
   void UpdateComponentHints(std::unique_ptr<StoreUpdateData> component_data,
                             bool update_success = true,
                             bool load_hint_entry_keys_success = true) {
-    EXPECT_CALL(*this, OnUpdateHints());
-    hint_store()->UpdateComponentHints(
+    EXPECT_CALL(*this, OnUpdateStore());
+    guide_store()->UpdateComponentHints(
         std::move(component_data),
-        base::BindOnce(&OptimizationGuideStoreTest::OnUpdateHints,
+        base::BindOnce(&OptimizationGuideStoreTest::OnUpdateStore,
                        base::Unretained(this)));
-    // OnUpdateHints callback
+    // OnUpdateStore callback
     db()->UpdateCallback(update_success);
     if (update_success) {
-      // OnLoadHintEntryKeys callback
+      // OnLoadEntryKeys callback
       db()->LoadCallback(load_hint_entry_keys_success);
     }
   }
@@ -200,33 +229,50 @@
   void UpdateFetchedHints(std::unique_ptr<StoreUpdateData> fetched_data,
                           bool update_success = true,
                           bool load_hint_entry_keys_success = true) {
-    EXPECT_CALL(*this, OnUpdateHints());
-    hint_store()->UpdateFetchedHints(
+    EXPECT_CALL(*this, OnUpdateStore());
+    guide_store()->UpdateFetchedHints(
         std::move(fetched_data),
-        base::BindOnce(&OptimizationGuideStoreTest::OnUpdateHints,
+        base::BindOnce(&OptimizationGuideStoreTest::OnUpdateStore,
                        base::Unretained(this)));
-    // OnUpdateHints callback
+    // OnUpdateStore callback
     db()->UpdateCallback(update_success);
     if (update_success) {
-      // OnLoadHintEntryKeys callback
+      // OnLoadEntryKeys callback
       db()->LoadCallback(load_hint_entry_keys_success);
     }
   }
 
+  void UpdatePredictionModels(
+      std::unique_ptr<StoreUpdateData> prediction_models_data,
+      bool update_success = true,
+      bool load_prediction_models_entry_keys_success = true) {
+    EXPECT_CALL(*this, OnUpdateStore());
+    guide_store()->UpdatePredictionModels(
+        std::move(prediction_models_data),
+        base::BindOnce(&OptimizationGuideStoreTest::OnUpdateStore,
+                       base::Unretained(this)));
+    // OnUpdateStore callback
+    db()->UpdateCallback(update_success);
+    if (update_success) {
+      // OnLoadEntryKeys callback
+      db()->LoadCallback(load_prediction_models_entry_keys_success);
+    }
+  }
+
   void ClearFetchedHintsFromDatabase() {
-    hint_store()->ClearFetchedHintsFromDatabase();
+    guide_store()->ClearFetchedHintsFromDatabase();
     db()->UpdateCallback(true);
     db()->LoadCallback(true);
   }
 
   void PurgeExpiredFetchedHints() {
-    hint_store()->PurgeExpiredFetchedHints();
+    guide_store()->PurgeExpiredFetchedHints();
 
     // OnFetchedHintsLoadedToMaybePurge
     db()->LoadCallback(true);
-    // OnUpdateHints
+    // OnUpdateStore
     db()->UpdateCallback(true);
-    // OnLoadHintEntryKeys callback
+    // OnLoadEntryKeys callback
     db()->LoadCallback(true);
   }
 
@@ -293,11 +339,11 @@
   }
 
   size_t GetDBStoreEntryCount() const { return db_store_.size(); }
-  size_t GetStoreHintEntryKeyCount() const {
-    return hint_store_->GetHintEntryKeyCount();
+  size_t GetStoreEntryKeyCount() const {
+    return guide_store_->GetEntryKeyCount();
   }
 
-  OptimizationGuideStore* hint_store() { return hint_store_.get(); }
+  OptimizationGuideStore* guide_store() { return guide_store_.get(); }
   FakeDB<proto::StoreEntry>* db() { return db_; }
 
   const OptimizationGuideStore::EntryKey& last_loaded_hint_entry_key() const {
@@ -306,22 +352,32 @@
 
   proto::Hint* last_loaded_hint() { return last_loaded_hint_.get(); }
 
+  proto::PredictionModel* last_loaded_prediction_model() {
+    return last_loaded_prediction_model_.get();
+  }
+
   void OnHintLoaded(const OptimizationGuideStore::EntryKey& hint_entry_key,
                     std::unique_ptr<proto::Hint> loaded_hint) {
     last_loaded_hint_entry_key_ = hint_entry_key;
     last_loaded_hint_ = std::move(loaded_hint);
   }
 
+  void OnPredictionModelLoaded(
+      std::unique_ptr<proto::PredictionModel> loaded_prediction_model) {
+    last_loaded_prediction_model_ = std::move(loaded_prediction_model);
+  }
+
   MOCK_METHOD0(OnInitialized, void());
-  MOCK_METHOD0(OnUpdateHints, void());
+  MOCK_METHOD0(OnUpdateStore, void());
 
  private:
   FakeDB<proto::StoreEntry>* db_;
   StoreEntryMap db_store_;
-  std::unique_ptr<OptimizationGuideStore> hint_store_;
+  std::unique_ptr<OptimizationGuideStore> guide_store_;
 
   OptimizationGuideStore::EntryKey last_loaded_hint_entry_key_;
   std::unique_ptr<proto::Hint> last_loaded_hint_;
+  std::unique_ptr<proto::PredictionModel> last_loaded_prediction_model_;
 
   DISALLOW_COPY_AND_ASSIGN(OptimizationGuideStoreTest);
 };
@@ -357,7 +413,7 @@
 
   // In the case where initialization fails, the store should be fully purged.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   histogram_tester.ExpectTotalCount(
       "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult", 0);
@@ -387,7 +443,7 @@
 
   // In the case where initialization fails, the store should be fully purged.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   histogram_tester.ExpectBucketCount(
       "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
@@ -421,7 +477,7 @@
 
   // In the case where initialization fails, the store should be fully purged.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   histogram_tester.ExpectBucketCount(
       "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
@@ -449,7 +505,7 @@
 
   // In the case where initialization fails, the store should be fully purged.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   histogram_tester.ExpectTotalCount(
       "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult", 0);
@@ -479,7 +535,7 @@
 
   // In the case where initialization fails, the store should be fully purged.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   histogram_tester.ExpectBucketCount(
       "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
@@ -512,7 +568,7 @@
 
   // In the case where initialization fails, the store should be fully purged.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   histogram_tester.ExpectBucketCount(
       "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
@@ -540,12 +596,12 @@
 
   // OnLoadMetadata callback
   db()->LoadCallback(true);
-  // OnLoadHintEntryKeys callback
+  // OnLoadEntryKeys callback
   db()->LoadCallback(false);
 
   // In the case where initialization fails, the store should be fully purged.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   histogram_tester.ExpectBucketCount(
       "OptimizationGuide.HintCacheLevelDBStore.LoadMetadataResult",
@@ -573,7 +629,7 @@
 
   // The store should contain the schema metadata entry and nothing else.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
 
@@ -603,7 +659,7 @@
 
   // The store should contain the schema metadata entry and nothing else.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
 
@@ -633,7 +689,7 @@
 
   // The store should contain the schema metadata entry and nothing else.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
 
@@ -671,7 +727,7 @@
   // The store should contain the schema metadata entry and nothing else, as
   // the initial component hints are all purged.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
 
@@ -701,7 +757,7 @@
 
   // The store should contain the schema metadata entry and nothing else.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(1));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 
   EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
 
@@ -734,7 +790,7 @@
   // entry, and all of the initial component hints.
   EXPECT_EQ(GetDBStoreEntryCount(),
             static_cast<size_t>(component_hint_count + 3));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), component_hint_count);
+  EXPECT_EQ(GetStoreEntryKeyCount(), component_hint_count);
 
   EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
   ExpectComponentHintsPresent(kDefaultComponentVersion, component_hint_count);
@@ -769,7 +825,7 @@
   // entry, and all of the initial component hints.
   EXPECT_EQ(GetDBStoreEntryCount(),
             static_cast<size_t>(component_hint_count + 2));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), component_hint_count);
+  EXPECT_EQ(GetStoreEntryKeyCount(), component_hint_count);
 
   EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
   ExpectComponentHintsPresent(kDefaultComponentVersion, component_hint_count);
@@ -810,7 +866,7 @@
   // entry, and all of the initial component hints.
   EXPECT_EQ(GetDBStoreEntryCount(),
             static_cast<size_t>(component_hint_count + 2));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), component_hint_count);
+  EXPECT_EQ(GetStoreEntryKeyCount(), component_hint_count);
 
   EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent());
 
@@ -838,7 +894,7 @@
 
   // StoreUpdateData for a component update should only be created if the store
   // is initialized.
-  EXPECT_FALSE(hint_store()->MaybeCreateUpdateDataForComponentHints(
+  EXPECT_FALSE(guide_store()->MaybeCreateUpdateDataForComponentHints(
       base::Version(kUpdateComponentVersion)));
 }
 
@@ -852,7 +908,7 @@
   // No StoreUpdateData for a component update should be created when the
   // component version of the update is older than the store's component
   // version.
-  EXPECT_FALSE(hint_store()->MaybeCreateUpdateDataForComponentHints(
+  EXPECT_FALSE(guide_store()->MaybeCreateUpdateDataForComponentHints(
       base::Version("0.0.0")));
 }
 
@@ -865,7 +921,7 @@
 
   // No StoreUpdateData should be created when the component version of the
   // update is the same as the store's component version.
-  EXPECT_FALSE(hint_store()->MaybeCreateUpdateDataForComponentHints(
+  EXPECT_FALSE(guide_store()->MaybeCreateUpdateDataForComponentHints(
       base::Version(kDefaultComponentVersion)));
 }
 
@@ -878,7 +934,7 @@
 
   // StoreUpdateData for a component update should be created when there is no
   // pre-existing component in the store.
-  EXPECT_TRUE(hint_store()->MaybeCreateUpdateDataForComponentHints(
+  EXPECT_TRUE(guide_store()->MaybeCreateUpdateDataForComponentHints(
       base::Version(kDefaultComponentVersion)));
 }
 
@@ -891,7 +947,7 @@
 
   // StoreUpdateData for a component update should be created when the component
   // version of the update is newer than the store's component version.
-  EXPECT_TRUE(hint_store()->MaybeCreateUpdateDataForComponentHints(
+  EXPECT_TRUE(guide_store()->MaybeCreateUpdateDataForComponentHints(
       base::Version(kUpdateComponentVersion)));
 }
 
@@ -902,7 +958,7 @@
   InitializeStore(schema_state);
 
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
   SeedComponentUpdateData(update_data.get(), 5);
@@ -911,7 +967,7 @@
 
   // The store should be purged if the component data update fails.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 }
 
 TEST_F(OptimizationGuideStoreTest, UpdateComponentHintsGetKeysFails) {
@@ -921,7 +977,7 @@
   InitializeStore(schema_state);
 
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
   SeedComponentUpdateData(update_data.get(), 5);
@@ -932,7 +988,7 @@
   // The store should be purged if loading the keys after the component update
   // fails.
   EXPECT_EQ(GetDBStoreEntryCount(), static_cast<size_t>(0));
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), static_cast<size_t>(0));
+  EXPECT_EQ(GetStoreEntryKeyCount(), static_cast<size_t>(0));
 }
 
 TEST_F(OptimizationGuideStoreTest, UpdateComponentHints) {
@@ -944,7 +1000,7 @@
   InitializeStore(schema_state);
 
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
   SeedComponentUpdateData(update_data.get(), update_hint_count);
@@ -954,7 +1010,7 @@
   // metadata entry, the component metadata entry, and all of the update's
   // component hints.
   EXPECT_EQ(GetDBStoreEntryCount(), update_hint_count + 2);
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), update_hint_count);
+  EXPECT_EQ(GetStoreEntryKeyCount(), update_hint_count);
   ExpectComponentHintsPresent(kUpdateComponentVersion, update_hint_count);
 }
 
@@ -968,7 +1024,7 @@
   InitializeStore(schema_state, true /*=purge_existing_data*/);
 
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
   SeedComponentUpdateData(update_data.get(), update_hint_count);
@@ -978,7 +1034,7 @@
   // metadata entry, the component metadata entry, and all of the update's
   // component hints.
   EXPECT_EQ(GetDBStoreEntryCount(), update_hint_count + 2);
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), update_hint_count);
+  EXPECT_EQ(GetStoreEntryKeyCount(), update_hint_count);
   ExpectComponentHintsPresent(kUpdateComponentVersion, update_hint_count);
 }
 
@@ -992,7 +1048,7 @@
   InitializeStore(schema_state);
 
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
   SeedComponentUpdateData(update_data.get(), update_hint_count);
@@ -1000,7 +1056,7 @@
 
   // StoreUpdateData for the component update should not be created for a second
   // component update with the same version as the first component update.
-  EXPECT_FALSE(hint_store()->MaybeCreateUpdateDataForComponentHints(
+  EXPECT_FALSE(guide_store()->MaybeCreateUpdateDataForComponentHints(
       base::Version(kUpdateComponentVersion)));
 }
 
@@ -1016,10 +1072,10 @@
 
   // Create two updates for the same component version with different counts.
   std::unique_ptr<StoreUpdateData> update_data_1 =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   std::unique_ptr<StoreUpdateData> update_data_2 =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data_1);
   SeedComponentUpdateData(update_data_1.get(), update_hint_count_1);
@@ -1030,16 +1086,16 @@
   // first with |update_data_1| and then with |update_data_2|.
   UpdateComponentHints(std::move(update_data_1));
 
-  EXPECT_CALL(*this, OnUpdateHints());
-  hint_store()->UpdateComponentHints(
+  EXPECT_CALL(*this, OnUpdateStore());
+  guide_store()->UpdateComponentHints(
       std::move(update_data_2),
-      base::BindOnce(&OptimizationGuideStoreTest::OnUpdateHints,
+      base::BindOnce(&OptimizationGuideStoreTest::OnUpdateStore,
                      base::Unretained(this)));
 
   // Verify that the store is populated with the component data from
   // |update_data_1| and not |update_data_2|.
   EXPECT_EQ(GetDBStoreEntryCount(), update_hint_count_1 + 2);
-  EXPECT_EQ(GetStoreHintEntryKeyCount(), update_hint_count_1);
+  EXPECT_EQ(GetStoreEntryKeyCount(), update_hint_count_1);
   ExpectComponentHintsPresent(kUpdateComponentVersion, update_hint_count_1);
 }
 
@@ -1049,7 +1105,7 @@
   CreateDatabase();
 
   const OptimizationGuideStore::EntryKey kInvalidEntryKey = "invalid";
-  hint_store()->LoadHint(
+  guide_store()->LoadHint(
       kInvalidEntryKey,
       base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
                      base::Unretained(this)));
@@ -1068,7 +1124,7 @@
   InitializeStore(schema_state);
 
   const OptimizationGuideStore::EntryKey kInvalidEntryKey = "invalid";
-  hint_store()->LoadHint(
+  guide_store()->LoadHint(
       kInvalidEntryKey,
       base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
                      base::Unretained(this)));
@@ -1094,12 +1150,12 @@
   for (size_t i = 0; i < hint_count; ++i) {
     std::string host_suffix = GetHostSuffix(i);
     OptimizationGuideStore::EntryKey hint_entry_key;
-    if (!hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
+    if (!guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
       FAIL() << "Hint entry not found for host suffix: " << host_suffix;
       continue;
     }
 
-    hint_store()->LoadHint(
+    guide_store()->LoadHint(
         hint_entry_key,
         base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
                        base::Unretained(this)));
@@ -1126,7 +1182,7 @@
   InitializeStore(schema_state);
 
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
   SeedComponentUpdateData(update_data.get(), update_hint_count);
@@ -1137,12 +1193,12 @@
   for (size_t i = 0; i < update_hint_count; ++i) {
     std::string host_suffix = GetHostSuffix(i);
     OptimizationGuideStore::EntryKey hint_entry_key;
-    if (!hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
+    if (!guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
       FAIL() << "Hint entry not found for host suffix: " << host_suffix;
       continue;
     }
 
-    hint_store()->LoadHint(
+    guide_store()->LoadHint(
         hint_entry_key,
         base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
                        base::Unretained(this)));
@@ -1169,7 +1225,7 @@
   OptimizationGuideStore::EntryKey hint_entry_key;
 
   // Verify that hint entry keys can't be found when the store is unavailable.
-  EXPECT_FALSE(hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key));
+  EXPECT_FALSE(guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key));
 }
 
 TEST_F(OptimizationGuideStoreTest, FindHintEntryKeyInitialData) {
@@ -1185,7 +1241,8 @@
   for (size_t i = 0; i < hint_count * 2; ++i) {
     std::string host_suffix = GetHostSuffix(i);
     OptimizationGuideStore::EntryKey hint_entry_key;
-    bool success = hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key);
+    bool success =
+        guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key);
     EXPECT_EQ(success, i < hint_count);
   }
 }
@@ -1199,7 +1256,7 @@
   InitializeStore(schema_state);
 
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
   SeedComponentUpdateData(update_data.get(), update_hint_count);
@@ -1211,7 +1268,8 @@
   for (size_t i = 0; i < update_hint_count * 2; ++i) {
     std::string host_suffix = GetHostSuffix(i);
     OptimizationGuideStore::EntryKey hint_entry_key;
-    bool success = hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key);
+    bool success =
+        guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key);
     EXPECT_EQ(success, i < update_hint_count);
   }
 }
@@ -1235,7 +1293,7 @@
   InitializeStore(schema_state);
 
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->CreateUpdateDataForFetchedHints(
+      guide_store()->CreateUpdateDataForFetchedHints(
           update_time, update_time + optimization_guide::features::
                                          StoredFetchedHintsFreshnessDuration());
   ASSERT_TRUE(update_data);
@@ -1245,7 +1303,8 @@
   for (size_t i = 0; i < update_hint_count; ++i) {
     std::string host_suffix = GetHostSuffix(i);
     OptimizationGuideStore::EntryKey hint_entry_key;
-    bool success = hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key);
+    bool success =
+        guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key);
     EXPECT_EQ(success, i < update_hint_count);
   }
 }
@@ -1262,7 +1321,7 @@
 
   base::Version version("2.0.0");
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
 
@@ -1279,7 +1338,7 @@
 
   // Add fetched hints to the store that overlap with the same hosts as the
   // initial set.
-  update_data = hint_store()->CreateUpdateDataForFetchedHints(
+  update_data = guide_store()->CreateUpdateDataForFetchedHints(
       update_time,
       update_time +
           optimization_guide::features::StoredFetchedHintsFreshnessDuration());
@@ -1295,7 +1354,7 @@
   // as fetched hints take priority.
   std::string host_suffix = "host.domain2.org";
   OptimizationGuideStore::EntryKey hint_entry_key;
-  if (!hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
+  if (!guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
     FAIL() << "Hint entry not found for host suffix: " << host_suffix;
   }
 
@@ -1303,7 +1362,7 @@
 
   host_suffix = "subdomain.domain1.org";
 
-  if (!hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
+  if (!guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
     FAIL() << "Hint entry not found for host suffix: " << host_suffix;
   }
 
@@ -1321,7 +1380,7 @@
 
   base::Version version("2.0.0");
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
 
@@ -1338,7 +1397,7 @@
 
   // Add fetched hints to the store that overlap with the same hosts as the
   // initial set.
-  update_data = hint_store()->CreateUpdateDataForFetchedHints(
+  update_data = guide_store()->CreateUpdateDataForFetchedHints(
       update_time, update_time + base::TimeDelta().FromDays(7));
 
   proto::Hint fetched_hint1;
@@ -1356,7 +1415,7 @@
   // as fetched hints take priority.
   std::string host_suffix = "host.domain2.org";
   OptimizationGuideStore::EntryKey hint_entry_key;
-  if (!hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
+  if (!guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
     FAIL() << "Hint entry not found for host suffix: " << host_suffix;
   }
 
@@ -1364,7 +1423,7 @@
 
   host_suffix = "subdomain.domain1.org";
 
-  if (!hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
+  if (!guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
     FAIL() << "Hint entry not found for host suffix: " << host_suffix;
   }
 
@@ -1375,16 +1434,16 @@
 
   host_suffix = "domain1.org";
   // Component hint should still exist.
-  EXPECT_TRUE(hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key));
+  EXPECT_TRUE(guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key));
 
   host_suffix = "domain3.org";
   // Fetched hint should not still exist.
-  EXPECT_FALSE(hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key));
+  EXPECT_FALSE(guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key));
 
   // Add Components back - newer version.
   base::Version version3("3.0.0");
   std::unique_ptr<StoreUpdateData> update_data2 =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(version3);
+      guide_store()->MaybeCreateUpdateDataForComponentHints(version3);
 
   ASSERT_TRUE(update_data2);
 
@@ -1396,9 +1455,9 @@
   UpdateComponentHints(std::move(update_data2));
 
   host_suffix = "host.domain2.org";
-  EXPECT_TRUE(hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key));
+  EXPECT_TRUE(guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key));
 
-  update_data = hint_store()->CreateUpdateDataForFetchedHints(
+  update_data = guide_store()->CreateUpdateDataForFetchedHints(
       update_time,
       update_time +
           optimization_guide::features::StoredFetchedHintsFreshnessDuration());
@@ -1413,7 +1472,7 @@
   // initial set.
   host_suffix = "subdomain.domain1.org";
 
-  if (!hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
+  if (!guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
     FAIL() << "Hint entry not found for host suffix: " << host_suffix;
   }
 
@@ -1431,7 +1490,7 @@
 
   base::Version version("2.0.0");
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
 
@@ -1448,7 +1507,7 @@
 
   // Add fetched hints to the store that overlap with the same hosts as the
   // initial set.
-  update_data = hint_store()->CreateUpdateDataForFetchedHints(
+  update_data = guide_store()->CreateUpdateDataForFetchedHints(
       update_time, update_time + base::TimeDelta().FromDays(7));
 
   proto::Hint fetched_hint1;
@@ -1463,7 +1522,7 @@
   UpdateFetchedHints(std::move(update_data));
 
   // Add expired fetched hints to the store.
-  update_data = hint_store()->CreateUpdateDataForFetchedHints(
+  update_data = guide_store()->CreateUpdateDataForFetchedHints(
       update_time, update_time - base::TimeDelta().FromDays(7));
 
   proto::Hint fetched_hint3;
@@ -1480,10 +1539,10 @@
   PurgeExpiredFetchedHints();
 
   OptimizationGuideStore::EntryKey hint_entry_key;
-  EXPECT_FALSE(hint_store()->FindHintEntryKey("domain4.org", &hint_entry_key));
-  EXPECT_FALSE(hint_store()->FindHintEntryKey("domain5.org", &hint_entry_key));
-  EXPECT_TRUE(hint_store()->FindHintEntryKey("domain2.org", &hint_entry_key));
-  EXPECT_TRUE(hint_store()->FindHintEntryKey("domain3.org", &hint_entry_key));
+  EXPECT_FALSE(guide_store()->FindHintEntryKey("domain4.org", &hint_entry_key));
+  EXPECT_FALSE(guide_store()->FindHintEntryKey("domain5.org", &hint_entry_key));
+  EXPECT_TRUE(guide_store()->FindHintEntryKey("domain2.org", &hint_entry_key));
+  EXPECT_TRUE(guide_store()->FindHintEntryKey("domain3.org", &hint_entry_key));
 }
 
 TEST_F(OptimizationGuideStoreTest, FetchedHintsLoadExpiredHint) {
@@ -1497,7 +1556,7 @@
 
   base::Version version("2.0.0");
   std::unique_ptr<StoreUpdateData> update_data =
-      hint_store()->MaybeCreateUpdateDataForComponentHints(
+      guide_store()->MaybeCreateUpdateDataForComponentHints(
           base::Version(kUpdateComponentVersion));
   ASSERT_TRUE(update_data);
 
@@ -1513,7 +1572,7 @@
   UpdateComponentHints(std::move(update_data));
 
   // Add fetched hints to the store that expired.
-  update_data = hint_store()->CreateUpdateDataForFetchedHints(
+  update_data = guide_store()->CreateUpdateDataForFetchedHints(
       update_time, update_time - base::TimeDelta().FromDays(10));
 
   proto::Hint fetched_hint1;
@@ -1531,11 +1590,11 @@
   // as fetched hints take priority.
   std::string host_suffix = "host.domain2.org";
   OptimizationGuideStore::EntryKey hint_entry_key;
-  if (!hint_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
+  if (!guide_store()->FindHintEntryKey(host_suffix, &hint_entry_key)) {
     FAIL() << "Hint entry not found for host suffix: " << host_suffix;
   }
   EXPECT_EQ(hint_entry_key, "3_domain2.org");
-  hint_store()->LoadHint(
+  guide_store()->LoadHint(
       hint_entry_key, base::BindOnce(&OptimizationGuideStoreTest::OnHintLoaded,
                                      base::Unretained(this)));
 
@@ -1550,4 +1609,136 @@
       1);
 }
 
+TEST_F(OptimizationGuideStoreTest, FindPredictionModelEntryKey) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 0);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  std::unique_ptr<StoreUpdateData> update_data =
+      guide_store()->CreateUpdateDataForPredictionModels();
+  ASSERT_TRUE(update_data);
+  SeedPredictionModelUpdateData(update_data.get(),
+                                proto::OPTIMIZATION_TARGET_UNKNOWN);
+  SeedPredictionModelUpdateData(update_data.get(),
+                                proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+  UpdatePredictionModels(std::move(update_data));
+
+  OptimizationGuideStore::EntryKey entry_key;
+  bool success = guide_store()->FindPredictionModelEntryKey(
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
+  EXPECT_TRUE(success);
+  EXPECT_EQ(entry_key, "4_1");
+}
+
+TEST_F(OptimizationGuideStoreTest,
+       FindEntryKeyMissingForMissingPredictionModel) {
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 0);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  std::unique_ptr<StoreUpdateData> update_data =
+      guide_store()->CreateUpdateDataForPredictionModels();
+  ASSERT_TRUE(update_data);
+  SeedPredictionModelUpdateData(update_data.get(),
+                                proto::OPTIMIZATION_TARGET_UNKNOWN);
+  UpdatePredictionModels(std::move(update_data));
+
+  OptimizationGuideStore::EntryKey entry_key;
+  bool success = guide_store()->FindPredictionModelEntryKey(
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
+  EXPECT_FALSE(success);
+  EXPECT_EQ(entry_key, "4_1");
+}
+
+TEST_F(OptimizationGuideStoreTest, LoadPredictionModel) {
+  base::HistogramTester histogram_tester;
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 0);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  std::unique_ptr<StoreUpdateData> update_data =
+      guide_store()->CreateUpdateDataForPredictionModels();
+  ASSERT_TRUE(update_data);
+  SeedPredictionModelUpdateData(update_data.get(),
+                                proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+  UpdatePredictionModels(std::move(update_data));
+
+  OptimizationGuideStore::EntryKey entry_key;
+  bool success = guide_store()->FindPredictionModelEntryKey(
+      proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &entry_key);
+  EXPECT_TRUE(success);
+
+  guide_store()->LoadPredictionModel(
+      entry_key,
+      base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
+                     base::Unretained(this)));
+  // OnPredictionModelLoaded callback
+  db()->GetCallback(true);
+
+  EXPECT_TRUE(last_loaded_prediction_model());
+
+  histogram_tester.ExpectBucketCount(
+      "OptimizationGuide.PredictionModelStore.OnLoadCollided", false, 1);
+}
+
+TEST_F(OptimizationGuideStoreTest, LoadPredictionModelOnUnavailableStore) {
+  base::HistogramTester histogram_tester;
+  size_t initial_hint_count = 10;
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, initial_hint_count);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  const OptimizationGuideStore::EntryKey kInvalidEntryKey = "4_2";
+  guide_store()->LoadPredictionModel(
+      kInvalidEntryKey,
+      base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
+                     base::Unretained(this)));
+  // OnPredictionModelLoaded callback
+  db()->GetCallback(true);
+
+  // Verify that the OnPredictionModelLoaded callback runs when the store is
+  // unavailable and that the prediction model was correctly set.
+  EXPECT_FALSE(last_loaded_prediction_model());
+  // The load failed because of an unavailable store, not because of a
+  // collision.
+  histogram_tester.ExpectBucketCount(
+      "OptimizationGuide.PredictionModelStore.OnLoadCollided", false, 1);
+}
+
+TEST_F(OptimizationGuideStoreTest, LoadPredictionModelWithUpdateInFlight) {
+  base::HistogramTester histogram_tester;
+  MetadataSchemaState schema_state = MetadataSchemaState::kValid;
+  SeedInitialData(schema_state, 0);
+  CreateDatabase();
+  InitializeStore(schema_state);
+
+  std::unique_ptr<StoreUpdateData> update_data =
+      guide_store()->CreateUpdateDataForPredictionModels();
+  ASSERT_TRUE(update_data);
+  SeedPredictionModelUpdateData(update_data.get(),
+                                proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+  guide_store()->UpdatePredictionModels(
+      std::move(update_data),
+      base::BindOnce(&OptimizationGuideStoreTest::OnUpdateStore,
+                     base::Unretained(this)));
+
+  const OptimizationGuideStore::EntryKey kEntryKey = "4_1";
+  guide_store()->LoadPredictionModel(
+      kEntryKey,
+      base::BindOnce(&OptimizationGuideStoreTest::OnPredictionModelLoaded,
+                     base::Unretained(this)));
+
+  db()->GetCallback(true);
+
+  // Verify that the OnPredictionModelLoaded callback runs when the store is
+  // unavailable and that the prediction model was correctly set.
+  EXPECT_FALSE(last_loaded_prediction_model());
+  histogram_tester.ExpectBucketCount(
+      "OptimizationGuide.PredictionModelStore.OnLoadCollided", true, 1);
+}
+
 }  // namespace optimization_guide
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index 0a31147..4da37838 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -225,6 +225,9 @@
     <message name="IDS_PAGE_INFO_TYPE_ADS" desc="The label used for the ads permission controls in the Page Info popup.">
       Ads
     </message>
+    <message name="IDS_PAGE_INFO_TYPE_PROTECTED_MEDIA_IDENTIFIER" desc="The label used for the protected media identifier permission controls in the Page Info popup.">
+      Protected content
+    </message>
     <message name="IDS_PAGE_INFO_TYPE_AUTOPLAY" desc="The label used for the autoplay permission controls in the Page Info popup.">
       Autoplay
     </message>
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index c5e7ac9..000cccea 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -88,24 +88,24 @@
                                   base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Field trial identifier for password generation requirements.
-const char* kGenerationRequirementsFieldTrial =
+const char kGenerationRequirementsFieldTrial[] =
     "PasswordGenerationRequirements";
 
 // The file version number of password requirements files. If the prefix length
 // changes, this version number needs to be updated.
 // Default to 0 in order to get an empty requirements file.
-const char* kGenerationRequirementsVersion = "version";
+const char kGenerationRequirementsVersion[] = "version";
 
 // Length of a hash prefix of domain names. This is used to shard domains
 // across multiple files.
 // Default to 0 in order to put all domain names into the same shard.
-const char* kGenerationRequirementsPrefixLength = "prefix_length";
+const char kGenerationRequirementsPrefixLength[] = "prefix_length";
 
 // Timeout (in milliseconds) for password requirements lookups. As this is a
 // network request in the background that does not block the UI, the impact of
 // high values is not strong.
 // Default to 5000 ms.
-const char* kGenerationRequirementsTimeout = "timeout";
+const char kGenerationRequirementsTimeout[] = "timeout";
 
 }  // namespace features
 
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index 50802fe..03f0f06 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -42,10 +42,10 @@
 //   --force-fieldtrials=PasswordGenerationRequirements/Enabled
 //   --force-fieldtrial-params=PasswordGenerationRequirements.Enabled:\
 //       version/0/prefix_length/0/timeout/5000
-extern const char* kGenerationRequirementsFieldTrial;
-extern const char* kGenerationRequirementsVersion;
-extern const char* kGenerationRequirementsPrefixLength;
-extern const char* kGenerationRequirementsTimeout;
+extern const char kGenerationRequirementsFieldTrial[];
+extern const char kGenerationRequirementsVersion[];
+extern const char kGenerationRequirementsPrefixLength[];
+extern const char kGenerationRequirementsTimeout[];
 
 }  // namespace features
 
diff --git a/components/previews/content/previews_optimization_guide_impl.cc b/components/previews/content/previews_optimization_guide_impl.cc
index e2a6534..486e2c3b 100644
--- a/components/previews/content/previews_optimization_guide_impl.cc
+++ b/components/previews/content/previews_optimization_guide_impl.cc
@@ -81,7 +81,8 @@
       hint_cache_(std::make_unique<optimization_guide::HintCache>(
           std::make_unique<optimization_guide::OptimizationGuideStore>(
               database_provider,
-              profile_path,
+              profile_path.AddExtensionASCII(
+                  optimization_guide::kOptimizationGuideHintStore),
               background_task_runner_))),
       top_host_provider_(top_host_provider),
       time_clock_(base::DefaultClock::GetInstance()),
diff --git a/components/viz/common/gl_helper.cc b/components/viz/common/gl_helper.cc
index 1637b30..cabd57e 100644
--- a/components/viz/common/gl_helper.cc
+++ b/components/viz/common/gl_helper.cc
@@ -95,9 +95,9 @@
   GLenum GetReadbackFormat() const override;
 
  protected:
-  // Returns true if the planerizer should use the faster, two-pass shaders to
-  // generate the YUV planar outputs. If false, the source will be scanned three
-  // times, once for each Y/U/V plane.
+  // Returns true if the planerizer should use the faster, two-pass shaders
+  // to generate the YUV planar outputs. If false, the source will be
+  // scanned three times, once for each Y/U/V plane.
   bool use_mrt() const { return !v_planerizer_; }
 
   // Reallocates the intermediate and plane textures, if needed.
@@ -112,9 +112,9 @@
 
  private:
   // These generate the Y/U/V planes. If MRT is being used, |y_planerizer_|
-  // generates the Y and interim UV plane, |u_planerizer_| generates the final U
-  // and V planes, and |v_planerizer_| is unused. If MRT is not being used, each
-  // of these generates only one of the Y/U/V planes.
+  // generates the Y and interim UV plane, |u_planerizer_| generates the
+  // final U and V planes, and |v_planerizer_| is unused. If MRT is not
+  // being used, each of these generates only one of the Y/U/V planes.
   const std::unique_ptr<GLHelper::ScalerInterface> y_planerizer_;
   const std::unique_ptr<GLHelper::ScalerInterface> u_planerizer_;
   const std::unique_ptr<GLHelper::ScalerInterface> v_planerizer_;
@@ -122,8 +122,8 @@
   // Intermediate texture, holding the scaler's output.
   base::Optional<TextureHolder> intermediate_;
 
-  // Intermediate texture, holding the UV interim output (if the MRT shader is
-  // being used).
+  // Intermediate texture, holding the UV interim output (if the MRT shader
+  // is being used).
   base::Optional<ScopedTexture> uv_;
 
   DISALLOW_COPY_AND_ASSIGN(I420ConverterImpl);
@@ -146,6 +146,7 @@
   ~CopyTextureToImpl() { CancelRequests(); }
 
   void ReadbackTextureAsync(GLuint texture,
+                            GLenum texture_target,
                             const gfx::Size& dst_size,
                             unsigned char* out,
                             SkColorType color_type,
@@ -267,8 +268,8 @@
     CopyTextureToImpl* copy_impl_;
     ReadbackSwizzle swizzle_;
 
-    // May be null if no scaling is required. This can be changed between calls
-    // to ReadbackYUV().
+    // May be null if no scaling is required. This can be changed between
+    // calls to ReadbackYUV().
     std::unique_ptr<GLHelper::ScalerInterface> scaler_;
 
     // These are the output textures for each Y/U/V plane.
@@ -366,6 +367,7 @@
 
 void GLHelper::CopyTextureToImpl::ReadbackTextureAsync(
     GLuint texture,
+    GLenum texture_target,
     const gfx::Size& dst_size,
     unsigned char* out,
     SkColorType color_type,
@@ -379,9 +381,10 @@
              IsBGRAReadbackSupported()) {
     format = GL_BGRA_EXT;
   } else {
-    // Note: It's possible the GL implementation supports other readback types.
-    // However, as of this writing, no caller of this method will request a
-    // different |color_type| (i.e., requiring using some other GL format).
+    // Note: It's possible the GL implementation supports other readback
+    // types. However, as of this writing, no caller of this method will
+    // request a different |color_type| (i.e., requiring using some other GL
+    // format).
     std::move(callback).Run(false);
     return;
   }
@@ -389,12 +392,13 @@
   ScopedFramebuffer dst_framebuffer(gl_);
   ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
                                                              dst_framebuffer);
-  ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture);
-  gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                            texture, 0);
+  gl_->BindTexture(texture_target, texture);
+  gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                            texture_target, texture, 0);
   ReadbackAsync(dst_size, dst_size.width() * kBytesPerPixel,
                 dst_size.width() * kBytesPerPixel, out, format,
                 GL_UNSIGNED_BYTE, kBytesPerPixel, std::move(callback));
+  gl_->BindTexture(texture_target, 0);
 }
 
 void GLHelper::CopyTextureToImpl::ReadbackDone(Request* finished_request,
@@ -490,13 +494,14 @@
 GLHelper::~GLHelper() {}
 
 void GLHelper::ReadbackTextureAsync(GLuint texture,
+                                    GLenum texture_target,
                                     const gfx::Size& dst_size,
                                     unsigned char* out,
                                     SkColorType color_type,
                                     base::OnceCallback<void(bool)> callback) {
   InitCopyTextToImpl();
-  copy_texture_to_impl_->ReadbackTextureAsync(texture, dst_size, out,
-                                              color_type, std::move(callback));
+  copy_texture_to_impl_->ReadbackTextureAsync(
+      texture, texture_target, dst_size, out, color_type, std::move(callback));
 }
 
 void GLHelper::InitCopyTextToImpl() {
@@ -744,8 +749,8 @@
   I420ConverterImpl::Convert(texture, src_texture_size, gfx::Vector2dF(),
                              scaler_.get(), output_rect, y_, u_, v_);
 
-  // Read back planes, one at a time. Keep the video frame alive while doing the
-  // readback.
+  // Read back planes, one at a time. Keep the video frame alive while doing
+  // the readback.
   const gfx::Rect paste_rect(paste_location, output_rect.size());
   const auto SetUpAndBindFramebuffer = [this](GLuint framebuffer,
                                               GLuint texture) {
diff --git a/components/viz/common/gl_helper.h b/components/viz/common/gl_helper.h
index 4f2e4aeb..8ae29d9 100644
--- a/components/viz/common/gl_helper.h
+++ b/components/viz/common/gl_helper.h
@@ -164,6 +164,7 @@
   // TODO(crbug.com/870036): DEPRECATED. This will be moved to be closer to its
   // one caller soon.
   void ReadbackTextureAsync(GLuint texture,
+                            GLenum texture_target,
                             const gfx::Size& dst_size,
                             unsigned char* out,
                             SkColorType color_type,
diff --git a/components/viz/common/gl_helper_unittest.cc b/components/viz/common/gl_helper_unittest.cc
index 31a99741..5895c38 100644
--- a/components/viz/common/gl_helper_unittest.cc
+++ b/components/viz/common/gl_helper_unittest.cc
@@ -1009,7 +1009,7 @@
     base::RunLoop run_loop;
     bool success = false;
     helper_->ReadbackTextureAsync(
-        src_texture, src_size, pixels, color_type,
+        src_texture, GL_TEXTURE_2D, src_size, pixels, color_type,
         base::BindOnce(
             [](bool* success, base::OnceClosure callback, bool result) {
               *success = result;
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 75d4b91c1..ffae69c 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -800,6 +800,18 @@
   if (root_render_pass->color_space == output_color_space_)
     return;
 
+  // An extra color conversion pass is only done if the display's color
+  // space is unsuitable as a working color space. This happens only
+  // on Windows, where HDR output is required to be in a space with a linear
+  // (or PQ) transfer function.
+  // TODO(ccameron,sunnyps): Determine if blending in PQ space is close
+  // enough to sRGB space as to not require this extra pass.
+  // Or at least to avoid changing behavior.
+  if (output_color_space_ != gfx::ColorSpace::CreateSCRGBLinear() &&
+      output_color_space_ != gfx::ColorSpace::CreateHDR10()) {
+    return;
+  }
+
   gfx::Rect output_rect = root_render_pass->output_rect;
   CHECK(root_render_pass->transform_to_root_target == gfx::Transform());
 
diff --git a/components/zucchini/disassembler_elf.cc b/components/zucchini/disassembler_elf.cc
index ff3b085b..2405374 100644
--- a/components/zucchini/disassembler_elf.cc
+++ b/components/zucchini/disassembler_elf.cc
@@ -208,8 +208,14 @@
 
     // Skip empty sections. These don't affect |offset_bound|, and don't
     // contribute to RVA-offset mapping.
-    if (section->sh_size == 0)
+    if (section->sh_size == 0) {
+      // Skipping empty sections is only safe if the |sh_offset| is within the
+      // image. Fail if this is not true as the input is ill-formed.
+      if (section->sh_offset >= image_.size())
+        return false;
+
       continue;
+    }
 
     // Extract dimensions to 32-bit integers to facilitate conversion. Range of
     // values was ensured above when checking that the section is bounded.
diff --git a/content/browser/accessibility/accessibility_tree_formatter_base.cc b/content/browser/accessibility/accessibility_tree_formatter_base.cc
index 90ac4e5..0340968f 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_base.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.cc
@@ -52,8 +52,10 @@
     formatter = Create();
   base::string16 accessibility_contents_utf16;
   formatter->SetPropertyFilters(property_filters);
-  formatter->FormatAccessibilityTree(ax_mgr->GetRoot(),
-                                     &accessibility_contents_utf16);
+  std::unique_ptr<base::DictionaryValue> dict =
+      static_cast<AccessibilityTreeFormatterBase*>(formatter.get())
+          ->BuildAccessibilityTree(ax_mgr->GetRoot());
+  formatter->FormatAccessibilityTree(*dict, &accessibility_contents_utf16);
   return accessibility_contents_utf16;
 }
 
@@ -95,12 +97,11 @@
   return false;
 }
 
-AccessibilityTreeFormatterBase::AccessibilityTreeFormatterBase()
-    : show_ids_(false) {}
+AccessibilityTreeFormatterBase::AccessibilityTreeFormatterBase() = default;
 
-AccessibilityTreeFormatterBase::~AccessibilityTreeFormatterBase() {}
+AccessibilityTreeFormatterBase::~AccessibilityTreeFormatterBase() = default;
 
-void AccessibilityTreeFormatterBase::FormatAccessibilityTree(
+void AccessibilityTreeFormatterBase::FormatAccessibilityTreeForTesting(
     BrowserAccessibility* root,
     base::string16* contents) {
   std::unique_ptr<base::DictionaryValue> dict = BuildAccessibilityTree(root);
diff --git a/content/browser/accessibility/accessibility_tree_formatter_base.h b/content/browser/accessibility/accessibility_tree_formatter_base.h
index 7b1f6b5..56e7244e 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_base.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.h
@@ -66,13 +66,14 @@
   virtual std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree(
       BrowserAccessibility* root) = 0;
 
+  void FormatAccessibilityTreeForTesting(BrowserAccessibility* root,
+                                         base::string16* contents);
+
   // AccessibilityTreeFormatter overrides.
   void AddDefaultFilters(
       std::vector<PropertyFilter>* property_filters) override;
   std::unique_ptr<base::DictionaryValue> FilterAccessibilityTree(
       const base::DictionaryValue& dict) override;
-  void FormatAccessibilityTree(BrowserAccessibility* root,
-                               base::string16* contents) override;
   void FormatAccessibilityTree(const base::DictionaryValue& tree_node,
                                base::string16* contents) override;
   void SetPropertyFilters(
@@ -149,7 +150,7 @@
   std::vector<NodeFilter> node_filters_;
 
   // Whether or not node ids should be included in the dump.
-  bool show_ids_;
+  bool show_ids_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(AccessibilityTreeFormatterBase);
 };
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index aad8de5..d9cc53c 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -38,6 +38,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/accessibility_notification_waiter.h"
+#include "content/public/test/browser_accessibility.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "content/test/content_browser_test_utils_internal.h"
@@ -128,9 +129,9 @@
   DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest);
 };
 
-AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() {}
+AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() = default;
 
-AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() {}
+AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() = default;
 
 base::string16 AccessibilityWinBrowserTest::PrintAXTree() const {
   std::unique_ptr<AccessibilityTreeFormatter> formatter(
@@ -141,11 +142,8 @@
       L"*", AccessibilityTreeFormatter::PropertyFilter::ALLOW)});
 
   base::string16 str;
-  formatter->FormatAccessibilityTree(
-      static_cast<WebContentsImpl*>(shell()->web_contents())
-          ->GetRootBrowserAccessibilityManager()
-          ->GetRoot(),
-      &str);
+  TestBrowserAccessibility::FormatAccessibilityTree(
+      formatter.get(), GetRootAccessibilityNode(shell()->web_contents()), &str);
   return str;
 }
 
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
index d7078fd9..55b6b10 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
@@ -31,6 +31,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/accessibility_notification_waiter.h"
+#include "content/public/test/browser_accessibility.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
@@ -133,11 +134,9 @@
       PropertyFilter(base::ASCIIToUTF16("*"), PropertyFilter::ALLOW));
   formatter->SetPropertyFilters(property_filters);
   formatter->set_show_ids(true);
-  WebContentsImpl* web_contents =
-      static_cast<WebContentsImpl*>(shell()->web_contents());
   base::string16 ax_tree_dump;
-  formatter->FormatAccessibilityTree(
-      web_contents->GetRootBrowserAccessibilityManager()->GetRoot(),
+  TestBrowserAccessibility::FormatAccessibilityTree(
+      formatter.get(), GetRootAccessibilityNode(shell()->web_contents()),
       &ax_tree_dump);
   return ax_tree_dump;
 }
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.h b/content/browser/accessibility/dump_accessibility_browsertest_base.h
index cbea701b..5577b91 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.h
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_ACCESSIBILITY_DUMP_ACCESSIBILITY_BROWSERTEST_BASE_H_
 #define CONTENT_BROWSER_ACCESSIBILITY_DUMP_ACCESSIBILITY_BROWSERTEST_BASE_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -16,6 +17,8 @@
 
 namespace content {
 
+class BrowserAccessibility;
+
 // Base class for an accessibility browsertest that takes an HTML file as
 // input, loads it into a tab, dumps some accessibility data in text format,
 // then compares that text to an expectation file in the same directory.
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 82be5b9f..50b8c8808 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -23,6 +23,7 @@
 #include "content/public/common/content_paths.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/accessibility_notification_waiter.h"
+#include "content/public/test/browser_accessibility.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "ui/accessibility/accessibility_switches.h"
@@ -147,10 +148,8 @@
     formatter->SetPropertyFilters(property_filters_);
     formatter->SetNodeFilters(node_filters_);
     base::string16 actual_contents_utf16;
-    WebContentsImpl* web_contents =
-        static_cast<WebContentsImpl*>(shell()->web_contents());
-    formatter->FormatAccessibilityTree(
-        web_contents->GetRootBrowserAccessibilityManager()->GetRoot(),
+    TestBrowserAccessibility::FormatAccessibilityTree(
+        formatter.get(), GetRootAccessibilityNode(shell()->web_contents()),
         &actual_contents_utf16);
     std::string actual_contents = base::UTF16ToUTF8(actual_contents_utf16);
     return base::SplitString(actual_contents, "\n", base::KEEP_WHITESPACE,
@@ -1064,6 +1063,11 @@
   RunAriaTest(FILE_PATH_LITERAL("aria-treegrid.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityAriaTreeDiscontinuous) {
+  RunAriaTest(FILE_PATH_LITERAL("aria-tree-discontinuous.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityAriaUndefined) {
   RunAriaTest(FILE_PATH_LITERAL("aria-undefined.html"));
 }
diff --git a/content/browser/browser_process_sub_thread.cc b/content/browser/browser_process_sub_thread.cc
index 3c6e699..550b8fc 100644
--- a/content/browser/browser_process_sub_thread.cc
+++ b/content/browser/browser_process_sub_thread.cc
@@ -17,7 +17,6 @@
 #include "content/browser/utility_process_host.h"
 #include "content/common/child_process_host_impl.h"
 #include "content/public/browser/browser_child_process_host_iterator.h"
-#include "content/public/browser/browser_thread_delegate.h"
 #include "content/public/common/process_type.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_request.h"
@@ -32,20 +31,6 @@
 
 namespace content {
 
-namespace {
-BrowserThreadDelegate* g_io_thread_delegate = nullptr;
-}  // namespace
-
-// static
-void BrowserThread::SetIOThreadDelegate(BrowserThreadDelegate* delegate) {
-  // |delegate| can only be set/unset while BrowserThread::IO isn't up.
-  DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::IO));
-  // and it cannot be set twice.
-  DCHECK(!g_io_thread_delegate || !delegate);
-
-  g_io_thread_delegate = delegate;
-}
-
 BrowserProcessSubThread::BrowserProcessSubThread(BrowserThread::ID identifier)
     : base::Thread(BrowserThreadImpl::GetThreadName(identifier)),
       identifier_(identifier) {
@@ -122,9 +107,6 @@
   if (BrowserThread::CurrentlyOn(BrowserThread::IO))
     IOThreadCleanUp();
 
-  if (identifier_ == BrowserThread::IO && g_io_thread_delegate)
-    g_io_thread_delegate->CleanUp();
-
   notification_service_.reset();
 
 #if defined(OS_WIN)
@@ -136,12 +118,6 @@
   DCHECK_CALLED_ON_VALID_THREAD(browser_thread_checker_);
 
   notification_service_ = std::make_unique<NotificationServiceImpl>();
-
-  if (identifier_ == BrowserThread::IO && g_io_thread_delegate) {
-    // Allow blocking calls while initializing the IO thread.
-    base::ScopedAllowBlocking allow_blocking_for_init;
-    g_io_thread_delegate->Init();
-  }
 }
 
 // Mark following two functions as NOINLINE so the compiler doesn't merge
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index a37639e..08fa62cf 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -1306,6 +1306,19 @@
       return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
     }
 
+    // Allow "no access" schemes to commit even though |url_origin| and
+    // |origin| tuples don't match. We have to allow this because Blink's
+    // SecurityOrigin::CreateWithReferenceOrigin() and url::Origin::Resolve()
+    // handle "no access" URLs differently. CreateWithReferenceOrigin() treats
+    // "no access" like data: URLs and returns an opaque origin with |origin|
+    // as a precursor. Resolve() returns a non-opaque origin consisting of the
+    // scheme and host portions of the original URL.
+    //
+    // TODO(1020201): Make CreateWithReferenceOrigin() & Resolve() consistent
+    // with each other and then remove this exception.
+    if (base::Contains(url::GetNoAccessSchemes(), url.scheme()))
+      return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
+
     return CanCommitStatus::CANNOT_COMMIT_ORIGIN;
   }
 
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index d7cc1cbb..319246ad 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -26,6 +26,7 @@
 #include "net/http/http_util.h"
 #include "net/url_request/redirect_util.h"
 #include "net/url_request/url_request.h"
+#include "services/network/public/cpp/cors/cors.h"
 #include "services/network/public/cpp/resource_request_body.h"
 #include "third_party/blink/public/platform/resource_request_blocked_reason.h"
 
@@ -161,6 +162,8 @@
 using InterceptionStage = DevToolsURLLoaderInterceptor::InterceptionStage;
 using protocol::Response;
 using GlobalRequestId = std::tuple<int32_t, int32_t, int32_t>;
+using network::mojom::CredentialsMode;
+using network::mojom::FetchResponseType;
 
 class BodyReader : public mojo::DataPipeDrainer::Client {
  public:
@@ -355,6 +358,10 @@
   bool CanGetResponseBody(std::string* error_reason);
   bool StartJobAndMaybeNotify();
 
+  void UpdateCORSFlag();
+  network::mojom::FetchResponseType CalculateResponseTainting();
+  network::ResourceRequest GetResourceRequestForCookies();
+
   const std::string id_prefix_;
   const GlobalRequestId global_req_id_;
   const base::UnguessableToken frame_token_;
@@ -390,6 +397,8 @@
 
   bool waiting_for_resolution_;
   int redirect_count_;
+  bool tainted_origin_ = false;
+  bool fetch_cors_flag_ = false;
   std::string current_id_;
 
   std::unique_ptr<BodyReader> body_reader_;
@@ -692,6 +701,7 @@
 }
 
 bool InterceptionJob::StartJobAndMaybeNotify() {
+  UpdateCORSFlag();
   start_ticks_ = base::TimeTicks::Now();
   start_time_ = base::Time::Now();
 
@@ -713,6 +723,42 @@
   return true;
 }
 
+// FIXME(caseq): The logic in the three methods below is borrowed from
+// CorsURLLoader as a matter of a quick and mergeable fix for crbug.com/1022173.
+// This logic should be unified with CorsURLLoader.
+network::mojom::FetchResponseType InterceptionJob::CalculateResponseTainting() {
+  if (fetch_cors_flag_)
+    return FetchResponseType::kCors;
+  if (create_loader_params_->request.mode ==
+          network::mojom::RequestMode::kNoCors &&
+      tainted_origin_) {
+    return FetchResponseType::kOpaque;
+  }
+  return FetchResponseType::kBasic;
+}
+
+network::ResourceRequest InterceptionJob::GetResourceRequestForCookies() {
+  FetchResponseType response_tainting =
+      fetch_cors_flag_ ? FetchResponseType::kCors : FetchResponseType::kBasic;
+
+  network::ResourceRequest result = create_loader_params_->request;
+  result.credentials_mode =
+      network::cors::CalculateCredentialsFlag(
+          create_loader_params_->request.credentials_mode, response_tainting)
+          ? CredentialsMode::kInclude
+          : CredentialsMode::kOmit;
+  return result;
+}
+
+void InterceptionJob::UpdateCORSFlag() {
+  if (fetch_cors_flag_)
+    return;
+
+  const network::ResourceRequest& request = create_loader_params_->request;
+  fetch_cors_flag_ = network::cors::ShouldCheckCors(
+      request.url, request.request_initiator, request.mode);
+}
+
 bool InterceptionJob::CanGetResponseBody(std::string* error_reason) {
   if (!(stage_ & InterceptionStage::RESPONSE)) {
     *error_reason =
@@ -1009,7 +1055,7 @@
 
 void InterceptionJob::ProcessSetCookies(const net::HttpResponseHeaders& headers,
                                         base::OnceClosure callback) {
-  if (!create_loader_params_->request.SavesCookies()) {
+  if (!GetResourceRequestForCookies().SavesCookies()) {
     std::move(callback).Run();
     return;
   }
@@ -1167,7 +1213,7 @@
 
 void InterceptionJob::FetchCookies(
     network::mojom::CookieManager::GetCookieListCallback callback) {
-  if (!create_loader_params_->request.SendsCookies()) {
+  if (!GetResourceRequestForCookies().SendsCookies()) {
     std::move(callback).Run({}, {});
     return;
   }
@@ -1233,6 +1279,12 @@
 
   network::ResourceRequest* request = &create_loader_params_->request;
   const net::RedirectInfo& info = *response_metadata_->redirect_info;
+  const auto current_origin = url::Origin::Create(request->url);
+  if (request->request_initiator &&
+      (!url::Origin::Create(info.new_url).IsSameOriginWith(current_origin) &&
+       !request->request_initiator->IsSameOriginWith(current_origin))) {
+    tainted_origin_ = true;
+  }
 
   bool clear_body = false;
   net::RedirectUtil::UpdateHttpRequest(request->url, request->method, info,
@@ -1247,6 +1299,8 @@
   request->referrer = GURL(info.new_referrer);
   response_metadata_.reset();
 
+  UpdateCORSFlag();
+
   if (interceptor_) {
     // Pretend that each redirect hop is a new request -- this is for
     // compatibilty with URLRequestJob-based interception implementation.
diff --git a/content/browser/devtools/protocol/tethering_handler.cc b/content/browser/devtools/protocol/tethering_handler.cc
index eab85ac..c0d8c328 100644
--- a/content/browser/devtools/protocol/tethering_handler.cc
+++ b/content/browser/devtools/protocol/tethering_handler.cc
@@ -33,7 +33,7 @@
 const int kMinTetheringPort = 1024;
 const int kMaxTetheringPort = 65535;
 
-net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
     net::DefineNetworkTrafficAnnotation("tethering_handler_socket", R"(
         semantics {
           sender: "Tethering Handler"
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 0a45632..cd93395cc 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -456,8 +456,26 @@
 // static
 std::unique_ptr<NavigationEntry> NavigationController::CreateNavigationEntry(
     const GURL& url,
-    const Referrer& referrer,
-    const base::Optional<url::Origin>& initiator_origin,
+    Referrer referrer,
+    base::Optional<url::Origin> initiator_origin,
+    ui::PageTransition transition,
+    bool is_renderer_initiated,
+    const std::string& extra_headers,
+    BrowserContext* browser_context,
+    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
+  return NavigationControllerImpl::CreateNavigationEntry(
+      url, referrer, std::move(initiator_origin),
+      nullptr /* source_site_instance */, transition, is_renderer_initiated,
+      extra_headers, browser_context, std::move(blob_url_loader_factory));
+}
+
+// static
+std::unique_ptr<NavigationEntryImpl>
+NavigationControllerImpl::CreateNavigationEntry(
+    const GURL& url,
+    Referrer referrer,
+    base::Optional<url::Origin> initiator_origin,
+    SiteInstance* source_site_instance,
     ui::PageTransition transition,
     bool is_renderer_initiated,
     const std::string& extra_headers,
@@ -469,6 +487,12 @@
   RewriteUrlForNavigation(url, browser_context, &url_to_load, &virtual_url,
                           &reverse_on_redirect);
 
+  // Let the NTP override the navigation params and pretend that this is a
+  // browser-initiated, bookmark-like navigation.
+  GetContentClient()->browser()->OverrideNavigationParams(
+      source_site_instance, &transition, &is_renderer_initiated, &referrer,
+      &initiator_origin);
+
   auto entry = std::make_unique<NavigationEntryImpl>(
       nullptr,  // The site instance for tabs is sent on navigation
                 // (WebContents::GetSiteInstance).
@@ -2231,8 +2255,9 @@
       // TODO(creis): Ensure this case can't exist in https://crbug.com/524208.
       entry = NavigationEntryImpl::FromNavigationEntry(CreateNavigationEntry(
           GURL(url::kAboutBlankURL), referrer, initiator_origin,
-          page_transition, is_renderer_initiated, extra_headers,
-          browser_context_, nullptr /* blob_url_loader_factory */));
+          source_site_instance, page_transition, is_renderer_initiated,
+          extra_headers, browser_context_,
+          nullptr /* blob_url_loader_factory */));
     }
     entry->AddOrUpdateFrameEntry(
         node, -1, -1, nullptr,
@@ -2242,8 +2267,9 @@
   } else {
     // Main frame case.
     entry = NavigationEntryImpl::FromNavigationEntry(CreateNavigationEntry(
-        url, referrer, initiator_origin, page_transition, is_renderer_initiated,
-        extra_headers, browser_context_, blob_url_loader_factory));
+        url, referrer, initiator_origin, source_site_instance, page_transition,
+        is_renderer_initiated, extra_headers, browser_context_,
+        blob_url_loader_factory));
     entry->root_node()->frame_entry->set_source_site_instance(
         static_cast<SiteInstanceImpl*>(source_site_instance));
     entry->root_node()->frame_entry->set_method(method);
@@ -2989,8 +3015,9 @@
       // TODO(creis): Ensure this case can't exist in https://crbug.com/524208.
       entry = NavigationEntryImpl::FromNavigationEntry(CreateNavigationEntry(
           GURL(url::kAboutBlankURL), params.referrer, params.initiator_origin,
-          params.transition_type, params.is_renderer_initiated,
-          extra_headers_crlf, browser_context_, blob_url_loader_factory));
+          params.source_site_instance.get(), params.transition_type,
+          params.is_renderer_initiated, extra_headers_crlf, browser_context_,
+          blob_url_loader_factory));
     }
 
     entry->AddOrUpdateFrameEntry(
@@ -3002,8 +3029,9 @@
     // Otherwise, create a pending entry for the main frame.
     entry = NavigationEntryImpl::FromNavigationEntry(CreateNavigationEntry(
         params.url, params.referrer, params.initiator_origin,
-        params.transition_type, params.is_renderer_initiated,
-        extra_headers_crlf, browser_context_, blob_url_loader_factory));
+        params.source_site_instance.get(), params.transition_type,
+        params.is_renderer_initiated, extra_headers_crlf, browser_context_,
+        blob_url_loader_factory));
     entry->set_source_site_instance(
         static_cast<SiteInstanceImpl*>(params.source_site_instance.get()));
     entry->SetRedirectChain(params.redirect_chain);
diff --git a/content/browser/frame_host/navigation_controller_impl.h b/content/browser/frame_host/navigation_controller_impl.h
index 97c5935..00ab09d 100644
--- a/content/browser/frame_host/navigation_controller_impl.h
+++ b/content/browser/frame_host/navigation_controller_impl.h
@@ -305,6 +305,19 @@
   // requests corresponding to the current pending entry.
   std::unique_ptr<PendingEntryRef> ReferencePendingEntry();
 
+  // Like NavigationController::CreateNavigationEntry, but takes an extra
+  // |source_site_instance| argument.
+  static std::unique_ptr<NavigationEntryImpl> CreateNavigationEntry(
+      const GURL& url,
+      Referrer referrer,
+      base::Optional<url::Origin> initiator_origin,
+      SiteInstance* source_site_instance,
+      ui::PageTransition transition,
+      bool is_renderer_initiated,
+      const std::string& extra_headers,
+      BrowserContext* browser_context,
+      scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory);
+
  private:
   friend class RestoreHelper;
 
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 8546749..b2759ee9 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -898,7 +898,7 @@
       commit_navigation_client_(mojo::NullAssociatedRemote()),
       rfh_restored_from_back_forward_cache_(
           rfh_restored_from_back_forward_cache) {
-  DCHECK(browser_initiated || common_params_->initiator_origin.has_value());
+  DCHECK(browser_initiated_ || common_params_->initiator_origin.has_value());
   DCHECK(!IsRendererDebugURL(common_params_->url));
   DCHECK(common_params_->method == "POST" || !common_params_->post_data);
   TRACE_EVENT_ASYNC_BEGIN2("navigation", "NavigationRequest", this,
@@ -957,6 +957,20 @@
     DCHECK(!RequiresSourceSiteInstance() || source_site_instance_);
   }
 
+  // Let the NTP override the navigation params and pretend that this is a
+  // browser-initiated, bookmark-like navigation.
+  if (!browser_initiated_ && source_site_instance_) {
+    bool is_renderer_initiated = !browser_initiated_;
+    Referrer referrer(*common_params_->referrer);
+    GetContentClient()->browser()->OverrideNavigationParams(
+        source_site_instance_.get(), &common_params_->transition,
+        &is_renderer_initiated, &referrer, &common_params_->initiator_origin);
+    common_params_->referrer =
+        blink::mojom::Referrer::New(referrer.url, referrer.policy);
+    browser_initiated_ = !is_renderer_initiated;
+    commit_params_->is_browser_initiated = browser_initiated_;
+  }
+
   // Store the old RenderFrameHost id at request creation to be used later.
   previous_render_frame_host_id_ = GlobalFrameRoutingId(
       frame_tree_node->current_frame_host()->GetProcess()->GetID(),
@@ -1026,11 +1040,11 @@
         common_params_->referrer->policy, frame_tree_node);
 
     if (begin_params_->is_form_submission) {
-      if (browser_initiated && !commit_params_->post_content_type.empty()) {
+      if (browser_initiated_ && !commit_params_->post_content_type.empty()) {
         // This is a form resubmit, so make sure to set the Content-Type header.
         headers.SetHeaderIfMissing(net::HttpRequestHeaders::kContentType,
                                    commit_params_->post_content_type);
-      } else if (!browser_initiated) {
+      } else if (!browser_initiated_) {
         // Save the Content-Type in case the form is resubmitted. This will get
         // sent back to the renderer in the CommitNavigation IPC. The renderer
         // will then send it back with the post body so that we can access it
@@ -1241,18 +1255,6 @@
       frame_tree_node->current_frame_host()->GetSiteInstance();
   site_url_ = GetSiteForCommonParamsURL();
 
-  // Let the NTP override the navigation params and pretend that this is a
-  // browser-initiated, bookmark-like navigation.
-  bool is_renderer_initiated = !browser_initiated_;
-  Referrer referrer(*common_params_->referrer);
-  GetContentClient()->browser()->OverrideNavigationParams(
-      starting_site_instance_.get(), &common_params_->transition,
-      &is_renderer_initiated, &referrer, &common_params_->initiator_origin);
-  common_params_->referrer =
-      blink::mojom::Referrer::New(referrer.url, referrer.policy);
-  browser_initiated_ = !is_renderer_initiated;
-  commit_params_->is_browser_initiated = browser_initiated_;
-
   // Compute the redirect chain.
   // TODO(clamy): Try to simplify this and have the redirects be part of
   // CommonNavigationParams.
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index a4688d0..e36e4fb 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -749,12 +749,20 @@
   if (has_transient_entry)
     return nullptr;
 
+  // Since GetNavigationEntryForRendererInitiatedNavigation is called from
+  // OnBeginNavigation, we can assume that no frame proxies are involved and
+  // therefore that |current_site_instance| is also the |source_site_instance|.
+  SiteInstance* current_site_instance =
+      frame_tree_node->current_frame_host()->GetSiteInstance();
+  SiteInstance* source_site_instance = current_site_instance;
+
   std::unique_ptr<NavigationEntryImpl> entry =
       NavigationEntryImpl::FromNavigationEntry(
-          NavigationController::CreateNavigationEntry(
+          NavigationControllerImpl::CreateNavigationEntry(
               common_params.url, content::Referrer(),
-              common_params.initiator_origin, ui::PAGE_TRANSITION_LINK,
-              true /* is_renderer_initiated */, std::string(),
+              common_params.initiator_origin, source_site_instance,
+              ui::PAGE_TRANSITION_LINK, true /* is_renderer_initiated */,
+              std::string() /* extra_headers */,
               controller_->GetBrowserContext(),
               nullptr /* blob_url_loader_factory */));
 
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index d822691d..6de95bbf 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -17,6 +17,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "components/network_session_configurator/common/network_switches.h"
+#include "content/browser/browser_url_handler_impl.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -55,6 +56,7 @@
 #include "content/shell/browser/shell_download_manager_delegate.h"
 #include "content/test/content_browser_test_utils_internal.h"
 #include "content/test/did_commit_navigation_interceptor.h"
+#include "content/test/fake_network_url_loader_factory.h"
 #include "ipc/ipc_security_test_util.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -3045,6 +3047,112 @@
   EXPECT_EQ(0u, response_2.http_request()->headers.count("Cookie"));
 }
 
+// Tests for validating URL rewriting behavior like chrome://history to
+// chrome-native://history.
+class NavigationUrlRewriteBrowserTest : public NavigationBaseBrowserTest {
+ protected:
+  static constexpr const char* kRewriteURL = "http://a.com/rewrite";
+  static constexpr const char* kNoAccessScheme = "no-access";
+  static constexpr const char* kNoAccessURL = "no-access://testing/";
+
+  class BrowserClient : public ContentBrowserClient {
+   public:
+    void BrowserURLHandlerCreated(BrowserURLHandler* handler) override {
+      handler->AddHandlerPair(RewriteUrl,
+                              BrowserURLHandlerImpl::null_handler());
+    }
+
+    void RegisterNonNetworkNavigationURLLoaderFactories(
+        int frame_tree_node_id,
+        NonNetworkURLLoaderFactoryMap* factories) override {
+      auto url_loader_factory = std::make_unique<FakeNetworkURLLoaderFactory>(
+          "HTTP/1.1 200 OK\nContent-Type: text/html\n\n", "This is a test",
+          /* network_accessed */ true, net::OK);
+      factories->emplace(std::string(kNoAccessScheme),
+                         std::move(url_loader_factory));
+    }
+
+    bool ShouldAssignSiteForURL(const GURL& url) override {
+      return !url.SchemeIs(kNoAccessScheme);
+    }
+
+    static bool RewriteUrl(GURL* url, BrowserContext* browser_context) {
+      if (*url == GURL(kRewriteURL)) {
+        *url = GURL(kNoAccessURL);
+        return true;
+      }
+      return false;
+    }
+  };
+
+  void SetUp() override {
+    url::AddStandardScheme(kNoAccessScheme, url::SCHEME_WITH_HOST);
+    url::AddNoAccessScheme(kNoAccessScheme);
+
+    NavigationBaseBrowserTest::SetUp();
+  }
+
+  void SetUpOnMainThread() override {
+    NavigationBaseBrowserTest::SetUpOnMainThread();
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    browser_client_ = std::make_unique<BrowserClient>();
+    old_browser_client_ = SetBrowserClientForTesting(browser_client_.get());
+  }
+
+  void TearDownOnMainThread() override {
+    SetBrowserClientForTesting(old_browser_client_);
+    old_browser_client_ = nullptr;
+    browser_client_.reset();
+
+    NavigationBaseBrowserTest::TearDownOnMainThread();
+  }
+
+  GURL GetRewriteToNoAccessURL() const { return GURL(kRewriteURL); }
+
+ private:
+  std::unique_ptr<BrowserClient> browser_client_;
+  ContentBrowserClient* old_browser_client_;
+};
+
+INSTANTIATE_TEST_SUITE_P(/* no prefix */,
+                         NavigationUrlRewriteBrowserTest,
+                         ::testing::Bool());
+
+// Tests navigating to a URL that gets rewritten to a "no access" URL. This
+// mimics the behavior of navigating to special URLs like chrome://newtab and
+// chrome://history which get rewritten to "no access" chrome-native:// URLs.
+IN_PROC_BROWSER_TEST_P(NavigationUrlRewriteBrowserTest, RewriteToNoAccess) {
+  // Perform an initial navigation.
+  {
+    TestNavigationObserver observer(shell()->web_contents());
+    GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
+    EXPECT_TRUE(NavigateToURL(shell(), url));
+    EXPECT_EQ(url, observer.last_navigation_url());
+    EXPECT_TRUE(observer.last_navigation_succeeded());
+    EXPECT_FALSE(observer.last_initiator_origin().has_value());
+  }
+
+  // Navigate to the URL that will get rewritten to a "no access" URL.
+  {
+    auto* web_contents = shell()->web_contents();
+    TestNavigationObserver observer(web_contents);
+
+    // Note: We are using LoadURLParams here because we need to have the
+    // initiator_origin set and NavigateToURL() does not do that.
+    NavigationController::LoadURLParams params(GetRewriteToNoAccessURL());
+    params.initiator_origin =
+        web_contents->GetMainFrame()->GetLastCommittedOrigin();
+    web_contents->GetController().LoadURLWithParams(params);
+    web_contents->Focus();
+    observer.Wait();
+
+    EXPECT_EQ(GURL(kNoAccessURL), observer.last_navigation_url());
+    EXPECT_TRUE(observer.last_navigation_succeeded());
+    EXPECT_TRUE(observer.last_initiator_origin().has_value());
+  }
+}
+
 // Update the fragment part of the URL while it is currently displaying an error
 // page. Regression test https://crbug.com/1018385
 IN_PROC_BROWSER_TEST_P(NavigationBrowserTest,
diff --git a/content/browser/renderer_host/input/fling_controller.h b/content/browser/renderer_host/input/fling_controller.h
index a3d7646..c524d559 100644
--- a/content/browser/renderer_host/input/fling_controller.h
+++ b/content/browser/renderer_host/input/fling_controller.h
@@ -30,6 +30,9 @@
 
   virtual void SendGeneratedGestureScrollEvents(
       const GestureEventWithLatencyInfo& gesture_event) = 0;
+
+  // Returns the size of visible viewport in screen space, in DIPs.
+  virtual gfx::Size GetRootWidgetViewportSize() = 0;
 };
 
 // Interface with which the fling progress gets scheduled.
diff --git a/content/browser/renderer_host/input/fling_controller_unittest.cc b/content/browser/renderer_host/input/fling_controller_unittest.cc
index aae4bca..ef3b524 100644
--- a/content/browser/renderer_host/input/fling_controller_unittest.cc
+++ b/content/browser/renderer_host/input/fling_controller_unittest.cc
@@ -67,6 +67,10 @@
     last_sent_gesture_ = gesture_event.event;
   }
 
+  gfx::Size GetRootWidgetViewportSize() override {
+    return gfx::Size(1920, 1080);
+  }
+
   // FlingControllerSchedulerClient
   void ScheduleFlingProgress(
       base::WeakPtr<FlingController> fling_controller) override {
diff --git a/content/browser/renderer_host/input/fling_scheduler_unittest.cc b/content/browser/renderer_host/input/fling_scheduler_unittest.cc
index 549b021..b5715c1c 100644
--- a/content/browser/renderer_host/input/fling_scheduler_unittest.cc
+++ b/content/browser/renderer_host/input/fling_scheduler_unittest.cc
@@ -105,6 +105,9 @@
       const MouseWheelEventWithLatencyInfo& wheel_event) override {}
   void SendGeneratedGestureScrollEvents(
       const GestureEventWithLatencyInfo& gesture_event) override {}
+  gfx::Size GetRootWidgetViewportSize() override {
+    return gfx::Size(1920, 1080);
+  }
 
   std::unique_ptr<FlingController> fling_controller_;
   std::unique_ptr<FakeFlingScheduler> fling_scheduler_;
diff --git a/content/browser/renderer_host/input/gesture_event_queue_unittest.cc b/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
index ebad9f6..eb144d60 100644
--- a/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
+++ b/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
@@ -94,6 +94,9 @@
       const MouseWheelEventWithLatencyInfo& wheel_event) override {}
   void SendGeneratedGestureScrollEvents(
       const GestureEventWithLatencyInfo& gesture_event) override {}
+  gfx::Size GetRootWidgetViewportSize() override {
+    return gfx::Size(1920, 1080);
+  }
 
   // FlingControllerSchedulerClient
   void ScheduleFlingProgress(
diff --git a/content/browser/renderer_host/input/input_router_client.h b/content/browser/renderer_host/input/input_router_client.h
index 2bd8ae5..e1ee03e 100644
--- a/content/browser/renderer_host/input/input_router_client.h
+++ b/content/browser/renderer_host/input/input_router_client.h
@@ -83,6 +83,9 @@
                                             bool up,
                                             bool down) = 0;
   virtual void FallbackCursorModeSetCursorVisibility(bool visible) = 0;
+
+  // Returns the size of visible viewport in screen space, in DIPs.
+  virtual gfx::Size GetRootWidgetViewportSize() = 0;
 };
 
 } // namespace content
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc
index e9615f0..4c23c997a 100644
--- a/content/browser/renderer_host/input/input_router_impl.cc
+++ b/content/browser/renderer_host/input/input_router_impl.cc
@@ -458,6 +458,10 @@
                                               gesture_event.latency);
 }
 
+gfx::Size InputRouterImpl::GetRootWidgetViewportSize() {
+  return client_->GetRootWidgetViewportSize();
+}
+
 void InputRouterImpl::SendMouseWheelEventImmediately(
     const MouseWheelEventWithLatencyInfo& wheel_event) {
   mojom::WidgetInputHandler::DispatchEventCallback callback = base::BindOnce(
diff --git a/content/browser/renderer_host/input/input_router_impl.h b/content/browser/renderer_host/input/input_router_impl.h
index d9f4da8b..1ad7df6b 100644
--- a/content/browser/renderer_host/input/input_router_impl.h
+++ b/content/browser/renderer_host/input/input_router_impl.h
@@ -156,6 +156,7 @@
       const MouseWheelEventWithLatencyInfo& wheel_event) override;
   void SendGeneratedGestureScrollEvents(
       const GestureEventWithLatencyInfo& gesture_event) override;
+  gfx::Size GetRootWidgetViewportSize() override;
 
   // MouseWheelEventQueueClient
   void SendMouseWheelEventImmediately(
diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc
index 2145050..6c236e3c 100644
--- a/content/browser/renderer_host/input/input_router_impl_unittest.cc
+++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc
@@ -114,6 +114,10 @@
 
   void FallbackCursorModeSetCursorVisibility(bool visible) override {}
 
+  gfx::Size GetRootWidgetViewportSize() override {
+    return gfx::Size(1920, 1080);
+  }
+
   MockWidgetInputHandler::MessageVector GetAndResetDispatchedMessages() {
     return widget_input_handler_.GetAndResetDispatchedMessages();
   }
diff --git a/content/browser/renderer_host/input/mock_input_router_client.cc b/content/browser/renderer_host/input/mock_input_router_client.cc
index 91f9cef2..d153e4a 100644
--- a/content/browser/renderer_host/input/mock_input_router_client.cc
+++ b/content/browser/renderer_host/input/mock_input_router_client.cc
@@ -90,6 +90,10 @@
   return false;
 }
 
+gfx::Size MockInputRouterClient::GetRootWidgetViewportSize() {
+  return gfx::Size(1920, 1080);
+}
+
 bool MockInputRouterClient::GetAndResetFilterEventCalled() {
   bool filter_input_event_called = filter_input_event_called_;
   filter_input_event_called_ = false;
diff --git a/content/browser/renderer_host/input/mock_input_router_client.h b/content/browser/renderer_host/input/mock_input_router_client.h
index fda1e1f..360706e 100644
--- a/content/browser/renderer_host/input/mock_input_router_client.h
+++ b/content/browser/renderer_host/input/mock_input_router_client.h
@@ -47,6 +47,7 @@
                                     bool up,
                                     bool down) override {}
   void FallbackCursorModeSetCursorVisibility(bool visible) override {}
+  gfx::Size GetRootWidgetViewportSize() override;
 
   bool GetAndResetFilterEventCalled();
   ui::DidOverscrollParams GetAndResetOverscroll();
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index c988820..2a9053a 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -3353,4 +3353,9 @@
                                                    transformed_rect_to_zoom));
 }
 
+gfx::Size RenderWidgetHostImpl::GetRootWidgetViewportSize() {
+  auto* root_view = view_->GetRootView();
+  return root_view->GetVisibleViewportSize();
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 85ae36e..c428f84 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -727,6 +727,7 @@
                                     bool up,
                                     bool down) override;
   void FallbackCursorModeSetCursorVisibility(bool visible) override;
+  gfx::Size GetRootWidgetViewportSize() override;
 
   // FrameTokenMessageQueue::Client:
   void OnInvalidFrameToken(uint32_t frame_token) override;
diff --git a/content/browser/ssl/ssl_error_handler.cc b/content/browser/ssl/ssl_error_handler.cc
index ee32e913..de1a214 100644
--- a/content/browser/ssl/ssl_error_handler.cc
+++ b/content/browser/ssl/ssl_error_handler.cc
@@ -4,8 +4,6 @@
 
 #include "content/browser/ssl/ssl_error_handler.h"
 
-#include "base/bind.h"
-#include "base/task/post_task.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -19,37 +17,14 @@
 
 namespace content {
 
-namespace {
-
-void CompleteCancelRequest(
-    const base::WeakPtr<SSLErrorHandler::Delegate>& delegate,
-    const net::SSLInfo& ssl_info,
-    int error) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (delegate.get())
-    delegate->CancelSSLRequest(error, &ssl_info);
-}
-
-void CompleteContinueRequest(
-    const base::WeakPtr<SSLErrorHandler::Delegate>& delegate) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (delegate.get()) {
-    delegate->ContinueSSLRequest();
-  }
-}
-
-}  // namespace
-
 SSLErrorHandler::SSLErrorHandler(WebContents* web_contents,
                                  const base::WeakPtr<Delegate>& delegate,
-                                 BrowserThread::ID delegate_thread,
                                  bool is_main_frame_request,
                                  const GURL& url,
                                  int net_error,
                                  const net::SSLInfo& ssl_info,
                                  bool fatal)
     : delegate_(delegate),
-      delegate_thread_(delegate_thread),
       request_url_(url),
       is_main_frame_request_(is_main_frame_request),
       ssl_info_(ssl_info),
@@ -57,45 +32,26 @@
       fatal_(fatal),
       web_contents_(web_contents) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(delegate_thread == BrowserThread::UI ||
-         delegate_thread == BrowserThread::IO);
 }
 
 SSLErrorHandler::~SSLErrorHandler() {}
 
 void SSLErrorHandler::CancelRequest() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (delegate_thread_ == BrowserThread::UI) {
-    if (delegate_)
-      delegate_->CancelSSLRequest(net::ERR_ABORTED, &ssl_info());
-    return;
-  }
-  base::PostTask(FROM_HERE, {BrowserThread::IO},
-                 base::BindOnce(&CompleteCancelRequest, delegate_, ssl_info(),
-                                net::ERR_ABORTED));
+  if (delegate_)
+    delegate_->CancelSSLRequest(net::ERR_ABORTED, &ssl_info());
 }
 
 void SSLErrorHandler::DenyRequest() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (delegate_thread_ == BrowserThread::UI) {
-    if (delegate_)
-      delegate_->CancelSSLRequest(cert_error_, &ssl_info());
-    return;
-  }
-  base::PostTask(FROM_HERE, {BrowserThread::IO},
-                 base::BindOnce(&CompleteCancelRequest, delegate_, ssl_info(),
-                                cert_error_));
+  if (delegate_)
+    delegate_->CancelSSLRequest(cert_error_, &ssl_info());
 }
 
 void SSLErrorHandler::ContinueRequest() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (delegate_thread_ == BrowserThread::UI) {
-    if (delegate_)
-      delegate_->ContinueSSLRequest();
-    return;
-  }
-  base::PostTask(FROM_HERE, {BrowserThread::IO},
-                 base::BindOnce(&CompleteContinueRequest, delegate_));
+  if (delegate_)
+    delegate_->ContinueSSLRequest();
 }
 
 }  // namespace content
diff --git a/content/browser/ssl/ssl_error_handler.h b/content/browser/ssl/ssl_error_handler.h
index d80dd098..7257e4a 100644
--- a/content/browser/ssl/ssl_error_handler.h
+++ b/content/browser/ssl/ssl_error_handler.h
@@ -31,8 +31,6 @@
 // call exactly one of those methods exactly once.
 class SSLErrorHandler {
  public:
-  // SSLErrorHandler's delegate lives on the UI or IO thread based on the passed
-  // in |delegate_thread|. The methods will be called on that thread.
   class CONTENT_EXPORT Delegate {
    public:
     // Called when SSLErrorHandler decides to cancel the request because of
@@ -49,7 +47,6 @@
 
   SSLErrorHandler(WebContents* web_contents,
                   const base::WeakPtr<Delegate>& delegate,
-                  BrowserThread::ID delegate_thread,
                   bool is_main_frame_request,
                   const GURL& url,
                   int net_error,
@@ -85,12 +82,8 @@
   void DenyRequest();
 
  private:
-  // This is called on |delegate_thread_|.
   base::WeakPtr<Delegate> delegate_;
 
-  // The thread that the delegate is called on.
-  BrowserThread::ID delegate_thread_;
-
   // The URL for the request that generated the error.
   const GURL request_url_;
 
diff --git a/content/browser/ssl/ssl_manager.cc b/content/browser/ssl/ssl_manager.cc
index 93cda186..cbf5e4d2 100644
--- a/content/browser/ssl/ssl_manager.cc
+++ b/content/browser/ssl/ssl_manager.cc
@@ -103,36 +103,6 @@
   DISALLOW_COPY_AND_ASSIGN(SSLManagerSet);
 };
 
-void HandleSSLErrorOnUI(
-    const base::Callback<WebContents*(void)>& web_contents_getter,
-    const base::WeakPtr<SSLErrorHandler::Delegate>& delegate,
-    BrowserThread::ID delegate_thread,
-    bool is_main_frame_request,
-    const GURL& url,
-    int net_error,
-    const net::SSLInfo& ssl_info,
-    bool fatal) {
-  content::WebContents* web_contents = web_contents_getter.Run();
-  std::unique_ptr<SSLErrorHandler> handler(new SSLErrorHandler(
-      web_contents, delegate, delegate_thread, is_main_frame_request, url,
-      net_error, ssl_info, fatal));
-
-  if (!web_contents) {
-    // Requests can fail to dispatch because they don't have a WebContents. See
-    // https://crbug.com/86537. In this case we have to make a decision in this
-    // function.
-    handler->CancelRequest();
-    return;
-  }
-
-  NavigationControllerImpl* controller =
-      static_cast<NavigationControllerImpl*>(&web_contents->GetController());
-  controller->SetPendingNavigationSSLError(true);
-
-  SSLManager* manager = controller->ssl_manager();
-  manager->OnCertError(std::move(handler));
-}
-
 void LogMixedContentMetrics(MixedContentType type,
                             ukm::SourceId source_id,
                             ukm::UkmRecorder* recorder) {
@@ -157,20 +127,27 @@
   DVLOG(1) << "OnSSLCertificateError() cert_error: " << net_error
            << " url: " << url.spec() << " cert_status: " << std::hex
            << ssl_info.cert_status;
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    HandleSSLErrorOnUI(web_contents_getter, delegate, BrowserThread::UI,
-                       is_main_frame_request, url, net_error, ssl_info, fatal);
+  content::WebContents* web_contents = web_contents_getter.Run();
+  std::unique_ptr<SSLErrorHandler> handler(
+      new SSLErrorHandler(web_contents, delegate, is_main_frame_request, url,
+                          net_error, ssl_info, fatal));
+
+  if (!web_contents) {
+    // Requests can fail to dispatch because they don't have a WebContents. See
+    // https://crbug.com/86537. In this case we have to make a decision in this
+    // function.
+    handler->CancelRequest();
     return;
   }
 
-  // TODO(jam): remove the logic to call this from IO thread once the
-  // network service code path is the only one.
-  base::PostTask(
-      FROM_HERE, {BrowserThread::UI},
-      base::BindOnce(&HandleSSLErrorOnUI, web_contents_getter, delegate,
-                     BrowserThread::IO, is_main_frame_request, url, net_error,
-                     ssl_info, fatal));
+  NavigationControllerImpl* controller =
+      static_cast<NavigationControllerImpl*>(&web_contents->GetController());
+  controller->SetPendingNavigationSSLError(true);
+
+  SSLManager* manager = controller->ssl_manager();
+  manager->OnCertError(std::move(handler));
 }
 
 // static
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/InterstitialPageTest.java b/content/public/android/javatests/src/org/chromium/content/browser/InterstitialPageTest.java
index 63b562e..57b37220f2 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/InterstitialPageTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/InterstitialPageTest.java
@@ -13,8 +13,8 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
@@ -92,7 +92,7 @@
     @Test
     @LargeTest
     @Feature({"Navigation"})
-    @RetryOnFailure
+    @DisabledTest(message = "crbug.com/1022324")
     public void testCloseInterstitial() throws ExecutionException {
         final String proceedCommand = "PROCEED";
         final String htmlContent = "<html>"
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index c4f537c..5dcf1099 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -99,7 +99,6 @@
     "browser_task_traits.h",
     "browser_thread.cc",
     "browser_thread.h",
-    "browser_thread_delegate.h",
     "browser_url_handler.h",
     "browsing_data_filter_builder.h",
     "browsing_data_remover.h",
diff --git a/content/public/browser/accessibility_tree_formatter.h b/content/public/browser/accessibility_tree_formatter.h
index 55c3c9f2..21077c5 100644
--- a/content/public/browser/accessibility_tree_formatter.h
+++ b/content/public/browser/accessibility_tree_formatter.h
@@ -27,8 +27,6 @@
 
 namespace content {
 
-class BrowserAccessibility;
-
 class AccessibilityTestExpectationsLocator {
  public:
   // Suffix of the expectation file corresponding to html file.
@@ -127,9 +125,6 @@
   virtual std::unique_ptr<base::DictionaryValue> FilterAccessibilityTree(
       const base::DictionaryValue& dict) = 0;
 
-  // Dumps a BrowserAccessibility tree into a string.
-  virtual void FormatAccessibilityTree(BrowserAccessibility* root,
-                                       base::string16* contents) = 0;
   virtual void FormatAccessibilityTree(const base::DictionaryValue& tree_node,
                                        base::string16* contents) = 0;
 
diff --git a/content/public/browser/browser_thread.h b/content/public/browser/browser_thread.h
index dcec2a0..af24784 100644
--- a/content/public/browser/browser_thread.h
+++ b/content/public/browser/browser_thread.h
@@ -21,7 +21,6 @@
 
 namespace content {
 
-class BrowserThreadDelegate;
 class BrowserThreadImpl;
 
 // Use DCHECK_CURRENTLY_ON(BrowserThread::ID) to assert that a function can only
@@ -121,16 +120,6 @@
   // sets identifier to its ID.  Otherwise returns false.
   static bool GetCurrentThreadIdentifier(ID* identifier) WARN_UNUSED_RESULT;
 
-  // Sets the delegate for BrowserThread::IO.
-  //
-  // Only one delegate may be registered at a time. The delegate may be
-  // unregistered by providing a nullptr pointer.
-  //
-  // The delegate can only be registered through this call before
-  // BrowserThreadImpl(BrowserThread::IO) is created and unregistered after
-  // it was destroyed and its underlying thread shutdown.
-  static void SetIOThreadDelegate(BrowserThreadDelegate* delegate);
-
   // Use these templates in conjunction with RefCountedThreadSafe or scoped_ptr
   // when you want to ensure that an object is deleted on a specific thread.
   // This is needed when an object can hop between threads (i.e. UI -> IO ->
diff --git a/content/public/browser/browser_thread_delegate.h b/content/public/browser/browser_thread_delegate.h
deleted file mode 100644
index 3eb823c..0000000
--- a/content/public/browser/browser_thread_delegate.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_PUBLIC_BROWSER_BROWSER_THREAD_DELEGATE_H_
-#define CONTENT_PUBLIC_BROWSER_BROWSER_THREAD_DELEGATE_H_
-
-#include "content/common/content_export.h"
-
-namespace content {
-
-// A Delegate for content embedders to perform extra initialization/cleanup on
-// BrowserThread::IO.
-class BrowserThreadDelegate {
- public:
-  virtual ~BrowserThreadDelegate() = default;
-
-  // Called prior to completing initialization of BrowserThread::IO.
-  virtual void Init() = 0;
-
-  // Called during teardown of BrowserThread::IO.
-  virtual void CleanUp() = 0;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_BROWSER_BROWSER_THREAD_DELEGATE_H_
diff --git a/content/public/browser/navigation_controller.h b/content/public/browser/navigation_controller.h
index db6a06f..7fa0063 100644
--- a/content/public/browser/navigation_controller.h
+++ b/content/public/browser/navigation_controller.h
@@ -105,8 +105,8 @@
   // Extra headers are separated by \n.
   CONTENT_EXPORT static std::unique_ptr<NavigationEntry> CreateNavigationEntry(
       const GURL& url,
-      const Referrer& referrer,
-      const base::Optional<url::Origin>& initiator_origin,
+      Referrer referrer,
+      base::Optional<url::Origin> initiator_origin,
       ui::PageTransition transition,
       bool is_renderer_initiated,
       const std::string& extra_headers,
diff --git a/content/public/browser/url_data_source.cc b/content/public/browser/url_data_source.cc
index 0cc65ec..9ca008d 100644
--- a/content/public/browser/url_data_source.cc
+++ b/content/public/browser/url_data_source.cc
@@ -127,10 +127,6 @@
   return std::string();
 }
 
-bool URLDataSource::IsGzipped(const std::string& path) {
-  return false;
-}
-
 void URLDataSource::DisablePolymer2ForHost(const std::string& host) {}
 
 bool URLDataSource::ShouldReplaceI18nInJS() {
diff --git a/content/public/browser/url_data_source.h b/content/public/browser/url_data_source.h
index ed64db6..9e9d150 100644
--- a/content/public/browser/url_data_source.h
+++ b/content/public/browser/url_data_source.h
@@ -165,9 +165,6 @@
   virtual std::string GetAccessControlAllowOriginForOrigin(
       const std::string& origin);
 
-  // Whether |path| is gzipped (and should be transmitted gzipped).
-  virtual bool IsGzipped(const std::string& path);
-
   // Called on the UI thread. For the shared resource, disables using Polymer 2
   // for requests from |host|, even if WebUIPolymer2 is enabled. Assumes this
   // method is only called from one host.
diff --git a/content/public/test/browser_accessibility.h b/content/public/test/browser_accessibility.h
new file mode 100644
index 0000000..5a85afe
--- /dev/null
+++ b/content/public/test/browser_accessibility.h
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_PUBLIC_TEST_BROWSER_ACCESSIBILITY_H_
+#define CONTENT_PUBLIC_TEST_BROWSER_ACCESSIBILITY_H_
+
+#include "ui/accessibility/platform/ax_platform_node_delegate.h"
+
+namespace content {
+
+class AccessibilityTreeFormatter;
+
+// A class that exposes BrowserAccessibility for testing in a controlled manner.
+// Additional methods can be added to expose methods from BrowserAccessibility,
+// as long as the methods do not expose non-public content/ classes.
+class TestBrowserAccessibility : public ui::AXPlatformNodeDelegate {
+ public:
+  // Dumps a BrowserAccessibility tree into a string.
+  static void FormatAccessibilityTree(AccessibilityTreeFormatter* formatter,
+                                      TestBrowserAccessibility* root,
+                                      base::string16* contents);
+
+  ~TestBrowserAccessibility() override;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_TEST_BROWSER_ACCESSIBILITY_H_
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index a69ae599..93a3a8d 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -89,6 +89,7 @@
 #include "content/public/test/test_fileapi_operation_waiter.h"
 #include "content/public/test/test_launcher.h"
 #include "content/public/test/test_navigation_observer.h"
+#include "content/test/browser_accessibility.h"
 #include "content/test/did_commit_navigation_interceptor.h"
 #include "ipc/ipc_security_test_util.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -2027,39 +2028,42 @@
   return manager->SnapshotAXTreeForTesting();
 }
 
-BrowserAccessibility* GetRootAccessibilityNode(WebContents* web_contents) {
+TestBrowserAccessibility* GetRootAccessibilityNode(WebContents* web_contents) {
   WebContentsImpl* web_contents_impl =
       static_cast<WebContentsImpl*>(web_contents);
   BrowserAccessibilityManager* manager =
       web_contents_impl->GetRootBrowserAccessibilityManager();
-  return manager ? manager->GetRoot() : nullptr;
+  return manager ? ToTestBrowserAccessibility(manager->GetRoot()) : nullptr;
 }
 
 FindAccessibilityNodeCriteria::FindAccessibilityNodeCriteria() = default;
 
 FindAccessibilityNodeCriteria::~FindAccessibilityNodeCriteria() = default;
 
-BrowserAccessibility* FindAccessibilityNode(
+TestBrowserAccessibility* FindAccessibilityNode(
     WebContents* web_contents,
     const FindAccessibilityNodeCriteria& criteria) {
-  BrowserAccessibility* root = GetRootAccessibilityNode(web_contents);
+  TestBrowserAccessibility* root = GetRootAccessibilityNode(web_contents);
   CHECK(root);
   return FindAccessibilityNodeInSubtree(root, criteria);
 }
 
-BrowserAccessibility* FindAccessibilityNodeInSubtree(
-    BrowserAccessibility* node,
+TestBrowserAccessibility* FindAccessibilityNodeInSubtree(
+    TestBrowserAccessibility* node,
     const FindAccessibilityNodeCriteria& criteria) {
+  BrowserAccessibility* node_internal = FromTestBrowserAccessibility(node);
   if ((!criteria.name ||
-       node->GetStringAttribute(ax::mojom::StringAttribute::kName) ==
+       node_internal->GetStringAttribute(ax::mojom::StringAttribute::kName) ==
            criteria.name.value()) &&
-      (!criteria.role || node->GetRole() == criteria.role.value())) {
+      (!criteria.role || node_internal->GetRole() == criteria.role.value())) {
     return node;
   }
 
-  for (unsigned int i = 0; i < node->PlatformChildCount(); ++i) {
-    BrowserAccessibility* result =
-        FindAccessibilityNodeInSubtree(node->PlatformGetChild(i), criteria);
+  for (unsigned int i = 0; i < node_internal->PlatformChildCount(); ++i) {
+    TestBrowserAccessibility* child =
+        ToTestBrowserAccessibility(node_internal->PlatformGetChild(i));
+    TestBrowserAccessibility* result =
+        FindAccessibilityNodeInSubtree(child, criteria);
     if (result)
       return result;
   }
@@ -2069,7 +2073,7 @@
 #if defined(OS_WIN)
 template <typename T>
 Microsoft::WRL::ComPtr<T> QueryInterfaceFromNode(
-    BrowserAccessibility* browser_accessibility) {
+    TestBrowserAccessibility* browser_accessibility) {
   Microsoft::WRL::ComPtr<T> result;
   EXPECT_HRESULT_SUCCEEDED(
       browser_accessibility->GetNativeViewAccessible()->QueryInterface(
@@ -2079,7 +2083,7 @@
 
 void UiaGetPropertyValueVtArrayVtUnknownValidate(
     PROPERTYID property_id,
-    BrowserAccessibility* target_browser_accessibility,
+    TestBrowserAccessibility* target_browser_accessibility,
     const std::vector<std::string>& expected_names) {
   ASSERT_NE(nullptr, target_browser_accessibility);
 
@@ -3154,7 +3158,7 @@
 // exposing them in the header.
 class EvictionStateWaiter : public DelegatedFrameHost::Observer {
  public:
-  EvictionStateWaiter(DelegatedFrameHost* delegated_frame_host)
+  explicit EvictionStateWaiter(DelegatedFrameHost* delegated_frame_host)
       : delegated_frame_host_(delegated_frame_host) {
     delegated_frame_host_->AddObserverForTesting(this);
   }
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index cdb6bc7..eeb3447 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -89,7 +89,6 @@
 
 namespace content {
 
-class BrowserAccessibility;
 class BrowserContext;
 struct FrameVisualProperties;
 class FrameTreeNode;
@@ -100,6 +99,7 @@
 class RenderWidgetHost;
 class RenderWidgetHostView;
 class ScopedAllowRendererCrashes;
+class TestBrowserAccessibility;
 class WebContents;
 
 // Navigates |web_contents| to |url|, blocking until the navigation finishes.
@@ -909,7 +909,7 @@
 ui::AXTreeUpdate GetAccessibilityTreeSnapshot(WebContents* web_contents);
 
 // Returns the root accessibility node for the given WebContents.
-BrowserAccessibility* GetRootAccessibilityNode(WebContents* web_contents);
+TestBrowserAccessibility* GetRootAccessibilityNode(WebContents* web_contents);
 
 // Finds an accessibility node matching the given criteria.
 struct FindAccessibilityNodeCriteria {
@@ -918,18 +918,18 @@
   base::Optional<ax::mojom::Role> role;
   base::Optional<std::string> name;
 };
-BrowserAccessibility* FindAccessibilityNode(
+TestBrowserAccessibility* FindAccessibilityNode(
     WebContents* web_contents,
     const FindAccessibilityNodeCriteria& criteria);
-BrowserAccessibility* FindAccessibilityNodeInSubtree(
-    BrowserAccessibility* node,
+TestBrowserAccessibility* FindAccessibilityNodeInSubtree(
+    TestBrowserAccessibility* node,
     const FindAccessibilityNodeCriteria& criteria);
 
 #if defined(OS_WIN)
 // Retrieve the specified interface from an accessibility node.
 template <typename T>
 Microsoft::WRL::ComPtr<T> QueryInterfaceFromNode(
-    BrowserAccessibility* browser_accessibility);
+    TestBrowserAccessibility* browser_accessibility);
 
 // Call GetPropertyValue with the given UIA property id with variant type
 // VT_ARRAY | VT_UNKNOWN  on the target browser accessibility node to retrieve
@@ -937,7 +937,7 @@
 // automation elements with the expected names.
 void UiaGetPropertyValueVtArrayVtUnknownValidate(
     PROPERTYID property_id,
-    BrowserAccessibility* target_browser_accessibility,
+    TestBrowserAccessibility* target_browser_accessibility,
     const std::vector<std::string>& expected_names);
 #endif
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index f496c51..950424b 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -99,6 +99,7 @@
     "../public/test/background_sync_test_util.h",
     "../public/test/blink_test_environment.cc",
     "../public/test/blink_test_environment.h",
+    "../public/test/browser_accessibility.h",
     "../public/test/browser_side_navigation_test_utils.cc",
     "../public/test/browser_side_navigation_test_utils.h",
     "../public/test/browser_task_environment.cc",
@@ -232,6 +233,8 @@
     "../public/test/web_contents_tester.h",
     "appcache_test_helper.cc",
     "appcache_test_helper.h",
+    "browser_accessibility.cc",
+    "browser_accessibility.h",
     "content_browser_sanity_checker.cc",
     "content_browser_sanity_checker.h",
     "content_test_suite.cc",
diff --git a/content/test/browser_accessibility.cc b/content/test/browser_accessibility.cc
new file mode 100644
index 0000000..61bd0f2
--- /dev/null
+++ b/content/test/browser_accessibility.cc
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/test/browser_accessibility.h"
+
+#include "content/browser/accessibility/accessibility_tree_formatter_base.h"
+
+namespace content {
+
+// static
+void TestBrowserAccessibility::FormatAccessibilityTree(
+    AccessibilityTreeFormatter* formatter,
+    TestBrowserAccessibility* root,
+    base::string16* contents) {
+  auto* formatter_internal =
+      static_cast<AccessibilityTreeFormatterBase*>(formatter);
+  BrowserAccessibility* root_internal = FromTestBrowserAccessibility(root);
+  formatter_internal->FormatAccessibilityTreeForTesting(root_internal,
+                                                        contents);
+}
+
+TestBrowserAccessibility* ToTestBrowserAccessibility(
+    BrowserAccessibility* browser_accessibility) {
+  return reinterpret_cast<TestBrowserAccessibility*>(browser_accessibility);
+}
+
+BrowserAccessibility* FromTestBrowserAccessibility(
+    TestBrowserAccessibility* test_browser_accessibility) {
+  return reinterpret_cast<BrowserAccessibility*>(test_browser_accessibility);
+}
+
+}  // namespace content
diff --git a/content/test/browser_accessibility.h b/content/test/browser_accessibility.h
new file mode 100644
index 0000000..cba2d37
--- /dev/null
+++ b/content/test/browser_accessibility.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_TEST_BROWSER_ACCESSIBILITY_H_
+#define CONTENT_TEST_BROWSER_ACCESSIBILITY_H_
+
+#include "content/public/test/browser_accessibility.h"
+
+namespace content {
+
+class BrowserAccessibility;
+
+// Helper functions that are implementation details that should not be exposed
+// via content/public/test/browser_accessibility.h.
+TestBrowserAccessibility* ToTestBrowserAccessibility(
+    BrowserAccessibility* browser_accessibility);
+BrowserAccessibility* FromTestBrowserAccessibility(
+    TestBrowserAccessibility* test_browser_accessibility);
+
+}  // namespace content
+
+#endif  // CONTENT_TEST_BROWSER_ACCESSIBILITY_H_
diff --git a/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-android.txt b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-android.txt
new file mode 100644
index 0000000..774f065
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-android.txt
@@ -0,0 +1,5 @@
+android.webkit.WebView focusable focused scrollable
+++android.view.View role_description='tree' collection hierarchical item_count=2 row_count=2
+++++android.view.View role_description='tree item' clickable collection_item focusable name='card content'
+++++android.view.View
+++++android.view.View role_description='tree item' clickable collection_item focusable name='card content' item_index=1 row_index=1
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-auralinux.txt
new file mode 100644
index 0000000..338497e7
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-auralinux.txt
@@ -0,0 +1,7 @@
+[document web]
+++[tree] setsize:2
+++++[tree item] name='card content' selectable level:1 posinset:1 setsize:2
+++++++[static] name='card content'
+++++[section]
+++++[tree item] name='card content' selectable level:1 posinset:2 setsize:2
+++++++[static] name='card content'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-blink.txt b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-blink.txt
new file mode 100644
index 0000000..911dcb3
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-blink.txt
@@ -0,0 +1,10 @@
+rootWebArea
+++genericContainer ignored
+++++tree setSize=2
+++++++treeItem name='card content' hierarchicalLevel=1 setSize=2 posInSet=1 selected=false
+++++++++staticText name='card content'
+++++++++++inlineTextBox name='card content'
+++++++genericContainer
+++++++treeItem name='card content' hierarchicalLevel=1 setSize=2 posInSet=2 selected=false
+++++++++staticText name='card content'
+++++++++++inlineTextBox name='card content'
diff --git a/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-mac.txt b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-mac.txt
new file mode 100644
index 0000000..c4d1a17
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-mac.txt
@@ -0,0 +1,7 @@
+AXWebArea
+++AXOutline AXARIASetSize='2'
+++++AXRow AXTitle='card content' AXARIASetSize='2' AXARIAPosInSet='1'
+++++++AXStaticText AXValue='card content'
+++++AXGroup
+++++AXRow AXTitle='card content' AXARIASetSize='2' AXARIAPosInSet='2'
+++++++AXStaticText AXValue='card content'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-uia-win.txt b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-uia-win.txt
new file mode 100644
index 0000000..a3a5a247
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-uia-win.txt
@@ -0,0 +1,5 @@
+document PositionInSet=0 SizeOfSet=0
+++tree PositionInSet=0 SizeOfSet=2 Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++++treeitem Name='card content' PositionInSet=1 SizeOfSet=2 ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false
+++++group PositionInSet=0 SizeOfSet=0
+++++treeitem Name='card content' PositionInSet=2 SizeOfSet=2 ExpandCollapse.ExpandCollapseState='LeafNode' SelectionItem.IsSelected=false
diff --git a/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-win.txt b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-win.txt
new file mode 100644
index 0000000..88a7d5c
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-win.txt
@@ -0,0 +1,7 @@
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
+++ROLE_SYSTEM_OUTLINE setsize:2
+++++ROLE_SYSTEM_OUTLINEITEM name='card content' FOCUSABLE setsize:2 posinset:1
+++++++ROLE_SYSTEM_STATICTEXT name='card content'
+++++IA2_ROLE_SECTION
+++++ROLE_SYSTEM_OUTLINEITEM name='card content' FOCUSABLE setsize:2 posinset:2
+++++++ROLE_SYSTEM_STATICTEXT name='card content'
diff --git a/content/test/data/accessibility/aria/aria-tree-discontinuous.html b/content/test/data/accessibility/aria/aria-tree-discontinuous.html
new file mode 100644
index 0000000..1ec2bb3
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tree-discontinuous.html
@@ -0,0 +1,30 @@
+<!--
+@MAC-ALLOW:AXARIA*
+@WIN-ALLOW:setsize*
+@WIN-ALLOW:posinset*
+@UIA-WIN-ALLOW:SizeOfSet*
+@UIA-WIN-ALLOW:PositionInSet*
+@AURALINUX-ALLOW:setsize*
+@AURALINUX-ALLOW:posinset*
+@AURALINUX-ALLOW:level*
+@BLINK-ALLOW:hierarchicalLevel*
+@BLINK-ALLOW:setSize*
+@BLINK-ALLOW:posInSet*
+-->
+<!DOCTYPE html>
+<html>
+
+<body>
+
+  <div role="tree">
+    <div role="treeitem" tabindex="0">
+      card content
+    </div>
+    <div aria-hidden="false"></div>
+    <div role="treeitem" tabindex="0">
+      card content
+    </div>
+  </div>
+</body>
+
+</html>
diff --git a/content/test/data/accessibility/html/img-empty-alt-expected-android.txt b/content/test/data/accessibility/html/img-empty-alt-expected-android.txt
index db07391..a358c0e 100644
--- a/content/test/data/accessibility/html/img-empty-alt-expected-android.txt
+++ b/content/test/data/accessibility/html/img-empty-alt-expected-android.txt
@@ -1,6 +1,4 @@
 android.webkit.WebView focusable focused scrollable
 ++android.view.View
-++++android.widget.Image role_description='graphic'
-++++android.widget.Image role_description='graphic'
 ++++android.widget.Image role_description='graphic' name='read'
 ++++android.widget.Image role_description='graphic' name='full'
diff --git a/content/test/data/accessibility/html/img-empty-alt-expected-auralinux.txt b/content/test/data/accessibility/html/img-empty-alt-expected-auralinux.txt
index 3fec5e3..51e48e3e 100644
--- a/content/test/data/accessibility/html/img-empty-alt-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/img-empty-alt-expected-auralinux.txt
@@ -1,6 +1,4 @@
 [document web]
 ++[section]
-++++[image] name=''
-++++[image] name=''
 ++++[image]
 ++++[image] name='full'
diff --git a/content/test/data/accessibility/html/img-empty-alt-expected-blink.txt b/content/test/data/accessibility/html/img-empty-alt-expected-blink.txt
index d51d33a..177dc10 100644
--- a/content/test/data/accessibility/html/img-empty-alt-expected-blink.txt
+++ b/content/test/data/accessibility/html/img-empty-alt-expected-blink.txt
@@ -1,7 +1,7 @@
 rootWebArea
 ++genericContainer
 ++++presentational ignored name=''
-++++image name=''
-++++image name=''
+++++image ignored name=''
+++++image ignored name=''
 ++++image
 ++++image name='full'
diff --git a/content/test/data/accessibility/html/img-empty-alt-expected-mac.txt b/content/test/data/accessibility/html/img-empty-alt-expected-mac.txt
index 6677448c..02ee4d9 100644
--- a/content/test/data/accessibility/html/img-empty-alt-expected-mac.txt
+++ b/content/test/data/accessibility/html/img-empty-alt-expected-mac.txt
@@ -1,6 +1,4 @@
 AXWebArea
 ++AXGroup
-++++AXUnknown
-++++AXUnknown
 ++++AXImage AXDescription='/read.jpg'
 ++++AXImage AXDescription='full'
diff --git a/content/test/data/accessibility/html/img-empty-alt-expected-uia-win.txt b/content/test/data/accessibility/html/img-empty-alt-expected-uia-win.txt
index 221a798d2..c31fc910 100644
--- a/content/test/data/accessibility/html/img-empty-alt-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/img-empty-alt-expected-uia-win.txt
@@ -1,6 +1,4 @@
 document
 ++group
 ++++img
-++++img
-++++img
 ++++img Name='full'
diff --git a/content/test/data/accessibility/html/img-empty-alt-expected-win.txt b/content/test/data/accessibility/html/img-empty-alt-expected-win.txt
index 249ee03..deaa7dc 100644
--- a/content/test/data/accessibility/html/img-empty-alt-expected-win.txt
+++ b/content/test/data/accessibility/html/img-empty-alt-expected-win.txt
@@ -1,6 +1,4 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++IA2_ROLE_SECTION
-++++ROLE_SYSTEM_GRAPHIC name='' READONLY
-++++ROLE_SYSTEM_GRAPHIC name='' READONLY
 ++++ROLE_SYSTEM_GRAPHIC READONLY
 ++++ROLE_SYSTEM_GRAPHIC name='full' READONLY
diff --git a/content/test/data/accessibility/html/img-expected-android.txt b/content/test/data/accessibility/html/img-expected-android.txt
index 1d3eb951..724685b1 100644
--- a/content/test/data/accessibility/html/img-expected-android.txt
+++ b/content/test/data/accessibility/html/img-expected-android.txt
@@ -2,6 +2,5 @@
 ++android.view.View has_image
 ++++android.widget.Image role_description='graphic' has_image name='pipe'
 ++++android.view.View name=' '
-++++android.widget.Image role_description='graphic' has_image
 ++++android.view.View name=' '
 ++++android.widget.Image role_description='graphic' has_image name='  '
diff --git a/content/test/data/accessibility/html/img-expected-auralinux.txt b/content/test/data/accessibility/html/img-expected-auralinux.txt
index bf45e65..562b250 100644
--- a/content/test/data/accessibility/html/img-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/img-expected-auralinux.txt
@@ -2,6 +2,5 @@
 ++[section]
 ++++[image] name='pipe' xml-roles:img
 ++++[static] name=' '
-++++[image] name='' xml-roles:img
 ++++[static] name=' '
-++++[image] name='  ' xml-roles:img
+++++[image] name='  ' xml-roles:img
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/img-expected-blink.txt b/content/test/data/accessibility/html/img-expected-blink.txt
index 81b28d2..ff9ae73 100644
--- a/content/test/data/accessibility/html/img-expected-blink.txt
+++ b/content/test/data/accessibility/html/img-expected-blink.txt
@@ -3,7 +3,6 @@
 ++++image name='pipe'
 ++++staticText name=' '
 ++++++inlineTextBox name=' '
-++++image name=''
 ++++staticText name=' '
 ++++++inlineTextBox name=' '
 ++++image name='  '
diff --git a/content/test/data/accessibility/html/img-expected-mac.txt b/content/test/data/accessibility/html/img-expected-mac.txt
index a438c34..3ba7d011 100644
--- a/content/test/data/accessibility/html/img-expected-mac.txt
+++ b/content/test/data/accessibility/html/img-expected-mac.txt
@@ -2,6 +2,5 @@
 ++AXGroup
 ++++AXImage AXRoleDescription='image' AXDescription='pipe'
 ++++AXStaticText AXValue=' '
-++++AXUnknown
 ++++AXStaticText AXValue=' '
 ++++AXImage AXRoleDescription='image' AXDescription='  '
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/img-expected-uia-win.txt b/content/test/data/accessibility/html/img-expected-uia-win.txt
index 56321fb..97ffe28 100644
--- a/content/test/data/accessibility/html/img-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/img-expected-uia-win.txt
@@ -2,6 +2,5 @@
 ++group
 ++++img Name='pipe'
 ++++description Name=' '
-++++img
 ++++description Name=' '
 ++++img Name='  '
diff --git a/content/test/data/accessibility/html/img-expected-win.txt b/content/test/data/accessibility/html/img-expected-win.txt
index ac8424f..af254395 100644
--- a/content/test/data/accessibility/html/img-expected-win.txt
+++ b/content/test/data/accessibility/html/img-expected-win.txt
@@ -2,6 +2,5 @@
 ++IA2_ROLE_SECTION
 ++++ROLE_SYSTEM_GRAPHIC name='pipe' READONLY xml-roles:img
 ++++ROLE_SYSTEM_STATICTEXT name=' '
-++++ROLE_SYSTEM_GRAPHIC name='' READONLY xml-roles:img
 ++++ROLE_SYSTEM_STATICTEXT name=' '
 ++++ROLE_SYSTEM_GRAPHIC name='  ' READONLY xml-roles:img
diff --git a/content/test/data/accessibility/html/img-link-empty-alt-expected-auralinux.txt b/content/test/data/accessibility/html/img-link-empty-alt-expected-auralinux.txt
index 2b16c3f..21b3c99 100644
--- a/content/test/data/accessibility/html/img-link-empty-alt-expected-auralinux.txt
+++ b/content/test/data/accessibility/html/img-link-empty-alt-expected-auralinux.txt
@@ -3,11 +3,9 @@
 ++++[link] name='unread '
 ++++++[static] name='unread '
 ++++[link] name='read '
-++++++[image] name=''
 ++++++[static] name='read '
 ++++[link] name='read '
-++++++[image] name=''
 ++++++[static] name='read '
 ++++[link] name='read'
 ++++++[image]
-++++++[static] name='read'
+++++++[static] name='read'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/img-link-empty-alt-expected-blink.txt b/content/test/data/accessibility/html/img-link-empty-alt-expected-blink.txt
index b2d8f10b..5671b2b6 100644
--- a/content/test/data/accessibility/html/img-link-empty-alt-expected-blink.txt
+++ b/content/test/data/accessibility/html/img-link-empty-alt-expected-blink.txt
@@ -5,11 +5,11 @@
 ++++++staticText name='unread '
 ++++++++inlineTextBox name='unread '
 ++++link name='read '
-++++++image name=''
+++++++image ignored name=''
 ++++++staticText name='read '
 ++++++++inlineTextBox name='read '
 ++++link name='read '
-++++++image name=''
+++++++image ignored name=''
 ++++++staticText name='read '
 ++++++++inlineTextBox name='read '
 ++++link name='read'
diff --git a/content/test/data/accessibility/html/img-link-empty-alt-expected-mac.txt b/content/test/data/accessibility/html/img-link-empty-alt-expected-mac.txt
index e446d47..8034c1d 100644
--- a/content/test/data/accessibility/html/img-link-empty-alt-expected-mac.txt
+++ b/content/test/data/accessibility/html/img-link-empty-alt-expected-mac.txt
@@ -3,10 +3,8 @@
 ++++AXLink AXTitle='unread '
 ++++++AXStaticText AXValue='unread '
 ++++AXLink AXTitle='read '
-++++++AXUnknown
 ++++++AXStaticText AXValue='read '
 ++++AXLink AXTitle='read '
-++++++AXUnknown
 ++++++AXStaticText AXValue='read '
 ++++AXLink AXTitle='read'
 ++++++AXImage AXDescription='/read.jpg'
diff --git a/content/test/data/accessibility/html/img-link-empty-alt-expected-uia-win.txt b/content/test/data/accessibility/html/img-link-empty-alt-expected-uia-win.txt
index a44e743..76b1408 100644
--- a/content/test/data/accessibility/html/img-link-empty-alt-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/img-link-empty-alt-expected-uia-win.txt
@@ -2,8 +2,6 @@
 ++group
 ++++link Name='unread '
 ++++link Name='read '
-++++++img
 ++++link Name='read '
-++++++img
 ++++link Name='read'
 ++++++img
diff --git a/content/test/data/accessibility/html/img-link-empty-alt-expected-win.txt b/content/test/data/accessibility/html/img-link-empty-alt-expected-win.txt
index b2ee6926..f25a244 100644
--- a/content/test/data/accessibility/html/img-link-empty-alt-expected-win.txt
+++ b/content/test/data/accessibility/html/img-link-empty-alt-expected-win.txt
@@ -3,10 +3,8 @@
 ++++ROLE_SYSTEM_LINK name='unread ' FOCUSABLE
 ++++++ROLE_SYSTEM_STATICTEXT name='unread '
 ++++ROLE_SYSTEM_LINK name='read ' FOCUSABLE
-++++++ROLE_SYSTEM_GRAPHIC name='' READONLY
 ++++++ROLE_SYSTEM_STATICTEXT name='read '
 ++++ROLE_SYSTEM_LINK name='read ' FOCUSABLE
-++++++ROLE_SYSTEM_GRAPHIC name='' READONLY
 ++++++ROLE_SYSTEM_STATICTEXT name='read '
 ++++ROLE_SYSTEM_LINK name='read' FOCUSABLE
 ++++++ROLE_SYSTEM_GRAPHIC READONLY
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index e116e7a..2670b19 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -65,7 +65,6 @@
         "android/gvr/gvr_device.h",
         "android/gvr/gvr_device_provider.cc",
         "android/gvr/gvr_device_provider.h",
-        "android/gvr/gvr_gamepad_data_provider.h",
         "android/gvr/gvr_utils.cc",
         "android/gvr/gvr_utils.h",
       ]
diff --git a/device/vr/android/gvr/gvr_gamepad_data_provider.h b/device/vr/android/gvr/gvr_gamepad_data_provider.h
deleted file mode 100644
index 8b3c0255..0000000
--- a/device/vr/android/gvr/gvr_gamepad_data_provider.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef DEVICE_VR_ANDROID_GVR_GVR_GAMEPAD_DATA_PROVIDER_H_
-#define DEVICE_VR_ANDROID_GVR_GVR_GAMEPAD_DATA_PROVIDER_H_
-
-#include "ui/gfx/geometry/quaternion.h"
-#include "ui/gfx/geometry/vector2d_f.h"
-#include "ui/gfx/geometry/vector3d_f.h"
-
-namespace device {
-
-class GvrGamepadDataFetcher;
-
-// Subset of GVR controller data needed for the gamepad API. Filled in
-// by vr_shell's VrController and consumed by GvrGamepadDataFetcher.
-struct GvrGamepadData {
-  GvrGamepadData()
-      : timestamp(0),
-        is_touching(false),
-        controller_button_pressed(false),
-        right_handed(true),
-        connected(false) {}
-  int64_t timestamp;
-  gfx::Vector2dF touch_pos;
-  gfx::Quaternion orientation;
-  gfx::Vector3dF accel;
-  gfx::Vector3dF gyro;
-  bool is_touching;
-  bool controller_button_pressed;
-  bool right_handed;
-  bool connected;
-};
-
-// This class exposes GVR controller data to the gamepad API. Data is
-// polled by GvrSchedulerDelegate, then pushed from VrShell which implements the
-// GvrGamepadDataProvider interface.
-//
-// More specifically, here's the lifecycle, assuming VrShell
-// implements GvrGamepadDataProvider:
-//
-// - VrShell creates GvrGamepadDataFetcherFactory from
-//   VrShell::UpdateGamepadData.
-//
-// - GvrGamepadDataFetcherFactory creates GvrGamepadDataFetcher.
-//
-// - GvrGamepadDataFetcher registers itself with VrShell via
-//   VrShell::RegisterGamepadDataFetcher.
-//
-// - While presenting, VrShell::UpdateGamepadData calls
-//   GvrGamepadDataFetcher->SetGamepadData to push poses,
-//   GvrGamepadDataFetcher::GetGamepadData returns these when polled.
-//
-// - VrShell starts executing its destructor.
-//
-// - VrShell destructor unregisters GvrGamepadDataFetcherFactory.
-//
-// - GvrGamepadDataFetcherFactory destructor destroys GvrGamepadDataFetcher.
-//
-class GvrGamepadDataProvider {
- public:
-  // Refresh current GVR controller data for use by gamepad API. The
-  // implementation also lazily creates and registers the GVR
-  // GamepadDataFetcherFactory with the GamepadDataFetcherManager as
-  // needed.
-  virtual void UpdateGamepadData(GvrGamepadData data) = 0;
-};
-
-}  // namespace device
-#endif  // DEVICE_VR_ANDROID_GVR_GVR_GAMEPAD_DATA_PROVIDER_H_
diff --git a/docs/speed/binary_size/optimization_advice.md b/docs/speed/binary_size/optimization_advice.md
index db7857a..d300dd3a 100644
--- a/docs/speed/binary_size/optimization_advice.md
+++ b/docs/speed/binary_size/optimization_advice.md
@@ -90,12 +90,12 @@
    the [android-binary-size trybot][size-trybot].
    * Or use [//tools/binary_size/diagnose_bloat.py][diagnose_bloat] to create
      diffs locally.
- * Ensure no symbols exists that are used only by tests.
+ * Ensure no symbols exist that are used only by tests.
  * Be concise with strings used for error handling.
    * Identical strings throughout the codebase are de-duped. Take advantage of
      this for error-related strings.
 
-### Optimizating Native Code
+### Optimizing Native Code
  * If there's a notable increase in `.data.rel.ro`:
    * Ensure there are not [excessive relocations][relocations].
  * If there's a notable increase in `.rodata`:
@@ -112,7 +112,7 @@
      * E.g. Use PODs wherever possible, and especially in containers. They will
        likely compile down to the same code as other pre-existing PODs.
        * Try also to use consistent field ordering within PODs.
-     * E.g. a `std::vector` of bare pointers will very likely by ICF'ed, but one
+     * E.g. a `std::vector` of bare pointers will very likely be ICF'ed, but one
        that uses smart pointers gets type-specific destructor logic inlined into
        it.
      * This advice is especially applicable to generated code.
@@ -125,7 +125,7 @@
          separate `const char *` and `const std::string&` overloads rather than
          a single `base::StringPiece`.
 
-### Optimizating Java Code
+### Optimizing Java Code
  * Prefer fewer large JNI calls over many small JNI calls.
  * Minimize the use of class initializers (`<clinit>()`).
    * If R8 cannot determine that they are "trivial", they will prevent
@@ -150,7 +150,7 @@
      single `onChanged()` that assumes everything changed.
  * Ensure unused code is optimized away by ProGuard / R8.
    * Add `@CheckDiscard` to methods or classes that you expect R8 to inline.
-   * Add `@RemovableInRelease` to force a method to be a no-op in when DCHECKs
+   * Add `@RemovableInRelease` to force a method to be a no-op when DCHECKs
      are disabled.
    * See [here][proguard-build-doc] for more info on how Chrome uses ProGuard.
 
diff --git a/extensions/renderer/resources/guest_view/guest_view_container_element.js b/extensions/renderer/resources/guest_view/guest_view_container_element.js
index 7dae2c3..317a0b6 100644
--- a/extensions/renderer/resources/guest_view/guest_view_container_element.js
+++ b/extensions/renderer/resources/guest_view/guest_view_container_element.js
@@ -15,20 +15,11 @@
 var IdGenerator = requireNative('id_generator');
 var logging = requireNative('logging');
 
-// Registers the browserplugin and guestview as custom elements once the
-// document has loaded.
+// Registers the browserplugin and guestview as custom elements.
 // |containerElementType| is a GuestViewContainerElement (e.g. WebViewElement)
 function registerElement(elementName, containerElementType) {
-  var useCapture = true;
-  window.addEventListener('readystatechange', function listener(event) {
-    if (document.readyState == 'loading')
-      return;
-
-    registerInternalElement($String.toLowerCase(elementName));
-    registerGuestViewElement(elementName, containerElementType);
-
-    $EventTarget.removeEventListener(window, event.type, listener, useCapture);
-  }, useCapture);
+  registerInternalElement($String.toLowerCase(elementName));
+  registerGuestViewElement(elementName, containerElementType);
 }
 
 // Registers the browser plugin <object> custom element. |viewType| is the
diff --git a/extensions/renderer/resources/guest_view/guest_view_deny.js b/extensions/renderer/resources/guest_view/guest_view_deny.js
index b3961b5..1d43703 100644
--- a/extensions/renderer/resources/guest_view/guest_view_deny.js
+++ b/extensions/renderer/resources/guest_view/guest_view_deny.js
@@ -11,7 +11,35 @@
 var $EventTarget = require('safeMethods').SafeMethods.$EventTarget;
 var GuestViewInternalNatives = requireNative('guest_view_internal');
 
-function registerDeniedElementInternal(viewType, permissionName) {
+// Once the document has loaded, expose the error-providing element's
+// constructor to user code via |window|.
+// GuestView elements used to be defined only once the document had loaded (see
+// https://crbug.com/810012). This has been fixed, but as seen in
+// https://crbug.com/1014385, user code that does not have permission for a
+// GuestView could be using the same name for another purpose. In order to avoid
+// potential name collisions with user code, we preserve the previous
+// asynchronous behaviour for exposing the constructor of the error-providing
+// element via |window|.
+function asyncProvideElementConstructor(viewType, elementConstructor) {
+  let useCapture = true;
+  window.addEventListener('readystatechange', function listener(event) {
+    if (document.readyState == 'loading')
+      return;
+
+    // If user code did use the name, we won't overwrite with the
+    // error-providing element.
+    if (!$Object.hasOwnProperty(window, viewType)) {
+      $Object.defineProperty(window, viewType, {
+        value: elementConstructor,
+      });
+    }
+
+    $EventTarget.removeEventListener(window, event.type, listener, useCapture);
+  }, useCapture);
+}
+
+// Registers an error-providing GuestView custom element.
+function registerDeniedElement(viewType, permissionName) {
   GuestViewInternalNatives.AllowGuestViewElementDefinition(() => {
     var DeniedElement = class extends HTMLElement {
       constructor() {
@@ -23,29 +51,9 @@
     }
     $CustomElementRegistry.define(
         window.customElements, $String.toLowerCase(viewType), DeniedElement);
-    // User code that does not have permission for this GuestView could be
-    // using the same name for another purpose, in which case we won't overwrite
-    // with the error-providing element.
-    if (!$Object.hasOwnProperty(window, viewType)) {
-      $Object.defineProperty(window, viewType, {
-        value: DeniedElement,
-      });
-    }
+    asyncProvideElementConstructor(viewType, DeniedElement);
   });
 }
 
-// Registers an error-providing GuestView custom element.
-function registerDeniedElement(viewType, permissionName) {
-  let useCapture = true;
-  window.addEventListener('readystatechange', function listener(event) {
-    if (document.readyState == 'loading')
-      return;
-
-    registerDeniedElementInternal(viewType, permissionName);
-
-    $EventTarget.removeEventListener(window, event.type, listener, useCapture);
-  }, useCapture);
-}
-
 // Exports.
 exports.$set('registerDeniedElement', registerDeniedElement);
diff --git a/gpu/ipc/client/image_decode_accelerator_proxy.h b/gpu/ipc/client/image_decode_accelerator_proxy.h
index a47df5d..3c50e47 100644
--- a/gpu/ipc/client/image_decode_accelerator_proxy.h
+++ b/gpu/ipc/client/image_decode_accelerator_proxy.h
@@ -53,7 +53,6 @@
   // Determines if |image_metadata| corresponds to an image that can be decoded
   // using hardware decode acceleration. The ScheduleImageDecode() method should
   // only be called for images for which IsImageSupported() returns true.
-  // Otherwise, the client faces a GPU channel teardown if the decode fails.
   bool IsImageSupported(
       const cc::ImageHeaderMetadata* image_metadata) const override;
 
diff --git a/gpu/ipc/service/image_decode_accelerator_stub.cc b/gpu/ipc/service/image_decode_accelerator_stub.cc
index 21aa72df..f61a9798e 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub.cc
+++ b/gpu/ipc/service/image_decode_accelerator_stub.cc
@@ -153,27 +153,11 @@
     uint64_t release_count) {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
   base::AutoLock lock(lock_);
-  if (!channel_ || destroying_channel_) {
+  if (!channel_) {
     // The channel is no longer available, so don't do anything.
     return;
   }
 
-  // Make sure the decode sync token is ordered with respect to the last decode
-  // request.
-  if (release_count <= last_release_count_) {
-    DLOG(ERROR) << "Out-of-order decode sync token";
-    OnError();
-    return;
-  }
-  last_release_count_ = release_count;
-
-  // Make sure the output dimensions are not too small.
-  if (decode_params.output_size.IsEmpty()) {
-    DLOG(ERROR) << "Output dimensions are too small";
-    OnError();
-    return;
-  }
-
   // Start the actual decode.
   worker_->Decode(
       std::move(decode_params.encoded_data), decode_params.output_size,
@@ -200,7 +184,7 @@
     uint64_t decode_release_count) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   base::AutoLock lock(lock_);
-  if (!channel_ || destroying_channel_) {
+  if (!channel_) {
     // The channel is no longer available, so don't do anything.
     return;
   }
@@ -208,6 +192,29 @@
   DCHECK(!pending_completed_decodes_.empty());
   std::unique_ptr<ImageDecodeAcceleratorWorker::DecodeResult> completed_decode =
       std::move(pending_completed_decodes_.front());
+  pending_completed_decodes_.pop();
+
+  // Regardless of what happens next, make sure the sync token gets released and
+  // the sequence gets disabled if there are no more completed decodes after
+  // this. base::Unretained(this) is safe because *this outlives the
+  // ScopedClosureRunner.
+  base::ScopedClosureRunner finalizer(
+      base::BindOnce(&ImageDecodeAcceleratorStub::FinishCompletedDecode,
+                     base::Unretained(this), decode_release_count));
+
+  if (!completed_decode) {
+    DLOG(ERROR) << "The image could not be decoded";
+    return;
+  }
+
+  // TODO(crbug.com/995883): the output_size parameter is going away, so this
+  // validation is not needed. Checking if the size is too small should happen
+  // at the level of the decoder (since that's the component that's aware of its
+  // own capabilities).
+  if (params.output_size.IsEmpty()) {
+    DLOG(ERROR) << "Output dimensions are too small";
+    return;
+  }
 
   // Gain access to the transfer cache through the GpuChannelManager's
   // SharedContextState. We will also use that to get a GrContext that will be
@@ -217,7 +224,6 @@
       channel_->gpu_channel_manager()->GetSharedContextState(&context_result);
   if (context_result != ContextResult::kSuccess) {
     DLOG(ERROR) << "Unable to obtain the SharedContextState";
-    OnError();
     return;
   }
   DCHECK(shared_context_state);
@@ -227,17 +233,14 @@
   // other graphics APIs).
   if (!shared_context_state->IsGLInitialized()) {
     DLOG(ERROR) << "GL has not been initialized";
-    OnError();
     return;
   }
   if (!shared_context_state->gr_context()) {
     DLOG(ERROR) << "Could not get the GrContext";
-    OnError();
     return;
   }
   if (!shared_context_state->MakeCurrent(nullptr /* surface */)) {
     DLOG(ERROR) << "Could not MakeCurrent the shared context";
-    OnError();
     return;
   }
 
@@ -269,7 +272,6 @@
   if (!safe_uv_width.AssignIfValid(&uv_width) ||
       !safe_uv_height.AssignIfValid(&uv_height)) {
     DLOG(ERROR) << "Could not calculate subsampled dimensions";
-    OnError();
     return;
   }
   gfx::Size uv_plane_size = gfx::Size(uv_width, uv_height);
@@ -343,13 +345,11 @@
     }
     if (!plane_image) {
       DLOG(ERROR) << "Could not create GL image";
-      OnError();
       return;
     }
     resource->gl_image = std::move(plane_image);
     if (!resource->gl_image->BindTexImage(GL_TEXTURE_EXTERNAL_OES)) {
       DLOG(ERROR) << "Could not bind GL image to texture";
-      OnError();
       return;
     }
 
@@ -372,7 +372,6 @@
         resource);
     if (!plane_sk_images[plane]) {
       DLOG(ERROR) << "Could not create planar SkImage";
-      OnError();
       return;
     }
     // No need for us to call the resource cleaner. Skia should do that.
@@ -383,7 +382,6 @@
   // |native_pixmap_handle| member of a GpuMemoryBufferHandle.
   NOTIMPLEMENTED()
       << "Image decode acceleration is unsupported for this platform";
-  OnError();
   return;
 #endif
 
@@ -395,7 +393,6 @@
       channel_->LookupCommandBuffer(params.raster_decoder_route_id);
   if (!command_buffer) {
     DLOG(ERROR) << "Could not find the command buffer";
-    OnError();
     return;
   }
   scoped_refptr<Buffer> handle_buffer =
@@ -403,13 +400,11 @@
   if (!DiscardableHandleBase::ValidateParameters(
           handle_buffer.get(), params.discardable_handle_shm_offset)) {
     DLOG(ERROR) << "Could not validate the discardable handle parameters";
-    OnError();
     return;
   }
   DCHECK(command_buffer->decoder_context());
   if (command_buffer->decoder_context()->GetRasterDecoderId() < 0) {
     DLOG(ERROR) << "Could not get the raster decoder ID";
-    OnError();
     return;
   }
 
@@ -441,21 +436,18 @@
                  completed_decode->yuv_color_space,
                  completed_decode->buffer_byte_size, params.needs_mips)) {
       DLOG(ERROR) << "Could not create and insert the transfer cache entry";
-      OnError();
       return;
     }
   }
   DCHECK(notify_gl_state_changed);
   notify_gl_state_changed->RunAndReset();
+}
 
-  // All done! The decoded image can now be used for rasterization, so we can
-  // release the decode sync token.
+void ImageDecodeAcceleratorStub::FinishCompletedDecode(
+    uint64_t decode_release_count) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  lock_.AssertAcquired();
   sync_point_client_state_->ReleaseFenceSync(decode_release_count);
-
-  // If there are no more completed decodes to be processed, we can disable the
-  // sequence: when the next decode is completed, the sequence will be
-  // re-enabled.
-  pending_completed_decodes_.pop();
   if (pending_completed_decodes_.empty())
     channel_->scheduler()->DisableSequence(sequence_);
 }
@@ -464,19 +456,13 @@
     gfx::Size expected_output_size,
     std::unique_ptr<ImageDecodeAcceleratorWorker::DecodeResult> result) {
   base::AutoLock lock(lock_);
-  if (!channel_ || destroying_channel_) {
+  if (!channel_) {
     // The channel is no longer available, so don't do anything.
     return;
   }
 
-  if (!result) {
-    DLOG(ERROR) << "The decode failed";
-    OnError();
-    return;
-  }
-
   // A sanity check on the output of the decoder.
-  DCHECK(expected_output_size == result->visible_size);
+  DCHECK(!result || expected_output_size == result->visible_size);
 
   // The decode is ready to be processed: add it to |pending_completed_decodes_|
   // so that ProcessCompletedDecode() can pick it up.
@@ -488,19 +474,4 @@
     channel_->scheduler()->EnableSequence(sequence_);
 }
 
-void ImageDecodeAcceleratorStub::OnError() {
-  lock_.AssertAcquired();
-  DCHECK(channel_);
-
-  // Trigger the destruction of the channel and stop processing further
-  // completed decodes, even if they're successful. We can't call
-  // GpuChannel::OnChannelError() directly because that will end up calling
-  // ImageDecodeAcceleratorStub::Shutdown() while |lock_| is still acquired. So,
-  // we post a task to the main thread instead.
-  destroying_channel_ = true;
-  channel_->task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&GpuChannel::OnChannelError, channel_->AsWeakPtr()));
-}
-
 }  // namespace gpu
diff --git a/gpu/ipc/service/image_decode_accelerator_stub.h b/gpu/ipc/service/image_decode_accelerator_stub.h
index 41256a30..b3552f9 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub.h
+++ b/gpu/ipc/service/image_decode_accelerator_stub.h
@@ -76,23 +76,22 @@
       uint64_t release_count);
 
   // Creates the service-side cache entry for a completed decode and releases
-  // the decode sync token.
+  // the decode sync token. If the decode was unsuccessful, no cache entry is
+  // created but the decode sync token is still released.
   void ProcessCompletedDecode(GpuChannelMsg_ScheduleImageDecode_Params params,
                               uint64_t decode_release_count);
 
-  // The |worker_| calls this when a decode is completed. If the decode is
-  // successful, |sequence_| will be enabled so that ProcessCompletedDecode() is
-  // called. If the decode is not successful, we destroy the channel (see
-  // OnError()).
+  // Releases the decode sync token corresponding to |decode_release_count| and
+  // disables |sequence_| if there are no more decodes to process for now.
+  void FinishCompletedDecode(uint64_t decode_release_count)
+      EXCLUSIVE_LOCKS_REQUIRED(lock_);
+
+  // The |worker_| calls this when a decode is completed. |result| is enqueued
+  // and |sequence_| is enabled so that ProcessCompletedDecode() picks it up.
   void OnDecodeCompleted(
       gfx::Size expected_output_size,
       std::unique_ptr<ImageDecodeAcceleratorWorker::DecodeResult> result);
 
-  // Triggers the destruction of the channel asynchronously and makes it so that
-  // we stop accepting completed decodes. On entry, |channel_| must not be
-  // nullptr.
-  void OnError() EXCLUSIVE_LOCKS_REQUIRED(lock_);
-
   // The object to which the actual decoding can be delegated.
   ImageDecodeAcceleratorWorker* worker_ = nullptr;
 
@@ -103,8 +102,6 @@
       GUARDED_BY(lock_);
   base::queue<std::unique_ptr<ImageDecodeAcceleratorWorker::DecodeResult>>
       pending_completed_decodes_ GUARDED_BY(lock_);
-  bool destroying_channel_ GUARDED_BY(lock_) = false;
-  uint64_t last_release_count_ GUARDED_BY(lock_) = 0;
 
   ImageFactory* external_image_factory_for_testing_ = nullptr;
 
diff --git a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
index 23830d15..2f88c3c 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
+++ b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
@@ -73,6 +73,11 @@
 
 namespace {
 
+struct ExpectedCacheEntry {
+  uint32_t id = 0u;
+  SkISize dimensions;
+};
+
 std::unique_ptr<MemoryTracker> CreateMockMemoryTracker(
     const GPUCreateCommandBufferConfig& init_params) {
   return std::make_unique<gles2::MockMemoryTracker>();
@@ -204,8 +209,7 @@
 
   int GetRasterDecoderId() {
     GpuChannel* channel = channel_manager()->LookupChannel(kChannelId);
-    if (!channel)
-      return -1;
+    DCHECK(channel);
     CommandBufferStub* command_buffer =
         channel->LookupCommandBuffer(kCommandBufferRouteId);
     if (!command_buffer || !command_buffer->decoder_context())
@@ -283,7 +287,7 @@
                                        scoped_refptr<Buffer> buffer,
                                        uint64_t handle_release_count) {
     GpuChannel* channel = channel_manager()->LookupChannel(kChannelId);
-    CHECK(channel);
+    DCHECK(channel);
     CommandBufferStub* command_buffer =
         channel->LookupCommandBuffer(kCommandBufferRouteId);
     CHECK(command_buffer);
@@ -295,12 +299,11 @@
   // the raster sequence) to register the handle's buffer and release the sync
   // token corresponding to |handle_release_count| (see the
   // RegisterDiscardableHandleBuffer() method). Returns an invalid handle if the
-  // GPU channel or the command buffer doesn't exist.
+  // command buffer doesn't exist.
   ClientDiscardableHandle CreateDiscardableHandle(
       uint64_t handle_release_count) {
     GpuChannel* channel = channel_manager()->LookupChannel(kChannelId);
-    if (!channel)
-      return ClientDiscardableHandle();
+    DCHECK(channel);
     CommandBufferStub* command_buffer =
         channel->LookupCommandBuffer(kCommandBufferRouteId);
     if (!command_buffer)
@@ -324,20 +327,14 @@
   // (|decode_release_count|), the transfer cache entry ID
   // (|transfer_cache_entry_id|), and the release count of the sync token that
   // is signaled after the discardable handle's buffer has been registered in
-  // the TransferBufferManager. If the channel does not exist or the discardable
-  // handle can't be created, this function returns an empty sync token.
+  // the TransferBufferManager. If the discardable handle can't be created, this
+  // function returns an empty sync token.
   SyncToken SendDecodeRequest(const gfx::Size& output_size,
                               uint64_t decode_release_count,
                               uint32_t transfer_cache_entry_id,
                               uint64_t handle_release_count) {
     GpuChannel* channel = channel_manager()->LookupChannel(kChannelId);
-    if (!channel) {
-      // It's possible that the channel was destroyed as part of an earlier
-      // SendDecodeRequest() call. This would happen if
-      // ImageDecodeAcceleratorStub::OnScheduleImageDecode decides to destroy
-      // the channel.
-      return SyncToken();
-    }
+    DCHECK(channel);
 
     // Create the decode sync token for the decode request so that we can test
     // that it's actually released.
@@ -383,7 +380,8 @@
     }
   }
 
-  void CheckTransferCacheEntries(std::vector<SkISize> expected_sizes) {
+  void CheckTransferCacheEntries(
+      const std::vector<ExpectedCacheEntry>& expected_entries) {
     ServiceTransferCache* transfer_cache = GetServiceTransferCache();
     ASSERT_TRUE(transfer_cache);
 
@@ -391,8 +389,8 @@
     // expected.
     const size_t num_actual_cache_entries =
         transfer_cache->entries_count_for_testing();
-    ASSERT_EQ(expected_sizes.size(), num_actual_cache_entries);
-    if (expected_sizes.empty())
+    ASSERT_EQ(expected_entries.size(), num_actual_cache_entries);
+    if (expected_entries.empty())
       return;
 
     // Then, check the dimensions of the entries to make sure they are as
@@ -402,7 +400,8 @@
     for (size_t i = 0; i < num_actual_cache_entries; i++) {
       auto* decode_entry = static_cast<cc::ServiceImageTransferCacheEntry*>(
           transfer_cache->GetEntry(ServiceTransferCache::EntryKey(
-              raster_decoder_id, cc::TransferCacheEntryType::kImage, i + 1)));
+              raster_decoder_id, cc::TransferCacheEntryType::kImage,
+              expected_entries[i].id)));
       ASSERT_TRUE(decode_entry);
       ASSERT_EQ(gfx::NumberOfPlanesForLinearBufferFormat(GetParam()),
                 decode_entry->plane_images().size());
@@ -412,9 +411,9 @@
         EXPECT_TRUE(decode_entry->plane_images()[plane]->isTextureBacked());
       }
       ASSERT_TRUE(decode_entry->image());
-      EXPECT_EQ(expected_sizes[i].width(),
+      EXPECT_EQ(expected_entries[i].dimensions.width(),
                 decode_entry->image()->dimensions().width());
-      EXPECT_EQ(expected_sizes[i].height(),
+      EXPECT_EQ(expected_entries[i].dimensions.height(),
                 decode_entry->image()->dimensions().height());
     }
   }
@@ -471,11 +470,9 @@
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode1_sync_token));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode2_sync_token));
 
-  // The channel should still exist at the end.
-  EXPECT_TRUE(channel_manager()->LookupChannel(kChannelId));
-
   // Check that the decoded images are in the transfer cache.
-  CheckTransferCacheEntries({SkISize::Make(100, 100), SkISize::Make(200, 200)});
+  CheckTransferCacheEntries(
+      {{1u, SkISize::Make(100, 100)}, {2u, SkISize::Make(200, 200)}});
 }
 
 // Tests the following flow: three decode requests are sent. The first decode
@@ -521,18 +518,14 @@
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode2_sync_token));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode3_sync_token));
 
-  // The channel should still exist at the end.
-  EXPECT_TRUE(channel_manager()->LookupChannel(kChannelId));
-
   // Check that the decoded images are in the transfer cache.
-  CheckTransferCacheEntries({SkISize::Make(100, 100), SkISize::Make(200, 200),
-                             SkISize::Make(300, 300)});
+  CheckTransferCacheEntries({{1u, SkISize::Make(100, 100)},
+                             {2u, SkISize::Make(200, 200)},
+                             {3u, SkISize::Make(300, 300)}});
 }
 
 // Tests the following flow: three decode requests are sent. The first decode
-// fails which should trigger the destruction of the channel. The second
-// succeeds and the third one fails. Regardless, the channel should still be
-// destroyed and all sync tokens should be released.
+// fails, the second succeeds, and the third one fails.
 TEST_P(ImageDecodeAcceleratorStubTest, FailedDecodes) {
   {
     InSequence call_sequence;
@@ -561,25 +554,29 @@
   EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(decode1_sync_token));
   EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(decode2_sync_token));
   EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(decode3_sync_token));
+
+  // All decode sync tokens should be released after completing all the decodes.
   image_decode_accelerator_worker_.FinishOneDecode(false);
   image_decode_accelerator_worker_.FinishOneDecode(true);
   image_decode_accelerator_worker_.FinishOneDecode(false);
-
-  // We expect the destruction of the ImageDecodeAcceleratorStub, which also
-  // implies that all decode sync tokens should be released.
   RunTasksUntilIdle();
-  EXPECT_FALSE(channel_manager()->LookupChannel(kChannelId));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode1_sync_token));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode2_sync_token));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode3_sync_token));
 
-  // We expect no entries in the transfer cache.
-  CheckTransferCacheEntries({});
+  // There should only be one image in the transfer cache (the one that
+  // succeeded).
+  CheckTransferCacheEntries({{2u, SkISize::Make(200, 200)}});
 }
 
 TEST_P(ImageDecodeAcceleratorStubTest, OutOfOrderDecodeSyncTokens) {
-  EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
-      .Times(1);
+  {
+    InSequence call_sequence;
+    EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
+        .Times(1);
+    EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(200, 200)))
+        .Times(1);
+  }
   const SyncToken decode1_sync_token = SendDecodeRequest(
       gfx::Size(100, 100) /* output_size */, 2u /* decode_release_count */,
       1u /* transfer_cache_entry_id */, 1u /* handle_release_count */);
@@ -590,62 +587,87 @@
       2u /* transfer_cache_entry_id */, 2u /* handle_release_count */);
   ASSERT_TRUE(decode2_sync_token.HasData());
 
-  // We expect the destruction of the ImageDecodeAcceleratorStub, which also
-  // implies that all decode sync tokens should be released.
+  // A decode sync token should not be released before a decode is finished.
   RunTasksUntilIdle();
-  EXPECT_FALSE(channel_manager()->LookupChannel(kChannelId));
+  EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(decode1_sync_token));
+  EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(decode2_sync_token));
+
+  // Since the sync tokens are out of order, releasing the first one should also
+  // release the second one.
+  image_decode_accelerator_worker_.FinishOneDecode(true);
+  RunTasksUntilIdle();
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode1_sync_token));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode2_sync_token));
 
-  // We expect no entries in the transfer cache.
-  CheckTransferCacheEntries({});
+  // We only expect the first image in the transfer cache.
+  CheckTransferCacheEntries({{1u, SkISize::Make(100, 100)}});
+
+  // Finishing the second decode should not "unrelease" the first sync token.
+  image_decode_accelerator_worker_.FinishOneDecode(true);
+  RunTasksUntilIdle();
+  EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode1_sync_token));
+  EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode2_sync_token));
+  CheckTransferCacheEntries(
+      {{1u, SkISize::Make(100, 100)}, {2u, SkISize::Make(200, 200)}});
 }
 
 TEST_P(ImageDecodeAcceleratorStubTest, ZeroReleaseCountDecodeSyncToken) {
+  EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 100)))
+      .Times(1);
   const SyncToken decode_sync_token = SendDecodeRequest(
       gfx::Size(100, 100) /* output_size */, 0u /* decode_release_count */,
       1u /* transfer_cache_entry_id */, 1u /* handle_release_count */);
   ASSERT_TRUE(decode_sync_token.HasData());
 
-  // We expect the destruction of the ImageDecodeAcceleratorStub, which also
-  // implies that all decode sync tokens should be released.
+  // A zero-release count sync token is always considered released.
   RunTasksUntilIdle();
-  EXPECT_FALSE(channel_manager()->LookupChannel(kChannelId));
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
 
-  // We expect no entries in the transfer cache.
-  CheckTransferCacheEntries({});
+  // Even though the release count is not really valid, we can still finish the
+  // decode.
+  image_decode_accelerator_worker_.FinishOneDecode(true);
+  RunTasksUntilIdle();
+  EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
+  CheckTransferCacheEntries({{1u, SkISize::Make(100, 100)}});
 }
 
 TEST_P(ImageDecodeAcceleratorStubTest, ZeroWidthOutputSize) {
+  EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(0, 100)))
+      .Times(1);
   const SyncToken decode_sync_token = SendDecodeRequest(
       gfx::Size(0, 100) /* output_size */, 1u /* decode_release_count */,
       1u /* transfer_cache_entry_id */, 1u /* handle_release_count */);
   ASSERT_TRUE(decode_sync_token.HasData());
 
-  // We expect the destruction of the ImageDecodeAcceleratorStub, which also
-  // implies that all decode sync tokens should be released.
+  // A decode sync token should not be released before a decode is finished.
   RunTasksUntilIdle();
-  EXPECT_FALSE(channel_manager()->LookupChannel(kChannelId));
-  EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
+  EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
 
-  // We expect no entries in the transfer cache.
+  // Even though the output size is not valid, we can still finish the decode.
+  // We just shouldn't get any entries in the transfer cache.
+  image_decode_accelerator_worker_.FinishOneDecode(true);
+  RunTasksUntilIdle();
+  EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
   CheckTransferCacheEntries({});
 }
 
 TEST_P(ImageDecodeAcceleratorStubTest, ZeroHeightOutputSize) {
+  EXPECT_CALL(image_decode_accelerator_worker_, DoDecode(gfx::Size(100, 0)))
+      .Times(1);
   const SyncToken decode_sync_token = SendDecodeRequest(
       gfx::Size(100, 0) /* output_size */, 1u /* decode_release_count */,
       1u /* transfer_cache_entry_id */, 1u /* handle_release_count */);
   ASSERT_TRUE(decode_sync_token.HasData());
 
-  // We expect the destruction of the ImageDecodeAcceleratorStub, which also
-  // implies that all decode sync tokens should be released.
+  // A decode sync token should not be released before a decode is finished.
   RunTasksUntilIdle();
-  EXPECT_FALSE(channel_manager()->LookupChannel(kChannelId));
-  EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
+  EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
 
-  // We expect no entries in the transfer cache.
+  // Even though the output size is not valid, we can still finish the decode.
+  // We just shouldn't get any entries in the transfer cache.
+  image_decode_accelerator_worker_.FinishOneDecode(true);
+  RunTasksUntilIdle();
+  EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
   CheckTransferCacheEntries({});
 }
 
@@ -683,14 +705,6 @@
   RunTasksUntilIdle();
   EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
 
-  // Let's make sure that the channel and the command buffer are still alive
-  // because if we didn't wait for the discardable handle's buffer to be
-  // registered, we could have caused a channel teardown.
-  ASSERT_TRUE(channel_manager()->LookupChannel(kChannelId));
-  ASSERT_TRUE(channel_manager()
-                  ->LookupChannel(kChannelId)
-                  ->LookupCommandBuffer(kCommandBufferRouteId));
-
   // Now let's register the discardable handle's buffer by re-enabling the
   // raster sequence. This should trigger the processing of the completed decode
   // and the subsequent release of the decode sync token.
@@ -698,11 +712,8 @@
   RunTasksUntilIdle();
   EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(decode_sync_token));
 
-  // The channel should still exist at the end.
-  EXPECT_TRUE(channel_manager()->LookupChannel(kChannelId));
-
   // Check that the decoded images are in the transfer cache.
-  CheckTransferCacheEntries({SkISize::Make(100, 100)});
+  CheckTransferCacheEntries({{1u, SkISize::Make(100, 100)}});
 }
 
 // TODO(andrescj): test the deletion of transfer cache entries.
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.h
index 973c0a7..89ade2b 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.h
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.h
@@ -38,6 +38,7 @@
   void InvalidateLine(size_t line) override {}
   void OnLineSelected(size_t line) override {}
   void UpdatePopupAppearance() override;
+  void ProvideButtonFocusHint(size_t line) override {}
   void OnMatchIconUpdated(size_t match_index) override {}
   void OnDragCanceled() override {}
 
diff --git a/ios/chrome/browser/ui/webui/BUILD.gn b/ios/chrome/browser/ui/webui/BUILD.gn
index fb4b139..943a0d9 100644
--- a/ios/chrome/browser/ui/webui/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/BUILD.gn
@@ -121,6 +121,7 @@
 
 source_set("eg_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
+  defines = [ "CHROME_EARL_GREY_1" ]
   testonly = true
   sources = [
     "inspect/inspect_ui_egtest.mm",
@@ -135,6 +136,7 @@
     "//ios/chrome/browser/ui/omnibox:omnibox_internal",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
+    "//ios/testing/earl_grey:earl_grey_support",
     "//ios/web",
     "//ios/web/public/test:element_selector",
     "//net:test_support",
@@ -143,3 +145,25 @@
   ]
   libs = [ "XCTest.framework" ]
 }
+
+source_set("eg2_tests") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+  sources = [
+    "inspect/inspect_ui_egtest.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/browser:chrome_url_constants",
+    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
+    "//ios/testing/earl_grey:eg_test_support+eg2",
+    "//ios/third_party/earl_grey2:test_lib",
+    "//ios/web/public/test:element_selector",
+    "//net:test_support",
+  ]
+  libs = [ "UIKit.framework" ]
+}
diff --git a/ios/chrome/browser/ui/webui/inspect/inspect_ui_egtest.mm b/ios/chrome/browser/ui/webui/inspect/inspect_ui_egtest.mm
index 0d5110e..40a2530a8 100644
--- a/ios/chrome/browser/ui/webui/inspect/inspect_ui_egtest.mm
+++ b/ios/chrome/browser/ui/webui/inspect/inspect_ui_egtest.mm
@@ -2,17 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import <EarlGrey/EarlGrey.h>
 #import <Foundation/Foundation.h>
 #import <XCTest/XCTest.h>
 
 #import "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
-#import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_actions.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/testing/earl_grey/earl_grey_test.h"
 #include "ios/web/public/test/element_selector.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
@@ -20,8 +19,6 @@
 #error "This file requires ARC support."
 #endif
 
-using chrome_test_util::GetCurrentWebState;
-
 namespace {
 // Directory containing the |kLogoPagePath| and |kLogoPageImageSourcePath|
 // resources.
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index b0fc51a..2cf08e7 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -89,6 +89,7 @@
     "//ios/chrome/browser/ui/tab_grid:eg2_tests",
     "//ios/chrome/browser/ui/tabs:eg2_tests",
     "//ios/chrome/browser/ui/toolbar:eg2_tests",
+    "//ios/chrome/browser/ui/webui:eg2_tests",
   ]
 }
 
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 67b576da..6e358325 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -231,12 +231,8 @@
 const base::Feature kMediaCastOverlayButton{"MediaCastOverlayButton",
                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Use AndroidOverlay rather than ContentVideoView in clank?
-const base::Feature kUseAndroidOverlay{"UseAndroidOverlay",
-                                       base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Use AndroidOverlay for more cases than just player-element fullscreen?  This
-// requires that |kUseAndroidOverlay| is true, else it is ignored.
+// requires that |kOverlayFullscreenVideo| is true, else it is ignored.
 const base::Feature kUseAndroidOverlayAggressively{
     "UseAndroidOverlayAggressively", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index cf1197b4..027a4d6 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -140,7 +140,6 @@
 MEDIA_EXPORT extern const base::Feature kRevokeMediaSourceObjectURLOnAttach;
 MEDIA_EXPORT extern const base::Feature kSpecCompliantCanPlayThrough;
 MEDIA_EXPORT extern const base::Feature kUnifiedAutoplay;
-MEDIA_EXPORT extern const base::Feature kUseAndroidOverlay;
 MEDIA_EXPORT extern const base::Feature kUseAndroidOverlayAggressively;
 MEDIA_EXPORT extern const base::Feature kUseFakeDeviceForMediaStream;
 MEDIA_EXPORT extern const base::Feature kUseMediaHistoryStore;
diff --git a/media/blink/BUILD.gn b/media/blink/BUILD.gn
index 7ec07f1..4643040 100644
--- a/media/blink/BUILD.gn
+++ b/media/blink/BUILD.gn
@@ -39,6 +39,8 @@
     "resource_fetch_context.h",
     "resource_multibuffer_data_provider.cc",
     "resource_multibuffer_data_provider.h",
+    "smoothness_helper.cc",
+    "smoothness_helper.h",
     "texttrack_impl.cc",
     "texttrack_impl.h",
     "url_index.cc",
@@ -147,6 +149,7 @@
     "multibuffer_unittest.cc",
     "resource_multibuffer_data_provider_unittest.cc",
     "run_all_unittests.cc",
+    "smoothness_helper_unittest.cc",
     "test_response_generator.cc",
     "test_response_generator.h",
     "url_index_unittest.cc",
diff --git a/media/blink/smoothness_helper.cc b/media/blink/smoothness_helper.cc
new file mode 100644
index 0000000..76c530b
--- /dev/null
+++ b/media/blink/smoothness_helper.cc
@@ -0,0 +1,145 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/blink/smoothness_helper.h"
+
+#include "base/bind.h"
+#include "base/optional.h"
+#include "base/timer/timer.h"
+#include "base/unguessable_token.h"
+#include "media/learning/common/learning_task_controller.h"
+
+namespace {
+static constexpr base::TimeDelta kSegmentSize =
+    base::TimeDelta::FromSeconds(60);
+}
+
+namespace media {
+
+using learning::FeatureVector;
+using learning::LearningTaskController;
+using learning::TargetValue;
+
+class SmoothnessHelperImpl : public SmoothnessHelper {
+ public:
+  SmoothnessHelperImpl(std::unique_ptr<LearningTaskController> controller,
+                       const FeatureVector& features,
+                       Client* player)
+      : controller_(std::move(controller)),
+        features_(features),
+        player_(player) {}
+
+  // This will ignore the last segment, if any, which is fine since it's not
+  // a complete segment.
+  ~SmoothnessHelperImpl() override = default;
+
+  void NotifyPlayState(bool playing) override {
+    if (playing) {
+      if (segment_decoded_frames_)
+        return;
+
+      // We're starting a new playback, so record the baseline frame counts.
+      segment_dropped_frames_ = player_->DroppedFrameCount();
+      segment_decoded_frames_ = player_->DecodedFrameCount();
+      worst_segment_during_playback_ = TargetValue(0);
+
+      DCHECK(!id_);
+
+      // Don't bother to start the observation until the timer fires, since we
+      // don't wanto to record short playbacks.
+
+      update_timer_.Start(FROM_HERE, kSegmentSize,
+                          base::BindRepeating(&SmoothnessHelperImpl::OnTimer,
+                                              base::Unretained(this)));
+    } else {
+      if (!segment_decoded_frames_)
+        return;
+
+      // If we started an observation, then complete it.  Otherwise, the segment
+      // wasn't long enough.  Note that we also don't update the worst NNR
+      // rate here, so that we don't include very short partial segments that
+      // might be artificially high.  Note that this might be a bad idea; if
+      // the site detects bad playback and adapts before we've measured one
+      // segment, then we'll never record those NNRs.  We might want to allow
+      // the final segment to be smaller than |kSegmentSize|, as long as it's
+      // not too small.
+      if (id_)
+        controller_->CompleteObservation(*id_, worst_segment_during_playback_);
+
+      // End the segment and the playback.
+      segment_decoded_frames_.reset();
+      segment_dropped_frames_.reset();
+      update_timer_.Stop();
+      id_.reset();
+    }
+  }
+
+  // Split playback into segments of length |kSegmentSize|, and update the
+  // default value of the current playback.
+  void OnTimer() {
+    DCHECK(segment_decoded_frames_);
+
+    auto new_dropped_frames = player_->DroppedFrameCount();
+    auto dropped_frames = new_dropped_frames - *segment_dropped_frames_;
+    segment_dropped_frames_ = new_dropped_frames;
+
+    auto new_decoded_frames = player_->DecodedFrameCount();
+    auto decoded_frames = new_decoded_frames - *segment_decoded_frames_;
+    segment_decoded_frames_ = new_decoded_frames;
+
+    if (!decoded_frames)
+      return;
+
+    // The target value is just the percentage of dropped frames.
+    auto target = TargetValue(((double)dropped_frames) / decoded_frames);
+
+    // See if this is worse than any previous segment.
+    if (target > worst_segment_during_playback_)
+      worst_segment_during_playback_ = target;
+
+    // Start an observation for this playback, or update the default.
+    if (!id_) {
+      id_ = base::UnguessableToken::Create();
+      controller_->BeginObservation(*id_, features_,
+                                    worst_segment_during_playback_);
+    } else {
+      controller_->UpdateDefaultTarget(*id_, worst_segment_during_playback_);
+    }
+  }
+
+  // Current dropped, decoded frames at the start of the segment, if any.
+  base::Optional<int64_t> segment_decoded_frames_;
+  base::Optional<int64_t> segment_dropped_frames_;
+
+  // Of all the segments in this playback, this is the worst NNR ratio.
+  TargetValue worst_segment_during_playback_;
+
+  std::unique_ptr<LearningTaskController> controller_;
+
+  FeatureVector features_;
+
+  base::RepeatingTimer update_timer_;
+
+  // WebMediaPlayer which will tell us about the decoded / dropped frame counts.
+  Client* player_;
+
+  // If an observation is in progress, then this is the id.
+  base::Optional<base::UnguessableToken> id_;
+};
+
+// static
+std::unique_ptr<SmoothnessHelper> SmoothnessHelper::Create(
+    std::unique_ptr<LearningTaskController> controller,
+    const FeatureVector& features,
+    Client* player) {
+  return std::make_unique<SmoothnessHelperImpl>(std::move(controller), features,
+                                                player);
+}
+
+// static
+base::TimeDelta SmoothnessHelper::SegmentSizeForTesting() {
+  return kSegmentSize;
+}
+
+}  // namespace media
diff --git a/media/blink/smoothness_helper.h b/media/blink/smoothness_helper.h
new file mode 100644
index 0000000..02694a1
--- /dev/null
+++ b/media/blink/smoothness_helper.h
@@ -0,0 +1,63 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BLINK_SMOOTHNESS_HELPER_H_
+#define MEDIA_BLINK_SMOOTHNESS_HELPER_H_
+
+#include <memory>
+
+#include "media/base/buffering_state.h"
+#include "media/base/pipeline_status.h"
+#include "media/blink/media_blink_export.h"
+#include "media/learning/common/labelled_example.h"
+
+namespace media {
+
+namespace learning {
+class LearningTaskController;
+}
+
+// Helper class to construct learning observations about the smoothness of a
+// video playback.  Currently measures the worst-case frame drop ratio observed
+// among fixed-length segments.
+class MEDIA_BLINK_EXPORT SmoothnessHelper {
+ public:
+  // Callback that provides the number of dropped / decoded frames since some
+  // point in the past.  We assume that these values are comparable during
+  // playback, so that we can compute deltas.
+  class Client {
+   public:
+    virtual ~Client() {}
+
+    virtual unsigned DecodedFrameCount() const = 0;
+    virtual unsigned DroppedFrameCount() const = 0;
+  };
+
+  virtual ~SmoothnessHelper() = default;
+
+  // |features| are the features that we'll use for any labelled examples that
+  // we create.  They should be features that could be captured at the time a
+  // prediction would be needed.
+  static std::unique_ptr<SmoothnessHelper> Create(
+      std::unique_ptr<learning::LearningTaskController> controller,
+      const learning::FeatureVector& features,
+      Client* player);
+
+  // Notify us when we start or stop playing.
+  // TODO(liberato): There is an open question whether we'd like one call of
+  // the form "ThePlayerIsInTheRightStateToRecordSmoothness(bool)", or whether
+  // we'd like multiple calls to record the state of the player, like
+  // "SetIsPlaying", "SetIsBackgrounded", etc.  The difference is whether the
+  // decision to record is made by the player or by us.
+  virtual void NotifyPlayState(bool playing) = 0;
+
+  // We split playbacks up into |kSegmentSize| units, and record the worst
+  // dropped frame ratio over all segments of a playback.  A playback is not
+  // recorded if it doesn't contain at least one full segment.
+  static base::TimeDelta SegmentSizeForTesting();
+};
+
+}  // namespace media
+
+#endif  // MEDIA_BLINK_SMOOTHNESS_HELPER_H_
diff --git a/media/blink/smoothness_helper_unittest.cc b/media/blink/smoothness_helper_unittest.cc
new file mode 100644
index 0000000..f4c9b8da
--- /dev/null
+++ b/media/blink/smoothness_helper_unittest.cc
@@ -0,0 +1,199 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/blink/smoothness_helper.h"
+
+#include "base/run_loop.h"
+#include "media/blink/blink_platform_with_task_environment.h"
+#include "media/learning/common/labelled_example.h"
+#include "media/learning/common/learning_task_controller.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+using learning::FeatureValue;
+using learning::FeatureVector;
+using learning::LearningTask;
+using learning::LearningTaskController;
+using learning::ObservationCompletion;
+using learning::TargetValue;
+
+using testing::_;
+using testing::AnyNumber;
+using testing::Eq;
+using testing::Gt;
+using testing::Lt;
+using testing::ResultOf;
+using testing::Return;
+
+// Helper for EXPECT_CALL argument matching on Optional<TargetValue>.  Applies
+// matcher |m| to the TargetValue as a double.  For example:
+// void Foo(base::Optional<TargetValue>);
+// EXPECT_CALL(..., Foo(OPT_TARGET(Gt(0.9)))) will expect that the value of the
+// Optional<TargetValue> passed to Foo() to be greather than 0.9 .
+#define OPT_TARGET(m) \
+  ResultOf([](const base::Optional<TargetValue>& v) { return (*v).value(); }, m)
+
+// Same as above, but expects an ObservationCompletion.
+#define COMPLETION_TARGET(m)                                                 \
+  ResultOf(                                                                  \
+      [](const ObservationCompletion& x) { return x.target_value.value(); }, \
+      m)
+
+class SmoothnessHelperTest : public testing::Test {
+  class MockLearningTaskController : public LearningTaskController {
+   public:
+    MOCK_METHOD3(BeginObservation,
+                 void(base::UnguessableToken id,
+                      const FeatureVector& features,
+                      const base::Optional<TargetValue>& default_target));
+
+    MOCK_METHOD2(CompleteObservation,
+                 void(base::UnguessableToken id,
+                      const ObservationCompletion& completion));
+
+    MOCK_METHOD1(CancelObservation, void(base::UnguessableToken id));
+
+    MOCK_METHOD2(UpdateDefaultTarget,
+                 void(base::UnguessableToken id,
+                      const base::Optional<TargetValue>& default_target));
+
+    MOCK_METHOD0(GetLearningTask, const LearningTask&());
+  };
+
+  class MockClient : public SmoothnessHelper::Client {
+   public:
+    ~MockClient() override = default;
+
+    MOCK_CONST_METHOD0(DecodedFrameCount, unsigned(void));
+    MOCK_CONST_METHOD0(DroppedFrameCount, unsigned(void));
+  };
+
+ public:
+  void SetUp() override {
+    auto ltc = std::make_unique<MockLearningTaskController>();
+    ltc_ = ltc.get();
+    features_.push_back(FeatureValue(123));
+    helper_ = SmoothnessHelper::Create(std::move(ltc), features_, &client_);
+    segment_size_ = SmoothnessHelper::SegmentSizeForTesting();
+  }
+
+  // Helper for EXPECT_CALL.
+  base::Optional<TargetValue> Opt(double x) {
+    return base::Optional<TargetValue>(TargetValue(x));
+  }
+
+  void FastForwardBy(base::TimeDelta amount) {
+    BlinkPlatformWithTaskEnvironment::GetTaskEnvironment()->FastForwardBy(
+        amount);
+  }
+
+  // Set the dropped / decoded totals that will be returned by the mock client.
+  void SetFrameCounters(int dropped, int decoded) {
+    ON_CALL(client_, DroppedFrameCount()).WillByDefault(Return(dropped));
+    ON_CALL(client_, DecodedFrameCount()).WillByDefault(Return(decoded));
+  }
+
+  // Helper under test
+  std::unique_ptr<SmoothnessHelper> helper_;
+
+  MockLearningTaskController* ltc_ = nullptr;
+  MockClient client_;
+  FeatureVector features_;
+
+  base::TimeDelta segment_size_;
+};
+
+TEST_F(SmoothnessHelperTest, PauseWithoutPlayDoesNothing) {
+  EXPECT_CALL(*ltc_, BeginObservation(_, _, _)).Times(0);
+  helper_->NotifyPlayState(false);
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(SmoothnessHelperTest, PlayThenImmediatePauseCancelsObservation) {
+  // If not enough time has elapsed, play then pause shouldn't record anything.
+  // Note that Begin then Cancel would be okay too, but it's hard to set
+  // expectations for either case.  So, we just pick the one that it actually
+  // does in this case.
+  EXPECT_CALL(*ltc_, BeginObservation(_, _, _)).Times(0);
+  helper_->NotifyPlayState(true);
+  helper_->NotifyPlayState(false);
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(SmoothnessHelperTest, PlayRecordsWorstSegment) {
+  // Record three segments, and see if it chooses the worst.
+  SetFrameCounters(0, 0);
+  helper_->NotifyPlayState(true);
+  base::RunLoop().RunUntilIdle();
+
+  // First segment has no dropped frames..
+  EXPECT_CALL(*ltc_, BeginObservation(_, _, OPT_TARGET(Eq(0.0)))).Times(1);
+  SetFrameCounters(0, 1000);
+  FastForwardBy(segment_size_);
+  base::RunLoop().RunUntilIdle();
+
+  // Second segment has quite a lot of dropped frames.
+  EXPECT_CALL(*ltc_, UpdateDefaultTarget(_, OPT_TARGET(Gt(0.99)))).Times(1);
+  SetFrameCounters(999, 2000);
+  FastForwardBy(segment_size_);
+  base::RunLoop().RunUntilIdle();
+
+  // Third segment has no dropped frames, so the default shouldn't change.
+  EXPECT_CALL(*ltc_, UpdateDefaultTarget(_, OPT_TARGET(Gt(0.99)))).Times(1);
+  SetFrameCounters(999, 3000);
+  FastForwardBy(segment_size_);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_CALL(*ltc_, CompleteObservation(_, COMPLETION_TARGET(Gt(0.99))))
+      .Times(1);
+  helper_->NotifyPlayState(false);
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(SmoothnessHelperTest, PlayIgnoresTrailingPartialSegments) {
+  helper_->NotifyPlayState(true);
+  base::RunLoop().RunUntilIdle();
+
+  // First segment has no dropped frames.
+  EXPECT_CALL(*ltc_, BeginObservation(_, _, OPT_TARGET(Eq(0.0)))).Times(1);
+  SetFrameCounters(0, 1000);
+  FastForwardBy(segment_size_);
+  base::RunLoop().RunUntilIdle();
+
+  // Second segment has a lot of dropped frames, but isn't a full segment.
+  SetFrameCounters(1000, 2000);
+  FastForwardBy(segment_size_ / 2);
+  base::RunLoop().RunUntilIdle();
+
+  // On completion, we the observation should have no dropped frames.
+  EXPECT_CALL(*ltc_, CompleteObservation(_, COMPLETION_TARGET(Lt(0.1))))
+      .Times(1);
+  helper_->NotifyPlayState(false);
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(SmoothnessHelperTest, DestructionRecordsObservations) {
+  // Destroying |helper_| should not send any observation; the last default
+  // value should be used.
+  helper_->NotifyPlayState(true);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_CALL(*ltc_, BeginObservation(_, _, _)).Times(AnyNumber());
+  EXPECT_CALL(*ltc_, UpdateDefaultTarget(_, _)).Times(AnyNumber());
+  EXPECT_CALL(*ltc_, CancelObservation(_)).Times(0);
+  EXPECT_CALL(*ltc_, CompleteObservation(_, _)).Times(0);
+
+  // Fast forward so that we're sure that there is something to record.
+  SetFrameCounters(0, 1000);
+  FastForwardBy(segment_size_);
+  SetFrameCounters(0, 2000);
+  FastForwardBy(segment_size_);
+  helper_.reset();
+
+  base::RunLoop().RunUntilIdle();
+}
+
+}  // namespace media
diff --git a/media/blink/video_frame_compositor.h b/media/blink/video_frame_compositor.h
index 876c27a..c8fe191 100644
--- a/media/blink/video_frame_compositor.h
+++ b/media/blink/video_frame_compositor.h
@@ -105,7 +105,7 @@
   // it was updated. In certain applications, one might need to periodically
   // call UpdateCurrentFrameIfStale on |task_runner_| to drive the updates.
   // Can be called from any thread.
-  scoped_refptr<VideoFrame> GetCurrentFrameOnAnyThread();
+  virtual scoped_refptr<VideoFrame> GetCurrentFrameOnAnyThread();
 
   // VideoRendererSink implementation. These methods must be called from the
   // same thread (typically the media thread).
@@ -132,7 +132,7 @@
   // Must be called on the compositor thread.
   virtual void SetOnNewProcessedFrameCallback(OnNewProcessedFrameCB cb);
 
-  void SetOnFramePresentedCallback(OnNewFramePresentedCB present_cb);
+  virtual void SetOnFramePresentedCallback(OnNewFramePresentedCB present_cb);
 
   // Updates the rotation information for frames given to |submitter_|.
   void UpdateRotation(VideoRotation rotation);
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 8cd0a85..c6c8b77 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -369,13 +369,10 @@
   always_enable_overlays_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kForceVideoOverlays);
 
-  if (base::FeatureList::IsEnabled(kOverlayFullscreenVideo)) {
-    bool use_android_overlay = base::FeatureList::IsEnabled(kUseAndroidOverlay);
-    overlay_mode_ = use_android_overlay ? OverlayMode::kUseAndroidOverlay
-                                        : OverlayMode::kUseContentVideoView;
-  } else {
+  if (base::FeatureList::IsEnabled(kOverlayFullscreenVideo))
+    overlay_mode_ = OverlayMode::kUseAndroidOverlay;
+  else
     overlay_mode_ = OverlayMode::kNoOverlays;
-  }
 
   delegate_id_ = delegate_->AddObserver(this);
   delegate_->SetIdle(delegate_id_, true);
@@ -550,15 +547,6 @@
     client_->OnPictureInPictureStateChange();
 }
 
-bool WebMediaPlayerImpl::SupportsOverlayFullscreenVideo() {
-#if defined(OS_ANDROID)
-  return !using_media_player_renderer_ &&
-         overlay_mode_ == OverlayMode::kUseContentVideoView;
-#else
-  return false;
-#endif
-}
-
 void WebMediaPlayerImpl::EnableOverlay() {
   overlay_enabled_ = true;
   if (request_routing_token_cb_ &&
@@ -579,9 +567,7 @@
 
 void WebMediaPlayerImpl::DisableOverlay() {
   overlay_enabled_ = false;
-  if (overlay_mode_ == OverlayMode::kUseContentVideoView) {
-    surface_created_cb_.Cancel();
-  } else if (overlay_mode_ == OverlayMode::kUseAndroidOverlay) {
+  if (overlay_mode_ == OverlayMode::kUseAndroidOverlay) {
     token_available_cb_.Cancel();
     overlay_routing_token_is_pending_ = false;
     overlay_routing_token_ = OverlayInfo::RoutingToken();
@@ -2782,6 +2768,9 @@
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   TRACE_EVENT0("media", "WebMediaPlayerImpl::GetCurrentFrameFromCompositor");
 
+  if (current_frame_override_)
+    return current_frame_override_;
+
   // Can be null.
   scoped_refptr<VideoFrame> video_frame =
       compositor_->GetCurrentFrameOnAnyThread();
@@ -3286,6 +3275,28 @@
   return bridge_->GetSurfaceId();
 }
 
+void WebMediaPlayerImpl::RequestAnimationFrame() {
+  vfc_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&VideoFrameCompositor::SetOnFramePresentedCallback,
+                     base::Unretained(compositor_.get()),
+                     BindToCurrentLoop(base::BindOnce(
+                         &WebMediaPlayerImpl::OnNewFramePresentedCallback,
+                         weak_factory_.GetWeakPtr()))));
+}
+
+void WebMediaPlayerImpl::OnNewFramePresentedCallback(
+    scoped_refptr<VideoFrame> presented_frame,
+    base::TimeTicks presentation_time,
+    base::TimeTicks expected_presentation_time,
+    uint32_t presentation_counter) {
+  current_frame_override_ = std::move(presented_frame);
+  client_->OnRequestAnimationFrame(
+      presentation_time, expected_presentation_time, presentation_counter,
+      *current_frame_override_);
+  current_frame_override_.reset();
+}
+
 base::WeakPtr<blink::WebMediaPlayer> WebMediaPlayerImpl::AsWeakPtr() {
   return weak_this_;
 }
@@ -3637,4 +3648,9 @@
   return mb_data_source_ ? mb_data_source_->GetUrlAfterRedirects() : GURL();
 }
 
+void WebMediaPlayerImpl::SetCurrentFrameOverrideForTesting(
+    scoped_refptr<VideoFrame> current_frame_override) {
+  current_frame_override_ = current_frame_override;
+}
+
 }  // namespace media
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 6e347ea..14ebb86c 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -219,7 +219,6 @@
       blink::WebContentDecryptionModule* cdm,
       blink::WebContentDecryptionModuleResult result) override;
 
-  bool SupportsOverlayFullscreenVideo() override;
   void EnteredFullscreen() override;
   void ExitedFullscreen() override;
   void BecameDominantVisibleContent(bool is_dominant) override;
@@ -281,6 +280,7 @@
   int GetDelegateId() override;
   base::Optional<viz::SurfaceId> GetSurfaceId() override;
   GURL GetSrcAfterRedirects() override;
+  void RequestAnimationFrame() override;
 
   base::WeakPtr<blink::WebMediaPlayer> AsWeakPtr() override;
 
@@ -607,11 +607,21 @@
   // Switch to SurfaceLayer, either initially or from VideoLayer.
   void ActivateSurfaceLayerForVideo();
 
+  // Called by |compositor_| upon presenting a frame, after
+  // RequestAnimationFrame() is called.
+  void OnNewFramePresentedCallback(scoped_refptr<VideoFrame> presented_frame,
+                                   base::TimeTicks presentation_time,
+                                   base::TimeTicks expected_presentation_time,
+                                   uint32_t presentation_counter);
+
   // Notifies |mb_data_source_| of playback and rate changes which may increase
   // the amount of data the DataSource buffers. Does nothing prior to reaching
   // kReadyStateHaveEnoughData for the first time.
   void MaybeUpdateBufferSizesForPlayback();
 
+  void SetCurrentFrameOverrideForTesting(
+      scoped_refptr<VideoFrame> current_frame_override);
+
   blink::WebLocalFrame* const frame_;
 
   // The playback state last reported to |delegate_|, to avoid setting duplicate
@@ -802,9 +812,6 @@
 
   std::unique_ptr<RendererFactorySelector> renderer_factory_selector_;
 
-  // For canceling ongoing surface creation requests when exiting fullscreen.
-  base::CancelableCallback<void(int)> surface_created_cb_;
-
   // For canceling AndroidOverlay routing token requests.
   base::CancelableCallback<void(const base::UnguessableToken&)>
       token_available_cb_;
@@ -941,9 +948,6 @@
     // All overlays are turned off.
     kNoOverlays,
 
-    // Use ContentVideoView for overlays.
-    kUseContentVideoView,
-
     // Use AndroidOverlay for overlays.
     kUseAndroidOverlay,
   };
@@ -994,6 +998,10 @@
   // Whether background video optimization is supported on current platform.
   bool is_background_video_track_optimization_supported_ = true;
 
+  // Valid while an active OnNewFramePresentedCallback() is in progress.
+  // Overrides the VideoFrame returned by GetCurrentFrameFromCompositor().
+  scoped_refptr<VideoFrame> current_frame_override_;
+
   base::CancelableOnceClosure have_enough_after_lazy_load_cb_;
 
   // State for simplified watch time reporting.
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 1fd9a16..af2eae8 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -152,6 +152,11 @@
   MOCK_METHOD0(RequestPause, void());
   MOCK_METHOD1(RequestMuted, void(bool));
   MOCK_METHOD0(GetFeatures, Features(void));
+  MOCK_METHOD4(OnRequestAnimationFrame,
+               void(base::TimeTicks,
+                    base::TimeTicks,
+                    uint32_t,
+                    const media::VideoFrame&));
 
   void set_was_always_muted(bool value) { was_always_muted_ = value; }
 
@@ -290,9 +295,9 @@
   ~MockVideoFrameCompositor() override = default;
 
   // MOCK_METHOD doesn't like OnceCallback.
-  void SetOnNewProcessedFrameCallback(OnNewProcessedFrameCB cb) override {}
+  MOCK_METHOD1(SetOnFramePresentedCallback, void(OnNewFramePresentedCB));
   MOCK_METHOD1(SetIsPageVisible, void(bool));
-  MOCK_METHOD0(GetCurrentFrameAndUpdateIfStale, scoped_refptr<VideoFrame>());
+  MOCK_METHOD0(GetCurrentFrameOnAnyThread, scoped_refptr<VideoFrame>());
   MOCK_METHOD4(
       EnableSubmission,
       void(const viz::SurfaceId&, base::TimeTicks, media::VideoRotation, bool));
@@ -617,6 +622,27 @@
     return wmpi_->mb_data_source_->media_has_played();
   }
 
+  scoped_refptr<VideoFrame> CreateFrame() {
+    gfx::Size size(8, 8);
+    return VideoFrame::CreateFrame(PIXEL_FORMAT_I420, size, gfx::Rect(size),
+                                   size, base::TimeDelta());
+  }
+
+  void RequestAnimationFrame() { wmpi_->RequestAnimationFrame(); }
+
+  void OnNewFramePresentedCallback() {
+    wmpi_->OnNewFramePresentedCallback(CreateFrame(), base::TimeTicks::Now(),
+                                       base::TimeTicks::Now(), 1);
+  }
+
+  scoped_refptr<VideoFrame> GetCurrentFrameFromCompositor() {
+    return wmpi_->GetCurrentFrameFromCompositor();
+  }
+
+  void SetCurrentFrameOverrideForTesting(scoped_refptr<VideoFrame> frame) {
+    wmpi_->SetCurrentFrameOverrideForTesting(frame);
+  }
+
   enum class LoadType { kFullyBuffered, kStreaming };
   void Load(std::string data_file,
             LoadType load_type = LoadType::kFullyBuffered) {
@@ -1105,6 +1131,40 @@
   EXPECT_FALSE(IsSuspended());
 }
 
+TEST_F(WebMediaPlayerImplTest, RequestAnimationFrame) {
+  InitializeWebMediaPlayerImpl();
+
+  EXPECT_CALL(*compositor_, SetOnFramePresentedCallback(_));
+  RequestAnimationFrame();
+}
+
+TEST_F(WebMediaPlayerImplTest, OnNewFramePresentedCallback) {
+  InitializeWebMediaPlayerImpl();
+  EXPECT_CALL(client_, OnRequestAnimationFrame(_, _, _, _));
+
+  OnNewFramePresentedCallback();
+}
+
+TEST_F(WebMediaPlayerImplTest, GetCurrentFrameFromCompositorOverride) {
+  scoped_refptr<VideoFrame> compositor_frame = CreateFrame();
+  scoped_refptr<VideoFrame> override_frame = CreateFrame();
+
+  InitializeWebMediaPlayerImpl();
+
+  EXPECT_CALL(*compositor_, GetCurrentFrameOnAnyThread())
+      .WillRepeatedly(Return(compositor_frame));
+
+  EXPECT_EQ(compositor_frame, GetCurrentFrameFromCompositor());
+
+  SetCurrentFrameOverrideForTesting(override_frame);
+  EXPECT_EQ(override_frame, GetCurrentFrameFromCompositor());
+
+  // After returning from OnNewFramePresentedCallback(), the overriding frame
+  // should be cleared.
+  OnNewFramePresentedCallback();
+  EXPECT_EQ(compositor_frame, GetCurrentFrameFromCompositor());
+}
+
 TEST_F(WebMediaPlayerImplTest, ComputePlayState_Constructed) {
   InitializeWebMediaPlayerImpl();
   WebMediaPlayerImpl::PlayState state = ComputePlayState();
diff --git a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
index b91cfc96e..b84f510 100644
--- a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
+++ b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
@@ -14,6 +14,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
@@ -501,7 +502,7 @@
     // 100 years after 01 January 1970 UTC.
     expiration = 3153600000.0;  // 100 * 365 * 24 * 60 * 60;
 
-    if (!has_set_renewal_timer_) {
+    if (!has_set_timer_) {
       // Make sure the CDM can get time and sleep if necessary.
       constexpr auto kSleepDuration = base::TimeDelta::FromSeconds(1);
       auto start_time = base::Time::Now();
@@ -509,8 +510,7 @@
       auto time_elapsed = base::Time::Now() - start_time;
       CHECK_GE(time_elapsed, kSleepDuration);
 
-      ScheduleNextRenewal();
-      has_set_renewal_timer_ = true;
+      ScheduleNextTimer();
     }
 
     // Also send an individualization request if never sent before. Only
@@ -581,19 +581,27 @@
 
 void ClearKeyCdm::TimerExpired(void* context) {
   DVLOG(1) << __func__;
-  DCHECK(has_set_renewal_timer_);
+  DCHECK(has_set_timer_);
   std::string renewal_message;
-  if (!next_renewal_message_.empty() && context == &next_renewal_message_[0]) {
-    renewal_message = next_renewal_message_;
-  } else {
-    renewal_message = "ERROR: Invalid timer context found!";
+
+  if (key_system_ == kExternalClearKeyMessageTypeTestKeySystem) {
+    if (!next_renewal_message_.empty() &&
+        context == &next_renewal_message_[0]) {
+      renewal_message = next_renewal_message_;
+    } else {
+      renewal_message = "ERROR: Invalid timer context found!";
+    }
+
+    cdm_host_proxy_->OnSessionMessage(
+        last_session_id_.data(), last_session_id_.length(),
+        cdm::kLicenseRenewal, renewal_message.data(), renewal_message.length());
+  } else if (key_system_ == kExternalClearKeyOutputProtectionTestKeySystem) {
+    // Check output protection again.
+    cdm_host_proxy_->QueryOutputProtectionStatus();
   }
 
-  cdm_host_proxy_->OnSessionMessage(
-      last_session_id_.data(), last_session_id_.length(), cdm::kLicenseRenewal,
-      renewal_message.data(), renewal_message.length());
-
-  ScheduleNextRenewal();
+  // Start the timer to schedule another timeout.
+  ScheduleNextTimer();
 }
 
 static void CopyDecryptResults(media::Decryptor::Status* status_copy,
@@ -770,8 +778,9 @@
   delete this;
 }
 
-void ClearKeyCdm::ScheduleNextRenewal() {
-  // Prepare the next renewal message and set timer.
+void ClearKeyCdm::ScheduleNextTimer() {
+  // Prepare the next renewal message and set timer. Renewal message is only
+  // needed for the renewal test, and is ignored for other uses of the timer.
   std::ostringstream msg_stream;
   msg_stream << "Renewal from ClearKey CDM set at time "
              << base::Time::FromDoubleT(cdm_host_proxy_->GetCurrentWallTime())
@@ -779,6 +788,7 @@
   next_renewal_message_ = msg_stream.str();
 
   cdm_host_proxy_->SetTimer(timer_delay_ms_, &next_renewal_message_[0]);
+  has_set_timer_ = true;
 
   // Use a smaller timer delay at start-up to facilitate testing. Increase the
   // timer delay up to a limit to avoid message spam.
@@ -838,27 +848,38 @@
     cdm::QueryResult result,
     uint32_t link_mask,
     uint32_t output_protection_mask) {
-  DVLOG(1) << __func__;
+  DVLOG(1) << __func__ << " result:" << result << ", link_mask:" << link_mask
+           << ", output_protection_mask:" << output_protection_mask;
 
   if (!is_running_output_protection_test_) {
     NOTREACHED() << "OnQueryOutputProtectionStatus() called unexpectedly.";
     return;
   }
 
-  is_running_output_protection_test_ = false;
-
-// On Chrome OS, status query will fail on Linux Chrome OS build. So we ignore
-// the query result. On all other platforms, status query should succeed.
-// TODO(xhwang): Improve the check on Chrome OS builds. For example, use
-// base::SysInfo::IsRunningOnChromeOS() to differentiate between real Chrome OS
-// build and Linux Chrome OS build.
-#if !defined(OS_CHROMEOS)
-  if (result != cdm::kQuerySucceeded || link_mask != 0) {
-    OnUnitTestComplete(false);
-    return;
+  // If the query fails or it succeeds and link mask contains kLinkTypeNetwork,
+  // send a 'keystatuschange' event with a key marked as output-restricted.
+  // As the JavaScript test doesn't check key IDs, use a dummy key ID.
+  //
+  // Note that QueryOutputProtectionStatus() is known to fail on Linux Chrome
+  // OS builds.
+  //
+  // Note that this does not modify any keys, so if the caller does not check
+  // the 'keystatuschange' event, nothing will happen as decoding will continue
+  // to work.
+  if (result != cdm::kQuerySucceeded || (link_mask & cdm::kLinkTypeNetwork)) {
+    // A session ID is needed, so use |last_session_id_|. However, if this is
+    // called before a session has been created, we have no session to send
+    // this to. Note that this only works with a single session, the same as
+    // renewal messages.
+    if (!last_session_id_.empty()) {
+      const uint8_t kDummyKeyId[] = {'d', 'u', 'm', 'm', 'y'};
+      std::vector<cdm::KeyInformation> keys_vector = {
+          {kDummyKeyId, base::size(kDummyKeyId), cdm::kOutputRestricted, 0}};
+      cdm_host_proxy_->OnSessionKeysChange(
+          last_session_id_.data(), last_session_id_.length(), false,
+          keys_vector.data(), keys_vector.size());
+    }
   }
-#endif
-  OnUnitTestComplete(true);
 }
 
 void ClearKeyCdm::OnStorageId(uint32_t version,
@@ -972,6 +993,9 @@
   DVLOG(1) << __func__;
   is_running_output_protection_test_ = true;
   cdm_host_proxy_->QueryOutputProtectionStatus();
+
+  // Also start the timer to run this periodically.
+  ScheduleNextTimer();
 }
 
 void ClearKeyCdm::StartPlatformVerificationTest() {
diff --git a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h
index 7e907c97..b33cd54 100644
--- a/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h
+++ b/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h
@@ -138,7 +138,7 @@
   void OnUpdateSuccess(uint32_t promise_id, const std::string& session_id);
 
   // Prepares next renewal message and sets a timer for it.
-  void ScheduleNextRenewal();
+  void ScheduleNextTimer();
 
   // Decrypts the |encrypted_buffer| and puts the result in |decrypted_buffer|.
   // Returns cdm::kSuccess if decryption succeeded. The decrypted result is
@@ -158,6 +158,7 @@
   void OnFileIOTestComplete(bool success);
 
   void StartOutputProtectionTest();
+
   void StartPlatformVerificationTest();
   void ReportVerifyCdmHostTestResult();
   void StartStorageIdTest();
@@ -182,9 +183,9 @@
   // Timer delay in milliseconds for the next cdm_host_proxy_->SetTimer() call.
   int64_t timer_delay_ms_ = kInitialTimerDelayMs;
 
-  // Indicates whether a renewal timer has been set to prevent multiple timers
-  // from running.
-  bool has_set_renewal_timer_ = false;
+  // Indicates whether a timer has been set to prevent multiple timers from
+  // running.
+  bool has_set_timer_ = false;
 
   bool has_sent_individualization_request_ = false;
 
diff --git a/media/test/data/eme_and_get_display_media.html b/media/test/data/eme_and_get_display_media.html
new file mode 100644
index 0000000..34dad7d
--- /dev/null
+++ b/media/test/data/eme_and_get_display_media.html
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<title>Test EME and getDisplayMedia()</title>
+<div id="logs"></div>
+<script src='eme_player_js/app_loader.js' type='text/javascript'></script>
+<script type='text/javascript'>
+  // This test only checks for 'createMediaRecorderBeforeMediaKeys' in the URL
+  // parameters. If it is there, then the MediaRecorder is setup and started
+  // before MediaKeys is created. If not there, then the MediaRecorder is only
+  // created after MediaKeys is created.
+  var createMediaRecorderBeforeMediaKeys =
+    (window.location.href.indexOf('createMediaRecorderBeforeMediaKeys') > -1);
+
+  // Use the default KEY_ID and KEY as specified in eme_player_js/globals.js.
+  const keyId = KEY_ID;
+  const key = KEY;
+
+  // Returns a MediaKeys object that is already setup with a single session
+  // containing the key needed to play a typical test file.
+  async function setUpEME() {
+    // This test doesn't play any media, so use a simple
+    // MediaKeySystemConfiguration that should be supported by
+    // all platforms where External ClearKey CDM is supported.
+    const config = [{
+      initDataTypes : [ 'keyids' ],
+      videoCapabilities: [{contentType: 'video/webm; codecs="vp8"'}],
+    }];
+    var access = await navigator.requestMediaKeySystemAccess(
+        OUTPUT_PROTECTION_TEST_KEYSYSTEM, config);
+    var mediaKeys = await access.createMediaKeys();
+    Utils.timeLog('Creating session');
+    var mediaKeySession = mediaKeys.createSession();
+
+    // Handle 'keystatuseschange' events. There will be one after update() is
+    // called, as well as a later one when output protection detects the media
+    // recording. As this is testing output protection, if it reports
+    // 'output-restricted', then the test is a success. If not, simply continue
+    // on.
+    mediaKeySession.addEventListener('keystatuseschange', function(event) {
+      var result = [];
+      for (let item of event.target.keyStatuses) {
+        result.push(`{kid:${
+            Utils.getHexString(
+                Utils.convertToUint8Array(item[0]))},status:${item[1]}}`);
+      }
+      Utils.timeLog('Event: keystatuseschange ' + result.join(','));
+      for (let item of event.target.keyStatuses) {
+        if (item[1] == 'output-restricted') {
+          Utils.setResultInTitle(UNIT_TEST_SUCCESS);
+        }
+      }
+    });
+
+    // Register for the 'message' event before it happens. Although the event
+    // shouldn't be generated until after the generateRequest() promise is
+    // resolved, the handlers may be queued before the JavaScript code runs
+    // (and thus be lost if an event handler is not registered).
+    const waitForMessagePromise = Utils.waitForEvent(
+        mediaKeySession, 'message', function(event, resolve, reject) {
+          // When the 'message' event happens, we know the key to be
+          // used, so simply call update() and then call |resolve| or
+          // |reject| as appropriate.
+          Utils.timeLog('Calling update()');
+          const mediaKeySession = event.target;
+          const jwkSet = Utils.createJWKData(keyId, key);
+          mediaKeySession.update(jwkSet).then(resolve, reject);
+        });
+
+    // As this is using 'webm' initDataType, the data to generateRequest()
+    // is simply the key ID.
+    Utils.timeLog('Calling generateRequest()');
+    const generateRequestPromise = mediaKeySession.generateRequest(
+        'webm', Utils.convertToUint8Array(keyId));
+
+    await Promise.all([generateRequestPromise, waitForMessagePromise]);
+    return mediaKeys;
+  }
+
+  // Return a MediaRecorder object setup to record something (browsertests set
+  // a flag to by default capture the screen, if run manually the user will
+  // have to select something).
+  async function setUpRecorder() {
+    Utils.timeLog(
+        'Creating MediaRecorder on navigator.mediaDevices.getDisplayMedia()');
+    captureStream = await navigator.mediaDevices.getDisplayMedia({});
+    var recorder = new MediaRecorder(captureStream, {});
+    recorder.addEventListener('start', function() {
+      Utils.timeLog('Event: MediaRecorder::start');
+    });
+    recorder.start();
+    return recorder;
+  }
+
+  async function sleep(timeout) {
+    return new Promise(function(resolve) {
+      Utils.timeLog('Sleeping for ' + timeout + 'ms');
+      window.setTimeout(function() {
+        resolve();
+      }, timeout);
+    });
+  }
+
+  async function runTest() {
+    Utils.resetTitleChange();
+
+    if (createMediaRecorderBeforeMediaKeys) {
+      // Create the MediaRecorder before setting up EME.
+      await setUpRecorder();
+    }
+
+    var mediaKeys = await setUpEME();
+
+    if (!createMediaRecorderBeforeMediaKeys) {
+      // Create the MediaRecorder after a delay of 1/2 second.
+      await sleep(500);
+      await setUpRecorder();
+    }
+  }
+
+  try {
+    runTest();
+  } catch (error) {
+    Utils.timeLog(error);
+    Utils.failTest('Failed test.');
+  }
+</script>
+</html>
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 069a4bfe..2c44d3b 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -153,6 +153,8 @@
     "lib/control_message_handler.h",
     "lib/control_message_proxy.cc",
     "lib/control_message_proxy.h",
+    "lib/generated_code_util.cc",
+    "lib/generated_code_util.h",
     "lib/interface_endpoint_client.cc",
     "lib/interface_ptr_state.cc",
     "lib/interface_ptr_state.h",
diff --git a/mojo/public/cpp/bindings/lib/generated_code_util.cc b/mojo/public/cpp/bindings/lib/generated_code_util.cc
new file mode 100644
index 0000000..7851d0f6
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/generated_code_util.cc
@@ -0,0 +1,123 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/lib/generated_code_util.h"
+
+#include <cstring>
+
+#include "mojo/public/cpp/bindings/lib/control_message_handler.h"
+#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+
+GenericValidationInfo FindGenericValidationInfo(
+    uint32_t name,
+    base::span<const std::pair<uint32_t, GenericValidationInfo>> info) {
+  for (const auto& pair : info) {
+    if (pair.first == name)
+      return pair.second;
+  }
+  return {nullptr, nullptr};
+}
+
+GenericValidationInfo FindGenericValidationInfo(
+    uint32_t name,
+    base::span<const GenericValidationInfo> info) {
+  if (name >= info.size())
+    return {nullptr, nullptr};
+  return info[name];
+}
+
+template <typename T>
+bool ValidateRequestGenericT(Message* message,
+                             const char* class_name,
+                             base::span<const T> info) {
+  if (!message->is_serialized() ||
+      ControlMessageHandler::IsControlMessage(message)) {
+    return true;
+  }
+
+  ValidationContext validation_context(message, class_name,
+                                       ValidationContext::kRequestValidator);
+
+  auto entry = FindGenericValidationInfo(message->header()->name, info);
+  if (!entry.request_validator) {
+    ReportValidationError(&validation_context,
+                          VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
+    return false;
+  }
+
+  const bool message_is_request =
+      entry.response_validator ? ValidateMessageIsRequestExpectingResponse(
+                                     message, &validation_context)
+                               : ValidateMessageIsRequestWithoutResponse(
+                                     message, &validation_context);
+  if (!message_is_request)
+    return false;
+
+  return entry.request_validator(message->payload(), &validation_context);
+}
+
+template <typename T>
+bool ValidateResponseGenericT(Message* message,
+                              const char* class_name,
+                              base::span<const T> info) {
+  if (!message->is_serialized() ||
+      ControlMessageHandler::IsControlMessage(message)) {
+    return true;
+  }
+
+  ValidationContext validation_context(message, class_name,
+                                       ValidationContext::kResponseValidator);
+
+  if (!ValidateMessageIsResponse(message, &validation_context))
+    return false;
+
+  auto entry = FindGenericValidationInfo(message->header()->name, info);
+  if (!entry.response_validator) {
+    ReportValidationError(&validation_context,
+                          VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
+    return false;
+  }
+
+  return entry.response_validator(message->payload(), &validation_context);
+}
+
+}  // namespace
+
+bool ValidateRequestGeneric(
+    Message* message,
+    const char* class_name,
+    base::span<const std::pair<uint32_t, GenericValidationInfo>> info) {
+  return ValidateRequestGenericT(message, class_name, info);
+}
+
+bool ValidateRequestGenericPacked(
+    Message* message,
+    const char* class_name,
+    base::span<const GenericValidationInfo> info) {
+  return ValidateRequestGenericT(message, class_name, info);
+}
+
+bool ValidateResponseGeneric(
+    Message* message,
+    const char* class_name,
+    base::span<const std::pair<uint32_t, GenericValidationInfo>> info) {
+  return ValidateResponseGenericT(message, class_name, info);
+}
+
+bool ValidateResponseGenericPacked(
+    Message* message,
+    const char* class_name,
+    base::span<const GenericValidationInfo> info) {
+  return ValidateResponseGenericT(message, class_name, info);
+}
+
+}  // namespace internal
+}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/generated_code_util.h b/mojo/public/cpp/bindings/lib/generated_code_util.h
new file mode 100644
index 0000000..46d68b74
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/generated_code_util.h
@@ -0,0 +1,62 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_GENERATED_CODE_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_GENERATED_CODE_UTIL_H_
+
+#include <utility>
+#include "base/component_export.h"
+#include "base/containers/span.h"
+
+namespace mojo {
+
+class Message;
+
+namespace internal {
+
+class ValidationContext;
+
+struct GenericValidationInfo {
+  // Non-null, unless this corresponds to a non-existent method.
+  bool (*request_validator)(const void* data, ValidationContext*);
+
+  // Non-null, unless this corresponds to a method that does not expect a
+  // response.
+  bool (*response_validator)(const void* data, ValidationContext*);
+};
+
+// Provides a generic implementation of the Mojo IPC request validation,
+// allowing callers to do a compact tail call in generated code.
+COMPONENT_EXPORT(MOJO_CPP_BINDINGS)
+bool ValidateRequestGeneric(
+    Message* message,
+    const char* class_name,
+    base::span<const std::pair<uint32_t, GenericValidationInfo>>);
+
+// As above, but assumes that the ordinals (names) are packed such that a
+// constant-time indexed table access is sufficient.
+COMPONENT_EXPORT(MOJO_CPP_BINDINGS)
+bool ValidateRequestGenericPacked(Message* message,
+                                  const char* class_name,
+                                  base::span<const GenericValidationInfo>);
+
+// Provides a generic implementation of the Mojo IPC response validation,
+// allowing callers to do a compact tail call in generated code.
+COMPONENT_EXPORT(MOJO_CPP_BINDINGS)
+bool ValidateResponseGeneric(
+    Message* message,
+    const char* class_name,
+    base::span<const std::pair<uint32_t, GenericValidationInfo>>);
+
+// As above, but assumes that the ordinals (names) are packed such that a
+// constant-time indexed table access is sufficient.
+COMPONENT_EXPORT(MOJO_CPP_BINDINGS)
+bool ValidateResponseGenericPacked(Message* message,
+                                   const char* class_name,
+                                   base::span<const GenericValidationInfo>);
+
+}  // namespace internal
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_GENERATED_CODE_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/validation_context.cc b/mojo/public/cpp/bindings/lib/validation_context.cc
index d8dc437..2130837 100644
--- a/mojo/public/cpp/bindings/lib/validation_context.cc
+++ b/mojo/public/cpp/bindings/lib/validation_context.cc
@@ -16,9 +16,11 @@
                                      size_t num_associated_endpoint_handles,
                                      Message* message,
                                      const char* description,
-                                     int stack_depth)
+                                     int stack_depth,
+                                     ValidatorType validator_type)
     : message_(message),
       description_(description),
+      validator_type_(validator_type),
       data_begin_(reinterpret_cast<uintptr_t>(data)),
       data_end_(data_begin_ + data_num_bytes),
       handle_begin_(0),
@@ -44,16 +46,33 @@
   }
 }
 
-ValidationContext::ValidationContext(Message* message, const char* description)
+ValidationContext::ValidationContext(Message* message,
+                                     const char* description,
+                                     ValidatorType validator_type)
     : ValidationContext(message->payload(),
                         message->payload_num_bytes(),
                         message->handles()->size(),
                         message->payload_num_interface_ids(),
                         message,
-                        description) {}
+                        description,
+                        0,
+                        validator_type) {}
 
 ValidationContext::~ValidationContext() {
 }
 
+std::string ValidationContext::GetFullDescription() const {
+  std::string full_description(description_);
+  switch (validator_type_) {
+    case kUnspecifiedValidator:
+    case kRequestValidator:
+      break;
+    case kResponseValidator:
+      full_description += " response";
+      break;
+  }
+  return full_description;
+}
+
 }  // namespace internal
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/validation_context.h b/mojo/public/cpp/bindings/lib/validation_context.h
index 9d24b4e..8989b49 100644
--- a/mojo/public/cpp/bindings/lib/validation_context.h
+++ b/mojo/public/cpp/bindings/lib/validation_context.h
@@ -7,6 +7,7 @@
 
 #include <stddef.h>
 #include <stdint.h>
+#include <string>
 
 #include "base/compiler_specific.h"
 #include "base/component_export.h"
@@ -25,6 +26,12 @@
 // indices in the payload of incoming messages.
 class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) ValidationContext {
  public:
+  enum ValidatorType {
+    kUnspecifiedValidator,
+    kRequestValidator,
+    kResponseValidator
+  };
+
   // [data, data + data_num_bytes) specifies the initial valid memory range.
   // [0, num_handles) specifies the initial valid range of handle indices.
   // [0, num_associated_endpoint_handles) specifies the initial valid range of
@@ -40,11 +47,14 @@
                     size_t num_associated_endpoint_handles,
                     Message* message = nullptr,
                     const char* description = "",
-                    int stack_depth = 0);
+                    int stack_depth = 0,
+                    ValidatorType validator_type = kUnspecifiedValidator);
 
   // As above, but infers most of the parameters from the Message payload.
   // Used heavily in generated code and so affects binary size.
-  ValidationContext(Message* message, const char* description);
+  ValidationContext(Message* message,
+                    const char* description,
+                    ValidatorType validator_type);
 
   ~ValidationContext();
 
@@ -139,6 +149,7 @@
 
   Message* message() const { return message_; }
   const char* description() const { return description_; }
+  std::string GetFullDescription() const;
 
  private:
   bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const {
@@ -147,6 +158,7 @@
 
   Message* const message_;
   const char* const description_;
+  const ValidatorType validator_type_;
 
   // [data_begin_, data_end_) is the valid memory range.
   uintptr_t data_begin_;
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.cc b/mojo/public/cpp/bindings/lib/validation_errors.cc
index 8634d483..472e8661 100644
--- a/mojo/public/cpp/bindings/lib/validation_errors.cc
+++ b/mojo/public/cpp/bindings/lib/validation_errors.cc
@@ -77,17 +77,19 @@
                  << " (" << description << ")";
     }
     if (context->message()) {
-      context->message()->NotifyBadMessage(base::StringPrintf(
-          "Validation failed for %s [%s (%s)]", context->description(),
-          ValidationErrorToString(error), description));
+      context->message()->NotifyBadMessage(
+          base::StringPrintf("Validation failed for %s [%s (%s)]",
+                             context->GetFullDescription().c_str(),
+                             ValidationErrorToString(error), description));
     }
   } else {
     if (!g_suppress_logging)
       LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error);
     if (context->message()) {
-      context->message()->NotifyBadMessage(base::StringPrintf(
-          "Validation failed for %s [%s]", context->description(),
-          ValidationErrorToString(error)));
+      context->message()->NotifyBadMessage(
+          base::StringPrintf("Validation failed for %s [%s]",
+                             context->GetFullDescription().c_str(),
+                             ValidationErrorToString(error)));
     }
   }
 }
diff --git a/mojo/public/js/BUILD.gn b/mojo/public/js/BUILD.gn
index 9054cb5..bf728bc 100644
--- a/mojo/public/js/BUILD.gn
+++ b/mojo/public/js/BUILD.gn
@@ -85,6 +85,7 @@
                       "language_out=ECMASCRIPT_2015",
                       "generate_exports",
                       "export_local_property_definitions",
+                      "isolation_mode=IIFE",
                     ]
   }
 } else {
diff --git a/mojo/public/tools/bindings/blink_bindings_configuration.gni b/mojo/public/tools/bindings/blink_bindings_configuration.gni
index 31d10363..0dff1689 100644
--- a/mojo/public/tools/bindings/blink_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/blink_bindings_configuration.gni
@@ -10,6 +10,7 @@
   "//cc/typemaps.gni",
   "//device/gamepad/public/cpp/typemaps.gni",
   "//mojo/public/cpp/bindings/tests/blink_typemaps.gni",
+  "//skia/public/mojom/typemaps.gni",
   "//third_party/blink/renderer/platform/mojo/blink_typemaps.gni",
   "//third_party/blink/public/blink_typemaps.gni",
 ]
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
index e8344eb..f232de6 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -3,7 +3,9 @@
 
 {%- set class_name = interface.name %}
 {%- set proxy_name = interface.name ~ "Proxy" %}
-{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %}
+{%- set namespace_as_string = "%s"|format(module_namespace|replace(".","::")) %}
+{%- set namespace_as_string_with_variant = namespace_as_string ~ ("::" ~ variant if variant) %}
+{%- set qualified_class_name = ("::" ~ namespace_as_string_with_variant if namespace_as_string_with_variant) ~ "::" ~ class_name %}
 
 {%- macro alloc_params(struct, params, message, method_number, is_response) %}
   mojo::internal::SerializationContext serialization_context;
@@ -518,82 +520,58 @@
   return false;
 }
 
+{% if interface.methods and (interface | has_packed_method_ordinals) %}
+static const mojo::internal::GenericValidationInfo k{{class_name}}ValidationInfo[] = {
+{%-   for i in range(interface.methods | map(attribute='ordinal') | max + 1) -%}
+{%-     set method = (interface.methods | selectattr('ordinal', 'equalto', i) | list)[0] %}
+{%-     if method %}
+    {&internal::{{class_name}}_{{method.name}}_Params_Data::Validate,
+{%-       if method.response_parameters != None %}
+     &internal::{{class_name}}_{{method.name}}_ResponseParams_Data::Validate},
+{%-       else %}
+     nullptr /* no response */},
+{%-       endif %}
+{%-     else %}
+    {nullptr, nullptr},  // nonexistent
+{%-     endif %}
+{%-   endfor %}
+};
+{%- elif interface.methods %}
+static const std::pair<uint32_t, mojo::internal::GenericValidationInfo> k{{class_name}}ValidationInfo[] = {
+{%-   for method in interface.methods %}
+    {internal::k{{class_name}}_{{method.name}}_Name,
+     {&internal::{{class_name}}_{{method.name}}_Params_Data::Validate,
+{%-     if method.response_parameters != None %}
+      &internal::{{class_name}}_{{method.name}}_ResponseParams_Data::Validate}},
+{%-     else %}
+      nullptr /* no response */}},
+{%-     endif %}
+{%-   endfor %}
+};
+{%- endif %}
+
 {#--- Request validator definitions #}
 
 bool {{class_name}}RequestValidator::Accept(mojo::Message* message) {
-  if (!message->is_serialized() ||
-      mojo::internal::ControlMessageHandler::IsControlMessage(message)) {
-    return true;
-  }
-
-  mojo::internal::ValidationContext validation_context(
-      message, "{{class_name}} RequestValidator");
-
-  switch (message->header()->name) {
-{%- for method in interface.methods %}
-    case internal::k{{class_name}}_{{method.name}}_Name: {
-{%-   if method.response_parameters != None %}
-      if (!mojo::internal::ValidateMessageIsRequestExpectingResponse(
-              message, &validation_context)) {
-        return false;
-      }
-{%-   else %}
-      if (!mojo::internal::ValidateMessageIsRequestWithoutResponse(
-              message, &validation_context)) {
-        return false;
-      }
-{%-   endif %}
-      if (!mojo::internal::ValidateMessagePayload<
-               internal::{{class_name}}_{{method.name}}_Params_Data>(
-                  message, &validation_context)) {
-        return false;
-      }
-      return true;
-    }
-{%- endfor %}
-    default:
-      break;
-  }
-
-  // Unrecognized message.
-  ReportValidationError(
-      &validation_context,
-      mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
-  return false;
+  {#- Not simply Name_ because there is a mojom interface called MessageReceiver #}
+  const char* name = {{qualified_class_name}}::Name_;
+{%- if not interface.methods %}
+  return mojo::internal::ValidateRequestGeneric(message, name, {});
+{%- elif interface | has_packed_method_ordinals %}
+  return mojo::internal::ValidateRequestGenericPacked(message, name, k{{class_name}}ValidationInfo);
+{%- else %}
+  return mojo::internal::ValidateRequestGeneric(message, name, k{{class_name}}ValidationInfo);
+{%- endif %}
 }
 
 {#--- Response validator definitions #}
 {% if interface|has_callbacks %}
 bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) {
-  if (!message->is_serialized() ||
-      mojo::internal::ControlMessageHandler::IsControlMessage(message)) {
-    return true;
-  }
-
-  mojo::internal::ValidationContext validation_context(
-      message, "{{class_name}} ResponseValidator");
-
-  if (!mojo::internal::ValidateMessageIsResponse(message, &validation_context))
-    return false;
-  switch (message->header()->name) {
-{%- for method in interface.methods if method.response_parameters != None %}
-    case internal::k{{class_name}}_{{method.name}}_Name: {
-      if (!mojo::internal::ValidateMessagePayload<
-               internal::{{class_name}}_{{method.name}}_ResponseParams_Data>(
-                    message, &validation_context)) {
-        return false;
-      }
-      return true;
-    }
-{%- endfor %}
-    default:
-      break;
-  }
-
-  // Unrecognized message.
-  ReportValidationError(
-      &validation_context,
-      mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
-  return false;
+  const char* name = {{qualified_class_name}}::Name_;
+{%-  if interface | has_packed_method_ordinals %}
+  return mojo::internal::ValidateResponseGenericPacked(message, name, k{{class_name}}ValidationInfo);
+{%-  else %}
+  return mojo::internal::ValidateResponseGeneric(message, name, k{{class_name}}ValidationInfo);
+{%   endif %}
 }
 {%- endif -%}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
index 89aed47..6a10b73 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -32,6 +32,7 @@
 #include "base/logging.h"
 #include "base/run_loop.h"
 #include "base/task/common/task_annotator.h"
+#include "mojo/public/cpp/bindings/lib/generated_code_util.h"
 #include "mojo/public/cpp/bindings/lib/message_internal.h"
 #include "mojo/public/cpp/bindings/lib/serialization_util.h"
 #include "mojo/public/cpp/bindings/lib/unserialized_message_context.h"
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
index e9c3e624b..4009887 100644
--- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -177,6 +177,13 @@
            for field in union.fields)
 
 
+def HasPackedMethodOrdinals(interface):
+  """Returns whether all method ordinals are packed such that indexing into a
+  table would be efficient."""
+  max_ordinal = len(interface.methods) * 2
+  return all(method.ordinal < max_ordinal for method in interface.methods)
+
+
 class StructConstructor(object):
   """Represents a constructor for a generated struct.
 
@@ -367,6 +374,7 @@
       "get_pad": pack.GetPad,
       "get_qualified_name_for_kind": self._GetQualifiedNameForKind,
       "has_callbacks": mojom.HasCallbacks,
+      "has_packed_method_ordinals": HasPackedMethodOrdinals,
       "has_sync_methods": mojom.HasSyncMethods,
       "method_supports_lazy_serialization":
           self._MethodSupportsLazySerialization,
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index 35c05bf..9344bdcd 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -940,6 +940,12 @@
         if (enable_kythe_annotations) {
           args += [ "--enable_kythe_annotations" ]
         }
+
+        if (!defined(invoker.scramble_message_ids) ||
+            invoker.scramble_message_ids) {
+          inputs += message_scrambling_inputs
+          args += message_scrambling_args
+        }
       }
     }
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 838971a..016a2ab 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -6211,6 +6211,9 @@
     "//net",
   ]
   dict = "data/fuzzer_dictionaries/net_data_url_fuzzer.dict"
+
+  # IsTokenChar() and ToLowerASCII() are surprisingly slow in instrumented builds.
+  libfuzzer_options = [ "max_len=100000" ]
 }
 
 fuzzer_test("net_mime_sniffer_fuzzer") {
diff --git a/net/base/hex_utils.cc b/net/base/hex_utils.cc
index aec608b..03a3c2a 100644
--- a/net/base/hex_utils.cc
+++ b/net/base/hex_utils.cc
@@ -5,22 +5,11 @@
 #include "net/base/hex_utils.h"
 
 #include <algorithm>
-#include <cstdint>
-#include <vector>
 
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 
 namespace net {
 
-std::string HexDecode(base::StringPiece input) {
-  std::vector<uint8_t> output;
-  std::string result;
-  if (base::HexStringToBytes(input, &output))
-    result.assign(reinterpret_cast<const char*>(output.data()), output.size());
-  return result;
-}
-
 std::string HexDump(base::StringPiece input) {
   const int kBytesPerLine = 16;  // Maximum bytes dumped per line.
   int offset = 0;
diff --git a/net/base/hex_utils.h b/net/base/hex_utils.h
index 3343d6d..b09be9b8 100644
--- a/net/base/hex_utils.h
+++ b/net/base/hex_utils.h
@@ -12,11 +12,6 @@
 
 namespace net {
 
-// Return a std::string of binary data represented by the hex string |input|.
-// For example, HexDecode("48656c6c6f20776f726c6421") == "Hello world!"
-// This is the inverse function of base::HexEncode().
-NET_EXPORT_PRIVATE std::string HexDecode(base::StringPiece input);
-
 // Return a std::string containing hex and ASCII representations of the binary
 // buffer |input|, with offsets at the beginning of each line, in the style of
 // hexdump.  Non-printable characters will be shown as '.' in the ASCII output.
diff --git a/net/base/hex_utils_test.cc b/net/base/hex_utils_test.cc
index bdf27526..a812be1 100644
--- a/net/base/hex_utils_test.cc
+++ b/net/base/hex_utils_test.cc
@@ -9,13 +9,6 @@
 
 namespace test {
 
-TEST(HexUtilsTest, HexDecode) {
-  EXPECT_EQ("", HexDecode(""));
-  EXPECT_EQ("a", HexDecode("61"));
-  // Mixed case input.
-  EXPECT_EQ("Hello world!", HexDecode("48656c6C6F20776f726C6421"));
-}
-
 TEST(HexUtilsTest, HexDump) {
   EXPECT_EQ("", HexDump(""));
   EXPECT_EQ("0x0000:  4865 6c6c 6f20 776f 726c 6421            Hello.world!\n",
@@ -26,11 +19,11 @@
       HexDump("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"));
   // Verify that 0x21 and 0x7e are printable, 0x20 and 0x7f are not.
   EXPECT_EQ("0x0000:  2021 7e7f                                .!~.\n",
-            HexDump(HexDecode("20217e7f")));
+            HexDump("\x20\x21\x7e\x7f"));
   // Verify that values above numeric_limits<unsigned char>::max() are cast
   // properly on platforms where char is unsigned.
   EXPECT_EQ("0x0000:  90aa ff                                  ...\n",
-            HexDump(HexDecode("90aaff")));
+            HexDump("\x90\xaa\xff"));
 }
 
 }  // namespace test
diff --git a/net/cert/ct_log_verifier_unittest.cc b/net/cert/ct_log_verifier_unittest.cc
index dcdc6ced..4bbc080a 100644
--- a/net/cert/ct_log_verifier_unittest.cc
+++ b/net/cert/ct_log_verifier_unittest.cc
@@ -155,10 +155,9 @@
 
 // Decodes a hexadecimal string into the binary data it represents.
 std::string HexToBytes(const std::string& hex_data) {
-  std::vector<uint8_t> output;
   std::string result;
-  if (base::HexStringToBytes(hex_data, &output))
-    result.assign(output.begin(), output.end());
+  if (!base::HexStringToString(hex_data, &result))
+    result.clear();
   return result;
 }
 
diff --git a/net/cert/x509_certificate_unittest.cc b/net/cert/x509_certificate_unittest.cc
index b378df5..f521740c 100644
--- a/net/cert/x509_certificate_unittest.cc
+++ b/net/cert/x509_certificate_unittest.cc
@@ -1337,11 +1337,10 @@
       ASSERT_NE(0U, addr_ascii.length());
       if (addr_ascii[0] == 'x') {  // Hex encoded address
         addr_ascii.erase(0, 1);
-        std::vector<uint8_t> bytes;
-        EXPECT_TRUE(base::HexStringToBytes(addr_ascii, &bytes))
+        std::string bytes;
+        EXPECT_TRUE(base::HexStringToString(addr_ascii, &bytes))
             << "Could not parse hex address " << addr_ascii << " i = " << i;
-        ip_addressses.push_back(
-            std::string(reinterpret_cast<char*>(bytes.data()), bytes.size()));
+        ip_addressses.push_back(std::move(bytes));
         ASSERT_EQ(16U, ip_addressses.back().size()) << i;
       } else {  // Decimal groups
         std::vector<std::string> decimals_ascii = base::SplitString(
diff --git a/net/http2/platform/impl/http2_string_utils_impl.h b/net/http2/platform/impl/http2_string_utils_impl.h
index 3bd8be7..4dd894b 100644
--- a/net/http2/platform/impl/http2_string_utils_impl.h
+++ b/net/http2/platform/impl/http2_string_utils_impl.h
@@ -41,7 +41,10 @@
 }
 
 inline std::string Http2HexDecodeImpl(Http2StringPiece data) {
-  return net::HexDecode(data);
+  std::string result;
+  if (!base::HexStringToString(data, &result))
+    result.clear();
+  return result;
 }
 
 inline std::string Http2HexDumpImpl(Http2StringPiece data) {
diff --git a/net/quic/platform/impl/quic_text_utils_impl.h b/net/quic/platform/impl/quic_text_utils_impl.h
index d9feafc..96b48214 100644
--- a/net/quic/platform/impl/quic_text_utils_impl.h
+++ b/net/quic/platform/impl/quic_text_utils_impl.h
@@ -87,9 +87,12 @@
   }
 
   // Converts |data| from a hexadecimal ASCII string to a binary string
-  // that is |data.length()/2| bytes long.
+  // that is |data.length()/2| bytes long. On failure returns empty string.
   static std::string HexDecode(QuicStringPiece data) {
-    return net::HexDecode(data);
+    std::string result;
+    if (!base::HexStringToString(data, &result))
+      result.clear();
+    return result;
   }
 
   // Base64 encodes with no padding |data_len| bytes of |data| into |output|.
diff --git a/net/quic/quic_chromium_packet_reader.cc b/net/quic/quic_chromium_packet_reader.cc
index e8f5f78..fd661b1b 100644
--- a/net/quic/quic_chromium_packet_reader.cc
+++ b/net/quic/quic_chromium_packet_reader.cc
@@ -125,8 +125,12 @@
     socket_->GetLocalAddress(&local_address_);
     socket_->GetPeerAddress(&peer_address_);
   }
+  auto self = weak_factory_.GetWeakPtr();
+  // Notifies the visitor that |this| reader gets a new packet, which may delete
+  // |this| if |this| is a connectivity probing reader.
   return visitor_->OnPacket(packet, ToQuicSocketAddress(local_address_),
-                            ToQuicSocketAddress(peer_address_));
+                            ToQuicSocketAddress(peer_address_)) &&
+         self;
 }
 
 void QuicChromiumPacketReader::OnReadComplete(int result) {
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 2f78757..a618289 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -4352,6 +4352,177 @@
   EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
 }
 
+// Regression test for https://crbug.com/1014092.
+TEST_P(QuicStreamFactoryTest, MultiplePortMigrationsExceedsMaxLimit) {
+  test_params_.quic_params.allow_port_migration = true;
+  socket_factory_.reset(new TestMigrationSocketFactory);
+  Initialize();
+
+  ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+  crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
+
+  // Using a testing task runner so that we can control time.
+  auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
+
+  int packet_number = 1;
+  MockQuicData quic_data1(version_);
+  quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging Read.
+  if (VersionUsesHttp3(version_.transport_version)) {
+    quic_data1.AddWrite(SYNCHRONOUS,
+                        ConstructInitialSettingsPacket(packet_number++));
+  }
+  quic_data1.AddWrite(
+      SYNCHRONOUS,
+      ConstructGetRequestPacket(packet_number++,
+                                GetNthClientInitiatedBidirectionalStreamId(0),
+                                true, true));
+  quic_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create request and QuicHttpStream.
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(
+      ERR_IO_PENDING,
+      request.Request(
+          host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY,
+          SocketTag(), NetworkIsolationKey(), false /* disable_secure_dns */,
+          /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_,
+          failed_on_default_network_callback_, callback_.callback()));
+  EXPECT_THAT(callback_.WaitForResult(), IsOk());
+  std::unique_ptr<HttpStream> stream = CreateStream(&request);
+  EXPECT_TRUE(stream.get());
+
+  // Cause QUIC stream to be created.
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = url_;
+  request_info.traffic_annotation =
+      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
+                                         net_log_, CompletionOnceCallback()));
+
+  // Ensure that session is alive and active.
+  QuicChromiumClientSession* session = GetActiveSession(host_port_pair_);
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  // Send GET request on stream.
+  HttpResponseInfo response;
+  HttpRequestHeaders request_headers;
+  EXPECT_EQ(OK, stream->SendRequest(request_headers, &response,
+                                    callback_.callback()));
+
+  int server_packet_num = 1;
+  base::TimeDelta next_task_delay;
+  // Perform 4 round of successful migration, and the 5th round will
+  // cancel after successful probing due to hitting the limit.
+  for (int i = 0; i <= 4; i++) {
+    // Set up a different socket data provider that is used for
+    // probing and migration.
+    MockQuicData quic_data2(version_);
+    // Connectivity probe to be sent on the new path.
+    quic_data2.AddWrite(SYNCHRONOUS,
+                        client_maker_.MakeConnectivityProbingPacket(
+                            packet_number, packet_number == 2));
+    packet_number++;
+    quic_data2.AddRead(ASYNC, ERR_IO_PENDING);  // Pause
+    // Connectivity probe to receive from the server.
+    quic_data2.AddRead(ASYNC, server_maker_.MakeConnectivityProbingPacket(
+                                  server_packet_num++, false));
+    // Ping packet to send after migration is completed.
+    if (i == 0) {
+      // First ack and PING are bundled, and version flag is set.
+      quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeAckAndPingPacket(
+                                           packet_number++, false, 1, 1, 1));
+    } else if (i != 4) {
+      // ACK and PING post migration after successful probing.
+      quic_data2.AddWrite(
+          SYNCHRONOUS, client_maker_.MakeAckPacket(packet_number++, 1 + 2 * i,
+                                                   1 + 2 * i, 1, true));
+      quic_data2.AddWrite(SYNCHRONOUS,
+                          client_maker_.MakePingPacket(packet_number++, false));
+    }
+    if (i == 4) {
+      // Add one more synchronous read on the last probing reader. The
+      // reader should be deleted on the read before this one.
+      // The test will verify this read is not consumed.
+      quic_data2.AddRead(SYNCHRONOUS,
+                         server_maker_.MakeConnectivityProbingPacket(
+                             server_packet_num++, false));
+    } else {
+      quic_data2.AddRead(ASYNC, server_maker_.MakeConnectivityProbingPacket(
+                                    server_packet_num++, false));
+    }
+    if (i == 3) {
+      // On the last allowed port migration, read one more packet so
+      // that ACK is sent. The next round of migration (which hists the limit)
+      // will not send any proactive ACK when reading the successful probing
+      // response.
+      quic_data2.AddRead(ASYNC, server_maker_.MakeConnectivityProbingPacket(
+                                    server_packet_num++, false));
+      quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeAckPacket(
+                                           packet_number++, 9, 9, 1, true));
+    }
+    quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // EOF.
+    quic_data2.AddSocketDataToFactory(socket_factory_.get());
+
+    // Cause the connection to report path degrading to the session.
+    // Session will start to probe a different port.
+    session->connection()->OnPathDegradingTimeout();
+
+    // Next connectivity probe is scheduled to be sent in 2 *
+    // kDefaultRTTMilliSecs.
+    EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
+    next_task_delay = task_runner->NextPendingTaskDelay();
+    EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs),
+              next_task_delay);
+
+    // The connection should still be alive, and not marked as going away.
+    EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+    EXPECT_TRUE(HasActiveSession(host_port_pair_));
+    EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+    // Resume quic data and a connectivity probe response will be read on the
+    // new socket.
+    quic_data2.Resume();
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+    EXPECT_TRUE(HasActiveSession(host_port_pair_));
+    EXPECT_EQ(1u, session->GetNumActiveStreams());
+
+    if (i < 4) {
+      // There should be pending tasks, the nearest one will complete
+      // migration to the new port.
+      EXPECT_EQ(2u, task_runner->GetPendingTaskCount());
+      next_task_delay = task_runner->NextPendingTaskDelay();
+      EXPECT_EQ(base::TimeDelta(), next_task_delay);
+    } else {
+      // Last attempt to migrate will abort due to hitting the limit of max
+      // number of allowed migrations.
+      EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
+      next_task_delay = task_runner->NextPendingTaskDelay();
+      EXPECT_NE(base::TimeDelta(), next_task_delay);
+    }
+    task_runner->FastForwardBy(next_task_delay);
+    EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
+    // The last round of migration will abort upon reading the probing response.
+    // Future reads in the same socket is ignored.
+    EXPECT_EQ(i != 4, quic_data2.AllReadDataConsumed());
+  }
+
+  EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
+
+  // Verify that the session is still alive.
+  EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+  EXPECT_TRUE(HasActiveSession(host_port_pair_));
+
+  stream.reset();
+  EXPECT_TRUE(quic_data1.AllReadDataConsumed());
+  EXPECT_TRUE(quic_data1.AllWriteDataConsumed());
+}
+
 // This test verifies that the session marks itself GOAWAY on path degrading
 // and it does not receive any new request
 TEST_P(QuicStreamFactoryTest, GoawayOnPathDegrading) {
diff --git a/net/socket/socket_bio_adapter.cc b/net/socket/socket_bio_adapter.cc
index 6f134af6..182b9d6 100644
--- a/net/socket/socket_bio_adapter.cc
+++ b/net/socket/socket_bio_adapter.cc
@@ -22,7 +22,7 @@
 
 namespace {
 
-net::NetworkTrafficAnnotationTag kTrafficAnnotation =
+const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
     net::DefineNetworkTrafficAnnotation("socket_bio_adapter", R"(
       semantics {
         sender: "Socket BIO Adapter"
diff --git a/net/spdy/platform/impl/spdy_string_utils_impl.h b/net/spdy/platform/impl/spdy_string_utils_impl.h
index f9cbf6f6..a621809 100644
--- a/net/spdy/platform/impl/spdy_string_utils_impl.h
+++ b/net/spdy/platform/impl/spdy_string_utils_impl.h
@@ -35,7 +35,10 @@
 }
 
 inline std::string SpdyHexDecodeImpl(SpdyStringPiece data) {
-  return net::HexDecode(data);
+  std::string result;
+  if (!base::HexStringToString(data, &result))
+    result.clear();
+  return result;
 }
 
 NET_EXPORT_PRIVATE bool SpdyHexDecodeToUInt32Impl(SpdyStringPiece data,
diff --git a/net/test/ct_test_util.cc b/net/test/ct_test_util.cc
index 24b3f3a..d01b59ca1 100644
--- a/net/test/ct_test_util.cc
+++ b/net/test/ct_test_util.cc
@@ -164,6 +164,13 @@
     "d3";
 size_t kSampleSTHTreeSize = 21u;
 
+std::string HexDecode(base::StringPiece input) {
+  std::string result;
+  if (!base::HexStringToString(input, &result))
+    result.clear();
+  return result;
+}
+
 }  // namespace
 
 void GetX509CertSignedEntry(SignedEntryData* entry) {
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 4cd5630..0add3e8a 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -642,8 +642,11 @@
   maybe_stored_cookies_.clear();
 
   GURL referrer_url(referrer_);
+  bool same_origin_for_metrics;
+
   if (referrer_url != URLRequestJob::ComputeReferrerForPolicy(
-                          referrer_policy_, referrer_url, initiator_, url())) {
+                          referrer_policy_, referrer_url, initiator_, url(),
+                          &same_origin_for_metrics)) {
     if (!network_delegate_ ||
         !network_delegate_->CancelURLRequestWithPolicyViolatingReferrerHeader(
             *this, url(), referrer_url)) {
@@ -660,6 +663,8 @@
     }
   }
 
+  RecordReferrerGranularityMetrics(same_origin_for_metrics);
+
   // Start() always completes asynchronously.
   //
   // Status is generally set by URLRequestJob itself, but Start() calls
@@ -1102,6 +1107,31 @@
   delegate_event_type_ = NetLogEventType::FAILED;
 }
 
+void URLRequest::RecordReferrerGranularityMetrics(
+    bool request_is_same_origin) const {
+  GURL referrer_url(referrer_);
+  bool referrer_more_descriptive_than_its_origin =
+      referrer_url.is_valid() && referrer_url.PathForRequestPiece().size() > 1;
+
+  // To avoid renaming the existing enum, we have to use the three-argument
+  // histogram macro.
+  if (request_is_same_origin) {
+    UMA_HISTOGRAM_ENUMERATION(
+        "Net.URLRequest.ReferrerPolicyForRequest.SameOrigin", referrer_policy_,
+        MAX_REFERRER_POLICY + 1);
+    UMA_HISTOGRAM_BOOLEAN(
+        "Net.URLRequest.ReferrerHasInformativePath.SameOrigin",
+        referrer_more_descriptive_than_its_origin);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION(
+        "Net.URLRequest.ReferrerPolicyForRequest.CrossOrigin", referrer_policy_,
+        MAX_REFERRER_POLICY + 1);
+    UMA_HISTOGRAM_BOOLEAN(
+        "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin",
+        referrer_more_descriptive_than_its_origin);
+  }
+}
+
 void URLRequest::GetConnectionAttempts(ConnectionAttempts* out) const {
   if (job_)
     job_->GetConnectionAttempts(out);
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 7d1b86c..672a5f93 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -93,32 +93,37 @@
   // of the initial leg of the request; the caller is responsible for
   // setting the initial Referer, and the ReferrerPolicy only controls
   // what happens to the Referer while following redirects.
+  //
+  // NOTE: This enum is persisted to histograms. Do not change or reorder
+  // values.
+  // TODO(~M82): Once the Net.URLRequest.ReferrerPolicyForRequest
+  // metric is retired, remove this notice.
   enum ReferrerPolicy {
     // Clear the referrer header if the header value is HTTPS but the request
     // destination is HTTP. This is the default behavior of URLRequest.
-    CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+    CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE = 0,
     // A slight variant on CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
     // If the request destination is HTTP, an HTTPS referrer will be cleared. If
     // the request's destination is cross-origin with the referrer (but does not
     // downgrade), the referrer's granularity will be stripped down to an origin
     // rather than a full URL. Same-origin requests will send the full referrer.
-    REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
+    REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN = 1,
     // Strip the referrer down to an origin when the origin of the referrer is
     // different from the destination's origin.
-    ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN,
+    ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN = 2,
     // Never change the referrer.
-    NEVER_CLEAR_REFERRER,
+    NEVER_CLEAR_REFERRER = 3,
     // Strip the referrer down to the origin regardless of the redirect
     // location.
-    ORIGIN,
+    ORIGIN = 4,
     // Clear the referrer when the request's referrer is cross-origin with
     // the request's destination.
-    CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN,
+    CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN = 5,
     // Strip the referrer down to the origin, but clear it entirely if the
     // referrer value is HTTPS and the destination is HTTP.
-    ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
+    ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE = 6,
     // Always clear the referrer regardless of the request destination.
-    NO_REFERRER,
+    NO_REFERRER = 7,
     MAX_REFERRER_POLICY = NO_REFERRER
   };
 
@@ -819,6 +824,12 @@
   // cancellation.
   void OnCallToDelegateComplete();
 
+  // Records the referrer policy of the given request, bucketed by
+  // whether the request is same-origin or not. To save computation,
+  // takes this fact as a boolean parameter rather than dynamically
+  // checking.
+  void RecordReferrerGranularityMetrics(bool request_is_same_origin) const;
+
   // Contextual information used for this request. Cannot be NULL. This contains
   // most of the dependencies which are shared between requests (disk cache,
   // cookie store, socket pool, etc.)
diff --git a/net/url_request/url_request_data_job.cc b/net/url_request/url_request_data_job.cc
deleted file mode 100644
index b4b67aa..0000000
--- a/net/url_request/url_request_data_job.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Simple implementation of a data: protocol handler.
-
-#include "net/url_request/url_request_data_job.h"
-
-#include "net/base/data_url.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_response_headers.h"
-#include "url/gurl.h"
-
-namespace net {
-
-int URLRequestDataJob::BuildResponse(const GURL& url,
-                                     base::StringPiece method,
-                                     std::string* mime_type,
-                                     std::string* charset,
-                                     std::string* data,
-                                     HttpResponseHeaders* headers) {
-  return DataURL::BuildResponse(url, method, mime_type, charset, data, headers);
-}
-
-URLRequestDataJob::URLRequestDataJob(
-    URLRequest* request, NetworkDelegate* network_delegate)
-    : URLRequestSimpleJob(request, network_delegate) {
-}
-
-int URLRequestDataJob::GetData(std::string* mime_type,
-                               std::string* charset,
-                               std::string* data,
-                               CompletionOnceCallback callback) const {
-  // Check if data URL is valid. If not, don't bother to try to extract data.
-  // Otherwise, parse the data from the data URL.
-  const GURL& url = request_->url();
-  if (!url.is_valid())
-    return ERR_INVALID_URL;
-
-  // TODO(tyoshino): Get the headers and export via
-  // URLRequestJob::GetResponseInfo().
-  return BuildResponse(url, request_->method(), mime_type, charset, data,
-                       nullptr);
-}
-
-URLRequestDataJob::~URLRequestDataJob() = default;
-
-}  // namespace net
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index e3d59c2..0246372 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -278,7 +278,8 @@
     URLRequest::ReferrerPolicy policy,
     const GURL& original_referrer,
     const base::Optional<url::Origin>& initiator,
-    const GURL& destination) {
+    const GURL& destination,
+    bool* same_origin_out_for_metrics) {
   bool secure_referrer_but_insecure_destination =
       original_referrer.SchemeIsCryptographic() &&
       !destination.SchemeIsCryptographic();
@@ -286,6 +287,8 @@
   url::Origin initiator_origin = initiator.value_or(referrer_origin);
   bool same_origin =
       initiator_origin.IsSameOriginWith(url::Origin::Create(destination));
+  if (same_origin_out_for_metrics)
+    *same_origin_out_for_metrics = same_origin;
   switch (policy) {
     case URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE:
       return secure_referrer_but_insecure_destination ? GURL()
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index 56d32806..3d372b3 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -246,11 +246,18 @@
   // returns the referrer URL mandated by |request|'s referrer policy. If the
   // initiator does not have a value, which is the case for many
   // browser-initiated requests, we fallback to using the origin of |referrer|.
+  //
+  // If |same_origin_out_for_metrics| is non-null, saves to
+  // |*same_origin_out_for_metrics| whether |initiator| and |destination| are
+  // cross-origin.
+  // (This allows reporting in a UMA whether the request is same-origin, without
+  // recomputing that information.)
   static GURL ComputeReferrerForPolicy(
       URLRequest::ReferrerPolicy policy,
       const GURL& original_referrer,
       const base::Optional<url::Origin>& initiator,
-      const GURL& destination);
+      const GURL& destination,
+      bool* same_origin_out_for_metrics = nullptr);
 
  protected:
   // Notifies the job that a certificate is requested.
diff --git a/net/url_request/url_request_job_unittest.cc b/net/url_request/url_request_job_unittest.cc
index 66dbc2df..055d4ec 100644
--- a/net/url_request/url_request_job_unittest.cc
+++ b/net/url_request/url_request_job_unittest.cc
@@ -635,4 +635,28 @@
   RemoveMockTransaction(&kBrotliSlowTransaction);
 }
 
+TEST(URLRequestJobComputeReferrer, SetsSameOriginForMetricsOnSameOrigin) {
+  bool same_origin = false;
+  URLRequestJob::ComputeReferrerForPolicy(
+      URLRequest::ReferrerPolicy(), /*original_referrer=*/GURL(),
+      url::Origin::Create(GURL("http://google.com")),
+      /*destination=*/GURL("http://google.com"), &same_origin);
+  EXPECT_TRUE(same_origin);
+}
+
+TEST(URLRequestJobComputeReferrer, SetsSameOriginForMetricsOnCrossOrigin) {
+  bool same_origin = true;
+  URLRequestJob::ComputeReferrerForPolicy(
+      URLRequest::ReferrerPolicy(), /*original_referrer=*/GURL(),
+      url::Origin::Create(GURL("http://google.com")),
+      /*destination=*/GURL("http://boggle.com"), &same_origin);
+  EXPECT_FALSE(same_origin);
+}
+
+TEST(URLRequestJobComputeReferrer, AcceptsNullptrInput) {
+  // Shouldn't segfault.
+  URLRequestJob::ComputeReferrerForPolicy(URLRequest::ReferrerPolicy(), GURL(),
+                                          base::nullopt, GURL(), nullptr);
+}
+
 }  // namespace net
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index f6cf52d..5df08d1 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -800,6 +800,178 @@
   EXPECT_TRUE(d.request_failed());
 }
 
+TEST_F(URLRequestTest, RecordsSameOriginReferrerHistogram) {
+  TestURLRequestContext context;
+  TestNetworkDelegate network_delegate;
+  network_delegate.set_cancel_request_with_policy_violating_referrer(false);
+  context.set_network_delegate(&network_delegate);
+  TestDelegate d;
+  std::unique_ptr<URLRequest> req(
+      context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
+                            TRAFFIC_ANNOTATION_FOR_TESTS));
+  req->set_initiator(url::Origin::Create(GURL("http://google.com")));
+  req->set_referrer_policy(URLRequest::NEVER_CLEAR_REFERRER);
+
+  base::HistogramTester histograms;
+
+  req->Start();
+  d.RunUntilComplete();
+  histograms.ExpectUniqueSample(
+      "Net.URLRequest.ReferrerPolicyForRequest.SameOrigin",
+      static_cast<int>(URLRequest::NEVER_CLEAR_REFERRER), 1);
+}
+
+TEST_F(URLRequestTest, RecordsCrossOriginReferrerHistogram) {
+  TestURLRequestContext context;
+  TestNetworkDelegate network_delegate;
+  context.set_network_delegate(&network_delegate);
+  TestDelegate d;
+  std::unique_ptr<URLRequest> req(
+      context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
+                            TRAFFIC_ANNOTATION_FOR_TESTS));
+  req->set_initiator(url::Origin::Create(GURL("http://origin.com")));
+
+  // Set a different policy just to make sure we aren't always logging the same
+  // policy.
+  req->set_referrer_policy(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
+
+  base::HistogramTester histograms;
+
+  req->Start();
+  d.RunUntilComplete();
+  histograms.ExpectUniqueSample(
+      "Net.URLRequest.ReferrerPolicyForRequest.CrossOrigin",
+      static_cast<int>(
+          URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE),
+      1);
+}
+
+TEST_F(URLRequestTest, RecordsReferrerHistogramAgainOnRedirect) {
+  TestURLRequestContext context;
+  BlockingNetworkDelegate network_delegate(
+      BlockingNetworkDelegate::SYNCHRONOUS);
+  network_delegate.set_redirect_url(GURL("http://redirect.com/"));
+  context.set_network_delegate(&network_delegate);
+  TestDelegate d;
+  std::unique_ptr<URLRequest> req(
+      context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
+                            TRAFFIC_ANNOTATION_FOR_TESTS));
+  req->set_initiator(url::Origin::Create(GURL("http://google.com")));
+
+  req->set_referrer_policy(
+      URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
+
+  base::HistogramTester histograms;
+
+  req->Start();
+  d.RunUntilRedirect();
+  histograms.ExpectUniqueSample(
+      "Net.URLRequest.ReferrerPolicyForRequest.SameOrigin",
+      static_cast<int>(
+          URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE),
+      1);
+  req->FollowDeferredRedirect(/*removed_headers=*/base::nullopt,
+                              /*modified_headers=*/base::nullopt);
+  d.RunUntilComplete();
+  histograms.ExpectUniqueSample(
+      "Net.URLRequest.ReferrerPolicyForRequest.CrossOrigin",
+      static_cast<int>(
+          URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE),
+      1);
+}
+
+TEST_F(URLRequestTest, RecordsReferrrerWithInformativePath) {
+  TestURLRequestContext context;
+  BlockingNetworkDelegate network_delegate(
+      BlockingNetworkDelegate::SYNCHRONOUS);
+  network_delegate.set_cancel_request_with_policy_violating_referrer(true);
+  context.set_network_delegate(&network_delegate);
+  network_delegate.set_redirect_url(GURL("http://redirect.com/"));
+  TestDelegate d;
+  std::unique_ptr<URLRequest> req(
+      context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
+                            TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  // Since this referrer is much more informative than the initiating origin,
+  // we should see the histograms' true buckets populated.
+  req->SetReferrer("http://google.com/very-informative-path");
+
+  base::HistogramTester histograms;
+
+  req->Start();
+  d.RunUntilRedirect();
+  histograms.ExpectUniqueSample(
+      "Net.URLRequest.ReferrerHasInformativePath.SameOrigin",
+      /* Check the count of the "true" bucket in the boolean histogram. */ true,
+      1);
+  req->FollowDeferredRedirect(/*removed_headers=*/base::nullopt,
+                              /*modified_headers=*/base::nullopt);
+  d.RunUntilComplete();
+  histograms.ExpectUniqueSample(
+      "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", true, 1);
+}
+
+TEST_F(URLRequestTest, RecordsReferrerWithInformativeQuery) {
+  TestURLRequestContext context;
+  BlockingNetworkDelegate network_delegate(
+      BlockingNetworkDelegate::SYNCHRONOUS);
+  network_delegate.set_cancel_request_with_policy_violating_referrer(true);
+  context.set_network_delegate(&network_delegate);
+  network_delegate.set_redirect_url(GURL("http://redirect.com/"));
+  TestDelegate d;
+  std::unique_ptr<URLRequest> req(
+      context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
+                            TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  // Since this referrer is much more informative than the initiating origin,
+  // we should see the histograms' true buckets populated.
+  req->SetReferrer("http://google.com/?very-informative-query");
+
+  base::HistogramTester histograms;
+
+  req->Start();
+  d.RunUntilRedirect();
+  histograms.ExpectUniqueSample(
+      "Net.URLRequest.ReferrerHasInformativePath.SameOrigin",
+      /* Check the count of the "true" bucket in the boolean histogram. */ true,
+      1);
+  req->FollowDeferredRedirect(/*removed_headers=*/base::nullopt,
+                              /*modified_headers=*/base::nullopt);
+  d.RunUntilComplete();
+  histograms.ExpectUniqueSample(
+      "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", true, 1);
+}
+
+TEST_F(URLRequestTest, RecordsReferrerWithoutInformativePathOrQuery) {
+  TestURLRequestContext context;
+  BlockingNetworkDelegate network_delegate(
+      BlockingNetworkDelegate::SYNCHRONOUS);
+  network_delegate.set_cancel_request_with_policy_violating_referrer(false);
+  context.set_network_delegate(&network_delegate);
+  network_delegate.set_redirect_url(GURL("http://origin.com/"));
+  TestDelegate d;
+  std::unique_ptr<URLRequest> req(
+      context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
+                            TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  // Since this referrer _isn't_ more informative than the initiating origin,
+  // we should see the histograms' false buckets populated.
+  req->SetReferrer("http://origin.com");
+
+  base::HistogramTester histograms;
+
+  req->Start();
+  d.RunUntilRedirect();
+  histograms.ExpectUniqueSample(
+      "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", false, 1);
+  req->FollowDeferredRedirect(/*removed_headers=*/base::nullopt,
+                              /*modified_headers=*/base::nullopt);
+  d.RunUntilComplete();
+  histograms.ExpectUniqueSample(
+      "Net.URLRequest.ReferrerHasInformativePath.SameOrigin", false, 1);
+}
+
 // An Interceptor for use with interceptor tests.
 class MockURLRequestInterceptor : public URLRequestInterceptor {
  public:
diff --git a/remoting/tools/mac/chromoting-set-channel.sh b/remoting/tools/mac/chromoting-set-channel.sh
deleted file mode 100755
index ab1e37a9..0000000
--- a/remoting/tools/mac/chromoting-set-channel.sh
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/bin/sh
-
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-set -e -u
-
-ME="$(basename "$0")"
-readonly ME
-
-KSADMIN=/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/ksadmin
-KSPID=com.google.chrome_remote_desktop
-
-usage() {
-  echo "Usage: ${ME} <channel>" >&2
-  echo "where <channel> is 'beta' or 'stable'" >&2
-}
-
-log() {
-  local message="$1"
-  echo "${message}"
-  logger "${message}"
-}
-
-checkroot() {
-  if [[ "$(id -u)" != "0" ]]; then
-     echo "This script requires root permissions" 1>&2
-     exit 1
-  fi
-}
-
-main() {
-  local channel="$1"
-
-  if [[ "${channel}" != "beta" && "${channel}" != "stable" ]]; then
-    usage
-    exit 1
-  fi
-
-  local channeltag="${channel}"
-  if [[ "${channel}" == "stable" ]]; then
-    channeltag=""
-  fi
-
-  log "Switching Chrome Remote Desktop channel to ${channel}"
-
-  $KSADMIN --productid "$KSPID" --tag "${channeltag}"
-
-  if [[ "${channel}" == "stable" ]]; then
-    echo "You're not done yet!"
-    echo "You must now UNINSTALL and RE-INSTALL the latest version of Chrome"
-    echo "Remote Desktop to get your machine back on the stable channel."
-    echo "Thank you!"
-  else
-    echo "Switch to ${channel} channel complete."
-    echo "You will download ${channel} binaries during the next update check."
-  fi
-}
-
-checkroot
-
-if [[ $# < 1 ]]; then
-  usage
-  exit 1
-fi
-
-main "$@"
diff --git a/remoting/tools/win/chromoting-set-channel.bat b/remoting/tools/win/chromoting-set-channel.bat
deleted file mode 100755
index 35c03fa..0000000
--- a/remoting/tools/win/chromoting-set-channel.bat
+++ /dev/null
@@ -1,49 +0,0 @@
-@echo off

-

-REM Copyright (c) 2012 The Chromium Authors. All rights reserved.

-REM Use of this source code is governed by a BSD-style license that can be

-REM found in the LICENSE file.

-

-set CHANNEL=%1

-

-REM Check if we are running as an Administrator.

-REM Based on method described at:

-REM http://stackoverflow.com/questions/4051883/batch-script-how-to-check-for-admin-rights

-net session >nul 2>&1

-if not %errorlevel% equ 0 (

-  echo This script updates the registry and needs to be run as Administrator.

-  echo Right-click "Command Prompt" and select "Run as Administrator" and run

-  echo this script from there.

-  goto :eof

-)

-

-REM Make sure the argument specifies a valid channel.

-if "_%CHANNEL%_"=="_beta_" goto validarg

-if "_%CHANNEL%_"=="_stable_" goto validarg

-goto usage

-

-:validarg

-set SYSTEM32=%SystemRoot%\system32

-if "_%PROCESSOR_ARCHITECTURE%_"=="_AMD64_" set SYSTEM32=%SystemRoot%\syswow64

-

-set REGKEY="HKLM\SOFTWARE\Google\Update\ClientState\{B210701E-FFC4-49E3-932B-370728C72662}"

-set VALUENAME=ap

-

-if "_%CHANNEL%_"=="_stable_" (

-  %SYSTEM32%\reg.exe delete %REGKEY% /v %VALUENAME% /f

-  echo ********************

-  echo You're not done yet!

-  echo ********************

-  echo You must now UNINSTALL and RE-INSTALL the latest version of Chrome

-  echo Remote Desktop to get your machine back on the stable channel.

-  echo Thank you!

-) else (

-  %SYSTEM32%\reg.exe add %REGKEY% /v %VALUENAME% /d %CHANNEL% /f

-  echo Switch to %CHANNEL% channel complete.

-  echo You will automatically get %CHANNEL% binaries during the next update.

-)

-goto :eof

-

-:usage

-echo Usage: %0 ^<channel^>

-echo where ^<channel^> is 'beta' or 'stable'.

diff --git a/services/data_decoder/data_decoder_service.cc b/services/data_decoder/data_decoder_service.cc
index 375c074..b365a374 100644
--- a/services/data_decoder/data_decoder_service.cc
+++ b/services/data_decoder/data_decoder_service.cc
@@ -32,14 +32,14 @@
 
 DataDecoderService::DataDecoderService(
     mojo::PendingReceiver<mojom::DataDecoderService> receiver) {
-  receiver_.Bind(std::move(receiver));
+  receivers_.Add(this, std::move(receiver));
 }
 
 DataDecoderService::~DataDecoderService() = default;
 
 void DataDecoderService::BindReceiver(
     mojo::PendingReceiver<mojom::DataDecoderService> receiver) {
-  receiver_.Bind(std::move(receiver));
+  receivers_.Add(this, std::move(receiver));
 }
 
 void DataDecoderService::BindImageDecoder(
diff --git a/services/data_decoder/data_decoder_service.h b/services/data_decoder/data_decoder_service.h
index 884bb7ebb..36fc4b55 100644
--- a/services/data_decoder/data_decoder_service.h
+++ b/services/data_decoder/data_decoder_service.h
@@ -9,7 +9,7 @@
 
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "services/data_decoder/public/mojom/bundled_exchanges_parser.mojom.h"
 #include "services/data_decoder/public/mojom/data_decoder_service.mojom.h"
 #include "services/data_decoder/public/mojom/image_decoder.mojom.h"
@@ -61,7 +61,9 @@
       mojo::PendingReceiver<mojom::BleScanParser> receiver) override;
 #endif  // OS_CHROMEOS
 
-  mojo::Receiver<mojom::DataDecoderService> receiver_{this};
+  // In-process instances (e.g. on iOS or in tests) may have multiple concurrent
+  // remote DataDecoderService clients.
+  mojo::ReceiverSet<mojom::DataDecoderService> receivers_;
 
   bool drop_image_decoders_ = false;
   bool drop_json_parsers_ = false;
diff --git a/services/preferences/tracked/pref_hash_calculator.cc b/services/preferences/tracked/pref_hash_calculator.cc
index fe62ab8..0a6cdb7 100644
--- a/services/preferences/tracked/pref_hash_calculator.cc
+++ b/services/preferences/tracked/pref_hash_calculator.cc
@@ -37,11 +37,9 @@
                         const std::string& message,
                         const std::string& digest_string) {
   crypto::HMAC hmac(crypto::HMAC::SHA256);
-  std::vector<uint8_t> digest;
-  return base::HexStringToBytes(digest_string, &digest) && hmac.Init(key) &&
-         hmac.Verify(message,
-                     base::StringPiece(reinterpret_cast<char*>(&digest[0]),
-                                       digest.size()));
+  std::string digest;
+  return base::HexStringToString(digest_string, &digest) && hmac.Init(key) &&
+         hmac.Verify(message, digest);
 }
 
 // Renders |value| as a string. |value| may be NULL, in which case the result
diff --git a/services/tracing/BUILD.gn b/services/tracing/BUILD.gn
index 389a7cb..72ad733 100644
--- a/services/tracing/BUILD.gn
+++ b/services/tracing/BUILD.gn
@@ -16,14 +16,10 @@
   sources = [
     "perfetto/consumer_host.cc",
     "perfetto/consumer_host.h",
-    "perfetto/json_trace_exporter.cc",
-    "perfetto/json_trace_exporter.h",
     "perfetto/perfetto_service.cc",
     "perfetto/perfetto_service.h",
     "perfetto/producer_host.cc",
     "perfetto/producer_host.h",
-    "perfetto/track_event_json_exporter.cc",
-    "perfetto/track_event_json_exporter.h",
     "tracing_service.cc",
     "tracing_service.h",
   ]
@@ -40,31 +36,40 @@
   ]
 
   deps = [
-    "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
+    "//third_party/perfetto:libproto_to_json",
   ]
 }
 
-executable("trace_json_exporter") {
+source_set("json_trace_exporter") {
   sources = [
-    "perfetto/json_exporter_main.cc",
     "perfetto/json_trace_exporter.cc",
     "perfetto/json_trace_exporter.h",
     "perfetto/track_event_json_exporter.cc",
     "perfetto/track_event_json_exporter.h",
   ]
-
-  configs += [ "//build/config/compiler:rtti" ]
-
   deps = [
     "//base",
     "//third_party/perfetto:libperfetto",
-    "//third_party/perfetto/include/perfetto/protozero:protozero",
     "//third_party/perfetto/protos/perfetto/common:lite",
     "//third_party/perfetto/protos/perfetto/trace:lite",
     "//third_party/perfetto/protos/perfetto/trace/chrome:lite",
     "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
     "//third_party/perfetto/protos/perfetto/trace/interned_data:lite",
     "//third_party/perfetto/protos/perfetto/trace/track_event:lite",
+  ]
+}
+
+executable("trace_json_exporter") {
+  sources = [
+    "perfetto/json_exporter_main.cc",
+  ]
+
+  configs += [ "//build/config/compiler:rtti" ]
+
+  deps = [
+    ":json_trace_exporter",
+    "//base",
+    "//third_party/perfetto:libperfetto",
     "//third_party/perfetto/src/protozero:protozero",
   ]
 }
@@ -145,6 +150,7 @@
   }
 
   deps = [
+    ":json_trace_exporter",
     ":lib",
     ":test_utils",
     "//base",
diff --git a/services/tracing/perfetto/consumer_host.cc b/services/tracing/perfetto/consumer_host.cc
index aa254286..ee60eaab 100644
--- a/services/tracing/perfetto/consumer_host.cc
+++ b/services/tracing/perfetto/consumer_host.cc
@@ -24,13 +24,15 @@
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/wait.h"
-#include "services/tracing/perfetto/json_trace_exporter.h"
 #include "services/tracing/perfetto/perfetto_service.h"
-#include "services/tracing/perfetto/track_event_json_exporter.h"
 #include "services/tracing/public/cpp/trace_event_args_whitelist.h"
+#include "third_party/perfetto/include/perfetto/ext/trace_processor/export_json.h"
 #include "third_party/perfetto/include/perfetto/ext/tracing/core/observable_events.h"
+#include "third_party/perfetto/include/perfetto/ext/tracing/core/slice.h"
 #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
 #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_stats.h"
+#include "third_party/perfetto/include/perfetto/trace_processor/basic_types.h"
+#include "third_party/perfetto/include/perfetto/trace_processor/trace_processor_storage.h"
 #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
 #include "third_party/perfetto/protos/perfetto/config/trace_config.pb.h"
 
@@ -40,6 +42,44 @@
 
 const int32_t kEnableTracingTimeoutSeconds = 10;
 
+class JsonStringOutputWriter
+    : public perfetto::trace_processor::json::OutputWriter {
+ public:
+  using FlushCallback =
+      base::RepeatingCallback<void(std::string json, bool has_more)>;
+
+  JsonStringOutputWriter(FlushCallback flush_callback)
+      : flush_callback_(std::move(flush_callback)) {
+    buffer_.reserve(kBufferReserveCapacity);
+  }
+
+  ~JsonStringOutputWriter() override {
+    flush_callback_.Run(std::move(buffer_), false);
+  }
+
+  perfetto::trace_processor::util::Status AppendString(
+      const std::string& string) override {
+    buffer_ += string;
+    if (buffer_.size() > kBufferLimitInBytes) {
+      flush_callback_.Run(std::move(buffer_), true);
+      // Reset the buffer_ after moving it above.
+      buffer_.clear();
+      buffer_.reserve(kBufferReserveCapacity);
+    }
+    return perfetto::trace_processor::util::OkStatus();
+  }
+
+ private:
+  static constexpr size_t kBufferLimitInBytes = 100 * 1024;
+  // Since we write each string before checking the limit, we'll always go
+  // slightly over and hence we reserve some extra space to avoid most
+  // reallocs.
+  static constexpr size_t kBufferReserveCapacity = kBufferLimitInBytes * 5 / 4;
+
+  FlushCallback flush_callback_;
+  std::string buffer_;
+};
+
 }  // namespace
 
 class ConsumerHost::StreamWriter {
@@ -291,7 +331,7 @@
 
   tracing_session_client_->OnTracingDisabled();
 
-  if (json_trace_exporter_) {
+  if (trace_processor_) {
     host_->consumer_endpoint()->ReadBuffers();
   }
 
@@ -343,6 +383,24 @@
                      weak_factory_.GetWeakPtr()),
       base::SequencedTaskRunnerHandle::Get());
 
+  if (privacy_filtering_enabled) {
+    // For filtering/whitelisting to be possible at JSON export time,
+    // filtering must not have been enabled during proto emission time
+    // (or there's nothing to pass through the whitelist).
+    DCHECK(!privacy_filtering_enabled_);
+    privacy_filtering_enabled_ = true;
+  }
+
+  json_agent_label_filter_ = agent_label_filter;
+
+  trace_processor_ =
+      perfetto::trace_processor::TraceProcessorStorage::CreateInstance(
+          perfetto::trace_processor::Config());
+
+  DisableTracing();
+}
+
+void ConsumerHost::TracingSession::ExportJson() {
   // In legacy backend, the trace event agent sets the predicate used by
   // TraceLog. For perfetto backend, ensure that predicate is always set
   // before creating the exporter. The agent can be created later than this
@@ -356,34 +414,55 @@
         base::BindRepeating(&IsMetadataWhitelisted));
   }
 
-  JSONTraceExporter::ArgumentFilterPredicate arg_filter_predicate;
-  JSONTraceExporter::MetadataFilterPredicate metadata_filter_predicate;
-  if (privacy_filtering_enabled) {
-    // For filtering/whitelisting to be possible at JSON export time,
-    // filtering must not have been enabled during proto emission time
-    // (or there's nothing to pass through the whitelist).
-    DCHECK(!privacy_filtering_enabled_);
+  perfetto::trace_processor::json::ArgumentFilterPredicate argument_filter;
+  perfetto::trace_processor::json::MetadataFilterPredicate metadata_filter;
+  perfetto::trace_processor::json::LabelFilterPredicate label_filter;
+
+  if (privacy_filtering_enabled_) {
     auto* trace_log = base::trace_event::TraceLog::GetInstance();
-    arg_filter_predicate = trace_log->GetArgumentFilterPredicate();
-    metadata_filter_predicate = trace_log->GetMetadataFilterPredicate();
+    base::trace_event::ArgumentFilterPredicate argument_filter_predicate =
+        trace_log->GetArgumentFilterPredicate();
+    argument_filter =
+        [argument_filter_predicate](
+            const char* category_group_name, const char* event_name,
+            perfetto::trace_processor::json::ArgumentNameFilterPredicate*
+                name_filter) {
+          base::trace_event::ArgumentNameFilterPredicate name_filter_predicate;
+          bool result = argument_filter_predicate.Run(
+              category_group_name, event_name, &name_filter_predicate);
+          if (name_filter_predicate) {
+            *name_filter = [name_filter_predicate](const char* arg_name) {
+              return name_filter_predicate.Run(arg_name);
+            };
+          }
+          return result;
+        };
+    base::trace_event::MetadataFilterPredicate metadata_filter_predicate =
+        trace_log->GetMetadataFilterPredicate();
+    metadata_filter = [metadata_filter_predicate](const char* metadata_name) {
+      return metadata_filter_predicate.Run(metadata_name);
+    };
   }
-  json_trace_exporter_ = std::make_unique<TrackEventJSONExporter>(
-      std::move(arg_filter_predicate), std::move(metadata_filter_predicate),
-      base::BindRepeating(&ConsumerHost::TracingSession::OnJSONTraceData,
-                          base::Unretained(this)));
 
-  json_trace_exporter_->set_label_filter(agent_label_filter);
+  if (!json_agent_label_filter_.empty()) {
+    label_filter = [this](const char* label) {
+      return strcmp(label, json_agent_label_filter_.c_str()) == 0;
+    };
+  }
 
-  DisableTracing();
+  JsonStringOutputWriter output_writer(base::BindRepeating(
+      &ConsumerHost::TracingSession::OnJSONTraceData, base::Unretained(this)));
+  auto status = perfetto::trace_processor::json::ExportJson(
+      trace_processor_.get(), &output_writer, argument_filter, metadata_filter,
+      label_filter);
+  DCHECK(status.ok()) << status.message();
 }
 
-void ConsumerHost::TracingSession::OnJSONTraceData(
-    std::string* json,
-    base::DictionaryValue* metadata,
-    bool has_more) {
+void ConsumerHost::TracingSession::OnJSONTraceData(std::string json,
+                                                   bool has_more) {
   auto slices = std::make_unique<StreamWriter::Slices>();
   slices->push_back(std::string());
-  slices->back().swap(*json);
+  slices->back().swap(json);
   read_buffers_stream_writer_.Post(FROM_HERE, &StreamWriter::WriteToStream,
                                    std::move(slices), has_more);
 
@@ -396,10 +475,40 @@
     std::vector<perfetto::TracePacket> packets,
     bool has_more) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (json_trace_exporter_) {
-    json_trace_exporter_->OnTraceData(std::move(packets), has_more);
+  if (trace_processor_) {
+    // Calculate space needed for trace chunk. Each packet has a preamble and
+    // payload size.
+    size_t max_size = packets.size() * perfetto::TracePacket::kMaxPreambleBytes;
+    for (const auto& packet : packets) {
+      max_size += packet.size();
+    }
+
+    // Copy packets into a trace file chunk.
+    size_t position = 0;
+    std::unique_ptr<uint8_t[]> data(new uint8_t[max_size]);
+    for (perfetto::TracePacket& packet : packets) {
+      char* preamble;
+      size_t preamble_size;
+      std::tie(preamble, preamble_size) = packet.GetProtoPreamble();
+      DCHECK_LT(position + preamble_size, max_size);
+      memcpy(&data[position], preamble, preamble_size);
+      position += preamble_size;
+      for (const perfetto::Slice& slice : packet.slices()) {
+        DCHECK_LT(position + slice.size, max_size);
+        memcpy(&data[position], slice.start, slice.size);
+        position += slice.size;
+      }
+    }
+
+    auto status = trace_processor_->Parse(std::move(data), position);
+    // TODO(eseckler): There's no way to propagate this error at the moment - If
+    // one occurs on production builds, we silently ignore it and will end up
+    // producing an empty JSON result.
+    DCHECK(status.ok()) << status.message();
     if (!has_more) {
-      json_trace_exporter_.reset();
+      trace_processor_->NotifyEndOfFile();
+      ExportJson();
+      trace_processor_.reset();
     }
     return;
   }
diff --git a/services/tracing/perfetto/consumer_host.h b/services/tracing/perfetto/consumer_host.h
index 63a400ca..64031683 100644
--- a/services/tracing/perfetto/consumer_host.h
+++ b/services/tracing/perfetto/consumer_host.h
@@ -23,17 +23,18 @@
 #include "third_party/perfetto/include/perfetto/ext/tracing/core/consumer.h"
 #include "third_party/perfetto/include/perfetto/ext/tracing/core/tracing_service.h"
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace service_manager {
 struct BindSourceInfo;
 }  // namespace service_manager
 
+namespace perfetto {
+namespace trace_processor {
+class TraceProcessorStorage;
+}  // namespace trace_processor
+}  // namespace perfetto
+
 namespace tracing {
 
-class JSONTraceExporter;
 class PerfettoService;
 
 // This is a Mojo interface which enables any client
@@ -89,9 +90,8 @@
         DisableTracingAndEmitJsonCallback callback) override;
 
    private:
-    void OnJSONTraceData(std::string* json,
-                         base::DictionaryValue* metadata,
-                         bool has_more);
+    void ExportJson();
+    void OnJSONTraceData(std::string json, bool has_more);
     void OnEnableTracingTimeout();
     void MaybeSendEnableTracingAck();
     bool IsExpectedPid(base::ProcessId pid) const;
@@ -102,7 +102,9 @@
     bool privacy_filtering_enabled_ = false;
     base::SequenceBound<StreamWriter> read_buffers_stream_writer_;
     RequestBufferUsageCallback request_buffer_usage_callback_;
-    std::unique_ptr<JSONTraceExporter> json_trace_exporter_;
+    std::unique_ptr<perfetto::trace_processor::TraceProcessorStorage>
+        trace_processor_;
+    std::string json_agent_label_filter_;
     base::OnceCallback<void(bool)> flush_callback_;
     const mojom::TracingClientPriority tracing_priority_;
     base::OnceClosure on_disabled_callback_;
diff --git a/services/tracing/public/cpp/perfetto/java_heap_profiler/hprof_buffer_android_unittest.cc b/services/tracing/public/cpp/perfetto/java_heap_profiler/hprof_buffer_android_unittest.cc
index ac969be4..133fc099 100644
--- a/services/tracing/public/cpp/perfetto/java_heap_profiler/hprof_buffer_android_unittest.cc
+++ b/services/tracing/public/cpp/perfetto/java_heap_profiler/hprof_buffer_android_unittest.cc
@@ -13,30 +13,30 @@
 TEST(HprofBufferTest, VerifyBasicGetBytes) {
   unsigned char file_data[7]{1, 1, 1, 1, 1, 1, 1};
   HprofBuffer hprof(file_data, 7);
-  EXPECT_EQ(hprof.GetOneByte(), (uint32_t)1);
-  EXPECT_EQ(hprof.GetTwoBytes(), (uint32_t)257);
-  EXPECT_EQ(hprof.GetFourBytes(), (uint32_t)16843009);
+  EXPECT_EQ(hprof.GetOneByte(), 1u);
+  EXPECT_EQ(hprof.GetTwoBytes(), 257u);
+  EXPECT_EQ(hprof.GetFourBytes(), 16843009u);
   EXPECT_EQ(hprof.HasRemaining(), false);
 }
 
 TEST(HprofBufferTest, VerifyBasicGetId) {
   unsigned char file_data[12]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
   HprofBuffer hprof(file_data, 12);
-  EXPECT_EQ(hprof.GetId(), (uint64_t)16843009);
+  EXPECT_EQ(hprof.GetId(), 16843009u);
   hprof.set_id_size(8);
-  EXPECT_EQ(hprof.GetId(), (uint64_t)72340172838076673);
+  EXPECT_EQ(hprof.GetId(), 72340172838076673u);
   EXPECT_EQ(hprof.HasRemaining(), false);
 }
 
 TEST(HprofBufferTest, VerifyBasicPositionalMethods) {
   unsigned char file_data[4]{1, 2, 3, 4};
   HprofBuffer hprof(file_data, 4);
-  EXPECT_EQ(hprof.GetOneByte(), (uint32_t)1);
+  EXPECT_EQ(hprof.GetOneByte(), 1u);
   hprof.Skip(2);
-  EXPECT_EQ(hprof.GetOneByte(), (uint32_t)4);
+  EXPECT_EQ(hprof.GetOneByte(), 4u);
   EXPECT_EQ(hprof.HasRemaining(), false);
 
   hprof.set_position(1);
-  EXPECT_EQ(hprof.GetOneByte(), (uint32_t)2);
+  EXPECT_EQ(hprof.GetOneByte(), 2u);
 }
 }  // namespace tracing
diff --git a/skia/public/mojom/BUILD.gn b/skia/public/mojom/BUILD.gn
index 76b549f..80e7bf1 100644
--- a/skia/public/mojom/BUILD.gn
+++ b/skia/public/mojom/BUILD.gn
@@ -4,6 +4,29 @@
 
 import("//mojo/public/tools/bindings/mojom.gni")
 
+# Normally typemap traits sources should be build directly into mojom targets
+# via the typemap file. This target is for typemapped mojo_base types whose
+# traits are shared between chromium and blink variants.
+component("shared_typemap_traits") {
+  output_name = "skia_shared_typemap_traits"
+
+  sources = [
+    "bitmap_skbitmap_mojom_traits.cc",
+    "bitmap_skbitmap_mojom_traits.h",
+    "image_info_mojom_traits.cc",
+    "image_info_mojom_traits.h",
+  ]
+
+  defines = [ "IS_SKIA_SHARED_TRAITS_IMPL" ]
+
+  public_deps = [
+    "//base",
+    "//mojo/public/cpp/base:shared_typemap_traits",
+    "//skia",
+    "//skia/public/mojom:mojom_shared",
+  ]
+}
+
 mojom("mojom") {
   generate_java = true
   sources = [
diff --git a/skia/public/mojom/bitmap_skbitmap_mojom_traits.h b/skia/public/mojom/bitmap_skbitmap_mojom_traits.h
index 0045fbe7..0f3397f1 100644
--- a/skia/public/mojom/bitmap_skbitmap_mojom_traits.h
+++ b/skia/public/mojom/bitmap_skbitmap_mojom_traits.h
@@ -5,11 +5,12 @@
 #ifndef SKIA_PUBLIC_MOJOM_BITMAP_SKBITMAP_MOJOM_TRAITS_H_
 #define SKIA_PUBLIC_MOJOM_BITMAP_SKBITMAP_MOJOM_TRAITS_H_
 
+#include "base/component_export.h"
 #include "base/containers/span.h"
 #include "mojo/public/cpp/base/big_buffer.h"
 #include "mojo/public/cpp/base/big_buffer_mojom_traits.h"
 #include "mojo/public/cpp/bindings/array_traits.h"
-#include "skia/public/mojom/bitmap.mojom.h"
+#include "skia/public/mojom/bitmap.mojom-shared.h"
 #include "skia/public/mojom/image_info_mojom_traits.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
@@ -17,7 +18,8 @@
 
 // Struct traits to use SkBitmap for skia::mojom::Bitmap in Chrome C++ code.
 template <>
-struct StructTraits<skia::mojom::BitmapDataView, SkBitmap> {
+struct COMPONENT_EXPORT(SKIA_SHARED_TRAITS)
+    StructTraits<skia::mojom::BitmapDataView, SkBitmap> {
   static bool IsNull(const SkBitmap& b);
   static void SetToNull(SkBitmap* b);
   static const SkImageInfo& image_info(const SkBitmap& b);
@@ -27,7 +29,8 @@
 };
 
 template <>
-struct StructTraits<skia::mojom::InlineBitmapDataView, SkBitmap> {
+struct COMPONENT_EXPORT(SKIA_SHARED_TRAITS)
+    StructTraits<skia::mojom::InlineBitmapDataView, SkBitmap> {
   static bool IsNull(const SkBitmap& b);
   static void SetToNull(SkBitmap* b);
   static const SkImageInfo& image_info(const SkBitmap& b);
diff --git a/skia/public/mojom/image_filter_mojom_traits.cc b/skia/public/mojom/image_filter_mojom_traits.cc
deleted file mode 100644
index d70ceda..0000000
--- a/skia/public/mojom/image_filter_mojom_traits.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "skia/public/mojom/image_filter_mojom_traits.h"
-
-namespace mojo {
-
-ImageFilterBuffer::ImageFilterBuffer() = default;
-
-ImageFilterBuffer::ImageFilterBuffer(const ImageFilterBuffer& other) = default;
-
-ImageFilterBuffer::~ImageFilterBuffer() = default;
-
-// static
-size_t ArrayTraits<ImageFilterBuffer>::GetSize(const ImageFilterBuffer& b) {
-  return b.data->size();
-}
-
-// static
-uint8_t* ArrayTraits<ImageFilterBuffer>::GetData(ImageFilterBuffer& b) {
-  return static_cast<uint8_t*>(b.data->writable_data());
-}
-
-// static
-const uint8_t* ArrayTraits<ImageFilterBuffer>::GetData(
-    const ImageFilterBuffer& b) {
-  return b.data->bytes();
-}
-
-// static
-uint8_t& ArrayTraits<ImageFilterBuffer>::GetAt(ImageFilterBuffer& b, size_t i) {
-  return *(static_cast<uint8_t*>(b.data->writable_data()) + i);
-}
-
-// static
-const uint8_t& ArrayTraits<ImageFilterBuffer>::GetAt(const ImageFilterBuffer& b,
-                                                     size_t i) {
-  return *(b.data->bytes() + i);
-}
-
-// static
-bool ArrayTraits<ImageFilterBuffer>::Resize(ImageFilterBuffer& b, size_t size) {
-  if (b.data)
-    return size == b.data->size();
-  b.data = SkData::MakeUninitialized(size);
-  return true;
-}
-
-}  // namespace mojo
diff --git a/skia/public/mojom/image_info_mojom_traits.h b/skia/public/mojom/image_info_mojom_traits.h
index eb8cbbfb..184ccfe 100644
--- a/skia/public/mojom/image_info_mojom_traits.h
+++ b/skia/public/mojom/image_info_mojom_traits.h
@@ -7,13 +7,15 @@
 
 #include <vector>
 
-#include "skia/public/mojom/image_info.mojom.h"
+#include "base/component_export.h"
+#include "skia/public/mojom/image_info.mojom-shared.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 
 namespace mojo {
 
 template <>
-struct StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo> {
+struct COMPONENT_EXPORT(SKIA_SHARED_TRAITS)
+    StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo> {
   static skia::mojom::ColorType color_type(const SkImageInfo& info);
   static skia::mojom::AlphaType alpha_type(const SkImageInfo& info);
   static std::vector<uint8_t> serialized_color_space(const SkImageInfo& info);
diff --git a/skia/public/mojom/skbitmap.typemap b/skia/public/mojom/skbitmap.typemap
index d4fe455..56b7e94 100644
--- a/skia/public/mojom/skbitmap.typemap
+++ b/skia/public/mojom/skbitmap.typemap
@@ -5,14 +5,12 @@
 mojom = "//skia/public/mojom/bitmap.mojom"
 public_headers = [ "//third_party/skia/include/core/SkBitmap.h" ]
 traits_headers = [ "//skia/public/mojom/bitmap_skbitmap_mojom_traits.h" ]
-sources = [
-  "//skia/public/mojom/bitmap_skbitmap_mojom_traits.cc",
-]
 deps = [
   "//mojo/public/cpp/bindings",
 ]
 public_deps = [
   "//skia",
+  "//skia/public/mojom:shared_typemap_traits",
 ]
 type_mappings = [
   "skia.mojom.Bitmap=::SkBitmap[nullable_is_same_type]",
diff --git a/skia/public/mojom/skbitmap_for_blink.typemap b/skia/public/mojom/skbitmap_for_blink.typemap
deleted file mode 100644
index a8f5d8b9..0000000
--- a/skia/public/mojom/skbitmap_for_blink.typemap
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-mojom = "//skia/public/mojom/bitmap.mojom"
-public_headers = [ "//third_party/skia/include/core/SkBitmap.h" ]
-traits_headers = [ "//skia/public/mojom/bitmap_skbitmap_mojom_traits.h" ]
-deps = [
-  "//mojo/public/cpp/bindings",
-]
-public_deps = [
-  "//skia",
-  "//skia/public/mojom",
-]
-type_mappings = [ "skia.mojom.Bitmap=::SkBitmap[nullable_is_same_type]" ]
diff --git a/skia/public/mojom/skimageinfo.typemap b/skia/public/mojom/skimageinfo.typemap
index b993ca5..a821d95 100644
--- a/skia/public/mojom/skimageinfo.typemap
+++ b/skia/public/mojom/skimageinfo.typemap
@@ -5,13 +5,11 @@
 mojom = "//skia/public/mojom/image_info.mojom"
 public_headers = [ "//third_party/skia/include/core/SkImageInfo.h" ]
 traits_headers = [ "//skia/public/mojom/image_info_mojom_traits.h" ]
-sources = [
-  "//skia/public/mojom/image_info_mojom_traits.cc",
-]
 deps = [
   "//mojo/public/cpp/bindings",
 ]
 public_deps = [
   "//skia",
+  "//skia/public/mojom:shared_typemap_traits",
 ]
 type_mappings = [ "skia.mojom.ImageInfo=::SkImageInfo" ]
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 8cf5f36..7f2c31b 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -553,14 +553,6 @@
     "grit/blink_resources.h",
     "blink_resources.pak",
   ]
-  grit_flags = [
-    "-E",
-    "blink_core_output_dir=" +
-        rebase_path(blink_core_output_dir, root_build_dir),
-  ]
-  deps = [
-    "//third_party/blink/renderer/core:make_minimized_css",
-  ]
 }
 
 grit("image_resources") {
diff --git a/third_party/blink/public/blink_resources.grd b/third_party/blink/public/blink_resources.grd
index 5a55146..c503bc46 100644
--- a/third_party/blink/public/blink_resources.grd
+++ b/third_party/blink/public/blink_resources.grd
@@ -8,40 +8,38 @@
   </outputs>
   <release seq="1">
     <includes>
-      <!-- Certain CSS files are processed through minimize_css.py -->
-      <include name="IDR_UASTYLE_HTML_CSS" file="${cwd}/${blink_core_output_dir}/html.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_QUIRKS_CSS" file="${cwd}/${blink_core_output_dir}/quirks.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_VIEW_SOURCE_CSS" file="${cwd}/${blink_core_output_dir}/view-source.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_THEME_CHROMIUM_ANDROID_CSS" file="${cwd}/${blink_core_output_dir}/android.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_FULLSCREEN_ANDROID_CSS" file="${cwd}/${blink_core_output_dir}/fullscreenAndroid.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_THEME_CHROMIUM_LINUX_CSS" file="${cwd}/${blink_core_output_dir}/linux.css" type="BINDATA" compress="gzip"/>
+      <!-- GRIT minimizes all CSS and Javascript -->
+      <include name="IDR_UASTYLE_HTML_CSS" file="../renderer/core/html/resources/html.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_QUIRKS_CSS" file="../renderer/core/html/resources/quirks.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_VIEW_SOURCE_CSS" file="../renderer/core/css/view-source.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_THEME_CHROMIUM_ANDROID_CSS" file="../renderer/core/html/resources/android.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_FULLSCREEN_ANDROID_CSS" file="../renderer/core/css/fullscreenAndroid.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_THEME_CHROMIUM_LINUX_CSS" file="../renderer/core/html/resources/linux.css" type="BINDATA" compress="gzip"/>
       <if expr="is_macosx">
         <include name="IDR_UASTYLE_THEME_MAC_CSS" file="../renderer/core/html/resources/mac.css" type="BINDATA" compress="gzip"/>
       </if>
-      <include name="IDR_UASTYLE_THEME_INPUT_MULTIPLE_FIELDS_CSS" file="${cwd}/${blink_core_output_dir}/input_multiple_fields.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_THEME_WIN_CSS" file="${cwd}/${blink_core_output_dir}/win.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_THEME_WIN_QUIRKS_CSS" file="${cwd}/${blink_core_output_dir}/win_quirks.css" type="BINDATA" compress="gzip"/>
-      <!-- TODO(crbug.com/1015410): Use minimized |controls_refresh.css| with relative urls properly revised -->
+      <include name="IDR_UASTYLE_THEME_INPUT_MULTIPLE_FIELDS_CSS" file="../renderer/core/html/resources/input_multiple_fields.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_THEME_WIN_CSS" file="../renderer/core/html/resources/win.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_THEME_WIN_QUIRKS_CSS" file="../renderer/core/html/resources/win_quirks.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_THEME_CONTROLS_REFRESH_CSS" file="../renderer/core/html/resources/controls_refresh.css" flattenhtml="true" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_THEME_FORCED_COLORS_CSS" file="${cwd}/${blink_core_output_dir}/forced_colors.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_SVG_CSS" file="${cwd}/${blink_core_output_dir}/svg.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_MATHML_CSS" file="${cwd}/${blink_core_output_dir}/mathml.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_FULLSCREEN_CSS" file="${cwd}/${blink_core_output_dir}/fullscreen.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_XHTMLMP_CSS" file="${cwd}/${blink_core_output_dir}/xhtmlmp.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_VIEWPORT_ANDROID_CSS" file="${cwd}/${blink_core_output_dir}/viewportAndroid.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_VIEWPORT_TELEVISION_CSS" file="${cwd}/${blink_core_output_dir}/viewportTelevision.css" type="BINDATA" compress="gzip"/>
-
+      <include name="IDR_UASTYLE_THEME_FORCED_COLORS_CSS" file="../renderer/core/html/resources/forced_colors.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_SVG_CSS" file="../renderer/core/css/svg.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_MATHML_CSS" file="../renderer/core/css/mathml.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_FULLSCREEN_CSS" file="../renderer/core/css/fullscreen.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_XHTMLMP_CSS" file="../renderer/core/css/xhtmlmp.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_VIEWPORT_ANDROID_CSS" file="../renderer/core/css/viewportAndroid.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_VIEWPORT_TELEVISION_CSS" file="../renderer/core/css/viewportTelevision.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_INSPECT_TOOL_COMMON_JS" file="../renderer/core/inspector/inspect_tool_common.js" type="BINDATA" compress="gzip"/>
-      <include name="IDR_INSPECT_TOOL_COMMON_CSS" file="${cwd}/${blink_core_output_dir}/inspect_tool_common.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_INSPECT_TOOL_COMMON_CSS" file="../renderer/core/inspector/inspect_tool_common.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_INSPECT_TOOL_DISTANCES_HTML" file="../renderer/core/inspector/inspect_tool_distances.html" type="BINDATA" compress="gzip"/>
       <include name="IDR_INSPECT_TOOL_HIGHLIGHT_HTML" file="../renderer/core/inspector/inspect_tool_highlight.html" type="BINDATA" compress="gzip"/>
       <include name="IDR_INSPECT_TOOL_PAUSED_HTML" file="../renderer/core/inspector/inspect_tool_paused.html" type="BINDATA" compress="gzip"/>
       <include name="IDR_INSPECT_TOOL_VIEWPORT_SIZE_HTML" file="../renderer/core/inspector/inspect_tool_viewport_size.html" type="BINDATA" compress="gzip"/>
       <include name="IDR_INSPECT_TOOL_SCREENSHOT_HTML" file="../renderer/core/inspector/inspect_tool_screenshot.html" type="BINDATA" compress="gzip"/>
-      <include name="IDR_DOCUMENTXMLTREEVIEWER_CSS" file="${cwd}/${blink_core_output_dir}/DocumentXMLTreeViewer.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_DOCUMENTXMLTREEVIEWER_CSS" file="../renderer/core/xml/DocumentXMLTreeViewer.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_DOCUMENTXMLTREEVIEWER_JS" file="../renderer/core/xml/DocumentXMLTreeViewer.js" type="BINDATA" compress="gzip"/>
       <include name="IDR_VALIDATION_BUBBLE_ICON" file="../renderer/core/html/forms/resources/input_alert.svg" type="BINDATA" compress="gzip"/>
-      <include name="IDR_VALIDATION_BUBBLE_CSS" file="${cwd}/${blink_core_output_dir}/validation_bubble.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_VALIDATION_BUBBLE_CSS" file="../renderer/core/html/forms/resources/validation_bubble.css" type="BINDATA" compress="gzip"/>
       <if expr="not is_android">
         <include name="IDR_PICKER_COMMON_JS" file="../renderer/core/html/forms/resources/pickerCommon.js" type="BINDATA" compress="gzip"/>
         <include name="IDR_PICKER_COMMON_CSS" file="../renderer/core/html/forms/resources/pickerCommon.css" type="BINDATA" compress="gzip"/>
@@ -64,10 +62,8 @@
         <include name="IDR_LIST_PICKER_JS" file="../renderer/core/html/forms/resources/listPicker.js" type="BINDATA" compress="gzip"/>
       </if>
       <include name="IDR_AUDIO_SPATIALIZATION_COMPOSITE" file="../renderer/platform/audio/resources/Composite.flac" type="BINDATA"/>
-
       <!-- Layered API scripts. -->
       <part file="../renderer/core/script/resources/layered_api/resources.grdp" />
-
     </includes>
   </release>
 </grit>
diff --git a/third_party/blink/public/blink_typemaps.gni b/third_party/blink/public/blink_typemaps.gni
index 46e49485..1e1fc204 100644
--- a/third_party/blink/public/blink_typemaps.gni
+++ b/third_party/blink/public/blink_typemaps.gni
@@ -23,7 +23,6 @@
   "//services/viz/public/cpp/compositing/returned_resource.typemap",
   "//services/viz/public/cpp/compositing/surface_id.typemap",
   "//services/viz/public/cpp/compositing/surface_info.typemap",
-  "//skia/public/mojom/skbitmap_for_blink.typemap",
   "//ui/display/mojom/display_rotation_for_blink.typemap",
   "//ui/gfx/mojom/gpu_fence_handle_for_blink.typemap",
 ]
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 1dbf29d1..64512327 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -144,6 +144,7 @@
   public_deps = [
     "//services/network/public/cpp:cpp",
     "//skia",
+    "//skia/public/mojom:shared_typemap_traits",
     "//third_party/blink/public/mojom:mojom_modules_headers",
     "//third_party/blink/public/mojom:web_bluetooth_mojo_bindings_headers",
   ]
diff --git a/third_party/blink/public/platform/DEPS b/third_party/blink/public/platform/DEPS
index 5b3c791..9c2d9a0 100644
--- a/third_party/blink/public/platform/DEPS
+++ b/third_party/blink/public/platform/DEPS
@@ -26,6 +26,7 @@
     "+media/base/audio_capturer_source.h",
     "+media/base/audio_renderer_sink.h",
     "+media/base/eme_constants.h",
+    "+media/base/video_frame.h",
     "+media/base/video_transformation.h",
     "+mojo/public",
     "+net/cert",
diff --git a/third_party/blink/public/platform/web_media_player.h b/third_party/blink/public/platform/web_media_player.h
index b4760b4..d8624b50 100644
--- a/third_party/blink/public/platform/web_media_player.h
+++ b/third_party/blink/public/platform/web_media_player.h
@@ -430,6 +430,12 @@
   // empty GURL, which will be interpreted as "use the original URL".
   virtual GURL GetSrcAfterRedirects() { return GURL(); }
 
+  // Register a request to be notified the next time a video frame is presented
+  // to the compositor. The video frame and its metadata will be surfaced via
+  // WebMediaPlayerClient::OnRequestAnimationFrame().
+  // TODO(https://crbug.com/1022186): Add pointer to spec.
+  virtual void RequestAnimationFrame() {}
+
   virtual base::WeakPtr<WebMediaPlayer> AsWeakPtr() = 0;
 };
 
diff --git a/third_party/blink/public/platform/web_media_player_client.h b/third_party/blink/public/platform/web_media_player_client.h
index 850b079..babc1ec 100644
--- a/third_party/blink/public/platform/web_media_player_client.h
+++ b/third_party/blink/public/platform/web_media_player_client.h
@@ -31,6 +31,8 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEDIA_PLAYER_CLIENT_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEDIA_PLAYER_CLIENT_H_
 
+#include "base/time/time.h"
+#include "media/base/video_frame.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_media_player.h"
 #include "ui/gfx/color_space.h"
@@ -177,6 +179,15 @@
   //  - Natural Size.
   virtual void OnPictureInPictureStateChange() = 0;
 
+  // Called when a video frame has been presented to the compositor, after a
+  // request was initiated via WebMediaPlayer::RequestAnimationFrame().
+  // TODO(https://crbug.com/1022186): Add pointer to spec.
+  virtual void OnRequestAnimationFrame(
+      base::TimeTicks presentation_time,
+      base::TimeTicks expected_presentation_time,
+      uint32_t presented_frames_counter,
+      const media::VideoFrame& presented_frame) {}
+
   struct Features {
     WebString id;
     WebString width;
diff --git a/third_party/blink/renderer/bindings/modules/v8/generated.gni b/third_party/blink/renderer/bindings/modules/v8/generated.gni
index f4467ba5..a215c14 100644
--- a/third_party/blink/renderer/bindings/modules/v8/generated.gni
+++ b/third_party/blink/renderer/bindings/modules/v8/generated.gni
@@ -119,6 +119,8 @@
   "$bindings_modules_v8_output_dir/v8_decode_success_callback.h",
   "$bindings_modules_v8_output_dir/v8_idb_observer_callback.cc",
   "$bindings_modules_v8_output_dir/v8_idb_observer_callback.h",
+  "$bindings_modules_v8_output_dir/v8_launch_consumer.cc",
+  "$bindings_modules_v8_output_dir/v8_launch_consumer.h",
   "$bindings_modules_v8_output_dir/v8_lock_granted_callback.cc",
   "$bindings_modules_v8_output_dir/v8_lock_granted_callback.h",
   "$bindings_modules_v8_output_dir/v8_media_session_action_handler.cc",
diff --git a/third_party/blink/renderer/build/scripts/minimize_css.py b/third_party/blink/renderer/build/scripts/minimize_css.py
deleted file mode 100755
index b5d6630..0000000
--- a/third_party/blink/renderer/build/scripts/minimize_css.py
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import functools
-import os.path
-import re
-import sys
-
-import in_generator
-
-
-class CSSMinimizer(object):
-
-    INITIAL = 0
-    MAYBE_COMMENT_START = 1
-    INSIDE_COMMENT = 2
-    MAYBE_COMMENT_END = 3
-    INSIDE_SINGLE_QUOTE = 4
-    INSIDE_SINGLE_QUOTE_ESCAPE = 5
-    INSIDE_DOUBLE_QUOTE = 6
-    INSIDE_DOUBLE_QUOTE_ESCAPE = 7
-
-    def __init__(self):
-        self._output = ''
-        self._codeblock = ''
-
-    def flush_codeblock(self):
-        stripped = re.sub(r"\s+", ' ', self._codeblock)
-        stripped = re.sub(r";?\s*(?P<op>[{};])\s*", r'\g<op>', stripped)
-        self._output += stripped
-        self._codeblock = ''
-
-    def parse(self, content):
-        state = self.INITIAL
-        for char in content:
-            if state == self.INITIAL:
-                if char == '/':
-                    state = self.MAYBE_COMMENT_START
-                elif char == "'":
-                    self.flush_codeblock()
-                    self._output += char
-                    state = self.INSIDE_SINGLE_QUOTE
-                elif char == '"':
-                    self.flush_codeblock()
-                    self._output += char
-                    state = self.INSIDE_DOUBLE_QUOTE
-                else:
-                    self._codeblock += char
-            elif state == self.MAYBE_COMMENT_START:
-                if char == '*':
-                    self.flush_codeblock()
-                    state = self.INSIDE_COMMENT
-                else:
-                    self._codeblock += '/' + char
-                    state = self.INITIAL
-            elif state == self.INSIDE_COMMENT:
-                if char == '*':
-                    state = self.MAYBE_COMMENT_END
-                else:
-                    pass
-            elif state == self.MAYBE_COMMENT_END:
-                if char == '/':
-                    state = self.INITIAL
-                else:
-                    state = self.INSIDE_COMMENT
-            elif state == self.INSIDE_SINGLE_QUOTE:
-                if char == '\\':
-                    self._output += char
-                    state = self.INSIDE_SINGLE_QUOTE_ESCAPE
-                elif char == "'":
-                    self._output += char
-                    state = self.INITIAL
-                else:
-                    self._output += char
-            elif state == self.INSIDE_SINGLE_QUOTE_ESCAPE:
-                self._output += char
-                state = self.INSIDE_SINGLE_QUOTE
-            elif state == self.INSIDE_DOUBLE_QUOTE:
-                if char == '\\':
-                    self._output += char
-                    state = self.INSIDE_DOUBLE_QUOTE_ESCAPE
-                elif char == '"':
-                    self._output += char
-                    state = self.INITIAL
-                else:
-                    self._output += char
-            elif state == self.INSIDE_DOUBLE_QUOTE_ESCAPE:
-                self._output += char
-                state = self.INSIDE_DOUBLE_QUOTE
-
-        self.flush_codeblock()
-        self._output = self._output.strip()
-        return self._output
-
-    @classmethod
-    def minimize_css(cls, content):
-        minimizer = CSSMinimizer()
-        return minimizer.parse(content)
-
-
-class CSSMinimizerWriter(in_generator.GenericWriter):
-
-    def __init__(self, in_file_paths):
-        super(CSSMinimizerWriter, self).__init__(in_file_paths)
-
-        self._outputs = {}
-        for in_file_path in in_file_paths:
-            out_path = os.path.basename(in_file_path)
-            self._outputs[out_path] = functools.partial(self.generate_implementation, in_file_path)
-
-    def generate_implementation(self, in_file_path):
-        content = ''
-        with open(os.path.abspath(in_file_path)) as in_file:
-            content = in_file.read()
-        return CSSMinimizer.minimize_css(content)
-
-
-if __name__ == '__main__':
-    in_generator.Maker(CSSMinimizerWriter).main(sys.argv)
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index b5d9b79f..4d1144d 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -807,61 +807,6 @@
 
 # One-off scripts --------------------------------------------------------------
 
-action("make_minimized_css") {
-  script = "../build/scripts/minimize_css.py"
-
-  inputs = [
-    "css/fullscreen.css",
-    "css/fullscreenAndroid.css",
-    "css/mathml.css",
-    "css/svg.css",
-    "css/view-source.css",
-    "css/viewportAndroid.css",
-    "css/viewportTelevision.css",
-    "css/xhtmlmp.css",
-    "html/forms/resources/validation_bubble.css",
-    "html/resources/android.css",
-    "html/resources/forced_colors.css",
-    "html/resources/html.css",
-    "html/resources/input_multiple_fields.css",
-    "html/resources/linux.css",
-    "html/resources/quirks.css",
-    "html/resources/win.css",
-    "html/resources/win_quirks.css",
-    "inspector/inspect_tool_common.css",
-    "xml/DocumentXMLTreeViewer.css",
-  ]
-  outputs = [
-    "$blink_core_output_dir/fullscreen.css",
-    "$blink_core_output_dir/fullscreenAndroid.css",
-    "$blink_core_output_dir/mathml.css",
-    "$blink_core_output_dir/svg.css",
-    "$blink_core_output_dir/view-source.css",
-    "$blink_core_output_dir/viewportAndroid.css",
-    "$blink_core_output_dir/viewportTelevision.css",
-    "$blink_core_output_dir/xhtmlmp.css",
-    "$blink_core_output_dir/validation_bubble.css",
-    "$blink_core_output_dir/android.css",
-    "$blink_core_output_dir/forced_colors.css",
-    "$blink_core_output_dir/html.css",
-    "$blink_core_output_dir/input_multiple_fields.css",
-    "$blink_core_output_dir/linux.css",
-    "$blink_core_output_dir/quirks.css",
-    "$blink_core_output_dir/win.css",
-    "$blink_core_output_dir/win_quirks.css",
-    "$blink_core_output_dir/inspect_tool_common.css",
-    "$blink_core_output_dir/DocumentXMLTreeViewer.css",
-  ]
-
-  args = [
-    "--output_dir",
-    rel_blink_core_gen_dir,
-  ]
-  args += rebase_path(inputs, root_build_dir)
-
-  deps = make_core_generated_deps
-}
-
 action("make_core_generated_html_entity_table") {
   visibility = []  # Allow re-assignment of list.
   visibility = [ ":*" ]
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 22243f5c..a716d44c 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -609,6 +609,17 @@
 
 void LocalFrameView::LayoutFromRootObject(LayoutObject& root) {
   LayoutState layout_state(root);
+  // Since we are starting layout from an arbitrary node, we need to notify all
+  // possible scroll anchors above us, as they aren't notified during layout.
+  for (auto* ancestor = root.Container(); ancestor;
+       ancestor = ancestor->Container()) {
+    if (ancestor->HasOverflowClip() && ancestor->IsLayoutBlock()) {
+      auto* scrollable_area =
+          ToLayoutBoxModelObject(ancestor)->GetScrollableArea();
+      DCHECK(scrollable_area);
+      scrollable_area->GetScrollAnchor()->NotifyBeforeLayout();
+    }
+  }
   root.UpdateLayout();
 }
 
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index c8454e99..089cd25 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -1287,14 +1287,11 @@
       script_state, ImageBitmap::Create(this, crop_rect, options));
 }
 
-void HTMLCanvasElement::SetOffscreenCanvasFrame(
+void HTMLCanvasElement::SetOffscreenCanvasResource(
     scoped_refptr<CanvasResource> image,
-    base::WeakPtr<CanvasResourceDispatcher> dispatcher,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     unsigned resource_id) {
-  OffscreenCanvasPlaceholder::SetOffscreenCanvasFrame(
-      std::move(image), std::move(dispatcher), std::move(task_runner),
-      resource_id);
+  OffscreenCanvasPlaceholder::SetOffscreenCanvasResource(std::move(image),
+                                                         resource_id);
   SetSize(OffscreenCanvasFrame()->Size());
   NotifyListenersCanvasChanged();
 }
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
index 978e4fd..e765067 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
@@ -237,10 +237,8 @@
                                   const ImageBitmapOptions*) override;
 
   // OffscreenCanvasPlaceholder implementation.
-  void SetOffscreenCanvasFrame(scoped_refptr<CanvasResource>,
-                               base::WeakPtr<CanvasResourceDispatcher>,
-                               scoped_refptr<base::SingleThreadTaskRunner>,
-                               unsigned resource_id) override;
+  void SetOffscreenCanvasResource(scoped_refptr<CanvasResource>,
+                                  unsigned resource_id) override;
   void Trace(Visitor*) override;
 
   void SetResourceProviderForTesting(std::unique_ptr<CanvasResourceProvider>,
diff --git a/third_party/blink/renderer/core/html/forms/file_chooser.cc b/third_party/blink/renderer/core/html/forms/file_chooser.cc
index 60dbf7f..cbf940f5 100644
--- a/third_party/blink/renderer/core/html/forms/file_chooser.cc
+++ b/third_party/blink/renderer/core/html/forms/file_chooser.cc
@@ -160,6 +160,8 @@
       chrome_client_impl_->UnregisterPopupOpeningObserver(client_);
   }
 
+  if (client_)
+    client_->DisconnectFileChooser();
   Release();
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/file_chooser.h b/third_party/blink/renderer/core/html/forms/file_chooser.h
index a98154b..b79759e 100644
--- a/third_party/blink/renderer/core/html/forms/file_chooser.h
+++ b/third_party/blink/renderer/core/html/forms/file_chooser.h
@@ -60,13 +60,14 @@
   // DisconnectFileChooser().
   FileChooser* FileChooserOrNull() const { return chooser_.get(); }
 
- protected:
-  FileChooser* NewFileChooser(const mojom::blink::FileChooserParams&);
-  bool HasConnectedFileChooser() const { return chooser_.get(); }
-
   // This should be called if a user chose files or cancel the dialog.
   void DisconnectFileChooser();
 
+  FileChooser* NewFileChooser(const mojom::blink::FileChooserParams&);
+
+ protected:
+  bool HasConnectedFileChooser() const { return chooser_.get(); }
+
  private:
   scoped_refptr<FileChooser> chooser_;
 };
@@ -96,7 +97,7 @@
   // mojom::blink::FileChooser callback
   void DidChooseFiles(mojom::blink::FileChooserResultPtr result);
 
-  WeakPersistent<FileChooserClient> client_;
+  Persistent<FileChooserClient> client_;
   mojom::blink::FileChooserParamsPtr params_;
   Persistent<ChromeClientImpl> chrome_client_impl_;
   mojo::Remote<mojom::blink::FileChooser> file_chooser_;
diff --git a/third_party/blink/renderer/core/html/resources/controls_refresh.css b/third_party/blink/renderer/core/html/resources/controls_refresh.css
index 98b3f6c..5f1e0e3b 100644
--- a/third_party/blink/renderer/core/html/resources/controls_refresh.css
+++ b/third_party/blink/renderer/core/html/resources/controls_refresh.css
@@ -168,16 +168,6 @@
   border-radius: 2px;
 }
 
-/* These options only apply to in-page 'multiple' select elements.
-   The options in the popup are handled in listPicker.css */
-select:-internal-list-box option, select:-internal-list-box optgroup {
-  padding: 0 3px;
-}
-
-select:-internal-list-box option {
-  border-radius: 2px;
-}
-
 select:-internal-list-box option:hover {
   background-color: #f3f3f3;
 }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
index 22bca26..7a1cc31 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
@@ -222,8 +222,7 @@
   }
 
   space_builder.SetAvailableSize(content_box_size_);
-  space_builder.SetPercentageResolutionSize(CalculateChildPercentageSize(
-      ConstraintSpace(), Node(), content_box_size_));
+  space_builder.SetPercentageResolutionSize(child_percentage_size_);
   return space_builder.ToConstraintSpace();
 }
 
@@ -500,6 +499,8 @@
   border_box_size_ = container_builder_.InitialBorderBoxSize();
   content_box_size_ =
       ShrinkAvailableSize(border_box_size_, border_scrollbar_padding_);
+  child_percentage_size_ = CalculateChildPercentageSize(
+      ConstraintSpace(), Node(), content_box_size_);
 
   const LayoutUnit line_break_length = MainAxisContentExtent(LayoutUnit::Max());
   algorithm_.emplace(&Style(), line_break_length);
@@ -562,11 +563,7 @@
       }
 
       space_builder.SetAvailableSize(available_size);
-      // CalculateChildPercentageSize probably has no effect here:
-      // content_box_size_ would already be indefinite without
-      // CalculateChildPercentageSize checking IsFixedBlockSizeIndefinite.
-      space_builder.SetPercentageResolutionSize(CalculateChildPercentageSize(
-          ConstraintSpace(), Node(), content_box_size_));
+      space_builder.SetPercentageResolutionSize(child_percentage_size_);
 
       // https://drafts.csswg.org/css-flexbox/#algo-cross-item
       // Determine the hypothetical cross size of each item by performing layout
@@ -626,8 +623,7 @@
     }
   }
   space_builder.SetAvailableSize(available_size);
-  space_builder.SetPercentageResolutionSize(CalculateChildPercentageSize(
-      ConstraintSpace(), Node(), content_box_size_));
+  space_builder.SetPercentageResolutionSize(child_percentage_size_);
   space_builder.SetIsFixedInlineSize(true);
   space_builder.SetIsFixedBlockSize(true);
   NGConstraintSpace child_space = space_builder.ToConstraintSpace();
diff --git a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h
index 6290436..16f6aaa0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h
@@ -73,6 +73,7 @@
   // ComputeMinMaxSize() or anything it calls.
   LogicalSize border_box_size_;
   LogicalSize content_box_size_;
+  LogicalSize child_percentage_size_;
   base::Optional<FlexLayoutAlgorithm> algorithm_;
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/table/interface_casting.h b/third_party/blink/renderer/core/layout/ng/table/interface_casting.h
index dab99df1..c74cecc 100644
--- a/third_party/blink/renderer/core/layout/ng/table/interface_casting.h
+++ b/third_party/blink/renderer/core/layout/ng/table/interface_casting.h
@@ -69,7 +69,8 @@
 
 template <typename Derived, typename Base>
 const Derived* ToInterface(const Base* from) {
-  SECURITY_DCHECK(InterfaceDowncastTraits<Derived>::AllowFrom(*from));
+  if (from)
+    SECURITY_DCHECK(InterfaceDowncastTraits<Derived>::AllowFrom(*from));
   return from ? &InterfaceDowncastTraits<Derived>::ConvertFrom(*from) : nullptr;
 }
 
@@ -83,7 +84,8 @@
 
 template <typename Derived, typename Base>
 Derived* ToInterface(Base* from) {
-  SECURITY_DCHECK(InterfaceDowncastTraits<Derived>::AllowFrom(*from));
+  if (from)
+    SECURITY_DCHECK(InterfaceDowncastTraits<Derived>::AllowFrom(*from));
   // const_cast is safe because from is not const.
   return from ? const_cast<Derived*>(
                     &InterfaceDowncastTraits<Derived>::ConvertFrom(*from))
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
index b90c058..2a85555 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/skia/include/core/SkFilterQuality.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
 namespace blink {
@@ -105,6 +106,9 @@
     if (animation_frame_provider)
       animation_frame_provider->RegisterOffscreenCanvas(this);
   }
+  if (frame_dispatcher_) {
+    frame_dispatcher_->SetPlaceholderCanvasDispatcher(placeholder_canvas_id_);
+  }
 }
 
 void OffscreenCanvas::setWidth(unsigned width) {
@@ -313,6 +317,9 @@
     // throughout the lifetime of this OffscreenCanvas.
     frame_dispatcher_ = std::make_unique<CanvasResourceDispatcher>(
         this, client_id_, sink_id_, placeholder_canvas_id_, size_);
+
+    if (HasPlaceholderCanvas())
+      frame_dispatcher_->SetPlaceholderCanvasDispatcher(placeholder_canvas_id_);
   }
   return frame_dispatcher_.get();
 }
@@ -411,6 +418,16 @@
   return PushFrameIfNeeded();
 }
 
+void OffscreenCanvas::SetFilterQualityInResource(
+    SkFilterQuality filter_quality) {
+  if (filter_quality_ == filter_quality)
+    return;
+
+  filter_quality_ = filter_quality;
+  if (ResourceProvider())
+    GetOrCreateResourceProvider()->SetFilterQuality(filter_quality);
+}
+
 bool OffscreenCanvas::PushFrameIfNeeded() {
   if (needs_push_frame_ && context_) {
     return context_->PushFrame();
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h
index c198d42..0ea8924 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h
@@ -63,6 +63,7 @@
 
   // CanvasResourceDispatcherClient
   bool BeginFrame() override;
+  void SetFilterQualityInResource(SkFilterQuality filter_quality) override;
 
   // API Methods
   ImageBitmap* transferToImageBitmap(ScriptState*, ExceptionState&);
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h
index 544294e..7ab1fb7 100644
--- a/third_party/blink/renderer/core/page/chrome_client.h
+++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -482,6 +482,8 @@
   virtual void DidUpdateTextAutosizerPageInfo(const WebTextAutosizerPageInfo&) {
   }
 
+  virtual void DocumentDetached(Document&) {}
+
  protected:
   ChromeClient() = default;
 
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index b04f661..19717a1 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -1280,4 +1280,11 @@
   web_view_->Client()->DidUpdateTextAutosizerPageInfo(page_info);
 }
 
+void ChromeClientImpl::DocumentDetached(Document& document) {
+  for (auto& it : file_chooser_queue_) {
+    if (it->FrameOrNull() == document.GetFrame())
+      it->DisconnectClient();
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.h b/third_party/blink/renderer/core/page/chrome_client_impl.h
index 93307ad8..9681f81 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.h
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.h
@@ -268,6 +268,8 @@
 
   void DidUpdateTextAutosizerPageInfo(const WebTextAutosizerPageInfo&) override;
 
+  void DocumentDetached(Document&) override;
+
  private:
   bool IsChromeClientImpl() const override { return true; }
 
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl_test.cc b/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
index 127fcc5..75815d3 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
@@ -274,8 +274,8 @@
   auto* client2 = MakeGarbageCollected<MockFileChooserClient>(frame);
   mojom::blink::FileChooserParams params;
   params.title = g_empty_string;
-  scoped_refptr<FileChooser> chooser1 = FileChooser::Create(client1, params);
-  scoped_refptr<FileChooser> chooser2 = FileChooser::Create(client2, params);
+  scoped_refptr<FileChooser> chooser1 = client1->NewFileChooser(params);
+  scoped_refptr<FileChooser> chooser2 = client2->NewFileChooser(params);
 
   chrome_client_impl_->OpenFileChooser(frame, chooser1);
   chrome_client_impl_->OpenFileChooser(frame, chooser2);
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index 9060d1a..a0a4133 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -318,6 +318,8 @@
 
   if (agent_metrics_collector_)
     agent_metrics_collector_->DidDetachDocument(*document);
+
+  GetChromeClient().DocumentDetached(*document);
 }
 
 bool Page::OpenedByDOM() const {
diff --git a/third_party/blink/renderer/core/paint/html_canvas_painter.cc b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
index 05c8f34a..41ad9be 100644
--- a/third_party/blink/renderer/core/paint/html_canvas_painter.cc
+++ b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
@@ -37,6 +37,8 @@
   paint_rect.Move(paint_offset);
 
   auto* canvas = To<HTMLCanvasElement>(layout_html_canvas_.GetNode());
+  if (canvas->IsOffscreenCanvasRegistered())
+    canvas->UpdateOffscreenCanvasFilterQuality(canvas->FilterQuality());
 
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
     if (auto* layer = canvas->ContentsCcLayer()) {
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index 71a4412..89afeba 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -70,6 +70,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
@@ -369,29 +370,22 @@
   if (timing_allow_origin_string.IsEmpty())
     return false;
 
-  // The condition below if only needed for use-counting purposes.
-  if (timing_allow_origin_string == "*") {
-    UseCounter::Count(context, WebFeature::kStarInTimingAllowOrigin);
-    return true;
-  }
-
-  // TODO(yoav): Use CommaDelimitedHeaderSet instead of this one-off parsing
-  // algorithm.
   const String& security_origin = initiator_security_origin.ToString();
-  Vector<String> timing_allow_origins;
-  timing_allow_origin_string.GetString().Split(',', timing_allow_origins);
-  if (timing_allow_origins.size() > 1) {
-    UseCounter::Count(context, WebFeature::kMultipleOriginsInTimingAllowOrigin);
-  } else if (timing_allow_origins.size() == 1 &&
-             timing_allow_origin_string != "*") {
-    UseCounter::Count(context, WebFeature::kSingleOriginInTimingAllowOrigin);
-  }
-  for (const String& allow_origin : timing_allow_origins) {
-    const String allow_origin_stripped = allow_origin.StripWhiteSpace();
-    if (allow_origin_stripped == security_origin ||
-        allow_origin_stripped == "*") {
+  CommaDelimitedHeaderSet tao_headers;
+  ParseCommaDelimitedHeader(timing_allow_origin_string, tao_headers);
+  if (tao_headers.size() == 1u) {
+    if (*tao_headers.begin() == "*") {
+      UseCounter::Count(context, WebFeature::kStarInTimingAllowOrigin);
       return true;
+    } else {
+      UseCounter::Count(context, WebFeature::kSingleOriginInTimingAllowOrigin);
     }
+  } else if (tao_headers.size() > 1u) {
+    UseCounter::Count(context, WebFeature::kMultipleOriginsInTimingAllowOrigin);
+  }
+  for (const String& header : tao_headers) {
+    if (header == "*" || header == security_origin)
+      return true;
   }
 
   return false;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 45c76003..d3e3af9 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -287,9 +287,14 @@
   if (HasGlobalARIAAttribute())
     return kIncludeObject;
 
-  if (IsImage())
-    return kIncludeObject;
-
+  bool has_non_empty_alt_attribute = !GetAttribute(kAltAttr).IsEmpty();
+  if (IsImage()) {
+    if (has_non_empty_alt_attribute || GetAttribute(kAltAttr).IsNull())
+      return kIncludeObject;
+    else if (ignored_reasons)
+      ignored_reasons->push_back(IgnoredReason(kAXEmptyAlt));
+    return kIgnoreObject;
+  }
   // Using the title or accessibility description (so we
   // check if there's some kind of accessible name for the element)
   // to decide an element's visibility is not as definitive as
@@ -298,8 +303,8 @@
   // These checks are simplified in the interest of execution speed;
   // for example, any element having an alt attribute will make it
   // not ignored, rather than just images.
-  if (HasAriaAttribute(GetElement()) || !GetAttribute(kAltAttr).IsEmpty() ||
-      !GetAttribute(kTitleAttr).IsEmpty())
+  if (HasAriaAttribute(GetElement()) || !GetAttribute(kTitleAttr).IsEmpty() ||
+      has_non_empty_alt_attribute)
     return kIncludeObject;
 
   // <span> tags are inline tags and not meant to convey information if they
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.cc b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.cc
index 66271ef4c..3593258de 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.cc
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module.cc
@@ -77,8 +77,8 @@
     offscreen_canvas->AllowHighPerformancePowerPreference();
 
   DOMNodeId canvas_id = DOMNodeIds::IdForNode(&canvas);
-  offscreen_canvas->SetPlaceholderCanvasId(canvas_id);
   canvas.RegisterPlaceholderCanvas(static_cast<int>(canvas_id));
+  offscreen_canvas->SetPlaceholderCanvasId(canvas_id);
 
   SurfaceLayerBridge* bridge = canvas.SurfaceLayerBridge();
   if (bridge) {
diff --git a/third_party/blink/renderer/modules/launch/BUILD.gn b/third_party/blink/renderer/modules/launch/BUILD.gn
index e722c899..86215df8 100644
--- a/third_party/blink/renderer/modules/launch/BUILD.gn
+++ b/third_party/blink/renderer/modules/launch/BUILD.gn
@@ -6,10 +6,12 @@
 
 blink_modules_sources("launch") {
   sources = [
-    "dom_window_launch_params.cc",
-    "dom_window_launch_params.h",
+    "dom_window_launch_queue.cc",
+    "dom_window_launch_queue.h",
     "launch_params.cc",
     "launch_params.h",
+    "launch_queue.cc",
+    "launch_queue.h",
     "web_launch_service_impl.cc",
     "web_launch_service_impl.h",
   ]
diff --git a/third_party/blink/renderer/modules/launch/dom_window_launch_params.cc b/third_party/blink/renderer/modules/launch/dom_window_launch_params.cc
deleted file mode 100644
index 1fbb94f..0000000
--- a/third_party/blink/renderer/modules/launch/dom_window_launch_params.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/modules/launch/dom_window_launch_params.h"
-
-#include "third_party/blink/renderer/core/frame/local_dom_window.h"
-#include "third_party/blink/renderer/modules/launch/launch_params.h"
-#include "third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h"
-#include "third_party/blink/renderer/platform/heap/heap.h"
-#include "third_party/blink/renderer/platform/heap/visitor.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-
-namespace blink {
-
-const char DOMWindowLaunchParams::kSupplementName[] = "DOMWindowLaunchParams";
-
-DOMWindowLaunchParams::DOMWindowLaunchParams()
-    : launch_params_(MakeGarbageCollected<LaunchParams>()) {}
-DOMWindowLaunchParams::~DOMWindowLaunchParams() = default;
-
-Member<LaunchParams> DOMWindowLaunchParams::launchParams(
-    LocalDOMWindow& window) {
-  return FromState(&window)->launch_params_;
-}
-
-void DOMWindowLaunchParams::UpdateLaunchFiles(
-    LocalDOMWindow* window,
-    HeapVector<Member<NativeFileSystemHandle>> files) {
-  FromState(window)->launch_params_->SetFiles(std::move(files));
-}
-
-void DOMWindowLaunchParams::Trace(blink::Visitor* visitor) {
-  visitor->Trace(launch_params_);
-  Supplement<LocalDOMWindow>::Trace(visitor);
-}
-
-// static
-DOMWindowLaunchParams* DOMWindowLaunchParams::FromState(
-    LocalDOMWindow* window) {
-  DOMWindowLaunchParams* supplement =
-      Supplement<LocalDOMWindow>::From<DOMWindowLaunchParams>(window);
-  if (!supplement) {
-    supplement = MakeGarbageCollected<DOMWindowLaunchParams>();
-    ProvideTo(*window, supplement);
-  }
-  return supplement;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/launch/dom_window_launch_queue.cc b/third_party/blink/renderer/modules/launch/dom_window_launch_queue.cc
new file mode 100644
index 0000000..a7ef74a
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/dom_window_launch_queue.cc
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/launch/dom_window_launch_queue.h"
+
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/modules/launch/launch_params.h"
+#include "third_party/blink/renderer/modules/native_file_system/native_file_system_handle.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+namespace blink {
+
+const char DOMWindowLaunchQueue::kSupplementName[] = "DOMWindowLaunchQueue";
+
+DOMWindowLaunchQueue::DOMWindowLaunchQueue()
+    : launch_queue_(MakeGarbageCollected<LaunchQueue>()) {}
+DOMWindowLaunchQueue::~DOMWindowLaunchQueue() = default;
+
+Member<LaunchQueue> DOMWindowLaunchQueue::launchQueue(LocalDOMWindow& window) {
+  return FromState(&window)->launch_queue_;
+}
+
+void DOMWindowLaunchQueue::UpdateLaunchFiles(
+    LocalDOMWindow* window,
+    HeapVector<Member<NativeFileSystemHandle>> files) {
+  FromState(window)->launch_queue_->Enqueue(
+      MakeGarbageCollected<LaunchParams>(std::move(files)));
+}
+
+void DOMWindowLaunchQueue::Trace(blink::Visitor* visitor) {
+  visitor->Trace(launch_queue_);
+  Supplement<LocalDOMWindow>::Trace(visitor);
+}
+
+// static
+DOMWindowLaunchQueue* DOMWindowLaunchQueue::FromState(LocalDOMWindow* window) {
+  DOMWindowLaunchQueue* supplement =
+      Supplement<LocalDOMWindow>::From<DOMWindowLaunchQueue>(window);
+  if (!supplement) {
+    supplement = MakeGarbageCollected<DOMWindowLaunchQueue>();
+    ProvideTo(*window, supplement);
+  }
+  return supplement;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/launch/dom_window_launch_params.h b/third_party/blink/renderer/modules/launch/dom_window_launch_queue.h
similarity index 72%
rename from third_party/blink/renderer/modules/launch/dom_window_launch_params.h
rename to third_party/blink/renderer/modules/launch/dom_window_launch_queue.h
index d20cfa4..4990a78e 100644
--- a/third_party/blink/renderer/modules/launch/dom_window_launch_params.h
+++ b/third_party/blink/renderer/modules/launch/dom_window_launch_queue.h
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_DOM_WINDOW_LAUNCH_PARAMS_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_DOM_WINDOW_LAUNCH_PARAMS_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_DOM_WINDOW_LAUNCH_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_DOM_WINDOW_LAUNCH_QUEUE_H_
 
 #include "third_party/blink/renderer/modules/launch/launch_params.h"
+#include "third_party/blink/renderer/modules/launch/launch_queue.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
@@ -19,19 +20,19 @@
 class LocalDOMWindow;
 class Visitor;
 
-class DOMWindowLaunchParams final
-    : public GarbageCollected<DOMWindowLaunchParams>,
+class DOMWindowLaunchQueue final
+    : public GarbageCollected<DOMWindowLaunchQueue>,
       public Supplement<LocalDOMWindow> {
-  USING_GARBAGE_COLLECTED_MIXIN(DOMWindowLaunchParams);
+  USING_GARBAGE_COLLECTED_MIXIN(DOMWindowLaunchQueue);
 
  public:
   static const char kSupplementName[];
 
-  explicit DOMWindowLaunchParams();
-  ~DOMWindowLaunchParams();
+  explicit DOMWindowLaunchQueue();
+  ~DOMWindowLaunchQueue();
 
   // IDL Interface.
-  static Member<LaunchParams> launchParams(LocalDOMWindow&);
+  static Member<LaunchQueue> launchQueue(LocalDOMWindow&);
 
   static void UpdateLaunchFiles(LocalDOMWindow*,
                                 HeapVector<Member<NativeFileSystemHandle>>);
@@ -39,11 +40,11 @@
   void Trace(blink::Visitor*) override;
 
  private:
-  static DOMWindowLaunchParams* FromState(LocalDOMWindow* window);
+  static DOMWindowLaunchQueue* FromState(LocalDOMWindow* window);
 
-  Member<LaunchParams> launch_params_;
+  Member<LaunchQueue> launch_queue_;
 };
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_DOM_WINDOW_LAUNCH_PARAMS_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_DOM_WINDOW_LAUNCH_QUEUE_H_
diff --git a/third_party/blink/renderer/modules/launch/dom_window_launch_params.idl b/third_party/blink/renderer/modules/launch/dom_window_launch_queue.idl
similarity index 79%
rename from third_party/blink/renderer/modules/launch/dom_window_launch_params.idl
rename to third_party/blink/renderer/modules/launch/dom_window_launch_queue.idl
index 1dd318ae..b0dd565 100644
--- a/third_party/blink/renderer/modules/launch/dom_window_launch_params.idl
+++ b/third_party/blink/renderer/modules/launch/dom_window_launch_queue.idl
@@ -6,8 +6,8 @@
 // Explainer: https://github.com/WICG/file-handling/blob/master/explainer.md
 
 [
-    ImplementedAs=DOMWindowLaunchParams,
+    ImplementedAs=DOMWindowLaunchQueue,
     RuntimeEnabled=FileHandling
 ] partial interface Window {
-    readonly attribute LaunchParams launchParams;
+    readonly attribute LaunchQueue launchQueue;
 };
diff --git a/third_party/blink/renderer/modules/launch/launch_params.cc b/third_party/blink/renderer/modules/launch/launch_params.cc
index 84fd390..280f67a 100644
--- a/third_party/blink/renderer/modules/launch/launch_params.cc
+++ b/third_party/blink/renderer/modules/launch/launch_params.cc
@@ -12,20 +12,11 @@
 
 namespace blink {
 
-LaunchParams::LaunchParams() {}
+LaunchParams::LaunchParams(HeapVector<Member<NativeFileSystemHandle>> files)
+    : files_(files) {}
 
 LaunchParams::~LaunchParams() = default;
 
-void LaunchParams::SetFiles(HeapVector<Member<NativeFileSystemHandle>> files) {
-  files_ = std::move(files);
-}
-
-void LaunchParams::SetFetchRequest(
-    mojom::blink::FetchAPIRequestPtr fetch_request) {
-  request_ = Member<Request>();
-  fetch_request_ = std::move(fetch_request);
-}
-
 Request* LaunchParams::request(ScriptState* script_state) {
   if (!fetch_request_)
     return nullptr;
diff --git a/third_party/blink/renderer/modules/launch/launch_params.h b/third_party/blink/renderer/modules/launch/launch_params.h
index 026f1423..7e958c1 100644
--- a/third_party/blink/renderer/modules/launch/launch_params.h
+++ b/third_party/blink/renderer/modules/launch/launch_params.h
@@ -22,12 +22,9 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  LaunchParams();
+  LaunchParams(HeapVector<Member<NativeFileSystemHandle>> files);
   ~LaunchParams() override;
 
-  void SetFiles(HeapVector<Member<NativeFileSystemHandle>> files);
-  void SetFetchRequest(mojom::blink::FetchAPIRequestPtr fetch_request);
-
   // LaunchParams IDL interface.
   const HeapVector<Member<NativeFileSystemHandle>>& files() { return files_; }
   Request* request(ScriptState* script_state);
diff --git a/third_party/blink/renderer/modules/launch/launch_queue.cc b/third_party/blink/renderer/modules/launch/launch_queue.cc
new file mode 100644
index 0000000..9c2e9ca
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/launch_queue.cc
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file
+
+#include "third_party/blink/renderer/modules/launch/launch_queue.h"
+
+#include "third_party/blink/renderer/bindings/modules/v8/v8_launch_consumer.h"
+#include "third_party/blink/renderer/modules/launch/launch_params.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+
+namespace blink {
+
+LaunchQueue::LaunchQueue() = default;
+
+LaunchQueue::~LaunchQueue() = default;
+
+void LaunchQueue::Enqueue(LaunchParams* params) {
+  if (!consumer_) {
+    unconsumed_launch_params_.push_back(params);
+    return;
+  }
+
+  consumer_->Invoke(nullptr, params).Check();
+}
+
+void LaunchQueue::setConsumer(V8LaunchConsumer* consumer) {
+  consumer_ = consumer;
+
+  // Consume all launch params now we have a consumer.
+  while (!unconsumed_launch_params_.IsEmpty()) {
+    // Get the first launch params and the queue and remove it before invoking
+    // the consumer, in case the consumer calls |setConsumer|. Each launchParams
+    // should be consumed by the most recently set consumer.
+    LaunchParams* params = unconsumed_launch_params_.at(0);
+    unconsumed_launch_params_.EraseAt(0);
+
+    consumer_->Invoke(nullptr, params).Check();
+  }
+}
+
+void LaunchQueue::Trace(Visitor* visitor) {
+  visitor->Trace(unconsumed_launch_params_);
+  visitor->Trace(consumer_);
+  ScriptWrappable::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/launch/launch_queue.h b/third_party/blink/renderer/modules/launch/launch_queue.h
new file mode 100644
index 0000000..b4037793
--- /dev/null
+++ b/third_party/blink/renderer/modules/launch/launch_queue.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_LAUNCH_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_LAUNCH_QUEUE_H_
+
+#include "third_party/blink/renderer/modules/launch/launch_params.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+
+namespace blink {
+
+class V8LaunchConsumer;
+class Visitor;
+
+class LaunchQueue final : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  LaunchQueue();
+  ~LaunchQueue() override;
+
+  void Enqueue(LaunchParams* params);
+
+  // IDL implementation:
+  void setConsumer(V8LaunchConsumer*);
+
+  // ScriptWrappable:
+  void Trace(blink::Visitor* visitor) override;
+
+ private:
+  HeapVector<Member<LaunchParams>> unconsumed_launch_params_;
+  Member<V8LaunchConsumer> consumer_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_LAUNCH_QUEUE_H_
diff --git a/third_party/blink/renderer/modules/launch/dom_window_launch_params.idl b/third_party/blink/renderer/modules/launch/launch_queue.idl
similarity index 66%
copy from third_party/blink/renderer/modules/launch/dom_window_launch_params.idl
copy to third_party/blink/renderer/modules/launch/launch_queue.idl
index 1dd318ae..46d0b9a 100644
--- a/third_party/blink/renderer/modules/launch/dom_window_launch_params.idl
+++ b/third_party/blink/renderer/modules/launch/launch_queue.idl
@@ -5,9 +5,8 @@
 // TODO(crbug.com/829689): Add link to spec once complete.
 // Explainer: https://github.com/WICG/file-handling/blob/master/explainer.md
 
-[
-    ImplementedAs=DOMWindowLaunchParams,
-    RuntimeEnabled=FileHandling
-] partial interface Window {
-    readonly attribute LaunchParams launchParams;
+[RuntimeEnabled=FileHandling] interface LaunchQueue {
+  void setConsumer(LaunchConsumer consumer);
 };
+
+callback LaunchConsumer = any (LaunchParams params);
diff --git a/third_party/blink/renderer/modules/launch/web_launch_service_impl.cc b/third_party/blink/renderer/modules/launch/web_launch_service_impl.cc
index e81a699..4cc9925 100644
--- a/third_party/blink/renderer/modules/launch/web_launch_service_impl.cc
+++ b/third_party/blink/renderer/modules/launch/web_launch_service_impl.cc
@@ -11,7 +11,7 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/script/script.h"
-#include "third_party/blink/renderer/modules/launch/dom_window_launch_params.h"
+#include "third_party/blink/renderer/modules/launch/dom_window_launch_queue.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
 
@@ -43,7 +43,7 @@
         std::move(entry), window_->GetExecutionContext()));
   }
 
-  DOMWindowLaunchParams::UpdateLaunchFiles(window_, std::move(files));
+  DOMWindowLaunchQueue::UpdateLaunchFiles(window_, std::move(files));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
index 41030d18..0a9a123 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
@@ -105,7 +105,7 @@
 
 constexpr int kMinScrubbingMessageWidth = 300;
 
-const char* kStateCSSClasses[8] = {
+const char* const kStateCSSClasses[8] = {
     "state-no-source",                 // kNoSource
     "state-no-metadata",               // kNotLoaded
     "state-loading-metadata-paused",   // kLoadingMetadataPaused
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc
index 85c5149b..3ffe246 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc
@@ -308,7 +308,7 @@
   DCHECK(result);
   DCHECK(context_provider->GetGLHelper());
   context_provider->GetGLHelper()->ReadbackTextureAsync(
-      texture_info.fID, image_size,
+      texture_info.fID, texture_info.fTarget, image_size,
       temp_argb_frame->visible_data(VideoFrame::kARGBPlane), kN32_SkColorType,
       WTF::Bind(&CanvasCaptureHandler::OnARGBPixelsReadAsync,
                 weak_ptr_factory_.GetWeakPtr(), image, temp_argb_frame,
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index c65f54c..3371822 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -157,6 +157,7 @@
           "filesystem/metadata.idl",
           "filesystem/metadata_callback.idl",
           "launch/launch_params.idl",
+          "launch/launch_queue.idl",
           "gamepad/gamepad.idl",
           "gamepad/gamepad_axis_event.idl",
           "gamepad/gamepad_button.idl",
@@ -945,7 +946,7 @@
           "filesystem/html_input_element_file_system.idl",
           "filesystem/shared_worker_global_scope_file_system.idl",
           "filesystem/window_file_system.idl",
-          "launch/dom_window_launch_params.idl",
+          "launch/dom_window_launch_queue.idl",
           "gamepad/navigator_gamepad.idl",
           "geolocation/navigator_geolocation.idl",
           "hid/navigator_hid.idl",
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc
index 839d8c8..bd76a6a7 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc
@@ -155,6 +155,20 @@
   }
 
   Initialize();
+
+  // Compute the base latency now and cache the value since it doesn't change
+  // once the context is constructed.  We need the destination to be initialized
+  // so we have to compute it here.
+  //
+  // TODO(hongchan): Due to the incompatible constructor between
+  // AudioDestinationNode and RealtimeAudioDestinationNode, casting directly
+  // from |destination()| is impossible. This is a temporary workaround until
+  // the refactoring is completed.
+  RealtimeAudioDestinationHandler& destination_handler =
+      static_cast<RealtimeAudioDestinationHandler&>(
+          destination()->GetAudioDestinationHandler());
+  base_latency_ = destination_handler.GetFramesPerBuffer() /
+                  static_cast<double>(sampleRate());
 }
 
 void AudioContext::Uninitialize() {
@@ -359,15 +373,7 @@
   DCHECK(IsMainThread());
   DCHECK(destination());
 
-  // TODO(hongchan): Due to the incompatible constructor between
-  // AudioDestinationNode and RealtimeAudioDestinationNode, casting directly
-  // from |destination()| is impossible. This is a temporary workaround until
-  // the refactoring is completed.
-  RealtimeAudioDestinationHandler& destination_handler =
-      static_cast<RealtimeAudioDestinationHandler&>(
-          destination()->GetAudioDestinationHandler());
-  return destination_handler.GetFramesPerBuffer() /
-         static_cast<double>(sampleRate());
+  return base_latency_;
 }
 
 MediaElementAudioSourceNode* AudioContext::createMediaElementSource(
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.h b/third_party/blink/renderer/modules/webaudio/audio_context.h
index e0d4cf1..013eee5 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.h
@@ -176,6 +176,9 @@
   // Represents whether a context is suspended by explicit |context.suspend()|.
   bool suspended_by_user_ = false;
 
+  // baseLatency for this context
+  double base_latency_ = 0;
+
   // AudioContextManager for reporting audibility.
   mojo::Remote<mojom::blink::AudioContextManager> audio_context_manager_;
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.h b/third_party/blink/renderer/platform/graphics/canvas_resource.h
index 2110069..b87748e0e 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -160,6 +160,7 @@
     Abandon();
   }
 
+  void SetFilterQuality(SkFilterQuality filter) { filter_quality_ = filter; }
   // The filter quality to use when the resource is drawn by the compositor.
   SkFilterQuality FilterQuality() const { return filter_quality_; }
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
index e1ee697..48e1f49 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/debug/stack_trace.h"
 #include "base/single_thread_task_runner.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
@@ -84,8 +85,6 @@
 namespace {
 
 void UpdatePlaceholderImage(
-    base::WeakPtr<CanvasResourceDispatcher> dispatcher,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     int placeholder_canvas_id,
     scoped_refptr<blink::CanvasResource> canvas_resource,
     viz::ResourceId resource_id) {
@@ -94,12 +93,24 @@
       OffscreenCanvasPlaceholder::GetPlaceholderCanvasById(
           placeholder_canvas_id);
   if (placeholder_canvas) {
-    placeholder_canvas->SetOffscreenCanvasFrame(
-        std::move(canvas_resource), std::move(dispatcher),
-        std::move(task_runner), resource_id);
+    placeholder_canvas->SetOffscreenCanvasResource(std::move(canvas_resource),
+                                                   resource_id);
   }
 }
 
+void UpdatePlaceholderDispatcher(
+    base::WeakPtr<CanvasResourceDispatcher> dispatcher,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    int placeholder_canvas_id) {
+  OffscreenCanvasPlaceholder* placeholder_canvas =
+      OffscreenCanvasPlaceholder::GetPlaceholderCanvasById(
+          placeholder_canvas_id);
+  // Note that the placeholder canvas may be destroyed when this post task get
+  // to executed.
+  if (placeholder_canvas)
+    placeholder_canvas->SetOffscreenCanvasDispatcher(dispatcher, task_runner);
+}
+
 }  // namespace
 
 void CanvasResourceDispatcher::PostImageToPlaceholderIfNotBlocked(
@@ -131,17 +142,13 @@
     viz::ResourceId resource_id) {
   scoped_refptr<base::SingleThreadTaskRunner> dispatcher_task_runner =
       Thread::Current()->GetTaskRunner();
-
   // After this point, |canvas_resource| can only be used on the main thread,
   // until it is returned.
   canvas_resource->Transfer();
-
   PostCrossThreadTask(
       *Thread::MainThread()->Scheduler()->CompositorTaskRunner(), FROM_HERE,
-      CrossThreadBindOnce(UpdatePlaceholderImage, this->GetWeakPtr(),
-                          WTF::Passed(std::move(dispatcher_task_runner)),
-                          placeholder_canvas_id_, std::move(canvas_resource),
-                          resource_id));
+      CrossThreadBindOnce(UpdatePlaceholderImage, placeholder_canvas_id_,
+                          std::move(canvas_resource), resource_id));
 }
 
 void CanvasResourceDispatcher::DispatchFrameSync(
@@ -405,6 +412,32 @@
     sink_->DidDeleteSharedBitmap(std::move(id));
 }
 
+void CanvasResourceDispatcher::SetFilterQuality(
+    SkFilterQuality filter_quality) {
+  if (Client())
+    Client()->SetFilterQualityInResource(filter_quality);
+}
+
+void CanvasResourceDispatcher::SetPlaceholderCanvasDispatcher(
+    int placeholder_canvas_id) {
+  scoped_refptr<base::SingleThreadTaskRunner> dispatcher_task_runner =
+      Thread::Current()->GetTaskRunner();
+
+  // If the offscreencanvas is in the same tread as the canvas, we will update
+  // the canvas resource dispatcher directly. So Offscreen Canvas can behave in
+  // a more synchronous way when it's on the main thread.
+  if (IsMainThread()) {
+    UpdatePlaceholderDispatcher(this->GetWeakPtr(), dispatcher_task_runner,
+                                placeholder_canvas_id);
+  } else {
+    PostCrossThreadTask(
+        *Thread::MainThread()->Scheduler()->CompositorTaskRunner(), FROM_HERE,
+        CrossThreadBindOnce(UpdatePlaceholderDispatcher, this->GetWeakPtr(),
+                            WTF::Passed(std::move(dispatcher_task_runner)),
+                            placeholder_canvas_id));
+  }
+}
+
 void CanvasResourceDispatcher::ReclaimResourceInternal(
     viz::ResourceId resource_id) {
   auto it = resources_.find(resource_id);
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
index 30710a8..9a6ac41 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h
@@ -23,6 +23,7 @@
 class CanvasResourceDispatcherClient {
  public:
   virtual bool BeginFrame() = 0;
+  virtual void SetFilterQualityInResource(SkFilterQuality filter_quality) = 0;
 };
 
 class PLATFORM_EXPORT CanvasResourceDispatcher
@@ -81,6 +82,9 @@
                                ::gpu::mojom::blink::MailboxPtr id);
   void DidDeleteSharedBitmap(::gpu::mojom::blink::MailboxPtr id);
 
+  void SetFilterQuality(SkFilterQuality filter_quality);
+  void SetPlaceholderCanvasDispatcher(int placeholder_canvas_id);
+
  private:
   friend class CanvasResourceDispatcherTest;
   struct FrameResource;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 2f5d4eb..0a580ab 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -222,6 +222,7 @@
     // readers.
     EndWriteAccess();
     scoped_refptr<CanvasResource> resource = resource_;
+    resource->SetFilterQuality(FilterQuality());
     if (ContextProviderWrapper()
             ->ContextProvider()
             ->GetCapabilities()
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
index 4a55245..7dce495a 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
 #include "third_party/blink/renderer/platform/graphics/test/gpu_test_utils.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/skia/include/core/SkFilterQuality.h"
 
 using testing::_;
 using testing::InSequence;
@@ -34,6 +35,7 @@
   MockCanvasResourceDispatcherClient() = default;
 
   MOCK_METHOD0(BeginFrame, bool());
+  MOCK_METHOD1(SetFilterQualityInResource, void(SkFilterQuality));
 };
 
 }  // anonymous namespace
diff --git a/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc b/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc
index 84dbe25..87831e86 100644
--- a/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc
+++ b/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc
@@ -17,7 +17,6 @@
 typedef HashMap<int, blink::OffscreenCanvasPlaceholder*> PlaceholderIdMap;
 
 PlaceholderIdMap& placeholderRegistry() {
-  DCHECK(IsMainThread());
   DEFINE_STATIC_LOCAL(PlaceholderIdMap, s_placeholderRegistry, ());
   return s_placeholderRegistry;
 }
@@ -40,6 +39,14 @@
   }
 }
 
+void UpdateDispatcherFilterQuality(
+    base::WeakPtr<blink::CanvasResourceDispatcher> dispatcher,
+    SkFilterQuality filter) {
+  if (dispatcher) {
+    dispatcher->SetFilterQuality(filter);
+  }
+}
+
 }  // unnamed namespace
 
 namespace blink {
@@ -48,17 +55,13 @@
   UnregisterPlaceholderCanvas();
 }
 
-void OffscreenCanvasPlaceholder::SetOffscreenCanvasFrame(
+void OffscreenCanvasPlaceholder::SetOffscreenCanvasResource(
     scoped_refptr<CanvasResource> new_frame,
-    base::WeakPtr<CanvasResourceDispatcher> dispatcher,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     viz::ResourceId resource_id) {
   DCHECK(IsOffscreenCanvasRegistered());
   DCHECK(new_frame);
   ReleaseOffscreenCanvasFrame();
   placeholder_frame_ = std::move(new_frame);
-  frame_dispatcher_ = std::move(dispatcher);
-  frame_dispatcher_task_runner_ = std::move(task_runner);
   placeholder_frame_resource_id_ = resource_id;
 
   if (animation_state_ == kShouldSuspendAnimation) {
@@ -72,16 +75,56 @@
   }
 }
 
+void OffscreenCanvasPlaceholder::SetOffscreenCanvasDispatcher(
+    base::WeakPtr<CanvasResourceDispatcher> dispatcher,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  DCHECK(IsOffscreenCanvasRegistered());
+  frame_dispatcher_ = std::move(dispatcher);
+  frame_dispatcher_task_runner_ = std::move(task_runner);
+  // The UpdateOffscreenCanvasFilterQuality could be called to change the filter
+  // quality before this function. We need to first apply the filter changes to
+  // the corresponding offscreen canvas.
+  if (filter_quality_) {
+    SkFilterQuality quality = filter_quality_.value();
+    filter_quality_ = base::nullopt;
+    UpdateOffscreenCanvasFilterQuality(quality);
+  }
+}
+
 void OffscreenCanvasPlaceholder::ReleaseOffscreenCanvasFrame() {
   DCHECK(IsOffscreenCanvasRegistered());
-  if (placeholder_frame_) {
-    DCHECK(frame_dispatcher_task_runner_);
-    placeholder_frame_->Transfer();
+  if (!placeholder_frame_)
+    return;
+
+  DCHECK(frame_dispatcher_task_runner_);
+  placeholder_frame_->Transfer();
+  PostCrossThreadTask(
+      *frame_dispatcher_task_runner_, FROM_HERE,
+      CrossThreadBindOnce(releaseFrameToDispatcher, frame_dispatcher_,
+                          std::move(placeholder_frame_),
+                          placeholder_frame_resource_id_));
+}
+
+void OffscreenCanvasPlaceholder::UpdateOffscreenCanvasFilterQuality(
+    SkFilterQuality filter_quality) {
+  DCHECK(IsOffscreenCanvasRegistered());
+  if (!frame_dispatcher_task_runner_) {
+    filter_quality_ = filter_quality;
+    return;
+  }
+
+  if (filter_quality_ == filter_quality)
+    return;
+
+  filter_quality_ = filter_quality;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      Thread::Current()->GetTaskRunner();
+  if (task_runner == frame_dispatcher_task_runner_) {
+    UpdateDispatcherFilterQuality(frame_dispatcher_, filter_quality);
+  } else {
     PostCrossThreadTask(*frame_dispatcher_task_runner_, FROM_HERE,
-                        CrossThreadBindOnce(releaseFrameToDispatcher,
-                                            std::move(frame_dispatcher_),
-                                            std::move(placeholder_frame_),
-                                            placeholder_frame_resource_id_));
+                        CrossThreadBindOnce(UpdateDispatcherFilterQuality,
+                                            frame_dispatcher_, filter_quality));
   }
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h b/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h
index dc91d6cb..c21fdd43 100644
--- a/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h
+++ b/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h
@@ -12,6 +12,7 @@
 #include "components/viz/common/resources/resource_id.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/skia/include/core/SkFilterQuality.h"
 
 namespace blink {
 
@@ -24,11 +25,12 @@
  public:
   ~OffscreenCanvasPlaceholder();
 
-  virtual void SetOffscreenCanvasFrame(
-      scoped_refptr<CanvasResource>,
+  virtual void SetOffscreenCanvasResource(scoped_refptr<CanvasResource>,
+                                          viz::ResourceId resource_id);
+  void SetOffscreenCanvasDispatcher(
       base::WeakPtr<CanvasResourceDispatcher>,
-      scoped_refptr<base::SingleThreadTaskRunner>,
-      viz::ResourceId resource_id);
+      scoped_refptr<base::SingleThreadTaskRunner>);
+
   void ReleaseOffscreenCanvasFrame();
 
   void SetSuspendOffscreenCanvasAnimation(bool);
@@ -46,6 +48,8 @@
     return placeholder_id_ != kNoPlaceholderId;
   }
 
+  void UpdateOffscreenCanvasFilterQuality(SkFilterQuality filter_quality);
+
  private:
   bool PostSetSuspendAnimationToOffscreenCanvasThread(bool suspend);
 
@@ -67,6 +71,7 @@
     kShouldActivateAnimation,
   };
   AnimationState animation_state_ = kActiveAnimation;
+  base::Optional<SkFilterQuality> filter_quality_ = base::nullopt;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/json/json_values.cc b/third_party/blink/renderer/platform/json/json_values.cc
index 21e84cc..1d35618 100644
--- a/third_party/blink/renderer/platform/json/json_values.cc
+++ b/third_party/blink/renderer/platform/json/json_values.cc
@@ -87,9 +87,9 @@
 
 }  // anonymous namespace
 
-const char* kJSONNullString = "null";
-const char* kJSONTrueString = "true";
-const char* kJSONFalseString = "false";
+const char kJSONNullString[] = "null";
+const char kJSONTrueString[] = "true";
+const char kJSONFalseString[] = "false";
 
 void EscapeStringForJSON(const String& str, StringBuilder* dst) {
   for (unsigned i = 0; i < str.length(); ++i) {
diff --git a/third_party/blink/renderer/platform/json/json_values.h b/third_party/blink/renderer/platform/json/json_values.h
index a3808a1eb..cd93b39 100644
--- a/third_party/blink/renderer/platform/json/json_values.h
+++ b/third_party/blink/renderer/platform/json/json_values.h
@@ -265,9 +265,9 @@
   Vector<std::unique_ptr<JSONValue>> data_;
 };
 
-extern const char* kJSONNullString;
-extern const char* kJSONTrueString;
-extern const char* kJSONFalseString;
+extern const char kJSONNullString[];
+extern const char kJSONTrueString[];
+extern const char kJSONFalseString[];
 
 PLATFORM_EXPORT void EscapeStringForJSON(const String&, StringBuilder*);
 void DoubleQuoteStringForJSON(const String&, StringBuilder*);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/nested-overflow-subtree-layout-ref.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/nested-overflow-subtree-layout-ref.html
new file mode 100644
index 0000000..77b0de7f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/nested-overflow-subtree-layout-ref.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<head>
+  <link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+  <link rel="help" href="https://drafts.csswg.org/css-scroll-anchoring/">
+  <script src="/common/reftest-wait.js"></script>
+</head>
+<style>
+#outer {
+  overflow: hidden;
+  width: 500px;
+  height: 500px;
+}
+#inner {
+  overflow: auto;
+  position: relative;
+  width: 500px;
+  height: 2000px;
+}
+p {
+
+  font: 48pt monospace;
+}
+</style>
+</head>
+<body>
+<div id="outer">
+  <div id="inner">
+    <p>Anchor</p>
+  </div>
+</div>
+<script>
+const outer = document.querySelector("#outer");
+const inner = document.querySelector("#inner");
+
+onload = () => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      outer.scrollTo(0, 70);
+      document.documentElement.removeAttribute("class");
+    });
+  });
+};
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/nested-overflow-subtree-layout.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/nested-overflow-subtree-layout.html
new file mode 100644
index 0000000..ffa52b6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/nested-overflow-subtree-layout.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<head>
+  <title>Test that subtree layout with nested overflow preserves scroll anchoring.</title>
+  <link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
+  <link rel="help" href="https://drafts.csswg.org/css-scroll-anchoring/">
+  <link rel="match" href="nested-overflow-subtree-layout-ref.html">
+  <script src="/common/reftest-wait.js"></script>
+</head>
+<style>
+#outer {
+  overflow: hidden;
+  width: 500px;
+  height: 500px;
+}
+#inner {
+  overflow: auto;
+  position: relative;
+  width: 500px;
+  height: 2000px;
+}
+p {
+
+  font: 48pt monospace;
+}
+</style>
+</head>
+<body>
+<div id="outer">
+  <div id="inner">
+    <p>Anchor</p>
+  </div>
+</div>
+<script>
+const outer = document.querySelector("#outer");
+const inner = document.querySelector("#inner");
+
+onload = () => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      outer.scrollTo(0, 70);
+      requestAnimationFrame(() => {
+          const elem = document.createElement("p");
+          elem.textContent = "FAIL";
+          inner.insertBefore(elem, inner.firstChild);
+           document.documentElement.removeAttribute("class");
+      });
+    });
+  });
+};
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-017.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-017.html
new file mode 100644
index 0000000..a2521cb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-017.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.3. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="The text is wrapped into two lines, since there is no need to break the second line using the space in the middle.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 3ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXX<br>X X</div>
+  <div class="test">XXXX<span> </span>X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-001.html
new file mode 100644
index 0000000..a4158dc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-001.html
@@ -0,0 +1,39 @@
+
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere and the white-space property</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="'line-break: anywhere' can't prevent overflow under 'white-space: pre', because it line wrapping is not allowed. ">
+<style>
+div {
+  position: relative;
+  font: 10px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 3ch;
+  line-break: anywhere;
+
+  white-space: pre;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXXX<span style="color: green">XXXX</span>XX</div>
+  <div class="test">XXXX    XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-002.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-002.html
new file mode 100644
index 0000000..2fbb53d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-002.html
@@ -0,0 +1,40 @@
+
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere and the white-space property</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-nowrap">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="'line-break: anywhere' can't prevent overflow under 'white-space: nowrap', because it line wrapping is not allowed. ">
+<style>
+div {
+  position: relative;
+  font: 10px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 5ch;
+  line-break: anywhere;
+
+  white-space: nowrap;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXXX XX</div>
+  <div class="test">XXXX    XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-003.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-003.html
new file mode 100644
index 0000000..3002dc10
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-003.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere and the white-space property</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="'line-break: anywhere' can't prevent overflow under 'white-space: pre', because it line wrapping is not allowed. ">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 2ch;
+  line-break: anywhere;
+
+  white-space: pre;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"><span style="color: green">X</span>XXX</div>
+  <div class="test"> XXX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-004.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-004.html
new file mode 100644
index 0000000..e6e1b39
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-004.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere and the white-space property</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-normal">
+<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="The line is wrapped ignoring the white space, which will be removed honoring 'white-space: normal'">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 4ch;
+  line-break: anywhere;
+
+  white-space: normal;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXX<br>XX</div>
+  <div class="test">XXX<span style="background: red"> </span>XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-005.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-005.html
new file mode 100644
index 0000000..2f64d870
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-005.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere and the white-space property</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-normal">
+<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="The line is wrapped ignoring the white space, which will be removed honoring 'white-space: normal'">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 4ch;
+  line-break: anywhere;
+
+  white-space: normal;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXXX<br>X</div>
+  <div class="test"><span style="background: red"> </span>XXXXX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-006.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-006.html
new file mode 100644
index 0000000..c804e37
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-006.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere and the white-space property</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
+<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="'line-break: anywhere' can't break a preserved sequence of spaces under 'white-spate: pre-wrap', which should hang instead.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 2ch;
+
+  line-break: anywhere;
+  white-space: pre-wrap;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXXX<br>XX</div>
+  <div class="test">X<span style="background: green">   </span>XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-007.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-007.html
new file mode 100644
index 0000000..d9b266d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-007.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere and the white-space property</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-pre-wrap">
+<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="'line-break: anywhere' can't break before the first white-space affter a word under 'white-spate: pre-wrap', which should hang instead.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 3ch;
+
+  line-break: anywhere;
+  white-space: pre-wrap;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXXX<br>XX</div>
+  <div class="test">XXX<span style="background: green"> </span>XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-008.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-008.html
new file mode 100644
index 0000000..aebd31ea
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-008.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere and the white-space property</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="'line-break: anywhere' allows preserved white spaces at the end of the line, honoring 'white-space: break-spaces'.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 4ch;
+
+  line-break: anywhere;
+  white-space: break-spaces;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XX<span style="color: green">XX<br>XX</span>XX<br>XXX</div>
+  <div class="test">XX    XXXXX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-009.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-009.html
new file mode 100644
index 0000000..f536c90
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-and-white-space-009.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere and the white-space property</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="3. White Space and Wrapping: the white-space property" href="https://drafts.csswg.org/css-text-3/#white-space-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-white-space-break-spaces">
+<link rel="help" title="4.1.3. Phase II: Trimming and Positioning" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="'line-break: anywhere' allows breaking before the first character of a preserved white space sequence, honoring 'white-space: break-spaces'.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 4ch;
+
+  line-break: anywhere;
+  white-space: break-spaces;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXXX<br><span style="color: green">XXX</span>X<br>XXXX<br><span style="color: green">X</span>X</div>
+  <div class="test">XXXX   XXXXX X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-001.html
new file mode 100644
index 0000000..d18df4b3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-001.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the WJ classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  width: 100px;
+  height: 100px;
+  background: red;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 2ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"></div>
+  <div class="test">XX&#x2060;XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-002.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-002.html
new file mode 100644
index 0000000..1255aa1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-002.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the WJ classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  width: 100px;
+  height: 100px;
+  background: red;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 2ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"></div>
+  <div class="test">XX&#xFEFF;XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-003.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-003.html
new file mode 100644
index 0000000..7fe230e890
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-003.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the ZW classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  width: 100px;
+  height: 100px;
+  color: red;
+  background: green;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 4ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXXX<br>XX</div>
+  <div class="test">..&#x200B;...X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-004.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-004.html
new file mode 100644
index 0000000..13706d5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-004.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  width: 100px;
+  height: 100px;
+  background: red;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 3ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"></div>
+  <div class="test">XX&nbsp;XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-005.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-005.html
new file mode 100644
index 0000000..175228d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-005.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 2ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XX<br><span style="color: green">X</span>X</div>
+  <div class="test">XX&nbsp;X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-006.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-006.html
new file mode 100644
index 0000000..9bb557e7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-006.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 4ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"></div>
+  <div class="test">XX &nbsp;XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-007.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-007.html
new file mode 100644
index 0000000..7f90bfd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-007.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 20px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  height: 100px;
+  color: red;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 5ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXXXX<br>XX</div>
+  <div class="test">XXX<span style="background: green"> &nbsp;X</span>XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-008.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-008.html
new file mode 100644
index 0000000..40826540
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-008.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 3ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XX<br>X</div>
+  <div class="test">XX&#x202F;X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-009.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-009.html
new file mode 100644
index 0000000..6d6ca3f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-009.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 2ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XX<br>X</div>
+  <div class="test">XX&#x180E;X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-010.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-010.html
new file mode 100644
index 0000000..5f152c6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-010.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 3ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"></div>
+  <div class="test">XX &#x180E;XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-011.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-011.html
new file mode 100644
index 0000000..2196d98
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-011.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 2ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XX<br>X</div>
+  <div class="test">XX&#x034F;X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-012.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-012.html
new file mode 100644
index 0000000..110d9ca
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-012.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 3ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"></div>
+  <div class="test">XX &#x034F;XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-013.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-013.html
new file mode 100644
index 0000000..2cda76c5c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-013.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 2ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"></div>
+  <div class="test">XX&#x2011;X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-014.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-014.html
new file mode 100644
index 0000000..d79efb7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-014.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the GL classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 4ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XX X<br>XX</div>
+  <div class="test">XX &#x2011;XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-015.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-015.html
new file mode 100644
index 0000000..5eecd03
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-015.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the ZWJ classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 50px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: red;
+  height: 100px;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 2ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"></div>
+  <div class="test">XX&#x200D;XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-016.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-016.html
new file mode 100644
index 0000000..d4ab337
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-break/line-break-anywhere-overrides-uax-behavior-016.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: line-break: anywhere overrides behavior defined for the WJ, ZW, GL, and ZWJ classes</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.1. Line Breaking Details" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="help" title="5.3. Line Breaking Strictness: the line-break property" href="https://drafts.csswg.org/css-text-3/#line-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-line-break-anywhere">
+<link rel="match" href="reference/line-break-anywhere-004-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="Except where explicitly defined by 'line-break: anywhere' line breaking behavior defined for the ZWJ classes in [UAX14] must be honored. ">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 4ch;
+  line-break: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XX X<br>X</div>
+  <div class="test">XX &#x200D;XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-anywhere-009.html b/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-anywhere-009.html
new file mode 100644
index 0000000..90f2ad33
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-anywhere-009.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: overflow-wrap: anywhere</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.5. Overflow Wrapping: the overflow-wrap/word-wrap property" href="https://drafts.csswg.org/css-text-3/#overflow-wrap-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-overflow-wrap-anywhere">
+<link rel="match" href="reference/overflow-wrap-break-word-001-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="The text is wrapped into two lines, since there is no need to break the second line using the space in the middle.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.ref {
+  position: absolute;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 3ch;
+
+  overflow-wrap: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="ref">XXX<br>X X</div>
+  <div class="test">XXXX<span> </span>X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-anywhere-010.html b/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-anywhere-010.html
new file mode 100644
index 0000000..0b4b75b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-anywhere-010.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: overflow-wrap: anywhere</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.5. Overflow Wrapping: the overflow-wrap/word-wrap property" href="https://drafts.csswg.org/css-text-3/#overflow-wrap-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-overflow-wrap-anywhere">
+<link rel="match" href="reference/overflow-wrap-break-word-001-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="'overflow-wrap: anywhere' applies correctly when there is styled text using 'span' elements.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: transparent;
+  width: 4ch;
+  overflow-wrap: anywhere;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red"><span style="color: green">XX</span>XX<br>XX</div>
+  <div class="test">XX<span style="color: green">XXXX</span>XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-break-word-009.html b/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-break-word-009.html
new file mode 100644
index 0000000..fbbcf22
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-break-word-009.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: overflow-wrap: break-word</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.5. Overflow Wrapping: the overflow-wrap/word-wrap property" href="https://drafts.csswg.org/css-text-3/#overflow-wrap-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-overflow-wrap-break-word">
+<link rel="match" href="reference/overflow-wrap-break-word-001-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="The text is wrapped into two lines, since there is no need to break the second line using the space in the middle.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.ref {
+  position: absolute;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 3ch;
+
+  overflow-wrap: break-word;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="ref">XXX<br>X X</div>
+  <div class="test">XXXX<span> </span>X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-break-word-010.html b/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-break-word-010.html
new file mode 100644
index 0000000..da45dea2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/overflow-wrap/overflow-wrap-break-word-010.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: overflow-wrap: break-word</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.5. Overflow Wrapping: the overflow-wrap/word-wrap property" href="https://drafts.csswg.org/css-text-3/#overflow-wrap-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-overflow-wrap-break-word">
+<link rel="match" href="reference/overflow-wrap-break-word-001-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="'overflow-wrap: break-word' applies correctly when there is styled text using 'span' elements.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.ref {
+  position: absolute;
+  background: green;
+  color: red;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: transparent;
+  width: 4ch;
+  overflow-wrap: break-word;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="ref"><span style="color: green">XX</span>XX<br>XX</div>
+  <div class="test">XX<span style="color: green">XXXX</span>XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-break-all-029.html b/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-break-all-029.html
new file mode 100644
index 0000000..3e0a1e14
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-break-all-029.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: word-break: break-all</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-all">
+<link rel="match" href="reference/word-break-break-all-010-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="The text is wrapped into two lines, since there is no need to break the second line using the space in the middle.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.red {
+  position: absolute;
+  background: green;
+  color: red;
+  width: 100px;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: green;
+  width: 3ch;
+  word-break: break-all;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="red">XXX<br>X X</div>
+  <div class="test">XXXX<span> </span>X</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-break-all-030.html b/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-break-all-030.html
new file mode 100644
index 0000000..e6a8a00
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-break-all-030.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset="utf-8">
+<title>CSS Text Test: word-break: break-all</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-all">
+<link rel="match" href="reference/word-break-break-all-010-ref.html">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<meta name="flags" content="Ahem">
+<meta name="assert" content="word-break: break-all applies correctly when there is styled text using 'span' elemetns.">
+<style>
+div {
+  position: relative;
+  font: 25px / 1 Ahem;
+}
+.ref {
+  position: absolute;
+  background: green;
+  color: red;
+  height: 100px;
+  z-index: -1;
+}
+.test {
+  color: transparent;
+  width: 4ch;
+  word-break: break-all;
+}
+</style>
+<body>
+  <p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
+  <div class="ref"><span style="color: green">XX</span>XX<br>XX</div>
+  <div class="test">XX<span style="color: green">XXXX</span>XX</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-break-all-inline-010.html b/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-break-all-inline-010.html
index 875a3623..3c2df3c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-break-all-inline-010.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/word-break/word-break-break-all-inline-010.html
@@ -5,9 +5,10 @@
 <link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
 <link rel="help" title="5.2. Breaking Rules for Letters: the word-break property" href="https://drafts.csswg.org/css-text-3/#word-break-property">
 <link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-word-break-break-all">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <meta name="flags" content="Ahem">
 <link rel="match" href="reference/word-break-break-all-010-ref.html">
-<meta name="assert" content="'overflow-wrap: anywhere' allows to break after the first character of the inline-block it applies to">
+<meta name="assert" content="'word-break: break-all' allows to break after the first character of the inline-block it applies to">
 <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
 <style>
 div, span {
diff --git a/third_party/blink/web_tests/fast/harness/results.html b/third_party/blink/web_tests/fast/harness/results.html
index 2682638..8d9a9bd 100644
--- a/third_party/blink/web_tests/fast/harness/results.html
+++ b/third_party/blink/web_tests/fast/harness/results.html
@@ -528,8 +528,7 @@
     let pathParser = new PathParser(path);
     let html = `
     <div class='expect' tabindex='0' data-id='${test.expectId}'>
-      <div class='details'></div>${Report.printFlag(test)}
-     ${pathParser.dir}<a target='test' tabindex='-1' href='${pathParser.testHref}'>${pathParser.file}${pathParser.query}</a>
+      <div class='details'></div>${Report.printFlag(test)}${pathParser.dir}<a target='test' tabindex='-1' href='${pathParser.testHref}'>${pathParser.file}${pathParser.query}</a>
     </div>`;
     traversal.html.push(html);
   },
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-cookies-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-cookies-expected.txt
new file mode 100644
index 0000000..91af266
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-cookies-expected.txt
@@ -0,0 +1,25 @@
+Tests that fetch exposes cookies for CORS XHRs.
+Request to https://site1.a.test/, type: Document
+Request to https://site2.a.test/, type: Document
+Request to https://site1.a.test/post, type: XHR
+Cookies after cross-origin XHR:
+undefined
+Request to https://site2.a.test/post, type: XHR
+Cookies after same-origin XHR:
+CrossOriginCookie=om-nom-nom-nom; SameOriginCookie=me-want-cookie
+Request to https://site1.a.test/post, type: XHR
+Cookies after cross-origin fetch with {credentials: 'include'}:
+CrossOriginCookie=om-nom-nom-nom
+Request to https://site1.a.test/post, type: XHR
+Cookies after cross-origin fetch with {credentials: 'same-origin'}:
+undefined
+Request to https://site2.a.test/post, type: XHR
+Cookies after same-origin fetch with {credentials: 'same-origin'}:
+CrossOriginCookie=om-nom-nom-nom; SameOriginCookie=me-want-cookie
+Request to https://site2.a.test/post, type: XHR
+Cookies after same-origin fetch with {credentials: 'include', mode: 'no-cors'}:
+CrossOriginCookie=om-nom-nom-nom; SameOriginCookie=me-want-cookie
+Request to https://site1.a.test/post, type: XHR
+Cookies after cross-origin fetch with {credentials: 'include', mode: 'no-cors'}:
+CrossOriginCookie=om-nom-nom-nom
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-cookies.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-cookies.js
new file mode 100644
index 0000000..8e6971d1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/fetch-cookies.js
@@ -0,0 +1,75 @@
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startBlank(
+      `Tests that fetch exposes cookies for CORS XHRs.`);
+
+  const FetchHelper = await testRunner.loadScript('resources/fetch-test.js');
+  const helper = new FetchHelper(testRunner, testRunner.browserP());
+  await helper.enable();
+
+  helper.onceRequest(/site1.a.test/).fulfill({
+    responseCode: 302,
+    responseHeaders: [
+      {name: 'Location', value: 'https://site2.a.test'},
+      {name: 'Set-Cookie', value: 'CrossOriginCookie=om-nom-nom-nom; path=/; domain=.a.test; Secure; HttpOnly; SameSite=none'},
+    ]
+  });
+
+  helper.onceRequest(/site2.a.test/).fulfill({
+    responseCode: 200,
+    responseHeaders: [
+        {name: 'Set-Cookie', value: 'SameOriginCookie=me-want-cookie; Secure; domain=site2.a.test; HttpOnly; SameSite=none'},
+    ],
+    body: btoa("<html></html>")
+  });
+
+  await dp.Page.enable();
+  await session.navigate('https://site1.a.test');
+
+  async function makeRequestAndDumpCookies(code, description) {
+    session.evaluate(code);
+    const request = await helper.onceRequest(/a.test/).matched();
+    testRunner.log(`Cookies after ${description}:`);
+    testRunner.log(request.request.headers['Cookie']);
+    dp.Fetch.fulfillRequest({requestId: request.requestId, responseCode: 200});
+  }
+
+  await makeRequestAndDumpCookies(`
+      const xhr = new XMLHttpRequest();
+      xhr.open('POST', 'https://site1.a.test/post');
+      xhr.send('postdata');
+      `, 'cross-origin XHR');
+
+  await makeRequestAndDumpCookies(`
+      const xhr2 = new XMLHttpRequest();
+      xhr2.open('POST', '/post');
+      xhr2.send('postdata');
+      `, 'same-origin XHR');
+
+  await makeRequestAndDumpCookies(`
+      fetch('https://site1.a.test/post',
+          {method: 'POST', body: 'postdata', credentials: 'include'});
+      `, `cross-origin fetch with {credentials: 'include'}`);
+
+  await makeRequestAndDumpCookies(`
+      fetch('https://site1.a.test/post',
+          {method: 'POST', body: 'postdata', credentials: 'same-origin'});
+      `, `cross-origin fetch with {credentials: 'same-origin'}`);
+
+  await makeRequestAndDumpCookies(`
+      fetch('/post',
+          {method: 'POST', body: 'postdata', credentials: 'same-origin'});
+      `, `same-origin fetch with {credentials: 'same-origin'}`);
+
+  await makeRequestAndDumpCookies(`
+      fetch('/post',
+          {method: 'POST', body: 'postdata', credentials: 'include', mode: 'no-cors'});
+      `, `same-origin fetch with {credentials: 'include', mode: 'no-cors'}`);
+
+  await makeRequestAndDumpCookies(`
+      fetch('https://site1.a.test/post',
+          {method: 'POST', body: 'postdata', credentials: 'include', mode: 'no-cors'});
+      `, `cross-origin fetch with {credentials: 'include', mode: 'no-cors'}`);
+
+  testRunner.completeTest();
+})
+
diff --git a/third_party/blink/web_tests/inspector-protocol/timeline/timeline-layout.js b/third_party/blink/web_tests/inspector-protocol/timeline/timeline-layout.js
index d9dc48c..9507a16 100644
--- a/third_party/blink/web_tests/inspector-protocol/timeline/timeline-layout.js
+++ b/third_party/blink/web_tests/inspector-protocol/timeline/timeline-layout.js
@@ -21,23 +21,21 @@
   await tracingHelper.invokeAsyncWithTracing(performActions);
 
   var schedRecalc = tracingHelper.findEvent('ScheduleStyleRecalculation', 'I');
-  var recalcBegin = tracingHelper.findEvent('UpdateLayoutTree', 'B');
-  var recalcEnd = tracingHelper.findEvent('UpdateLayoutTree', 'E');
-  testRunner.log('UpdateLayoutTree frames match: ' + (schedRecalc.args.data.frame === recalcBegin.args.beginData.frame));
-  testRunner.log('UpdateLayoutTree elementCount > 0: ' + (recalcEnd.args.elementCount > 0));
+  var recalc = tracingHelper.findEvent('UpdateLayoutTree', 'X');
+  testRunner.log('UpdateLayoutTree frames match: ' + (schedRecalc.args.data.frame === recalc.args.beginData.frame));
+  testRunner.log('UpdateLayoutTree elementCount > 0: ' + (recalc.args.elementCount > 0));
 
   var invalidate = tracingHelper.findEvent('InvalidateLayout', 'I');
-  var layoutBegin = tracingHelper.findEvent('Layout', 'B');
-  var layoutEnd = tracingHelper.findEvent('Layout', 'E');
+  var layout = tracingHelper.findEvent('Layout', 'X');
 
-  testRunner.log('InvalidateLayout frames match: ' + (recalcBegin.args.beginData.frame === invalidate.args.data.frame));
+  testRunner.log('InvalidateLayout frames match: ' + (recalc.args.beginData.frame === invalidate.args.data.frame));
 
-  var beginData = layoutBegin.args.beginData;
+  var beginData = layout.args.beginData;
   testRunner.log('Layout frames match: ' + (invalidate.args.data.frame === beginData.frame));
   testRunner.log('dirtyObjects > 0: ' + (beginData.dirtyObjects > 0));
   testRunner.log('totalObjects > 0: ' + (beginData.totalObjects > 0));
 
-  var endData = layoutEnd.args.endData;
+  var endData = layout.args.endData;
   testRunner.log('has rootNode id: ' + (endData.rootNode > 0));
   testRunner.log('has root quad: ' + !!endData.root);
 
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
index 62554e4..810bfa7f 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..b1f1372a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
index 068fcacd..92616c5 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..3d591ddf
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
new file mode 100644
index 0000000..67ca40b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..cec2c10
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
new file mode 100644
index 0000000..67ca40b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..cec2c10
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
new file mode 100644
index 0000000..67ca40b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..cec2c10
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
new file mode 100644
index 0000000..67ca40b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..cec2c10
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
index 891200f..67ca40b 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..cec2c10
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
index a61614d..ac67df2 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..60d93a46
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
new file mode 100644
index 0000000..26dd754
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..c13cbb4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
index 8e796d7e..e1b37cb 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
new file mode 100644
index 0000000..26dd754
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..c13cbb4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic.html b/third_party/blink/web_tests/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic.html
index 45700a5..28c32c7 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic.html
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic.html
@@ -73,6 +73,20 @@
   </optgroup>
 </select>
 
+<select multiple style="background-color: blue;">
+  <optgroup label="background-color: blue">
+    <option selected>first selected option</option>
+    <option>unselected option</option>
+    <option selected>second selected option</option>
+  </optgroup>
+</select>
+
+<select multiple style="background-color: blue;">
+  <option selected>selected option</option>
+  <option>unselected option</option>
+  <option>unselected option</option>
+</select> <br>
+
 <!-- shadow -->
 <select multiple style="box-shadow: 4px 4px 10px rgba(255,0,0,0.5), inset 4px 4px 4px rgba(0,255,0,0.5);">
   <optgroup label="box-shadow">
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png b/third_party/blink/web_tests/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
new file mode 100644
index 0000000..433dc87
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic.html b/third_party/blink/web_tests/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic.html
new file mode 100644
index 0000000..9e72a60
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color-scheme/select/select-popup-appearance-basic.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<script>
+testRunner.waitUntilDone();
+</script>
+<script src="../../../../fast/forms/resources/picker-common.js"></script>
+<select id="menu" style="background-color: blue;">
+  <optgroup label="Theropods">
+      <option>Tyrannosaurus</option>
+      <option>Velociraptor</option>
+      <option>Deinonychus</option>
+  </optgroup>
+  <optgroup label="Sauropods">
+      <option>Diplodocus</option>
+      <option>Saltasaurus</option>
+      <option>Apatosaurus</option>
+  </optgroup>
+</select>
+<script>
+openPicker(document.getElementById('menu'), () => testRunner.notifyDone(), () => testRunner.notifyDone());
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/file-handling/file-handling/launchParams-exist.tentative.window-expected.txt b/third_party/blink/web_tests/virtual/file-handling/file-handling/launchParams-exist.tentative.window-expected.txt
deleted file mode 100644
index 265b596..0000000
--- a/third_party/blink/web_tests/virtual/file-handling/file-handling/launchParams-exist.tentative.window-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS launchParams should be available off the global object.
-PASS files should default to an empty array.
-FAIL request is available and correct. assert_true: request must be a Request. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/file-handling/file-handling/launchParams-exist.tentative.window.html b/third_party/blink/web_tests/virtual/file-handling/file-handling/launchParams-exist.tentative.window.html
deleted file mode 100644
index 03a5eda8..0000000
--- a/third_party/blink/web_tests/virtual/file-handling/file-handling/launchParams-exist.tentative.window.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!doctype html>
-<title>Launch Params are available from JavaScript.</title>
-<link rel="help" href="https://github.com/WICG/file-handling/blob/master/explainer.md">
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script>
-
-test(() => {
-  assert_true('launchParams' in window, 'window must have launchParams.');
-  assert_true(window.launchParams instanceof LaunchParams, 'launchParams must be a LaunchParams.');
-}, 'launchParams should be available off the global object.');
-
-test(() => {
-  assert_true('files' in window.launchParams, 'launchParams has files.');
-  assert_true(Array.isArray(window.launchParams.files), 'files must be an array.');
-
-  assert_equals(window.launchParams.files.length, 0);
-}, 'files should default to an empty array.');
-
-test(() => {
-  assert_true('request' in window.launchParams, 'launchParams has request.');
-  assert_true(window.launchParams.request instanceof Request, 'request must be a Request.');
-
-  assert_true('url' in window.launchParams.request, 'request has a url.');
-  assert_equals(window.launchParams.request.url, window.location.href);
-}, 'request is available and correct.');
-
-</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/file-handling/file-handling/launchQueue-exists.tentative.window.html b/third_party/blink/web_tests/virtual/file-handling/file-handling/launchQueue-exists.tentative.window.html
new file mode 100644
index 0000000..2542dc39
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/file-handling/file-handling/launchQueue-exists.tentative.window.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>Launch Params are available from JavaScript.</title>
+<link rel="help" href="https://github.com/WICG/file-handling/blob/master/explainer.md">
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script>
+
+test(() => {
+  assert_true('launchQueue' in window, 'window must have a launchQueue.');
+  assert_true(window.launchQueue instanceof LaunchQueue, 'launchQueue must be a LaunchQueue.');
+}, 'launchQueue should be available off the global object.');
+
+test(() => {
+  assert_true('setConsumer' in window.launchQueue, 'clients should be able to set a consumer for launches.');
+  assert_equals(typeof window.launchQueue.setConsumer, 'function', 'launchQueue.setConsumer should be a function.');
+
+  // There should be no events in the launchQueue in the test.
+  window.launchQueue.setConsumer(launchParams => {
+    console.error(launchParams);
+    throw new Error("This should not be reached in the test");
+  });
+}, 'setConsumer should be a function.');
+
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAuto-expected.html b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAuto-expected.html
new file mode 100644
index 0000000..644a610
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAuto-expected.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+<head>
+    <style type="text/css">
+              #can {
+                width: 500px;
+                height: 500px;
+                image-rendering: auto;
+              }
+    </style>
+</head>
+<body>
+    <canvas id="can" width="50" height="50"></canvas>
+<script>
+
+var outputCanvas = document.getElementById("can");
+var outputContext = outputCanvas.getContext("2d");
+outputContext.fillRect(10, 10, 30, 30);
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAuto.html b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAuto.html
new file mode 100644
index 0000000..66d006e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAuto.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Test if the users can change Image Rendering Quality in Offscreen Canvas</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <style type="text/css">
+#can {
+  width: 500px;
+  height: 500px;
+  image-rendering: pixelated;
+}
+    </style>
+  </head>
+  <body>
+  <canvas id="can" width="50" height="50"></canvas>
+  <script>
+const c2 = document.getElementById("can");
+const offscreen_canvas = c2.transferControlToOffscreen();
+const ctx_o = offscreen_canvas.getContext('2d');
+
+c2.style.imageRendering = "auto";
+
+ctx_o.clearRect(0, 0, 50, 50);
+ctx_o.fillRect(10, 10, 30, 30);
+
+function waitForCanvasToDraw() {
+  return new Promise(resolve => {
+    const testPixel = function() {
+      const d = ctx_o.getImageData(20, 20, 1, 1);
+      if (d.data[0] == 0 && d.data[3] == 255) {
+        setTimeout(resolve, 2000);
+      } else {
+        requestAnimationFrame(testPixel);
+      }
+    }
+    testPixel();
+  })
+}
+
+if (window.testRunner) {
+  testRunner.waitUntilDone();
+}
+waitForCanvasToDraw().then(() => {
+  if (window.testRunner) {
+    testRunner.notifyDone();
+  }
+}).catch(err => {
+  testRunner.notifyDone();
+  throw err;
+});
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAutoWorker-expected.html b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAutoWorker-expected.html
new file mode 100644
index 0000000..644a610
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAutoWorker-expected.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+<head>
+    <style type="text/css">
+              #can {
+                width: 500px;
+                height: 500px;
+                image-rendering: auto;
+              }
+    </style>
+</head>
+<body>
+    <canvas id="can" width="50" height="50"></canvas>
+<script>
+
+var outputCanvas = document.getElementById("can");
+var outputContext = outputCanvas.getContext("2d");
+outputContext.fillRect(10, 10, 30, 30);
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAutoWorker.html b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAutoWorker.html
new file mode 100644
index 0000000..31c97fd
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingAutoWorker.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<title>Test using a placeholder canvas as an image source.</title>
+<head>
+  <title>Test if the users can change Image Rendering Quality in Offscreen Canvas</title>
+  <style type="text/css">
+#can {
+width: 500px;
+height: 500px;
+image-rendering: pixelated;
+}
+  </style>
+</head>
+
+<body>
+  <canvas id="can" width="50" height="50"></canvas>
+
+<script id='myWorker' type='text/worker'>
+
+var ctx_o;
+self.onmessage = function(msg) {
+  ctx_o = msg.data.getContext('2d');
+  draw();
+};
+
+function draw() {
+  ctx_o.clearRect(0, 0, 50, 50);
+  ctx_o.fillRect(10, 10, 30, 30);
+  const d = ctx_o.getImageData(20, 20, 1, 1);
+  if (d.data[0] == 0 && d.data[3] == 255) {
+    self.postMessage("done");
+  } else {
+    requestAnimationFrame(draw);
+  }
+}
+</script>
+
+<script>
+var canvas = document.getElementById("can");
+canvas.width = 50;
+canvas.height = 50;
+var offscreen = canvas.transferControlToOffscreen();
+
+var blob = new Blob([document.getElementById('myWorker').textContent]);
+var worker = new Worker(URL.createObjectURL(blob));
+canvas.style.imageRendering = "auto";
+worker.postMessage(offscreen, [offscreen]);
+
+function waitForCanvasToDraw() {
+  return new Promise(resolve => {
+    var i = 0;
+    const testPixel = function() {
+      canvas.toBlob(blob => {
+        createImageBitmap(blob).then(image => {
+          if (verifyImage(image, "verify if image is drawn on offscreencanvas.")) {
+            setTimeout(resolve, 2000);
+          } else {
+            requestAnimationFrame(testPixel);
+          }
+        })
+      });
+    }
+    testPixel();
+  })
+}
+
+function verifyImage(image, description) {
+  var testCanvas = document.createElement('canvas');
+  var testCtx = testCanvas.getContext('2d');
+  testCtx.drawImage(image, 0, 0);
+  const d = testCtx.getImageData(20, 20, 1, 1);
+  return (d.data[0] == 0 && d.data[3] == 255);
+}
+
+if (window.testRunner) {
+  testRunner.waitUntilDone();
+}
+
+waitForCanvasToDraw().then(() => {
+  if (window.testRunner) {
+    testRunner.notifyDone();
+  }
+}).catch(err => {
+  testRunner.notifyDone();
+  throw err;
+});
+
+</script>
+
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelated-expected.html b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelated-expected.html
new file mode 100644
index 0000000..ee57b481
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelated-expected.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+<head>
+    <style type="text/css">
+              #can {
+                width: 500px;
+                height: 500px;
+                image-rendering: pixelated;
+              }
+    </style>
+</head>
+<body>
+    <canvas id="can" width="50" height="50"></canvas>
+<script>
+
+var outputCanvas = document.getElementById("can");
+var outputContext = outputCanvas.getContext("2d");
+outputContext.fillRect(10, 10, 30, 30);
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelated.html b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelated.html
new file mode 100644
index 0000000..73819cc
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelated.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Test if the users can change Image Rendering Quality in Offscreen Canvas</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <style type="text/css">
+#can {
+  width: 500px;
+  height: 500px;
+  image-rendering: auto;
+}
+    </style>
+  </head>
+  <body>
+  <canvas id="can" width="50" height="50"></canvas>
+  <script>
+const c2 = document.getElementById("can");
+const offscreen_canvas = c2.transferControlToOffscreen();
+const ctx_o = offscreen_canvas.getContext('2d');
+
+c2.style.imageRendering = "pixelated";
+
+ctx_o.clearRect(0, 0, 50, 50);
+ctx_o.fillRect(10, 10, 30, 30);
+
+function waitForCanvasToDraw() {
+  return new Promise(resolve => {
+    const testPixel = function() {
+      const d = ctx_o.getImageData(20, 20, 1, 1);
+      if (d.data[0] == 0 && d.data[3] == 255) {
+        setTimeout(resolve, 2000);
+      } else {
+        requestAnimationFrame(testPixel);
+      }
+    }
+    testPixel();
+  })
+}
+
+if (window.testRunner) {
+  testRunner.waitUntilDone();
+}
+waitForCanvasToDraw().then(() => {
+  if (window.testRunner) {
+    testRunner.notifyDone();
+  }
+}).catch(err => {
+  testRunner.notifyDone();
+  throw err;
+});
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelatedWorker-expected.html b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelatedWorker-expected.html
new file mode 100644
index 0000000..ee57b481
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelatedWorker-expected.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+<head>
+    <style type="text/css">
+              #can {
+                width: 500px;
+                height: 500px;
+                image-rendering: pixelated;
+              }
+    </style>
+</head>
+<body>
+    <canvas id="can" width="50" height="50"></canvas>
+<script>
+
+var outputCanvas = document.getElementById("can");
+var outputContext = outputCanvas.getContext("2d");
+outputContext.fillRect(10, 10, 30, 30);
+
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelatedWorker.html b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelatedWorker.html
new file mode 100644
index 0000000..08b6d00
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/gpu/fast/canvas/OffscreenCanvasImageRenderingPixelatedWorker.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<title>Test using a placeholder canvas as an image source.</title>
+<head>
+  <title>Test if the users can change Image Rendering Quality in Offscreen Canvas</title>
+  <style type="text/css">
+#can {
+width: 500px;
+height: 500px;
+image-rendering: auto;
+}
+  </style>
+</head>
+
+<body>
+  <canvas id="can" width="50" height="50"></canvas>
+
+<script id='myWorker' type='text/worker'>
+
+var ctx_o;
+self.onmessage = function(msg) {
+  ctx_o = msg.data.getContext('2d');
+  draw();
+};
+
+function draw() {
+  ctx_o.clearRect(0, 0, 50, 50);
+  ctx_o.fillRect(10, 10, 30, 30);
+  const d = ctx_o.getImageData(20, 20, 1, 1);
+  if (d.data[0] == 0 && d.data[3] == 255) {
+    self.postMessage("done");
+  } else {
+    requestAnimationFrame(draw);
+  }
+}
+</script>
+
+<script>
+var canvas = document.getElementById("can");
+canvas.width = 50;
+canvas.height = 50;
+var offscreen = canvas.transferControlToOffscreen();
+
+var blob = new Blob([document.getElementById('myWorker').textContent]);
+var worker = new Worker(URL.createObjectURL(blob));
+canvas.style.imageRendering = "pixelated";
+worker.postMessage(offscreen, [offscreen]);
+
+function waitForCanvasToDraw() {
+  return new Promise(resolve => {
+    var i = 0;
+    const testPixel = function() {
+      canvas.toBlob(blob => {
+        createImageBitmap(blob).then(image => {
+          if (verifyImage(image, "verify if image is drawn on offscreencanvas.")) {
+            setTimeout(resolve, 2000);
+          } else {
+            requestAnimationFrame(testPixel);
+          }
+        })
+      });
+    }
+    testPixel();
+  })
+}
+
+function verifyImage(image, description) {
+  var testCanvas = document.createElement('canvas');
+  var testCtx = testCanvas.getContext('2d');
+  testCtx.drawImage(image, 0, 0);
+  const d = testCtx.getImageData(20, 20, 1, 1);
+  return (d.data[0] == 0 && d.data[3] == 255);
+}
+
+if (window.testRunner) {
+  testRunner.waitUntilDone();
+}
+
+waitForCanvasToDraw().then(() => {
+  if (window.testRunner) {
+    testRunner.notifyDone();
+  }
+}).catch(err => {
+  testRunner.notifyDone();
+  throw err;
+});
+
+</script>
+
+</body>
\ No newline at end of file
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index f3d72b10..559271e6 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 27322abb7ec40f46020f93b3814c19f77ea2ccfb
+Revision: bd1aa246ca185519dea0af1c5ca11656c41b0c80
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/BUILD.gn b/third_party/crashpad/crashpad/BUILD.gn
index d1a2b72..b1294cf 100644
--- a/third_party/crashpad/crashpad/BUILD.gn
+++ b/third_party/crashpad/crashpad/BUILD.gn
@@ -23,14 +23,18 @@
 if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
   test("crashpad_tests") {
     deps = [
-      "client:client_test",
-      "handler:handler_test",
-      "minidump:minidump_test",
-      "snapshot:snapshot_test",
       "test:gmock_main",
       "test:test_test",
-      "util:util_test",
     ]
+    if (!crashpad_is_ios) {
+      deps += [
+        "client:client_test",
+        "handler:handler_test",
+        "minidump:minidump_test",
+        "snapshot:snapshot_test",
+        "util:util_test",
+      ]
+    }
   }
 
   if (crashpad_is_in_fuchsia) {
@@ -135,6 +139,9 @@
       "client:client_test",
       "test:gmock_main",
     ]
+    if (crashpad_is_ios) {
+      deps -= [ "client:client_test" ]
+    }
   }
 
   test("crashpad_handler_test") {
@@ -142,6 +149,9 @@
       "handler:handler_test",
       "test:gtest_main",
     ]
+    if (crashpad_is_ios) {
+      deps -= [ "handler:handler_test" ]
+    }
   }
 
   test("crashpad_minidump_test") {
@@ -149,6 +159,9 @@
       "minidump:minidump_test",
       "test:gtest_main",
     ]
+    if (crashpad_is_ios) {
+      deps -= [ "minidump:minidump_test" ]
+    }
   }
 
   test("crashpad_snapshot_test") {
@@ -156,6 +169,9 @@
       "snapshot:snapshot_test",
       "test:gtest_main",
     ]
+    if (crashpad_is_ios) {
+      deps -= [ "snapshot:snapshot_test" ]
+    }
   }
 
   test("crashpad_test_test") {
@@ -170,5 +186,8 @@
       "test:gmock_main",
       "util:util_test",
     ]
+    if (crashpad_is_ios) {
+      deps -= [ "util:util_test" ]
+    }
   }
 }
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS
index 90c103d..4cf8825 100644
--- a/third_party/crashpad/crashpad/DEPS
+++ b/third_party/crashpad/crashpad/DEPS
@@ -33,7 +33,7 @@
       '8048ece6c16c91acfe0d36d1d3cc0890ab6e945c',
   'crashpad/third_party/mini_chromium/mini_chromium':
       Var('chromium_git') + '/chromium/mini_chromium@' +
-      '2298dbe9d0b3213720ac0e5418ad0013a19aeb5d',
+      'cdab1e6263ec7f3f61763efc1dac863f8dc07c80',
   'crashpad/third_party/libfuzzer/src':
       Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
       'fda403cf93ecb8792cb1d061564d89a6553ca020',
diff --git a/third_party/crashpad/crashpad/build/BUILD.gn b/third_party/crashpad/crashpad/build/BUILD.gn
index 0ef1127f..bc77bc8 100644
--- a/third_party/crashpad/crashpad/build/BUILD.gn
+++ b/third_party/crashpad/crashpad/build/BUILD.gn
@@ -45,7 +45,26 @@
     "-fsanitize=fuzzer",
   ]
 
-  ldflags = [
-    "-fsanitize=address",
-  ]
+  ldflags = [ "-fsanitize=address" ]
+}
+
+if (crashpad_is_ios) {
+  group("ios_enable_arc") {
+    if (crashpad_is_in_chromium) {
+      public_configs = [ "//build/config/compiler:enable_arc" ]
+    } else if (crashpad_is_standalone) {
+      public_configs =
+          [ "//third_party/mini_chromium/mini_chromium/build:ios_enable_arc" ]
+    }
+  }
+
+  group("ios_xctest") {
+    if (crashpad_is_in_chromium) {
+      public_configs = [ "//build/config/ios:xctest_config" ]
+    } else if (crashpad_is_standalone) {
+      public_configs = [
+        "//third_party/mini_chromium/mini_chromium/build/ios:xctest_config",
+      ]
+    }
+  }
 }
diff --git a/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni b/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni
index 7db56d6..e1ef71f2 100644
--- a/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni
+++ b/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni
@@ -23,11 +23,10 @@
   }
 }
 
-assert(crashpad_dependencies == "chromium" ||
-       crashpad_dependencies == "fuchsia" ||
-       crashpad_dependencies == "standalone" ||
-       crashpad_dependencies == "external" ||
-       crashpad_dependencies == "dart")
+assert(
+    crashpad_dependencies == "chromium" || crashpad_dependencies == "fuchsia" ||
+    crashpad_dependencies == "standalone" ||
+    crashpad_dependencies == "external" || crashpad_dependencies == "dart")
 
 crashpad_is_in_chromium = crashpad_dependencies == "chromium"
 crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia"
@@ -37,6 +36,7 @@
 
 if (crashpad_is_in_chromium) {
   crashpad_is_mac = is_mac
+  crashpad_is_ios = is_ios
   crashpad_is_win = is_win
   crashpad_is_linux = is_linux
   crashpad_is_android = is_android
@@ -57,6 +57,7 @@
     import("../third_party/mini_chromium/mini_chromium/build/platform.gni")
   }
   crashpad_is_mac = mini_chromium_is_mac
+  crashpad_is_ios = mini_chromium_is_ios
   crashpad_is_win = mini_chromium_is_win
   crashpad_is_linux = mini_chromium_is_linux
   crashpad_is_android = mini_chromium_is_android
diff --git a/third_party/crashpad/crashpad/test/BUILD.gn b/third_party/crashpad/crashpad/test/BUILD.gn
index 7f44863..5159744b 100644
--- a/third_party/crashpad/crashpad/test/BUILD.gn
+++ b/third_party/crashpad/crashpad/test/BUILD.gn
@@ -43,13 +43,36 @@
     "test_paths.h",
   ]
 
+  if (crashpad_is_ios) {
+    sources -= [
+      "errors.cc",
+      "errors.h",
+      "file.cc",
+      "file.h",
+      "filesystem.cc",
+      "filesystem.h",
+      "multiprocess.h",
+      "multiprocess_exec.cc",
+      "multiprocess_exec.h",
+      "process_type.cc",
+      "process_type.h",
+      "scoped_guarded_page.h",
+      "scoped_module_handle.cc",
+      "scoped_module_handle.h",
+      "scoped_temp_dir.cc",
+      "scoped_temp_dir.h",
+      "test_paths.cc",
+      "test_paths.h",
+    ]
+  }
+
   if (crashpad_is_posix || crashpad_is_fuchsia) {
     sources += [
       "scoped_guarded_page_posix.cc",
       "scoped_temp_dir_posix.cc",
     ]
 
-    if (!crashpad_is_fuchsia) {
+    if (!crashpad_is_fuchsia && !crashpad_is_ios) {
       sources += [
         "multiprocess_exec_posix.cc",
         "multiprocess_posix.cc",
@@ -118,6 +141,13 @@
     "../util",
   ]
 
+  if (crashpad_is_ios) {
+    deps -= [
+      "../compat",
+      "../util",
+    ]
+  }
+
   if (crashpad_is_mac) {
     libs = [ "bsm" ]
     deps += [
@@ -152,9 +182,18 @@
     "test_paths_test.cc",
   ]
 
+  if (crashpad_is_ios) {
+    sources -= [
+      "multiprocess_exec_test.cc",
+      "scoped_guarded_page_test.cc",
+      "scoped_temp_dir_test.cc",
+      "test_paths_test.cc",
+    ]
+  }
+
   # TODO(crbug.com/812974): Remove !crashpad_is_fuchsia when Fuchsia is no
   # longer treated as a posix platform.
-  if (crashpad_is_posix && !crashpad_is_fuchsia) {
+  if (crashpad_is_posix && !crashpad_is_fuchsia && !crashpad_is_ios) {
     sources += [ "multiprocess_posix_test.cc" ]
   }
 
@@ -181,16 +220,27 @@
   data_deps = [
     ":crashpad_test_test_multiprocess_exec_test_child",
   ]
+
+  if (crashpad_is_ios) {
+    deps -= [
+      "../compat",
+      "../util",
+    ]
+
+    data_deps -= [ ":crashpad_test_test_multiprocess_exec_test_child" ]
+  }
 }
 
-crashpad_executable("crashpad_test_test_multiprocess_exec_test_child") {
-  sources = [
-    "multiprocess_exec_test_child.cc",
-  ]
+if (!crashpad_is_ios) {
+  crashpad_executable("crashpad_test_test_multiprocess_exec_test_child") {
+    sources = [
+      "multiprocess_exec_test_child.cc",
+    ]
 
-  deps = [
-    "../third_party/mini_chromium:base",
-  ]
+    deps = [
+      "../third_party/mini_chromium:base",
+    ]
+  }
 }
 
 static_library("gmock_main") {
diff --git a/third_party/crashpad/crashpad/test/gtest_death.h b/third_party/crashpad/crashpad/test/gtest_death.h
index 69f6361e..f205a2f 100644
--- a/third_party/crashpad/crashpad/test/gtest_death.h
+++ b/third_party/crashpad/crashpad/test/gtest_death.h
@@ -25,7 +25,7 @@
 
 //! \file
 
-#if defined(OS_MACOSX) || DOXYGEN
+#if (defined(OS_MACOSX) && !defined(OS_IOS)) || DOXYGEN
 
 //! \brief Wraps the gtest `ASSERT_DEATH_IF_SUPPORTED()` macro to make
 //!     assertions about death caused by crashes.
@@ -73,14 +73,14 @@
                  regex);                                                  \
   } while (false)
 
-#else  // OS_MACOSX
+#else  // OS_MACOSX && !OS_IOS
 
 #define ASSERT_DEATH_CRASH(statement, regex) \
   ASSERT_DEATH_IF_SUPPORTED(statement, regex)
 #define EXPECT_DEATH_CRASH(statement, regex) \
   EXPECT_DEATH_IF_SUPPORTED(statement, regex)
 
-#endif  // OS_MACOSX
+#endif  // OS_MACOSX && !OS_IOS
 
 #if !(!defined(MINI_CHROMIUM_BASE_LOGGING_H_) && \
       defined(OFFICIAL_BUILD) &&                 \
diff --git a/third_party/crashpad/crashpad/test/gtest_main.cc b/third_party/crashpad/crashpad/test/gtest_main.cc
index 5a54691..e65f921 100644
--- a/third_party/crashpad/crashpad/test/gtest_main.cc
+++ b/third_party/crashpad/crashpad/test/gtest_main.cc
@@ -33,6 +33,7 @@
 
 namespace {
 
+#if !defined(OS_IOS)
 bool GetChildTestFunctionName(std::string* child_func_name) {
   constexpr size_t arg_length =
       sizeof(crashpad::test::internal::kChildTestFunction) - 1;
@@ -45,17 +46,20 @@
   }
   return false;
 }
+#endif  // !OS_IOS
 
 }  // namespace
 
 int main(int argc, char* argv[]) {
   crashpad::test::InitializeMainArguments(argc, argv);
 
+#if !defined(OS_IOS)
   std::string child_func_name;
   if (GetChildTestFunctionName(&child_func_name)) {
     return crashpad::test::internal::CheckedInvokeMultiprocessChild(
         child_func_name);
   }
+#endif  // !OS_IOS
 
 #if defined(CRASHPAD_IS_IN_CHROMIUM)
 
diff --git a/third_party/crashpad/crashpad/util/BUILD.gn b/third_party/crashpad/crashpad/util/BUILD.gn
index d225f1d..0255890 100644
--- a/third_party/crashpad/crashpad/util/BUILD.gn
+++ b/third_party/crashpad/crashpad/util/BUILD.gn
@@ -332,6 +332,7 @@
       "linux/traits.h",
       "misc/capture_context_linux.S",
       "misc/paths_linux.cc",
+      "misc/time_linux.cc",
       "posix/process_info_linux.cc",
       "process/process_memory_linux.cc",
       "process/process_memory_linux.h",
diff --git a/third_party/crashpad/crashpad/util/linux/proc_stat_reader.cc b/third_party/crashpad/crashpad/util/linux/proc_stat_reader.cc
index 88658f5..dd663d6c 100644
--- a/third_party/crashpad/crashpad/util/linux/proc_stat_reader.cc
+++ b/third_party/crashpad/crashpad/util/linux/proc_stat_reader.cc
@@ -85,7 +85,8 @@
   return ReadTimeAtIndex(14, system_time);
 }
 
-bool ProcStatReader::StartTime(timeval* start_time) const {
+bool ProcStatReader::StartTime(const timeval& boot_time,
+                               timeval* start_time) const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
 
   timeval time_after_boot;
@@ -93,24 +94,7 @@
     return false;
   }
 
-  timespec uptime;
-  if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) {
-    PLOG(ERROR) << "clock_gettime";
-    return false;
-  }
-
-  timespec current_time;
-  if (clock_gettime(CLOCK_REALTIME, &current_time) != 0) {
-    PLOG(ERROR) << "clock_gettime";
-    return false;
-  }
-
-  timespec boot_time_ts;
-  SubtractTimespec(current_time, uptime, &boot_time_ts);
-  timeval boot_time_tv;
-  TimespecToTimeval(boot_time_ts, &boot_time_tv);
-  timeradd(&boot_time_tv, &time_after_boot, start_time);
-
+  timeradd(&boot_time, &time_after_boot, start_time);
   return true;
 }
 
diff --git a/third_party/crashpad/crashpad/util/linux/proc_stat_reader.h b/third_party/crashpad/crashpad/util/linux/proc_stat_reader.h
index c0b30520..6eae8fb 100644
--- a/third_party/crashpad/crashpad/util/linux/proc_stat_reader.h
+++ b/third_party/crashpad/crashpad/util/linux/proc_stat_reader.h
@@ -60,11 +60,12 @@
 
   //! \brief Determines the target thread’s start time.
   //!
+  //! \param[in] boot_time The kernel boot time.
   //! \param[out] start_time The time that the thread started.
   //!
   //! \return `true` on success, with \a start_time set. Otherwise, `false` with
   //!     a message logged.
-  bool StartTime(timeval* start_time) const;
+  bool StartTime(const timeval& boot_time, timeval* start_time) const;
 
  private:
   bool FindColumn(int index, const char** column) const;
diff --git a/third_party/crashpad/crashpad/util/linux/proc_stat_reader_test.cc b/third_party/crashpad/crashpad/util/linux/proc_stat_reader_test.cc
index 1a8a1afb9..01b9afd 100644
--- a/third_party/crashpad/crashpad/util/linux/proc_stat_reader_test.cc
+++ b/third_party/crashpad/crashpad/util/linux/proc_stat_reader_test.cc
@@ -19,9 +19,9 @@
 #include <unistd.h>
 
 #include "base/logging.h"
-#include "build/build_config.h"
 #include "gtest/gtest.h"
 #include "test/linux/fake_ptrace_connection.h"
+#include "util/misc/time.h"
 #include "util/thread/thread.h"
 
 namespace crashpad {
@@ -35,8 +35,13 @@
   ProcStatReader stat;
   ASSERT_TRUE(stat.Initialize(&connection, getpid()));
 
+  timespec boot_time_ts;
+  ASSERT_TRUE(GetBootTime(&boot_time_ts));
+  timeval boot_time;
+  TimespecToTimeval(boot_time_ts, &boot_time);
+
   timeval start_time;
-  ASSERT_TRUE(stat.StartTime(&start_time));
+  ASSERT_TRUE(stat.StartTime(boot_time, &start_time));
 
   time_t now;
   time(&now);
@@ -57,36 +62,38 @@
   return syscall(SYS_gettid);
 }
 
-void GetStartTime(timeval* start_time) {
+void GetStartTime(const timeval& boot_time, timeval* start_time) {
   FakePtraceConnection connection;
   ASSERT_TRUE(connection.Initialize(getpid()));
 
   ProcStatReader stat;
   ASSERT_TRUE(stat.Initialize(&connection, gettid()));
-  ASSERT_TRUE(stat.StartTime(start_time));
+  ASSERT_TRUE(stat.StartTime(boot_time, start_time));
 }
 
 class StatTimeThread : public Thread {
  public:
-  StatTimeThread(timeval* start_time) : start_time_(start_time) {}
+  StatTimeThread(const timeval& boot_time, timeval* start_time)
+      : boot_time_(boot_time), start_time_(start_time) {}
 
  private:
-  void ThreadMain() override { GetStartTime(start_time_); }
+  void ThreadMain() override { GetStartTime(boot_time_, start_time_); }
+
+  const timeval& boot_time_;
   timeval* start_time_;
 };
 
-// TODO(https://crbug.com/1016765): Flaky on Linux.
-#if defined(OS_LINUX)
-#define MAYBE_Threads DISABLED_Threads
-#else
-#define MAYBE_Threads Threads
-#endif
-TEST(ProcStatReader, MAYBE_Threads) {
+TEST(ProcStatReader, Threads) {
+  timespec boot_time_ts;
+  ASSERT_TRUE(GetBootTime(&boot_time_ts));
+  timeval boot_time;
+  TimespecToTimeval(boot_time_ts, &boot_time);
+
   timeval main_time;
-  ASSERT_NO_FATAL_FAILURE(GetStartTime(&main_time));
+  ASSERT_NO_FATAL_FAILURE(GetStartTime(boot_time, &main_time));
 
   timeval thread_time;
-  StatTimeThread thread(&thread_time);
+  StatTimeThread thread(boot_time, &thread_time);
   thread.Start();
   ASSERT_NO_FATAL_FAILURE(thread.Join());
 
@@ -96,7 +103,7 @@
          time_t thread_sec,
          suseconds_t thread_usec) {
         return (thread_sec > main_sec) ||
-               (thread_sec == main_sec && thread_usec > main_usec);
+               (thread_sec == main_sec && thread_usec >= main_usec);
       },
       main_time.tv_sec,
       main_time.tv_usec,
diff --git a/third_party/crashpad/crashpad/util/misc/time.h b/third_party/crashpad/crashpad/util/misc/time.h
index aabe8e64..dffe1a8a 100644
--- a/third_party/crashpad/crashpad/util/misc/time.h
+++ b/third_party/crashpad/crashpad/util/misc/time.h
@@ -69,6 +69,14 @@
 
 #endif  // OS_WIN
 
+#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN
+//! \brief Get the kernel boot time. Subsequent calls to this function may
+//!     return different results due to the system clock being changed or
+//!     imprecision in measuring the boot time.
+//! \return `true` on success. Otherwise, `false` with a message logged.
+bool GetBootTime(timespec* ts);
+#endif  // OS_LINUX || OS_ANDROID || DOXYGEN
+
 }  // namespace crashpad
 
 #endif  // CRASHPAD_UTIL_MISC_TIME_H_
diff --git a/third_party/crashpad/crashpad/util/misc/time_linux.cc b/third_party/crashpad/crashpad/util/misc/time_linux.cc
new file mode 100644
index 0000000..bc73533
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/misc/time_linux.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/time.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+bool GetBootTime(timespec* boot_time) {
+  timespec uptime;
+  if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) {
+    PLOG(ERROR) << "clock_gettime";
+    return false;
+  }
+
+  timespec current_time;
+  if (clock_gettime(CLOCK_REALTIME, &current_time) != 0) {
+    PLOG(ERROR) << "clock_gettime";
+    return false;
+  }
+
+  SubtractTimespec(current_time, uptime, boot_time);
+  return true;
+}
+
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/posix/process_info_linux.cc b/third_party/crashpad/crashpad/util/posix/process_info_linux.cc
index 72a2bd7..3ffd96a 100644
--- a/third_party/crashpad/crashpad/util/posix/process_info_linux.cc
+++ b/third_party/crashpad/crashpad/util/posix/process_info_linux.cc
@@ -23,6 +23,7 @@
 #include "util/file/string_file.h"
 #include "util/linux/proc_stat_reader.h"
 #include "util/misc/lexing.h"
+#include "util/misc/time.h"
 
 namespace crashpad {
 
@@ -238,7 +239,13 @@
     if (!reader.Initialize(connection_, pid_)) {
       return false;
     }
-    if (!reader.StartTime(&start_time_)) {
+    timespec boot_time_ts;
+    if (!GetBootTime(&boot_time_ts)) {
+      return false;
+    }
+    timeval boot_time;
+    TimespecToTimeval(boot_time_ts, &boot_time);
+    if (!reader.StartTime(boot_time, &start_time_)) {
       return false;
     }
     start_time_initialized_.set_valid();
diff --git a/third_party/crashpad/crashpad/util/util.gyp b/third_party/crashpad/crashpad/util/util.gyp
index b9da232..82ba393 100644
--- a/third_party/crashpad/crashpad/util/util.gyp
+++ b/third_party/crashpad/crashpad/util/util.gyp
@@ -168,6 +168,7 @@
         'misc/symbolic_constants_common.h',
         'misc/time.cc',
         'misc/time.h',
+        'misc/time_linux.cc',
         'misc/time_win.cc',
         'misc/tri_state.h',
         'misc/uuid.cc',
diff --git a/tools/chrome_proxy/webdriver/video.py b/tools/chrome_proxy/webdriver/video.py
index 720975a..653f2a1c 100644
--- a/tools/chrome_proxy/webdriver/video.py
+++ b/tools/chrome_proxy/webdriver/video.py
@@ -74,7 +74,7 @@
   def testRangeRequest(self):
     with TestDriver() as t:
       t.AddChromeArg('--enable-spdy-proxy-auth')
-      t.LoadURL('http://check.googlezip.net/connect')
+      t.LoadURL('http://check.googlezip.net/report')
       time.sleep(2) # wait for page load
       t.ExecuteJavascript(
         'var xhr = new XMLHttpRequest();'
diff --git a/tools/grit/grit/format/minifier.py b/tools/grit/grit/format/minifier.py
index 011cbea..9bbb2ad2 100644
--- a/tools/grit/grit/format/minifier.py
+++ b/tools/grit/grit/format/minifier.py
@@ -10,19 +10,27 @@
 import sys
 
 __js_minifier = None
-
+__css_minifier = None
 
 def SetJsMinifier(minifier):
   global __js_minifier
   __js_minifier = minifier.split()
 
+def SetCssMinifier(minifier):
+  global __css_minifier
+  __css_minifier = minifier.split()
 
 def Minify(source, filename):
   file_type = path.splitext(filename)[1]
-  if not file_type == '.js' or not __js_minifier:
+  minifier = None
+  if file_type == '.js':
+    minifier = __js_minifier
+  elif file_type == '.css':
+    minifier = __css_minifier
+  if not minifier:
     return source
   p = subprocess.Popen(
-      __js_minifier,
+      minifier,
       stdin=subprocess.PIPE,
       stdout=subprocess.PIPE,
       stderr=subprocess.PIPE)
diff --git a/tools/grit/grit/tool/build.py b/tools/grit/grit/tool/build.py
index c868f2da..7de2f1a 100644
--- a/tools/grit/grit/tool/build.py
+++ b/tools/grit/grit/tool/build.py
@@ -136,6 +136,12 @@
                     minified Javascript to standard output. A non-zero exit
                     status will be taken as indicating failure.
 
+  --css-minifier    A command to run the CSS minifier. If not set then CSS won't
+                    be minified. The command should read the original CSS from
+                    standard input, and output the minified CSS to standard
+                    output. A non-zero exit status will be taken as indicating
+                    failure.
+
   --brotli          The full path to the brotli executable generated by
                     third_party/brotli/BUILD.gn, required if any entries use
                     compress="brotli".
@@ -165,13 +171,14 @@
     write_only_new = False
     depend_on_stamp = False
     js_minifier = None
+    css_minifier = None
     replace_ellipsis = True
     (own_opts, args) = getopt.getopt(
         args, 'a:p:o:D:E:f:w:t:',
         ('depdir=', 'depfile=', 'assert-file-list=', 'help',
          'output-all-resource-defines', 'no-output-all-resource-defines',
          'no-replace-ellipsis', 'depend-on-stamp', 'js-minifier=',
-         'write-only-new=', 'whitelist-support', 'brotli='))
+         'css-minifier=', 'write-only-new=', 'whitelist-support', 'brotli='))
     for (key, val) in own_opts:
       if key == '-a':
         assert_output_files.append(val)
@@ -209,6 +216,8 @@
         depend_on_stamp = True
       elif key == '--js-minifier':
         js_minifier = val
+      elif key == '--css-minifier':
+        css_minifier = val
       elif key == '--whitelist-support':
         whitelist_support = True
       elif key == '--brotli':
@@ -235,6 +244,9 @@
     if js_minifier:
       minifier.SetJsMinifier(js_minifier)
 
+    if css_minifier:
+      minifier.SetCssMinifier(css_minifier)
+
     self.write_only_new = write_only_new
 
     self.res = grd_reader.Parse(opts.input,
diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
index bd45b63..be60d0d 100644
--- a/tools/grit/grit_rule.gni
+++ b/tools/grit/grit_rule.gni
@@ -226,6 +226,7 @@
 
 _strip_resource_files = is_android && is_official_build
 _js_minifier = "//tools/grit/minify_with_uglify.py"
+_css_minifier = "//tools/grit/minimize_css.py"
 
 grit_resource_id_file = "//tools/gritsettings/resource_ids"
 grit_info_script = "//tools/grit/grit_info.py"
@@ -407,12 +408,16 @@
     }
     if (_strip_resource_files) {
       js_minifier_command = rebase_path(_js_minifier, root_build_dir)
+      css_minifier_command = rebase_path(_css_minifier, root_build_dir)
       args += [
         "--js-minifier",
         js_minifier_command,
+        "--css-minifier",
+        css_minifier_command,
       ]
       inputs += [
         _js_minifier,
+        _css_minifier,
         "//third_party/closure_compiler/compiler/compiler.jar",
       ]
     }
diff --git a/tools/grit/minimize_css.py b/tools/grit/minimize_css.py
new file mode 100755
index 0000000..2c3b8ae
--- /dev/null
+++ b/tools/grit/minimize_css.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+import sys
+
+class CSSMinimizer(object):
+
+  INITIAL = 0
+  MAYBE_COMMENT_START = 1
+  INSIDE_COMMENT = 2
+  MAYBE_COMMENT_END = 3
+  INSIDE_SINGLE_QUOTE = 4
+  INSIDE_SINGLE_QUOTE_ESCAPE = 5
+  INSIDE_DOUBLE_QUOTE = 6
+  INSIDE_DOUBLE_QUOTE_ESCAPE = 7
+
+  def __init__(self):
+    self._output = ''
+    self._codeblock = ''
+
+  def flush_codeblock(self):
+    stripped = re.sub(r"\s+", ' ', self._codeblock)
+    stripped = re.sub(r";?\s*(?P<op>[{};])\s*", r'\g<op>', stripped)
+    self._output += stripped
+    self._codeblock = ''
+
+  def parse(self, content):
+    state = self.INITIAL
+    for char in content:
+      if state == self.INITIAL:
+        if char == '/':
+          state = self.MAYBE_COMMENT_START
+        elif char == "'":
+          self.flush_codeblock()
+          self._output += char
+          state = self.INSIDE_SINGLE_QUOTE
+        elif char == '"':
+          self.flush_codeblock()
+          self._output += char
+          state = self.INSIDE_DOUBLE_QUOTE
+        else:
+          self._codeblock += char
+      elif state == self.MAYBE_COMMENT_START:
+        if char == '*':
+          self.flush_codeblock()
+          state = self.INSIDE_COMMENT
+        else:
+          self._codeblock += '/' + char
+          state = self.INITIAL
+      elif state == self.INSIDE_COMMENT:
+        if char == '*':
+          state = self.MAYBE_COMMENT_END
+        else:
+          pass
+      elif state == self.MAYBE_COMMENT_END:
+        if char == '/':
+          state = self.INITIAL
+        else:
+          state = self.INSIDE_COMMENT
+      elif state == self.INSIDE_SINGLE_QUOTE:
+        if char == '\\':
+          self._output += char
+          state = self.INSIDE_SINGLE_QUOTE_ESCAPE
+        elif char == "'":
+          self._output += char
+          state = self.INITIAL
+        else:
+          self._output += char
+      elif state == self.INSIDE_SINGLE_QUOTE_ESCAPE:
+        self._output += char
+        state = self.INSIDE_SINGLE_QUOTE
+      elif state == self.INSIDE_DOUBLE_QUOTE:
+        if char == '\\':
+          self._output += char
+          state = self.INSIDE_DOUBLE_QUOTE_ESCAPE
+        elif char == '"':
+          self._output += char
+          state = self.INITIAL
+        else:
+          self._output += char
+      elif state == self.INSIDE_DOUBLE_QUOTE_ESCAPE:
+        self._output += char
+        state = self.INSIDE_DOUBLE_QUOTE
+
+    self.flush_codeblock()
+    self._output = self._output.strip()
+    return self._output
+
+  @classmethod
+  def minimize_css(cls, content):
+    minimizer = CSSMinimizer()
+    return minimizer.parse(content)
+
+def main():
+  result = ''
+  try:
+    result = CSSMinimizer.minimize_css(sys.stdin.read())
+  finally:
+    print(result)
+
+if __name__ == '__main__':
+  main()
diff --git a/third_party/blink/renderer/build/scripts/minimize_css_unittest.py b/tools/grit/minimize_css_unittest.py
similarity index 100%
rename from third_party/blink/renderer/build/scripts/minimize_css_unittest.py
rename to tools/grit/minimize_css_unittest.py
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 4ca6a9c..040c482 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -2360,6 +2360,11 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="AppList_BackButtonPressed">
+  <owner>andrewxu@chromium.org</owner>
+  <description>User pressed the back button.</description>
+</action>
+
 <action name="AppList_ClickOnApp">
   <owner>vadimt@chromium.org</owner>
   <description>
@@ -2403,6 +2408,16 @@
   <description>CrOS Launcher went from full to peeking state.</description>
 </action>
 
+<action name="AppList_HomeButtonPressedClamshell">
+  <owner>andrewxu@chromium.org</owner>
+  <description>User pressed the launcher button in clamshell.</description>
+</action>
+
+<action name="AppList_HomeButtonPressedTablet">
+  <owner>andrewxu@chromium.org</owner>
+  <description>User pressed the launcher button in tablet.</description>
+</action>
+
 <action name="AppList_HomeLauncherToMRUWindow">
   <owner>newcomer@chromium.org</owner>
   <owner>sammiequon@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d851843..2afc6959 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -6633,6 +6633,11 @@
   <int value="1" label="Latched"/>
 </enum>
 
+<enum name="BooleanLoadCollided">
+  <int value="0" label="Load did not collide"/>
+  <int value="1" label="Load collided"/>
+</enum>
+
 <enum name="BooleanLoaded">
   <int value="0" label="Not loaded"/>
   <int value="1" label="Loaded"/>
@@ -6813,6 +6818,11 @@
   <int value="1" label="Manual fallback"/>
 </enum>
 
+<enum name="BooleanPathIsInformative">
+  <int value="0" label="Path contains nothing except possibly a slash"/>
+  <int value="1" label="Path contains text beyond just an opening slash"/>
+</enum>
+
 <enum name="BooleanPending">
   <int value="0" label="Decided"/>
   <int value="1" label="Pending"/>
@@ -40254,6 +40264,11 @@
   <int value="645" label="overscroll-behavior-block"/>
   <int value="646" label="content-size"/>
   <int value="647" label="font-optical-sizing"/>
+  <int value="648" label="intrinsic-block-size"/>
+  <int value="649" label="intrinsic-height"/>
+  <int value="650" label="intrinsic-inline-size"/>
+  <int value="651" label="intrinsic-size"/>
+  <int value="652" label="intrinsic-width"/>
 </enum>
 
 <enum name="MappedEditingCommands">
@@ -62916,6 +62931,18 @@
   <int value="137457845" label="web_history_counter"/>
 </enum>
 
+<enum name="URLRequestReferrerPolicy">
+  <int value="0" label="CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE"/>
+  <int value="1"
+      label="REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN"/>
+  <int value="2" label="ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN"/>
+  <int value="3" label="NEVER_CLEAR_REFERRER"/>
+  <int value="4" label="ORIGIN"/>
+  <int value="5" label="CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN"/>
+  <int value="6" label="ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE"/>
+  <int value="7" label="NO_REFERRER"/>
+</enum>
+
 <enum name="UrlResolutionResult">
   <int value="0" label="Absolute URL"/>
   <int value="1" label="Resolutions Differ"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f118ddcc..43d7229 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -21584,7 +21584,7 @@
   </summary>
 </histogram>
 
-<histogram name="ChromeOS.UrlXattrsCount" units="units" expires_after="M80">
+<histogram name="ChromeOS.UrlXattrsCount" units="units" expires_after="M81">
   <owner>jorgelo@chromium.org</owner>
   <owner>tnagel@chromium.org</owner>
   <summary>
@@ -85557,6 +85557,32 @@
   <summary>The time spent in closesocket call in UDPSocketWin::Close.</summary>
 </histogram>
 
+<histogram name="Net.URLRequest.ReferrerHasInformativePath"
+    enum="BooleanPathIsInformative" expires_after="2020-03-30">
+<!-- Name completed by histogram_suffixes name="ReferrerPolicySameOrigin" -->
+
+  <owner>davidvc@chromium.org</owner>
+  <owner>kaustubhag@chromium.org</owner>
+  <summary>
+    Records for each URLRequest whether its referrer contains more information
+    than just its origin (this is the case exactly when it has at least one
+    character apart from a leading slash in its path, e.g. http://example.com/a
+    but not http://example.com/ nor http://example.com).
+  </summary>
+</histogram>
+
+<histogram name="Net.URLRequest.ReferrerPolicyForRequest"
+    enum="URLRequestReferrerPolicy" expires_after="2020-03-30">
+<!-- Name completed by histogram_suffixes name="ReferrerPolicySameOrigin" -->
+
+  <owner>davidvc@chromium.org</owner>
+  <owner>kaustubhag@chromium.org</owner>
+  <summary>
+    Records the distribution of referrer policies provided with outgoing
+    URLRequests, and whether or not the requests were cross-origin.
+  </summary>
+</histogram>
+
 <histogram name="Net.URLRequest.ReferrerPolicyHeaderPresentOnRedirect"
     enum="BooleanPresent">
   <owner>estark@chromium.org</owner>
@@ -97690,6 +97716,16 @@
   </summary>
 </histogram>
 
+<histogram name="OptimizationGuide.PredictionModelStore.OnLoadCollided"
+    enum="BooleanLoadCollided" expires_after="M85">
+  <owner>mcrouse@chromium.org</owner>
+  <owner>sophiechang@chromium.org</owner>
+  <summary>
+    For each load of a prediction model from the OptimizationGuideStore, reports
+    whether the load collided with an update being made to the store.
+  </summary>
+</histogram>
+
 <histogram name="OptimizationGuide.ProcessHintsResult"
     enum="OptimizationGuideProcessHintsResult" expires_after="M85">
   <owner>dougarnett@chromium.org</owner>
@@ -179020,6 +179056,15 @@
   <affected-histogram name="SafeBrowsing.ReferrerURLChainSize"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="ReferrerPolicySameOrigin" separator=".">
+  <suffix name="CrossOrigin" label="Cross-origin requests."/>
+  <suffix name="SameOrigin"
+      label="Same-origin requests (relative to the initiator, if present, and
+             otherwise the initial referrer URL)."/>
+  <affected-histogram name="Net.URLRequest.ReferrerHasInformativePath"/>
+  <affected-histogram name="Net.URLRequest.ReferrerPolicyForRequest"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="RelaunchNotificationStyle" separator=".">
   <suffix name="Recommended" label="The relaunch recommended bubble."/>
   <suffix name="Required" label="The relaunch required dialog."/>
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml
index cc553ec8..292703b 100644
--- a/tools/metrics/rappor/rappor.xml
+++ b/tools/metrics/rappor/rappor.xml
@@ -174,27 +174,6 @@
   </summary>
 </rappor-metric>
 
-<rappor-metric name="Plugins.FlashOriginUrl" type="ETLD_PLUS_ONE">
-  <owner>wfh@chromium.org</owner>
-  <summary>
-    The domain and registry of the top level URL of a page which attempts to
-    launch a Flash NPAPI or PPAPI plugin, if the client has Flash installed and
-    enabled. Recorded when the plugin frame appears for each Flash object found
-    on the page, even if the plugin is click-to-play.
-  </summary>
-</rappor-metric>
-
-<rappor-metric name="Plugins.FlashUrl" type="ETLD_PLUS_ONE">
-  <owner>wfh@chromium.org</owner>
-  <summary>
-    The domain and registry of the URL from where Flash SWF or SPL content is
-    being loaded from, while attempting to launch a Flash (NPAPI or PPAPI)
-    plugin that is installed and enabled. Recorded when the plugin frame appears
-    for each Flash object found in the page, even if the plugin is
-    click-to-play.
-  </summary>
-</rappor-metric>
-
 <rappor-metric name="PowerfulFeatureUse.ETLDPlus1.GetUserMedia.Insecure"
     type="ETLD_PLUS_ONE">
   <owner>guidou@chromium.org</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 350b00e..6210309 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -233,20 +233,20 @@
   </summary>
   <metric name="AdBytes">
     <summary>
-      Amount of bytes used to load ad resource on the page. Includes resources
-      that did not finish, and top-level ads.
+      Amount of network bytes used to load ad resource on the page. Includes
+      resources that did not finish, and top-level ads. Measured in kilobytes.
     </summary>
   </metric>
   <metric name="AdBytesPerSecond">
     <summary>
-      Amount of bytes used to load ad resources on the page, per second the page
-      was alive after commit.
+      Amount of network bytes used to load ad resources on the page, per second
+      the page was alive after commit. Measured in kilobytes per second.
     </summary>
   </metric>
   <metric name="AdBytesPerSecondAfterInteractive">
     <summary>
-      Amount of bytes used to load ad resources on the page per second after the
-      page was interactive.
+      Amount of network bytes used to load ad resources on the page per second
+      after the page was interactive. Measured in kilobytes.
     </summary>
   </metric>
   <metric name="AdCpuTime">
@@ -257,20 +257,28 @@
   </metric>
   <metric name="AdJavascriptBytes">
     <summary>
-      Amount of bytes used to load ad resources with a supported javascript mime
-      type on the page.
+      Amount of network bytes used to load ad resources with a supported
+      javascript mime type on the page. Measured in kilobytes.
     </summary>
   </metric>
   <metric name="AdVideoBytes">
     <summary>
-      Amount of bytes used to load ad resources with a video mime type on the
-      page.
+      Amount of network bytes used to load ad resources with a video mime type
+      on the page. Measured in kilobytes.
+    </summary>
+  </metric>
+  <metric name="MainframeAdBytes">
+    <summary>
+      Amount of network bytes used to load ad resources in the main frame.
+      Includes resources that did not finish but does not include resources in
+      subframes. Measured in bytes. This is rounded to the nearest exponential
+      bucket (with a bucket ratio of 1.3).
     </summary>
   </metric>
   <metric name="TotalBytes">
     <summary>
-      Amount of bytes used to load resources on the page. Includes resources
-      that did not finish.
+      Amount of network bytes used to load resources on the page. Includes
+      resources that did not finish. Measured in kilobytes.
     </summary>
   </metric>
 </event>
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index 1e46510..4785ec1 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -1847,34 +1847,32 @@
       continue;
     }
 
-    int child_level =
-        child->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel);
-
-    if (child_level < original_level) {
-      // If a decrease in level occurs after the original node has been
-      // examined, stop adding to this set.
-      if (original_node_index < i)
-        break;
-
-      // If a decrease in level has been detected before the original node
-      // has been examined, then everything previously added to items actually
-      // belongs to a different set. Clear items vector.
-      items.clear();
-      continue;
-    } else if (child_level > original_level) {
-      continue;
-    }
-
-    // If role of node is kRadioButton, only add other kRadioButtons.
-    if (node_is_radio_button &&
-        child->data().role == ax::mojom::Role::kRadioButton)
-      items.push_back(child);
-
     // Add child to items if role matches with ordered set's role. If role of
     // node is kRadioButton, don't add items of other roles, even if item role
     // matches ordered set role.
-    if (!node_is_radio_button && child->SetRoleMatchesItemRole(ordered_set))
+    if ((node_is_radio_button &&
+         child->data().role == ax::mojom::Role::kRadioButton) ||
+        (!node_is_radio_button && child->SetRoleMatchesItemRole(ordered_set))) {
+      int child_level =
+          child->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel);
+
+      if (child_level < original_level) {
+        // If a decrease in level occurs after the original node has been
+        // examined, stop adding to this set.
+        if (original_node_index < i)
+          break;
+
+        // If a decrease in level has been detected before the original node
+        // has been examined, then everything previously added to items actually
+        // belongs to a different set. Clear items vector.
+        items.clear();
+        continue;
+      } else if (child_level > original_level) {
+        continue;
+      }
+
       items.push_back(child);
+    }
 
     // Recurse if there is a generic container, ignored, or unknown.
     if (child->IsIgnored() ||
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 4647c4a..91a9610 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -764,31 +764,11 @@
                                  value);
 }
 
-#if defined(ATK_212)
-void SetValue(AtkValue* atk_value, const double new_value) {
-  g_return_if_fail(ATK_VALUE(atk_value));
-
-  AtkObject* atk_object = ATK_OBJECT(atk_value);
-  AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object);
-  if (!obj)
-    return;
-
-  AXActionData data;
-  data.action = ax::mojom::Action::kSetValue;
-  data.value = base::NumberToString(new_value);
-  obj->GetDelegate()->AccessibilityPerformAction(data);
-}
-#endif  // defined(ATK_212)
-
 void Init(AtkValueIface* iface) {
   iface->get_current_value = GetCurrentValue;
   iface->get_maximum_value = GetMaximumValue;
   iface->get_minimum_value = GetMinimumValue;
   iface->get_minimum_increment = GetMinimumIncrement;
-
-#if defined(ATK_212)
-  iface->set_value = SetValue;
-#endif  // defined(ATK_212)
 }
 
 const GInterfaceInfo Info = {reinterpret_cast<GInterfaceInitFunc>(Init),
@@ -3429,8 +3409,10 @@
   // If this is a non-web-content text entry, then we need to trigger text
   // change signals when the value changes. This is handled by browser
   // accessibility for web content.
-  if (IsPlainTextField() && !GetDelegate()->IsWebContent())
+  if (IsPlainTextField() || !GetDelegate()->IsWebContent()) {
     UpdateHypertext();
+    return;
+  }
 
   if (!IsRangeValueSupported(GetData()))
     return;
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
index 3ee9ebb..fe973d51 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
@@ -993,66 +993,6 @@
   g_object_unref(root_obj);
 }
 
-#if ATK_CHECK_VERSION(2, 12, 0)
-typedef bool (*SetValueFunction)(AtkValue* component, double value);
-
-TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkValueChangedSignal) {
-  // There's a chance we may be compiled with a newer version of ATK and then
-  // run with an older one, so we need to do a runtime check for this method
-  // that is available in ATK 2.12 instead of linking directly.
-  SetValueFunction atk_value_set_value = reinterpret_cast<SetValueFunction>(
-      dlsym(RTLD_DEFAULT, "atk_value_set_value"));
-  if (!atk_value_set_value) {
-    LOG(WARNING) << "Skipping TestAtkValueChangedSignal"
-                    " because ATK version < 2.12 detected.";
-    return;
-  }
-
-  AXNodeData root;
-  root.id = 1;
-  root.role = ax::mojom::Role::kSlider;
-  root.AddFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange, 5.0);
-  Init(root);
-
-  AtkObject* root_object(GetRootAtkObject());
-  ASSERT_TRUE(ATK_IS_OBJECT(root_object));
-  ASSERT_TRUE(ATK_IS_VALUE(root_object));
-  g_object_ref(root_object);
-
-  bool saw_value_change = false;
-  g_signal_connect(
-      root_object, "property-change::accessible-value",
-      G_CALLBACK(+[](AtkObject*, void* property, bool* saw_value_change) {
-        *saw_value_change = true;
-      }),
-      &saw_value_change);
-
-  atk_value_set_value(ATK_VALUE(root_object), 24.0);
-  GetRootPlatformNode()->NotifyAccessibilityEvent(
-      ax::mojom::Event::kValueChanged);
-
-  GValue current_value = G_VALUE_INIT;
-  atk_value_get_current_value(ATK_VALUE(root_object), &current_value);
-  EXPECT_EQ(G_TYPE_FLOAT, G_VALUE_TYPE(&current_value));
-  EXPECT_EQ(24.0, g_value_get_float(&current_value));
-  EXPECT_TRUE(saw_value_change);
-
-  saw_value_change = false;
-  atk_value_set_value(ATK_VALUE(root_object), 100.0);
-  GetRootPlatformNode()->NotifyAccessibilityEvent(
-      ax::mojom::Event::kValueChanged);
-
-  g_value_unset(&current_value);
-  atk_value_get_current_value(ATK_VALUE(root_object), &current_value);
-  EXPECT_EQ(G_TYPE_FLOAT, G_VALUE_TYPE(&current_value));
-  EXPECT_EQ(100.0, g_value_get_float(&current_value));
-  EXPECT_TRUE(saw_value_change);
-
-  g_value_unset(&current_value);
-  g_object_unref(root_object);
-}
-#endif  // ATK_CHECK_VERSION(2, 12, 0)
-
 //
 // AtkHyperlinkImpl interface
 //
diff --git a/ui/color/BUILD.gn b/ui/color/BUILD.gn
index 42f18df..abda7ae0 100644
--- a/ui/color/BUILD.gn
+++ b/ui/color/BUILD.gn
@@ -64,8 +64,8 @@
     "color_mixer_unittest.cc",
     "color_provider_unittest.cc",
     "color_recipe_unittest.cc",
+    "color_test_ids.h",
     "color_transform_unittest.cc",
-    "color_unittest_utils.h",
     "run_all_unittests.cc",
   ]
 
diff --git a/ui/color/color_mixer_unittest.cc b/ui/color/color_mixer_unittest.cc
index 34eddcf..f2ad20f1 100644
--- a/ui/color/color_mixer_unittest.cc
+++ b/ui/color/color_mixer_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/color/color_recipe.h"
-#include "ui/color/color_unittest_utils.h"
+#include "ui/color/color_test_ids.h"
 #include "ui/gfx/color_palette.h"
 
 namespace ui {
diff --git a/ui/color/color_provider_unittest.cc b/ui/color/color_provider_unittest.cc
index eda0fa5..c094c03 100644
--- a/ui/color/color_provider_unittest.cc
+++ b/ui/color/color_provider_unittest.cc
@@ -5,7 +5,7 @@
 #include "ui/color/color_provider.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/color/color_unittest_utils.h"
+#include "ui/color/color_test_ids.h"
 #include "ui/gfx/color_palette.h"
 
 namespace ui {
diff --git a/ui/color/color_recipe_unittest.cc b/ui/color/color_recipe_unittest.cc
index 9974b06c..2550214 100644
--- a/ui/color/color_recipe_unittest.cc
+++ b/ui/color/color_recipe_unittest.cc
@@ -7,7 +7,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/color/color_mixer.h"
 #include "ui/color/color_set.h"
-#include "ui/color/color_unittest_utils.h"
+#include "ui/color/color_test_ids.h"
 #include "ui/gfx/color_palette.h"
 
 namespace ui {
diff --git a/ui/color/color_unittest_utils.h b/ui/color/color_test_ids.h
similarity index 82%
rename from ui/color/color_unittest_utils.h
rename to ui/color/color_test_ids.h
index 40c5b87..a79c216e 100644
--- a/ui/color/color_unittest_utils.h
+++ b/ui/color/color_test_ids.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef UI_COLOR_COLOR_UNITTEST_UTILS_H_
-#define UI_COLOR_COLOR_UNITTEST_UTILS_H_
+#ifndef UI_COLOR_COLOR_TEST_IDS_H_
+#define UI_COLOR_COLOR_TEST_IDS_H_
 
 #include "ui/color/color_id.h"
 
@@ -33,4 +33,4 @@
 
 }  // namespace ui
 
-#endif  // UI_COLOR_COLOR_UNITTEST_UTILS_H_
+#endif  // UI_COLOR_COLOR_TEST_IDS_H_
diff --git a/ui/color/color_transform_unittest.cc b/ui/color/color_transform_unittest.cc
index 857cdb9..f8cc4a3 100644
--- a/ui/color/color_transform_unittest.cc
+++ b/ui/color/color_transform_unittest.cc
@@ -7,7 +7,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/color/color_mixer.h"
 #include "ui/color/color_recipe.h"
-#include "ui/color/color_unittest_utils.h"
+#include "ui/color/color_test_ids.h"
 #include "ui/gfx/color_palette.h"
 
 namespace ui {
diff --git a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc
index 46c9ae5..3d4f710 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc
+++ b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc
@@ -8,6 +8,8 @@
 #include <utility>
 
 #include "base/feature_list.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
 #include "base/time/time.h"
 #include "ui/events/ozone/evdev/event_device_info.h"
 #include "ui/events/ozone/evdev/touch_filter/heuristic_stylus_palm_detection_filter.h"
@@ -25,6 +27,10 @@
 const base::Feature kEnableNeuralPalmDetectionFilter{
     "EnableNeuralPalmDetectionFilter", base::FEATURE_DISABLED_BY_DEFAULT};
 
+EVENTS_OZONE_EVDEV_EXPORT
+extern const base::FeatureParam<std::string> kNeuralPalmRadiusPolynomial{
+    &kEnableNeuralPalmDetectionFilter, "neural_palm_radius_polynomial", ""};
+
 const base::FeatureParam<double> kHeuristicCancelThresholdSeconds{
     &kEnableHeuristicPalmDetectionFilter,
     "heuristic_palm_cancel_threshold_seconds", 0.4};
@@ -36,15 +42,38 @@
 const base::FeatureParam<int> kHeuristicStrokeCount{
     &kEnableHeuristicPalmDetectionFilter, "heuristic_palm_stroke_count", 0};
 
+namespace internal {
+
+std::vector<float> ParseRadiusPolynomial(const std::string& radius_string) {
+  std::vector<std::string> split_radii = base::SplitString(
+      radius_string, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  std::vector<float> return_value;
+  for (const std::string& unparsed : split_radii) {
+    double item;
+    if (!base::StringToDouble(unparsed, &item) && !std::isnan(item)) {
+      LOG(DFATAL) << "Unable to parse " << unparsed
+                  << " to a floating point; Returning empty.";
+      return {};
+    }
+    return_value.push_back(item);
+  }
+  return return_value;
+}
+
+}  // namespace internal
+
 std::unique_ptr<PalmDetectionFilter> CreatePalmDetectionFilter(
     const EventDeviceInfo& devinfo,
     SharedPalmDetectionFilterState* shared_palm_state) {
   if (base::FeatureList::IsEnabled(kEnableNeuralPalmDetectionFilter) &&
       NeuralStylusPalmDetectionFilter::
           CompatibleWithNeuralStylusPalmDetectionFilter(devinfo)) {
+    std::vector<float> radius_polynomial =
+        internal::ParseRadiusPolynomial(kNeuralPalmRadiusPolynomial.Get());
     // Theres only one model right now.
     std::unique_ptr<NeuralStylusPalmDetectionFilterModel> model =
-        std::make_unique<OneDeviceTrainNeuralStylusPalmDetectionFilterModel>();
+        std::make_unique<OneDeviceTrainNeuralStylusPalmDetectionFilterModel>(
+            radius_polynomial);
     return std::make_unique<NeuralStylusPalmDetectionFilter>(
         devinfo, std::move(model), shared_palm_state);
   }
diff --git a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h
index 3afe8059..96c87c5 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h
+++ b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h
@@ -25,6 +25,9 @@
 extern const base::Feature kEnableNeuralPalmDetectionFilter;
 
 EVENTS_OZONE_EVDEV_EXPORT
+extern const base::FeatureParam<std::string> kNeuralPalmRadiusPolynomial;
+
+EVENTS_OZONE_EVDEV_EXPORT
 extern const base::FeatureParam<double> kHeuristicCancelThresholdSeconds;
 
 EVENTS_OZONE_EVDEV_EXPORT
@@ -37,6 +40,13 @@
 CreatePalmDetectionFilter(const EventDeviceInfo& devinfo,
                           SharedPalmDetectionFilterState* shared_palm_state);
 
+namespace internal {
+// In a named namespace for testing.
+
+EVENTS_OZONE_EVDEV_EXPORT std::vector<float> ParseRadiusPolynomial(
+    const std::string& radius_string);
+}  // namespace internal
+
 }  // namespace ui
 
 #endif  // UI_EVENTS_OZONE_EVDEV_TOUCH_FILTER_PALM_DETECTION_FILTER_FACTORY_H_
diff --git a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc
index f17260ec..bb52e22 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc
+++ b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc
@@ -42,6 +42,9 @@
   DISALLOW_COPY_AND_ASSIGN(PalmDetectionFilterFactoryTest);
 };
 
+class PalmDetectionFilterFactoryDeathTest
+    : public PalmDetectionFilterFactoryTest {};
+
 TEST_F(PalmDetectionFilterFactoryTest, AllDisabled) {
   scoped_feature_list_->InitWithFeatures(
       {}, {ui::kEnableHeuristicPalmDetectionFilter,
@@ -114,4 +117,32 @@
   ASSERT_EQ(NeuralStylusPalmDetectionFilter::kFilterName,
             palm_filter->FilterNameForTesting());
 }
+
+TEST_F(PalmDetectionFilterFactoryTest, ParseTest) {
+  EXPECT_EQ(std::vector<float>(), internal::ParseRadiusPolynomial(""));
+  // Note we test for whitespace trimming.
+  EXPECT_EQ(std::vector<float>({3.7, 2.91, 15.19191}),
+            internal::ParseRadiusPolynomial("3.7, 2.91, 15.19191  "));
+}
+
+TEST_F(PalmDetectionFilterFactoryDeathTest, BadParseRecovery) {
+  // in debug, die. In non debug, expect {}
+  EXPECT_DEBUG_DEATH(EXPECT_EQ(std::vector<float>(),
+                               internal::ParseRadiusPolynomial("cheese")),
+                     "Unable to parse.*cheese");
+}
+
+TEST_F(PalmDetectionFilterFactoryDeathTest, BadNeuralParamParse) {
+  scoped_feature_list_->InitWithFeaturesAndParameters(
+      {base::test::ScopedFeatureList::FeatureAndParams(
+          ui::kEnableNeuralPalmDetectionFilter,
+          {
+              {"neural_palm_radius_polynomial", "1.0,chicken"},
+          })},
+      {ui::kEnableHeuristicPalmDetectionFilter});
+  EXPECT_DEBUG_DEATH(CreatePalmDetectionFilter(nocturne_touchscreen_info_,
+                                               &shared_palm_state_),
+                     "Unable to parse.*chicken");
+}
+
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc
index cc141787..e84b9f9 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc
+++ b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.cc
@@ -59,4 +59,11 @@
   config_.heuristic_palm_area_limit = 400.0f;
 }
 
+OneDeviceTrainNeuralStylusPalmDetectionFilterModel::
+    OneDeviceTrainNeuralStylusPalmDetectionFilterModel(
+        const std::vector<float>& radius_poly)
+    : OneDeviceTrainNeuralStylusPalmDetectionFilterModel() {
+  config_.radius_polynomial_resize = radius_poly;
+}
+
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h
index f8f3ab9..1b974d2 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h
+++ b/ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h
@@ -20,7 +20,8 @@
     : public NeuralStylusPalmDetectionFilterModel {
  public:
   OneDeviceTrainNeuralStylusPalmDetectionFilterModel();
-
+  explicit OneDeviceTrainNeuralStylusPalmDetectionFilterModel(
+      const std::vector<float>& radius_poly);
   float Inference(const std::vector<float>& features) const override;
 
   const NeuralStylusPalmDetectionFilterModelConfig& config() const override;
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index f4dc935a..66005ab1 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -238,6 +238,12 @@
   background: -webkit-image-set(
       url(../images/files/ui/eject.png) 1x,
       url(../images/files/ui/2x/eject.png) 2x) no-repeat center;
+  border-style: none;
+  box-shadow: none;
+  min-width: 16px;
+  outline-color: rgb(77, 144, 254);
+  outline-width: 1px;
+  padding: 10px;
 }
 
 #directory-tree .tree-row[selected] > .root-eject {
@@ -246,6 +252,10 @@
       url(../images/files/ui/2x/eject_active.png) 2x);
 }
 
+#directory-tree .tree-row > .root-eject:active {
+  outline-width: 0;
+}
+
 #directory-tree .root-item[disabled] {
   opacity: 0.5;
   pointer-events: none;
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 93bb5833..0062610 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -703,17 +703,12 @@
    * @private
    */
   setupEjectButton_(rowElement) {
-    const ejectButton = document.createElement('button');
+    const ejectButton = document.createElement('cr-button');
 
     ejectButton.className = 'root-eject align-right-icon';
     ejectButton.setAttribute('aria-label', str('UNMOUNT_DEVICE_BUTTON_LABEL'));
     ejectButton.setAttribute('tabindex', '0');
-
-    // Add paper-ripple effect on the eject button.
-    const ripple = document.createElement('paper-ripple');
-    ripple.setAttribute('fit', '');
-    ripple.className = 'circle recenteringTouch';
-    ejectButton.appendChild(ripple);
+    ejectButton.circleRipple = true;
 
     // Block mouse handlers, handle click.
     ejectButton.addEventListener('mouseup', (event) => {
diff --git a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
index 535eb51..677920db 100644
--- a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
@@ -869,7 +869,7 @@
 
     // Wait for the eject button to appear.
     const ejectButtonQuery =
-        ['#directory-tree [entry-label="archive.zip"] button.root-eject'];
+        ['#directory-tree [entry-label="archive.zip"] .root-eject'];
     await remoteCall.waitForElement(appId, ejectButtonQuery);
 
     // Focus on the eject button.
diff --git a/ui/webui/resources/js/assert.js b/ui/webui/resources/js/assert.js
index 2befddb..7bb03af 100644
--- a/ui/webui/resources/js/assert.js
+++ b/ui/webui/resources/js/assert.js
@@ -15,6 +15,7 @@
  * @param {string=} opt_message A message to show on failure.
  * @return {T} A non-null |condition|.
  * @closurePrimitive {asserts.truthy}
+ * @suppress {reportUnknownTypes} because T is not sufficiently constrained.
  */
 /* #export */ function assert(condition, opt_message) {
   if (!condition) {
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
index 172c074..0844908 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
@@ -184,7 +184,9 @@
             }
             mViewController = null;
         }
-        mWindowAndroid = null;
-        mViewController = null;
+        if (mWindowAndroid != null) {
+            mWindowAndroid.destroy();
+            mWindowAndroid = null;
+        }
     }
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java b/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
index 2a73263ee..8b1f24e4 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java
@@ -9,6 +9,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.weblayer_private.interfaces.NewTabType;
 
 /**
  * Owns the c++ NewTabCallback class, which is responsible for forwarding all
@@ -30,8 +31,24 @@
         mNativeNewTabCallbackProxy = 0;
     }
 
+    @NewTabType
+    private static int implTypeToJavaType(@ImplNewTabType int type) {
+        switch (type) {
+            case ImplNewTabType.FOREGROUND:
+                return NewTabType.FOREGROUND_TAB;
+            case ImplNewTabType.BACKGROUND:
+                return NewTabType.BACKGROUND_TAB;
+            case ImplNewTabType.NEW_POPUP:
+                return NewTabType.NEW_POPUP;
+            case ImplNewTabType.NEW_WINDOW:
+                return NewTabType.NEW_WINDOW;
+        }
+        assert false;
+        return NewTabType.FOREGROUND_TAB;
+    }
+
     @CalledByNative
-    public void onNewTab(long nativeTab, int mode) throws RemoteException {
+    public void onNewTab(long nativeTab, @ImplNewTabType int mode) throws RemoteException {
         // This class should only be created while the tab is attached to a fragment.
         assert mTab.getBrowser() != null;
         TabImpl tab =
diff --git a/weblayer/browser/ssl_browsertest.cc b/weblayer/browser/ssl_browsertest.cc
index b75c30a..1550290 100644
--- a/weblayer/browser/ssl_browsertest.cc
+++ b/weblayer/browser/ssl_browsertest.cc
@@ -5,57 +5,106 @@
 #include "weblayer/test/weblayer_browser_test.h"
 
 #include "base/files/file_path.h"
+#include "base/macros.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "weblayer/shell/browser/shell.h"
 #include "weblayer/test/interstitial_utils.h"
+#include "weblayer/test/test_navigation_observer.h"
 #include "weblayer/test/weblayer_browser_test_utils.h"
 
 namespace weblayer {
 
-IN_PROC_BROWSER_TEST_F(WebLayerBrowserTest, SSLErrorMismatchedCertificate) {
-  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  https_server.AddDefaultHandlers(
-      base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
+class SSLBrowserTest : public WebLayerBrowserTest {
+ public:
+  SSLBrowserTest() = default;
+  ~SSLBrowserTest() override = default;
 
-  net::EmbeddedTestServer https_server_mismatched(
-      net::EmbeddedTestServer::TYPE_HTTPS);
-  https_server_mismatched.SetSSLConfig(
-      net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
-  https_server_mismatched.AddDefaultHandlers(
-      base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
+  // WebLayerBrowserTest:
+  void PreRunTestOnMainThread() override {
+    WebLayerBrowserTest::PreRunTestOnMainThread();
 
-  ASSERT_TRUE(https_server.Start());
-  ASSERT_TRUE(https_server_mismatched.Start());
+    https_server_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTPS);
+    https_server_->AddDefaultHandlers(
+        base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
 
-  // First navigate to an OK page.
-  GURL initial_url = https_server.GetURL("/simple_page.html");
-  ASSERT_EQ("127.0.0.1", initial_url.host());
+    https_server_mismatched_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTPS);
+    https_server_mismatched_->SetSSLConfig(
+        net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
+    https_server_mismatched_->AddDefaultHandlers(
+        base::FilePath(FILE_PATH_LITERAL("weblayer/test/data")));
 
-  NavigateAndWaitForCompletion(initial_url, shell());
-  EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
+    ASSERT_TRUE(https_server_->Start());
+    ASSERT_TRUE(https_server_mismatched_->Start());
+  }
 
-  // Now do a navigation that should result in an SSL error.
-  GURL url_with_mismatched_cert =
-      https_server_mismatched.GetURL("/simple_page.html");
+  void PostRunTestOnMainThread() override {
+    https_server_.reset();
+    https_server_mismatched_.reset();
+    WebLayerBrowserTest::PostRunTestOnMainThread();
+  }
 
-  NavigateAndWaitForFailure(url_with_mismatched_cert, shell());
+  void NavigateToOkPage() {
+    ASSERT_EQ("127.0.0.1", ok_url().host());
+    NavigateAndWaitForCompletion(ok_url(), shell());
+    EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
+  }
 
-  // First check that there *is* an interstitial.
-  EXPECT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
+  void NavigateToPageWithSslError() {
+    // Now do a navigation that should result in an SSL error.
+    GURL url_with_mismatched_cert =
+        https_server_mismatched_->GetURL("/simple_page.html");
 
-  // Now verify that the interstitial is in fact an SSL interstitial.
-  EXPECT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
+    NavigateAndWaitForFailure(url_with_mismatched_cert, shell());
 
-  // TODO(blundell): Check the security state once security state is available
-  // via the public WebLayer API, following the example of //chrome's
-  // ssl_browsertest.cc's CheckAuthenticationBrokenState() function.
+    // First check that there *is* an interstitial.
+    ASSERT_TRUE(IsShowingSecurityInterstitial(shell()->tab()));
 
-  // TODO(blundell): Bring up test of the "Take me back" functionality
-  // following the example of //chrome's ssl_browsertest.cc:1467, once that
-  // functionality is implemented in weblayer.
+    // Now verify that the interstitial is in fact an SSL interstitial.
+    EXPECT_TRUE(IsShowingSSLInterstitial(shell()->tab()));
+
+    // TODO(blundell): Check the security state once security state is available
+    // via the public WebLayer API, following the example of //chrome's
+    // ssl_browsertest.cc's CheckAuthenticationBrokenState() function.
+  }
+
+  GURL ok_url() { return https_server_->GetURL("/simple_page.html"); }
+
+ protected:
+  std::unique_ptr<net::EmbeddedTestServer> https_server_;
+  std::unique_ptr<net::EmbeddedTestServer> https_server_mismatched_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SSLBrowserTest);
+};
+
+// Tests clicking "take me back" on the interstitial page.
+IN_PROC_BROWSER_TEST_F(SSLBrowserTest, TakeMeBack) {
+  NavigateToOkPage();
+  NavigateToPageWithSslError();
+
+  // Click "Take me back".
+  TestNavigationObserver navigation_observer(
+      ok_url(), TestNavigationObserver::NavigationEvent::Completion, shell());
+  ExecuteScript(shell(), "window.certificateErrorPageController.dontProceed();",
+                false /*use_separate_isolate*/);
+  navigation_observer.Wait();
+  EXPECT_FALSE(IsShowingSSLInterstitial(shell()->tab()));
 
   // Check that it's possible to navigate to a new page.
-  NavigateAndWaitForCompletion(initial_url, shell());
+  NavigateAndWaitForCompletion(https_server_->GetURL("/simple_page2.html"),
+                               shell());
+  EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
+}
+
+// Tests navigating away from the interstitial page.
+IN_PROC_BROWSER_TEST_F(SSLBrowserTest, NavigateAway) {
+  NavigateToOkPage();
+  NavigateToPageWithSslError();
+
+  NavigateAndWaitForCompletion(https_server_->GetURL("/simple_page2.html"),
+                               shell());
   EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab()));
 }
 
diff --git a/weblayer/public/javatests/BUILD.gn b/weblayer/public/javatests/BUILD.gn
new file mode 100644
index 0000000..9369d79
--- /dev/null
+++ b/weblayer/public/javatests/BUILD.gn
@@ -0,0 +1,16 @@
+# 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("//build/config/android/config.gni")
+import("//build/config/android/rules.gni")
+
+android_library("weblayer_public_javatests") {
+  testonly = true
+  java_files = [ "org/chromium/weblayer/ObserverListTest.java" ]
+  deps = [
+    "//base:base_java_test_support",
+    "//third_party/junit:junit",
+    "//weblayer/public/java",
+  ]
+}
diff --git a/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java b/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java
new file mode 100644
index 0000000..3534381
--- /dev/null
+++ b/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java
@@ -0,0 +1,332 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Tests for (@link ObserverList}.
+ */
+@RunWith(BaseJUnit4ClassRunner.class)
+public class ObserverListTest {
+    interface Observer {
+        void observe(int x);
+    }
+
+    private static class Foo implements Observer {
+        private final int mScalar;
+        private int mTotal;
+
+        Foo(int scalar) {
+            mScalar = scalar;
+        }
+
+        @Override
+        public void observe(int x) {
+            mTotal += x * mScalar;
+        }
+    }
+
+    /**
+     * An observer which add a given Observer object to the list when observe is called.
+     */
+    private static class FooAdder implements Observer {
+        private final ObserverList<Observer> mList;
+        private final Observer mLucky;
+
+        FooAdder(ObserverList<Observer> list, Observer oblivious) {
+            mList = list;
+            mLucky = oblivious;
+        }
+
+        @Override
+        public void observe(int x) {
+            mList.addObserver(mLucky);
+        }
+    }
+
+    /**
+     * An observer which removes a given Observer object from the list when observe is called.
+     */
+    private static class FooRemover implements Observer {
+        private final ObserverList<Observer> mList;
+        private final Observer mDoomed;
+
+        FooRemover(ObserverList<Observer> list, Observer innocent) {
+            mList = list;
+            mDoomed = innocent;
+        }
+
+        @Override
+        public void observe(int x) {
+            mList.removeObserver(mDoomed);
+        }
+    }
+
+    private static <T> int getSizeOfIterable(Iterable<T> iterable) {
+        if (iterable instanceof Collection<?>) return ((Collection<?>) iterable).size();
+        int num = 0;
+        for (T el : iterable) num++;
+        return num;
+    }
+
+    @Test
+    @SmallTest
+    public void testRemoveWhileIteration() {
+        ObserverList<Observer> observerList = new ObserverList<Observer>();
+        Foo a = new Foo(1);
+        Foo b = new Foo(-1);
+        Foo c = new Foo(1);
+        Foo d = new Foo(-1);
+        Foo e = new Foo(-1);
+        FooRemover evil = new FooRemover(observerList, c);
+
+        observerList.addObserver(a);
+        observerList.addObserver(b);
+
+        for (Observer obs : observerList) obs.observe(10);
+
+        // Removing an observer not in the list should do nothing.
+        observerList.removeObserver(e);
+
+        observerList.addObserver(evil);
+        observerList.addObserver(c);
+        observerList.addObserver(d);
+
+        for (Observer obs : observerList) obs.observe(10);
+
+        // observe should be called twice on a.
+        Assert.assertEquals(20, a.mTotal);
+        // observe should be called twice on b.
+        Assert.assertEquals(-20, b.mTotal);
+        // evil removed c from the observerList before it got any callbacks.
+        Assert.assertEquals(0, c.mTotal);
+        // observe should be called once on d.
+        Assert.assertEquals(-10, d.mTotal);
+        // e was never added to the list, observe should not be called.
+        Assert.assertEquals(0, e.mTotal);
+    }
+
+    @Test
+    @SmallTest
+    public void testAddWhileIteration() {
+        ObserverList<Observer> observerList = new ObserverList<Observer>();
+        Foo a = new Foo(1);
+        Foo b = new Foo(-1);
+        Foo c = new Foo(1);
+        FooAdder evil = new FooAdder(observerList, c);
+
+        observerList.addObserver(evil);
+        observerList.addObserver(a);
+        observerList.addObserver(b);
+
+        for (Observer obs : observerList) obs.observe(10);
+
+        Assert.assertTrue(observerList.hasObserver(c));
+        Assert.assertEquals(10, a.mTotal);
+        Assert.assertEquals(-10, b.mTotal);
+        Assert.assertEquals(0, c.mTotal);
+    }
+
+    @Test
+    @SmallTest
+    public void testIterator() {
+        ObserverList<Integer> observerList = new ObserverList<Integer>();
+        observerList.addObserver(5);
+        observerList.addObserver(10);
+        observerList.addObserver(15);
+        Assert.assertEquals(3, getSizeOfIterable(observerList));
+
+        observerList.removeObserver(10);
+        Assert.assertEquals(2, getSizeOfIterable(observerList));
+
+        Iterator<Integer> it = observerList.iterator();
+        Assert.assertTrue(it.hasNext());
+        Assert.assertTrue(5 == it.next());
+        Assert.assertTrue(it.hasNext());
+        Assert.assertTrue(15 == it.next());
+        Assert.assertFalse(it.hasNext());
+
+        boolean removeExceptionThrown = false;
+        try {
+            it.remove();
+            Assert.fail("Expecting UnsupportedOperationException to be thrown here.");
+        } catch (UnsupportedOperationException e) {
+            removeExceptionThrown = true;
+        }
+        Assert.assertTrue(removeExceptionThrown);
+        Assert.assertEquals(2, getSizeOfIterable(observerList));
+
+        boolean noElementExceptionThrown = false;
+        try {
+            it.next();
+            Assert.fail("Expecting NoSuchElementException to be thrown here.");
+        } catch (NoSuchElementException e) {
+            noElementExceptionThrown = true;
+        }
+        Assert.assertTrue(noElementExceptionThrown);
+    }
+
+    @Test
+    @SmallTest
+    public void testRewindableIterator() {
+        ObserverList<Integer> observerList = new ObserverList<Integer>();
+        observerList.addObserver(5);
+        observerList.addObserver(10);
+        observerList.addObserver(15);
+        Assert.assertEquals(3, getSizeOfIterable(observerList));
+
+        ObserverList.RewindableIterator<Integer> it = observerList.rewindableIterator();
+        Assert.assertTrue(it.hasNext());
+        Assert.assertTrue(5 == it.next());
+        Assert.assertTrue(it.hasNext());
+        Assert.assertTrue(10 == it.next());
+        Assert.assertTrue(it.hasNext());
+        Assert.assertTrue(15 == it.next());
+        Assert.assertFalse(it.hasNext());
+
+        it.rewind();
+
+        Assert.assertTrue(it.hasNext());
+        Assert.assertTrue(5 == it.next());
+        Assert.assertTrue(it.hasNext());
+        Assert.assertTrue(10 == it.next());
+        Assert.assertTrue(it.hasNext());
+        Assert.assertTrue(15 == it.next());
+        Assert.assertEquals(5, (int) observerList.mObservers.get(0));
+        observerList.removeObserver(5);
+        Assert.assertEquals(null, observerList.mObservers.get(0));
+
+        it.rewind();
+
+        Assert.assertEquals(10, (int) observerList.mObservers.get(0));
+        Assert.assertTrue(it.hasNext());
+        Assert.assertTrue(10 == it.next());
+        Assert.assertTrue(it.hasNext());
+        Assert.assertTrue(15 == it.next());
+    }
+
+    @Test
+    @SmallTest
+    public void testAddObserverReturnValue() {
+        ObserverList<Object> observerList = new ObserverList<Object>();
+
+        Object a = new Object();
+        Assert.assertTrue(observerList.addObserver(a));
+        Assert.assertFalse(observerList.addObserver(a));
+
+        Object b = new Object();
+        Assert.assertTrue(observerList.addObserver(b));
+        Assert.assertFalse(observerList.addObserver(null));
+    }
+
+    @Test
+    @SmallTest
+    public void testRemoveObserverReturnValue() {
+        ObserverList<Object> observerList = new ObserverList<Object>();
+
+        Object a = new Object();
+        Object b = new Object();
+        observerList.addObserver(a);
+        observerList.addObserver(b);
+
+        Assert.assertTrue(observerList.removeObserver(a));
+        Assert.assertFalse(observerList.removeObserver(a));
+        Assert.assertFalse(observerList.removeObserver(new Object()));
+        Assert.assertTrue(observerList.removeObserver(b));
+        Assert.assertFalse(observerList.removeObserver(null));
+
+        // If we remove an object while iterating, it will be replaced by 'null'.
+        observerList.addObserver(a);
+        Assert.assertTrue(observerList.removeObserver(a));
+        Assert.assertFalse(observerList.removeObserver(null));
+    }
+
+    @Test
+    @SmallTest
+    public void testSize() {
+        ObserverList<Object> observerList = new ObserverList<Object>();
+
+        Assert.assertEquals(0, observerList.size());
+        Assert.assertTrue(observerList.isEmpty());
+
+        observerList.addObserver(null);
+        Assert.assertEquals(0, observerList.size());
+        Assert.assertTrue(observerList.isEmpty());
+
+        Object a = new Object();
+        observerList.addObserver(a);
+        Assert.assertEquals(1, observerList.size());
+        Assert.assertFalse(observerList.isEmpty());
+
+        observerList.addObserver(a);
+        Assert.assertEquals(1, observerList.size());
+        Assert.assertFalse(observerList.isEmpty());
+
+        observerList.addObserver(null);
+        Assert.assertEquals(1, observerList.size());
+        Assert.assertFalse(observerList.isEmpty());
+
+        Object b = new Object();
+        observerList.addObserver(b);
+        Assert.assertEquals(2, observerList.size());
+        Assert.assertFalse(observerList.isEmpty());
+
+        observerList.removeObserver(null);
+        Assert.assertEquals(2, observerList.size());
+        Assert.assertFalse(observerList.isEmpty());
+
+        observerList.removeObserver(new Object());
+        Assert.assertEquals(2, observerList.size());
+        Assert.assertFalse(observerList.isEmpty());
+
+        observerList.removeObserver(b);
+        Assert.assertEquals(1, observerList.size());
+        Assert.assertFalse(observerList.isEmpty());
+
+        observerList.removeObserver(b);
+        Assert.assertEquals(1, observerList.size());
+        Assert.assertFalse(observerList.isEmpty());
+
+        observerList.removeObserver(a);
+        Assert.assertEquals(0, observerList.size());
+        Assert.assertTrue(observerList.isEmpty());
+
+        observerList.removeObserver(a);
+        observerList.removeObserver(b);
+        observerList.removeObserver(null);
+        observerList.removeObserver(new Object());
+        Assert.assertEquals(0, observerList.size());
+        Assert.assertTrue(observerList.isEmpty());
+
+        observerList.addObserver(new Object());
+        observerList.addObserver(new Object());
+        observerList.addObserver(new Object());
+        observerList.addObserver(a);
+        Assert.assertEquals(4, observerList.size());
+        Assert.assertFalse(observerList.isEmpty());
+
+        observerList.clear();
+        Assert.assertEquals(0, observerList.size());
+        Assert.assertTrue(observerList.isEmpty());
+
+        observerList.removeObserver(a);
+        observerList.removeObserver(b);
+        observerList.removeObserver(null);
+        observerList.removeObserver(new Object());
+        Assert.assertEquals(0, observerList.size());
+        Assert.assertTrue(observerList.isEmpty());
+    }
+}
diff --git a/weblayer/shell/android/BUILD.gn b/weblayer/shell/android/BUILD.gn
index be8f8e5..7028929 100644
--- a/weblayer/shell/android/BUILD.gn
+++ b/weblayer/shell/android/BUILD.gn
@@ -192,6 +192,7 @@
     "//content/public/test/android:content_java_test_support",
     "//net/android:net_java_test_support",
     "//third_party/android_support_test_runner:runner_java",
+    "//weblayer/public/javatests:weblayer_public_javatests",
   ]
   java_files = [
     "javatests/src/org/chromium/weblayer/test/DataClearingTest.java",
diff --git a/weblayer/test/BUILD.gn b/weblayer/test/BUILD.gn
index 262ab3c..db48205 100644
--- a/weblayer/test/BUILD.gn
+++ b/weblayer/test/BUILD.gn
@@ -94,6 +94,8 @@
     "interstitial_utils.h",
     "test_launcher_delegate_impl.cc",
     "test_launcher_delegate_impl.h",
+    "test_navigation_observer.cc",
+    "test_navigation_observer.h",
     "weblayer_browser_test.cc",
     "weblayer_browser_test.h",
     "weblayer_browser_test_test.cc",
diff --git a/weblayer/test/test_navigation_observer.cc b/weblayer/test/test_navigation_observer.cc
new file mode 100644
index 0000000..00de95c5
--- /dev/null
+++ b/weblayer/test/test_navigation_observer.cc
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "weblayer/test/test_navigation_observer.h"
+
+#include "base/test/bind_test_util.h"
+#include "url/gurl.h"
+#include "weblayer/public/navigation.h"
+#include "weblayer/public/navigation_controller.h"
+#include "weblayer/public/tab.h"
+#include "weblayer/shell/browser/shell.h"
+
+namespace weblayer {
+
+TestNavigationObserver::TestNavigationObserver(const GURL& url,
+                                               NavigationEvent target_event,
+                                               Shell* shell)
+    : url_(url), target_event_(target_event), tab_(shell->tab()) {
+  tab_->GetNavigationController()->AddObserver(this);
+}
+
+TestNavigationObserver::~TestNavigationObserver() {
+  tab_->GetNavigationController()->RemoveObserver(this);
+}
+
+void TestNavigationObserver::NavigationCompleted(Navigation* navigation) {
+  if (navigation->GetURL() == url_)
+    observed_event_ = NavigationEvent::Completion;
+  CheckNavigationCompleted();
+}
+
+void TestNavigationObserver::NavigationFailed(Navigation* navigation) {
+  if (navigation->GetURL() == url_)
+    observed_event_ = NavigationEvent::Failure;
+  CheckNavigationCompleted();
+}
+
+void TestNavigationObserver::LoadStateChanged(bool is_loading,
+                                              bool to_different_document) {
+  done_loading_ = !is_loading;
+  CheckNavigationCompleted();
+}
+
+void TestNavigationObserver::CheckNavigationCompleted() {
+  if (done_loading_ && observed_event_ == target_event_)
+    run_loop_.Quit();
+}
+
+void TestNavigationObserver::Wait() {
+  run_loop_.Run();
+}
+
+}  // namespace weblayer
diff --git a/weblayer/test/test_navigation_observer.h b/weblayer/test/test_navigation_observer.h
new file mode 100644
index 0000000..fbfddee8
--- /dev/null
+++ b/weblayer/test/test_navigation_observer.h
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBLAYER_TEST_TEST_NAVIGATION_OBSERVER_H_
+#define WEBLAYER_TEST_TEST_NAVIGATION_OBSERVER_H_
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/run_loop.h"
+#include "url/gurl.h"
+#include "weblayer/public/navigation_observer.h"
+
+namespace weblayer {
+
+class Shell;
+class Tab;
+
+// A helper that waits for a navigation to finish.
+class TestNavigationObserver : public NavigationObserver {
+ public:
+  enum class NavigationEvent { Completion, Failure };
+
+  // Creates an instance that begins waiting for a Navigation within |shell| and
+  // to |url| to either complete or fail as per |target_event|.
+  TestNavigationObserver(const GURL& url,
+                         NavigationEvent target_event,
+                         Shell* shell);
+  ~TestNavigationObserver() override;
+
+  // Spins a RunLoop until the requested type of navigation event is observed.
+  void Wait();
+
+ private:
+  // NavigationObserver implementation:
+  void NavigationCompleted(Navigation* navigation) override;
+  void NavigationFailed(Navigation* navigation) override;
+  void LoadStateChanged(bool is_loading, bool to_different_document) override;
+
+  void CheckNavigationCompleted();
+
+  const GURL url_;
+  base::Optional<NavigationEvent> observed_event_;
+  NavigationEvent target_event_;
+  Tab* tab_;
+  bool done_loading_ = false;
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestNavigationObserver);
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_TEST_TEST_NAVIGATION_OBSERVER_H_
diff --git a/weblayer/test/weblayer_browser_test_utils.cc b/weblayer/test/weblayer_browser_test_utils.cc
index f5891bc3..19b69f4 100644
--- a/weblayer/test/weblayer_browser_test_utils.cc
+++ b/weblayer/test/weblayer_browser_test_utils.cc
@@ -4,98 +4,37 @@
 
 #include "weblayer/test/weblayer_browser_test_utils.h"
 
-#include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "url/gurl.h"
-#include "weblayer/public/navigation.h"
 #include "weblayer/public/navigation_controller.h"
-#include "weblayer/public/navigation_observer.h"
 #include "weblayer/public/tab.h"
 #include "weblayer/shell/browser/shell.h"
+#include "weblayer/test/test_navigation_observer.h"
 
 namespace weblayer {
 
 namespace {
 
-// Runs |closure| once |url| is successfully navigated to.
-class TestNavigationObserver : public NavigationObserver {
- public:
-  enum class NavigationEventToObserve { Completion, Failure };
-
-  TestNavigationObserver(base::OnceClosure closure,
-                         const GURL& url,
-                         NavigationEventToObserve event,
-                         Shell* shell)
-      : closure_(std::move(closure)),
-        url_(url),
-        event_(event),
-        tab_(shell->tab()) {
-    tab_->GetNavigationController()->AddObserver(this);
-  }
-
-  ~TestNavigationObserver() override {
-    tab_->GetNavigationController()->RemoveObserver(this);
-  }
-
- private:
-  // NavigationObserver implementation:
-  void NavigationCompleted(Navigation* navigation) override {
-    if (navigation->GetURL() == url_ &&
-        event_ == NavigationEventToObserve::Completion) {
-      navigation_complete_ = true;
-      CheckComplete();
-    }
-  }
-
-  void NavigationFailed(Navigation* navigation) override {
-    if (navigation->GetURL() == url_ &&
-        event_ == NavigationEventToObserve::Failure) {
-      std::move(closure_).Run();
-    }
-  }
-
-  void LoadStateChanged(bool is_loading, bool to_different_document) override {
-    done_loading_ = !is_loading;
-    CheckComplete();
-  }
-
-  void CheckComplete() {
-    if (done_loading_ && navigation_complete_)
-      std::move(closure_).Run();
-  }
-
-  base::OnceClosure closure_;
-  const GURL url_;
-  NavigationEventToObserve event_;
-  Tab* tab_;
-  bool done_loading_ = false;
-  bool navigation_complete_ = false;
-};
-
 // Navigates to |url| in |shell| and waits for |event| to occur.
-void NavigateAndWaitForEvent(
-    const GURL& url,
-    Shell* shell,
-    TestNavigationObserver::NavigationEventToObserve event) {
-  base::RunLoop run_loop;
-  TestNavigationObserver test_observer(run_loop.QuitClosure(), url, event,
-                                       shell);
-
+void NavigateAndWaitForEvent(const GURL& url,
+                             Shell* shell,
+                             TestNavigationObserver::NavigationEvent event) {
+  TestNavigationObserver test_observer(url, event, shell);
   shell->tab()->GetNavigationController()->Navigate(url);
-  run_loop.Run();
+  test_observer.Wait();
 }
 
 }  // namespace
 
 void NavigateAndWaitForCompletion(const GURL& url, Shell* shell) {
-  NavigateAndWaitForEvent(
-      url, shell, TestNavigationObserver::NavigationEventToObserve::Completion);
+  NavigateAndWaitForEvent(url, shell,
+                          TestNavigationObserver::NavigationEvent::Completion);
 }
 
 void NavigateAndWaitForFailure(const GURL& url, Shell* shell) {
-  NavigateAndWaitForEvent(
-      url, shell, TestNavigationObserver::NavigationEventToObserve::Failure);
+  NavigateAndWaitForEvent(url, shell,
+                          TestNavigationObserver::NavigationEvent::Failure);
 }
 
 base::Value ExecuteScript(Shell* shell,